Tkinter GUI布局 - 使用框架和网格

40

我的GUI布局

gui layout

看起来与我预期的几乎不一样

what I expect

所以我认为有一些基础知识我不明白。

我以为框架包含自己的“网格空间”(行、列),但我看到的行为并不支持这个想法,而且我迷失在了为了让顶部的框架按照自己的意愿工作而尝试各种方法。我的标签应该在同一行从左到右排列,在整个框架下面有一个“框架标签”,但它们并没有如此。我希望实际效果更像目标图片,并使用网格来实现它。

你可以看到绿色框架右边的一个条目字段。为什么会在那里?

from Tkinter import *

root = Tk()
root.title('Model Definition')
root.resizable(width=FALSE, height=FALSE)
root.geometry('{}x{}'.format(460, 350))

top_frame = Frame(root, bg='cyan', width = 450, height=50, pady=3).grid(row=0, columnspan=3)
Label(top_frame, text = 'Model Dimensions').grid(row = 0, columnspan = 3)
Label(top_frame, text = 'Width:').grid(row = 1, column = 0)
Label(top_frame, text = 'Length:').grid(row = 1, column = 2)
entry_W = Entry(top_frame).grid(row = 1, column = 1)
entry_L = Entry(top_frame).grid(row = 1, column = 3)
#Label(top_frame, text = '').grid(row = 2, column = 2)

center = Frame(root, bg='gray2', width=50, height=40, padx=3, pady=3).grid(row=1, columnspan=3)
ctr_left = Frame(center, bg='blue', width=100, height=190).grid(column = 0, row = 1, rowspan = 2)
ctr_mid = Frame(center, bg='yellow', width=250, height=190, padx=3, pady=3).grid(column = 1, row=1, rowspan=2)
ctr_right = Frame(center, bg='green', width=100, height=190, padx=3, pady=3).grid(column = 2, row=1, rowspan=2)

btm_frame = Frame(root, bg='white', width = 450, height = 45, pady=3).grid(row = 3, columnspan = 3)
btm_frame2 = Frame(root, bg='lavender', width = 450, height = 60, pady=3).grid(row = 4, columnspan = 3)


root.mainloop()

那么具体来说,我的标签和输入框去哪了?如何让它们看起来更像目标(顶部的框架,其余部分留到以后处理)。


如果这两个图像使用相同的颜色方案,那么它会稍微有些帮助,除非它们是一样的,并且您正在说蓝色窗口实际上应该在底部,即使代码将其放在左侧。 - Bryan Oakley
2个回答

73

我假设框架包含它们自己的“网格空间”

这是一个正确的假设。

你可以在绿色框架右侧看到其中一个输入字段。它为什么会去那里?

问题从这里开始:

top_frame = Frame(root, ...).grid(row=0, ...)
在Python中,x = y().z()语句总是将x设置为.z()的结果。对于top_frame = Frame(...).grid(...)这种情况,grid(...)总是返回None,因此top_frame将是None。这导致您认为放置在顶部框架中的每个小部件实际上都放在根窗口中。
解决方案概述
作为一般准则,您永远不应在创建小部件的相同语句中调用gridpackplace。部分原因是由于您正在经历的确切行为,但还因为我认为它使您的代码更难编写并且难以随着时间的推移进行维护。
小部件创建和小部件布局是两个不同的事物。根据我的经验,当您将布局命令组合在一起时,排列问题要容易得多。
此外,您在使用grid时应保持一致,并始终以相同的顺序放置选项,以便更轻松地可视化布局。最后,在使用grid时,您应该养成始终指定sticky选项的习惯,并始终在每个包含框中给一个行和一列一个非零权重。
解决方案示例
以下是我将如何编写您的代码。它更长,但更容易理解。
from Tkinter import *

root = Tk()
root.title('Model Definition')
root.geometry('{}x{}'.format(460, 350))

# create all of the main containers
top_frame = Frame(root, bg='cyan', width=450, height=50, pady=3)
center = Frame(root, bg='gray2', width=50, height=40, padx=3, pady=3)
btm_frame = Frame(root, bg='white', width=450, height=45, pady=3)
btm_frame2 = Frame(root, bg='lavender', width=450, height=60, pady=3)

# layout all of the main containers
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)

top_frame.grid(row=0, sticky="ew")
center.grid(row=1, sticky="nsew")
btm_frame.grid(row=3, sticky="ew")
btm_frame2.grid(row=4, sticky="ew")

# create the widgets for the top frame
model_label = Label(top_frame, text='Model Dimensions')
width_label = Label(top_frame, text='Width:')
length_label = Label(top_frame, text='Length:')
entry_W = Entry(top_frame, background="pink")
entry_L = Entry(top_frame, background="orange")

# layout the widgets in the top frame
model_label.grid(row=0, columnspan=3)
width_label.grid(row=1, column=0)
length_label.grid(row=1, column=2)
entry_W.grid(row=1, column=1)
entry_L.grid(row=1, column=3)

# create the center widgets
center.grid_rowconfigure(0, weight=1)
center.grid_columnconfigure(1, weight=1)

ctr_left = Frame(center, bg='blue', width=100, height=190)
ctr_mid = Frame(center, bg='yellow', width=250, height=190, padx=3, pady=3)
ctr_right = Frame(center, bg='green', width=100, height=190, padx=3, pady=3)

ctr_left.grid(row=0, column=0, sticky="ns")
ctr_mid.grid(row=0, column=1, sticky="nsew")
ctr_right.grid(row=0, column=2, sticky="ns")

root.mainloop()

结果:

运行示例的截图


哇喔 - 非常非常感谢。我会仔细检查的。我同意颜色方案的评论,我没有注意到得足够仔细。 - shawn
我发现在Python 2.7或3.5中,将脚本中所有四个神秘的rowconfigurecolumnconfigure命令注释掉对结果没有影响(对窗口及其框架限制的外观)。 - Mike O'Connor
2
@MikeO'Connor:当你手动调整窗口大小时,你会立刻注意到有所不同。如果没有rowconfigurecolumnconfigure命令,窗口将无法正确地调整大小。 - Bryan Oakley
啊哈!感谢您的回信。我之前对于中心框架有一个初始高度为40的赋值感到困惑,但是使用rowconfigurecolumnconfigure后,中心框架的高度却变得更高了...而顶部和底部框架似乎保持了它们的赋值高度。但现在我明白了,实际上是中心框架的赋值高度有效地设置了中心框架的初始高度。 - Mike O'Connor
@Bryan Oakley:非常好的答案,也是tkinter项目的一个很好的模板! - paddyr

7

variable = Widget(...).grid()None赋值给变量,因为grid()/pack()/place()返回None

使用:

variable = Widget(...)
variable.grid() # .pack() .place()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接