Tkinter toplevel: Opening treeview to fit the frame - python

Am having challenge letting my treeview fill the toplevel window have created have tried fill= "x" and fill="both" but not getting the result.Any suggestion to do this.
from tkinter import ttk
import tkinter as tk
def my_treeview():
mt = tk.Toplevel()
mt.geometry("1000x580")
tree = ttk.Treeview(mt)
tree.insert("", "0", "item1", text="fill width")
tree.insert("", "1", "item2", text="fill height")
tree.pack(fill="both")
root = tk.Tk()
root.geometry("400x400")
treeview = tk.Button(root, text="open treeview", command=my_treeview).pack()
root.mainloop()

fill="both" means "fill all the area that has been allocated to you". It is doing just that. The treeview widget has a certain height it wants to be, so pack allocates just enough space for it to fit. That leaves a lot of extra space that has not been allocated.
If you want the treeview to expand to fill all remaining space and not just the space that it needs, use the expand option in addition to the fill option.
Example:
tree.pack(fill="both", expand=True)

Related

Why does "ttk.style()" adds additional styles to the Entry widget?

So, I have 4 Entry Widgets on my window and I just wanted to add some internal left padding on the last Entry Widget. I did so using ttk.style(), which added the desired padding but it also added some additional styling like a black border, some hover effect, then the entry widget gets a blue border when selected.
This is my Code:
from tkinter import *
from tkinter import ttk
root = Tk()
root.configure(padx=50)
input1 = Entry(root)
input1.grid(row=1, column=0, pady=10)
input2 = Entry(root)
input2.grid(row=2, column=0, pady=10)
input3 = Entry(root)
input3.grid(row=3, column=0, pady=10)
style = ttk.Style(root)
style.configure('padded.TEntry', padding=[15, 0, 0, 0])
e = ttk.Entry(root, style='padded.TEntry')
e.grid(row=4,column=0, pady=10)
root.mainloop()
Look how the 4th Entry Widget has a Black Border around it
Look how a Blue Border appears when the widget is selected
The only styling that I was excepting is the little increase in width because of the left-padding, but how are these other styles being triggered.
It is because the fourth entry is a ttk.Entry widget but the other three are tkinter.Entry widgets. If you make all four ttk.Entry widgets you'll see that they all have the additional styles.
Even though the tkinter and ttk modules have widgets with the same name, they are completely different widgets with their own sets of defaults.

My frame is resizing instead of the listbox - tkinter

I'm making a GUI and I'm stuck trying to resize a listbox. The listbox is supposed to expand to fill the frame but instead, the frame shrinks to fit the listbox.
Thanks, in advance, for any help.
I have tried many variations of the code, but none seem to work, so I simplified the code (it still does't work) to put it on here.
import tkinter as tk
w = tk.Tk() # New window
f = tk.Frame(w, width=300, height=500, bg='red') # New frame with specific size
f.grid_propagate(0)
f.grid(row=0, column=0)
lb = tk.Listbox(f, bg='blue') # New listbox
lb.pack(fill=tk.BOTH, expand=True)
I ran these sequentially in IDLE and the frame appears (in red) at the correct size, however, when I pack the listbox, the whole window shrinks to the size of the listbox (and turns completely blue, which is expected).
In my experience, turning off geometry propagation is almost never the right solution. With a couple of decades of using tk and tkinter I've done that only two or three times for very specific edge cases.
Tkinter is very good at making the widget the best size based on the set of widgets you're using. Turning off propagation means you are responsible for doing all calculations to get the window to look right, and your calculations may not be correct when your program runs on a machine with different fonts or a different resolution. Tkinter can handle all of that for you.
Unfortunately, with such a small code example and without knowing your end goal it's hard to solve your layout problems. If your goal is to have a window that is 300x500, the best solution is to make the window that size and then have the frame fill the window, which is easier to do with pack than grid:
import tkinter as tk
w = tk.Tk() # New window
w.geometry("300x500")
f = tk.Frame(w, bg='red') # New frame with specific size
f.pack(fill="both", expand=True)
lb = tk.Listbox(f, bg='blue') # New listbox
lb.pack(fill=tk.BOTH, expand=True)
w.mainloop()
Thank you so much to Tls Chris for your answer and explanation. What I didn't realize is that grid_propagate() and pack_propagate() also affect a widget's children.
Therefore, with the help of Tls Chris, I have fixed my code and it now expands the listbox and doesn't shrink the frame.
Fixed code:
import tkinter as tk
w = tk.Tk() # New window
f = tk.Frame(w, width=300, height=500, bg='red') # New frame
f.grid_propagate(False) # Stopping things (that use grid) resizing the frame
f.pack_propagate(False) # Stopping things (that use pack) resizing the frame
f.grid(row=0, column=0)
lb = tk.Listbox(f, bg='blue') # New listbox
lb.pack(fill=tk.BOTH, expand=True) # Packing the listbox and making it expand and fill the frame
Edit: I think you actually want the listbox to expand to fit the frame. Amended the code to do that as an option
I haven't used pack much but I suspect that grid_propagate changes the behaviour of the grid geometry manager but not of the pack manager.
The below lets app() run with or without propagate set. It uses the grid geometry manager throughout.
import tkinter as tk
def app(propagate = False, expand = False ):
w = tk.Tk() # New window
tk.Label( w, text = 'Propagate: {} \nExpand: {}'.format(propagate, expand) ).grid()
f = tk.Frame(w, width=300, height=500, bg='red') # New frame with specific size
f.grid_propagate(propagate)
f.grid( row=1, column=0 )
lb = tk.Listbox(f, bg='blue') # New listbox
if expand:
f.columnconfigure(0, weight = 1 )
f.rowconfigure(0, weight = 1 )
# lb.pack(fill=tk.BOTH, expand=True)
lb.grid( row=0, column=0, sticky = 'nsew' )
# My guess is that grid_propagate has changed the behaviour of grid, not of pack.
lb.insert(tk.END, 'Test 1', 'Test 2', 'Test 3')
w.mainloop()
This changes the listbox geometry manager to grid. Run app() as below.
app(True, True) # propagate and Expand
app(False, True) # no propagate but expand
app(True, False) # propagate without expand
app() # no propagate or expand

Adjust height of a row in Tkinter Treeview depending on wraped strings

I was making a treeview for my tkinter program, and when I insert the data into it, there are some rows with too many characters and some of them are unseen.
Is there any way to set an auto line feed for the row height? I don't want to add a horizontal scrollbar to solve this. This means when the width of a strings is bigger then th columns with a newline should be iserted (automaticly). In that case the height of the row should be adjusted (automaicly) in that case to display more then one line.
from tkinter import *
import tkinter.ttk as ttk
root = Tk()
frame = Frame(root)
frame.pack()
tree = ttk.Treeview(frame, height=15,
columns=('c1', 'c2'),
show="headings")
tree.column('c1', width=100, anchor='center')
tree.column('c2', width=200, anchor='center')
tree.heading('c1', text='text1')
tree.heading('c2', text='text2')
tree.pack()
tree.insert('', 1, values=['edew', 'dewd'])
tree.insert('', 2, values=['abcd'*10, 'wxyz'*10])
root.resizable(width=False, height=False)
root.mainloop()
The height of alls row can be modified with this code.
root = Tk()
style = ttk.Style(root)
style.configure('Treeview', rowheight=45)
It would be very nice to know if each row in a Treeview can have it's own style to defined individual rowheight (depending on newline characters).
In response to Aleksandar Beat's question:
In order to change the style of a specific Treeview, you have to define a different identifier before the ttk widget name:
Style().configure('AnythingYouWantHere.Treeview', rowheight=45)
# The '.Treeview' can not be changed
However, if you do it this way, you have to specify the style option of the ttk widget.
Sample usage:
root = Tk()
Style().configure('MyStyle1.Treeview', rowheight=45)
styledTreeView = Treeview(root, style='MyStyle1.Treeview')
normalTreeView = Treeview(root) # Style will not automatically apply
This also applies to any ttk widgets.

get default background of ttk.Frame

I combined a scrollbar with a ttk notebook by adapting this example and additionally porting it to Python3.
I am using ttk widgets as often as possible to get a more 'modern' UI. However, there is no ttk canvas widget, so I used the standard tkinter canvas. By default the backgroundcolor of the canvas widget seems to be white (at least on Mac OS), while the default background of all ttk widgets is grey (see screenshot below).
How can I get the default background of the ttk frame containing the label widgets, so that I can pass it to the canvas by Canvas(parent, background='bg_color') and remove the white space around the ttk frame containing the ttk labels?
I know that ttk uses styles to define the look of the widgets. However, I have no idea about how to read the background value of the (default) style.
#!/usr/bin/env python3
# coding: utf-8
from tkinter import *
from tkinter import ttk
master = Tk()
def populate():
"""Put in some fake data"""
for row in range(100):
ttk.Label(frame, text="%s" % row, width=3, borderwidth="1", relief="solid").grid(row=row, column=0)
t="this is the second column for row %s" % row
ttk.Label(frame, text=t).grid(row=row, column=1)
def OnFrameConfigure(event):
"""Reset the scroll region to encompass the inner frame"""
canvas.configure(scrollregion=canvas.bbox("all"))
nbook = ttk.Notebook(master)
nbook.grid()
tab = Frame(nbook)
canvas = Canvas(tab, borderwidth=0)
frame = ttk.Frame(canvas)
vsb = Scrollbar(tab, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
canvas.pack(side='left', fill='both', expand=True)
canvas.create_window((0, 0), window=frame, anchor='nw', tags='frame')
frame.bind("<Configure>", OnFrameConfigure)
populate()
nbook.add(tab, text='Test')
master.mainloop()
You can use s.lookup() to get settings of a style. If you want the background setting of your ttk Frame use:
s = ttk.Style()
bg = s.lookup('TFrame', 'background')
Which you can apply to your Canvas:
canvas = Canvas(tab, borderwidth=0, background=bg)
I came across, a similar problem. Setting the background colour to 'systemWindowBody' does not do anything.
Upon searching further, I came across this website with tkinter colors (http://www.science.smith.edu/dftwiki/index.php/Color_Charts_for_TKinter)
I found the closest match to Mac OS grey as 'gray93'. Maybe that helps you achieve the same colour.
For anyone curious about how to just get the color of a frame (or widget), you can use the following line
widget.cget('bg')
where widget is the widget from which you want to retrieve the background color.
Thus, for a frame you can do:
your_frame.cget('bg')

Python: Listbox without border

I want to get a table, but when I realized, that there's no table in python, I decided to fix it with two listboxes.
The point is, that I don't want a border between them.
So my Question is: How to remove the border from the Tk Listbox in Python?
Even if it'll become white I had a solution...
You want to set the borderwidth to zero, but you also want to set highlightthickness to zero. Highlightthickness represents a rectangle that surrounds the whole widget when it has keyboard focus. Finally, when you use pack or grid, make sure you don't add any padding between them.
If you want to complete the illusion that the two widgets are one, put them in a frame and give the frame a borderwidth and relief.
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent, borderwidth=1, relief="sunken")
lb1 = tk.Listbox(self, borderwidth=0, highlightthickness=0)
lb2 = tk.Listbox(self, borderwidth=0, highlightthickness=0)
lb1.pack(side="left", fill="both", expand=True)
lb2.pack(side="left", fill="both", expand=True)
lb1.insert(0, "left")
lb2.insert(0, "right")
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True, padx=8, pady=8)
root.mainloop()
I think the best you can achieve would be this:
import tkinter as tk
root = tk.Tk()
wrapper = tk.Frame(root, bd=2, relief="sunken")
L1 = tk.Listbox(wrapper, bd=0)
L2 = tk.Listbox(wrapper, bd=0)
L1.grid(column = 1, row = 1)
L2.grid(column = 2, row = 1)
wrapper.pack()
root.mainloop()
note setting the border of each listbox to 0, (bd=0) and to give the overall widget a similar look to the original listbox i've wrapped it in a frame and given that the same border style as the default listbox.
also worth nothing that you need to get the bindings right to make them scroll as expected, just binding to the scroll wheel and scroll bar is insufficient as the lists can be moved with the arrow keys when an item is highlighted, like in the second example on this page:
scrolling multiple listboxes together
by Santiclause
Speicfy borderwidth as 0 when you create a listbox.
For example:
from Tkinter import * # from tkinter import * (Python 3.x)
root = Tk()
lb = Listbox(borderwidth=0) # <---
lb.pack()
root.mainloop()

Categories

Resources