关于python:Tkinter网格间距问题

Tkinter grid spacing problems

我正在尝试了解tk网格布局的工作原理,因为该界面并不符合我的预期。 我正在尝试在同一行上放置一个标签,后跟2个按钮,并在下一行超出其标签和按钮宽度的下一行上放置一个树状视图。 使它看起来像我想要的唯一方法是,如果我在Treeview的columnspan中使用了巨大的值。 这是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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()

当columnpan为3时,第一行在标签和按钮之间有很多空格。 当columnpan为100时,标签和按钮的间距看起来要好得多,但是我知道这不是正确的方法。 正确的方法是什么?


在这个小程序中,您有几件事阴谋反对您。例如,frame设置为None,因此实际上是将所有这些小部件放置在根窗口中而不是框架中。这是因为x=y().z()始终将x设置为z的结果,而grid()始终返回None

其次,grid的一个好的经验法则是,您需要给至少一行(通常恰好是一行)和一行赋予权重,以便tkinter知道如何分配额外的空间。您还需要使用sticky选项,以便使窗口小部件展开以填充为其分配的空间。您没有使用这两种技术。

第三,根据我的经验,当您的布局语句分散在整个代码中时,我认为很难调试布局问题。最好将它们一起分组,以便您可以更轻松地可视化布局。

grid解决问题

您可以通过将第3列的权重设置为1,然后使treeview跨越该列来解决您的问题。这样可以防止按钮所在的列扩展。如果还将frame设为None来解决问题,并且使用适当的sticky选项,则可以得到所需的外观。

这是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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()

使用pack的替代解决方案

话虽这么说,我认为有比尝试使所有内容都适合网格更好的解决方案。我的理念是为工作使用正确的工具,在这种情况下,正确的工具是pack,因为它擅长于从上到下或从左到右堆叠(反之亦然)。

在您的情况下,您的程序中有两个不同的区域:顶部是工具栏,下面是树形视图。既然这就是您的全部,那么使用pack将一个放置在另一个之上是很有意义的。因此,我将从创建两个框架并将其打包开始:

1
2
3
4
5
toolbar = ttk.Frame(root)
treeframe = ttk.Frame(root)

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

现在,您在toolbar中放入的任何内容都不会影响treeframe中的内容,反之亦然。您可以自由地在每个框架中进行所需的操作。随着程序的增长,您会发现拥有不同的区域使布局问题更容易解决。

由于工具栏包含左对齐的按钮,因此您也可以在其中使用pack。只要确保它们的父级是工具栏框架,就可以像这样打包它们:

1
2
3
4
5
6
7
label1 = tk.Label(toolbar, ...)
button1 = tk.Button(toolbar, ...)
button2 = tk.Button(toolbar, ...)

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

最后,如果底部只有一个树形视图(即:没有滚动条或其他小部件),则可以使用pack。确保其父级为treeframe。如果需要,可以使用grid,但是pack更为简单。

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

pack的优点是您可以将所有内容真正地放在一行上。您不必采取额外的步骤即可为行或列赋予权重。

这是一个完整的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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()