简体   繁体   中英

Tkinter grid spacing problems

I'm trying to understand how tk grid layouts work since the interface isn't looking the way I thought it would. I'm trying to place a label followed by 2 buttons on the same row, and a treeview on the next row that spans beyond the width of the label and buttons. The only way to get this to look how I want is if I use a huge value for the treeview's columnspan. Here is my code:

import tkinter as tk
from tkinter import ttk

root = tk.Tk()

columnHeadings = ("Heading 1", "Heading 2")

def printMsg():
    print("Ok")

frame = ttk.Frame(root).grid(row=0, column=0)

label1 = tk.Label(frame, text="Label here").grid(row=0, column=0, columnspan=1)
button1 = tk.Button(frame, text="Yes", width=2, command=printMsg).grid(row=0, column=1)
button2 = tk.Button(frame, text="No", width=2, command=printMsg).grid(row=0, column=2)
#Label and buttons too far apart
#treeview1 = ttk.Treeview(frame, columns=columnHeadings, show='headings').grid(row=1,column=0, columnspan=3)

#Right distance but that's a huge columnspan
treeview1 = ttk.Treeview(frame, columns=columnHeadings, show='headings').grid(row=1,column=0, columnspan=100)

root.mainloop()

When columnspan is 3, the first row has a lot of spaces between the label and buttons. When columnspan is 100, the label and button spacing looks a lot better but I know this isn't the correct way. What's the correct way to do this?

You have several things conspiring against you in this little program. For one, frame is set to None , so you are actually putting all these widgets in the root window rather than the frame. This is because x=y().z() always sets x to the result of z , and grid() always returns None .

Second, a good rule of thumb for grid is that you need to give at least one (and usually exactly one) row and one column to have a weight, so that tkinter knows how to allocate extra space. You also need to use the sticky option so that your widgets expand to fill the space that's been given them. You are using neither of these techniques.

Third, in my experience I think it makes it very hard to debug layout problems when your layout statements are scattered throughout your code. It's best to group them altogether so you can more easily visualize your layout.

Solving the problem with grid

You can solve your problem by giving column 3 a weight of 1, and then having your treeview span that column. This prevents the columns that your buttons are in from expanding. If you also fix the problem with frame being None , and if you use the appropriate sticky options, you can get the look you want.

Here's an example:

import tkinter as tk
from tkinter import ttk

root = tk.Tk()

columnHeadings = ("Heading 1", "Heading 2")

def printMsg():
    print("Ok")

frame = tk.Frame(root)
frame.grid(row=0, column=0, sticky="nsew")
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)

label1 = tk.Label(frame, text="Label here")
button1 = tk.Button(frame, text="Yes", width=2, command=printMsg)
button2 = tk.Button(frame, text="No", width=2, command=printMsg)
treeview1 = ttk.Treeview(frame, columns=columnHeadings, show='headings')

label1.grid(row=0, column=0, columnspan=1)
button1.grid(row=0, column=1)
button2.grid(row=0, column=2)
treeview1.grid(row=1,column=0, columnspan=4, sticky="nsew")

frame.grid_columnconfigure(3, weight=1)
frame.grid_rowconfigure(1, weight=1)

root.mainloop()

An alternate solution, using pack

All that being said, I think there are better solutions than to try to get everything to fit in a grid. My philosophy is to use the right tool for the job, and in this case the right tool is pack because it excels at stacking things top-to-bottom OR left-to-right (and visa versa).

In your case, you have two distinct regions in your program: a toolbar across the top, and a treeview down below. Since that's all you have, it makes sense to use pack , to place one on top of the other. So I would start by creating two frames and packing them:

toolbar = ttk.Frame(root)
treeframe = ttk.Frame(root)

toolbar.pack(side="top", fill="x")
treeframe.pack(side="bottom", fill="both", expand=True)

Now, anything you put in toolbar will not affect things in treeframe and visa versa. You are free to do whatever you want in each of those frames. As your program grows, you'll find that having distinct regions makes layout problems much easier to solve.

Since the toolbar contains buttons that are left-justified, you can use pack there too. Just make sure their parent is the toolbar frame, and you can pack them like this:

label1 = tk.Label(toolbar, ...)
button1 = tk.Button(toolbar, ...)
button2 = tk.Button(toolbar, ...)

label1.pack(side="left")
button1.pack(side="left")
button2.pack(side="left")

Finally, if you only have a treeview in the bottom (ie: no scrollbars or other widgets), you can use pack . Make sure it's parent is treeframe . You can use grid if you want, but pack is a bit more straight-forward.

treeview1.pack(fill="both", expand=True)

The nice thing about pack is you can truly put everything on one line. You don't have to take the extra step and give a row or column a weight.

Here's a complete example:

import tkinter as tk
from tkinter import ttk

root = tk.Tk()

columnHeadings = ("Heading 1", "Heading 2")

def printMsg():
    print("Ok")

toolbar = ttk.Frame(root)
treeframe = ttk.Frame(root)

toolbar.pack(side="top", fill="x")
treeframe.pack(side="bottom", fill="both", expand=True)

label1 = tk.Label(toolbar, text="Label here")
button1 = tk.Button(toolbar, text="Yes", width=2, command=printMsg)
button2 = tk.Button(toolbar, text="No", width=2, command=printMsg)

label1.pack(side="left")
button1.pack(side="left")
button2.pack(side="left")

treeview1 = ttk.Treeview(treeframe, columns=columnHeadings, show='headings')

treeview1.pack(side="top", fill="both", expand=True)

root.mainloop()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM