我正在为软件构建GUI并希望实现这一目标:
######################################
# | some title #
# menu upper |----------------------#
# | #
# | CANVAS #
# menu lower | #
# | #
#------------------------------------#
# statusbar #
######################################
菜单上方具有一些高级功能,菜单下限正在根据用户输入而改变。状态栏经常更改其内容。
不幸的是,Tkinter拒绝工作。
使用网格布局管理器我无法创建稳定的设计,并在左侧的菜单中添加标签和按钮等内容:
self.root = tk.Tk()
self.root.resizable(width=0, height=0)
self.root.title("some application")
# menu left
self.menu_left = tk.Frame(self.root, width=150, bg="#ababab")
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="ns")
self.menu_left_upper = tk.Frame(self.menu_left, width=150, height=150, bg="red")
self.menu_left_upper.grid(row=0, column=0)
# this label breaks the design
#self.test = tk.Label(self.menu_left_upper, text="test")
#self.test.pack()
self.menu_left_lower = tk.Frame(self.menu_left, width=150, bg="blue")
self.menu_left_lower.grid(row=1, column=0)
# right area
self.some_title_frame = tk.Frame(self.root, bg="#dfdfdf")
self.some_title_frame.grid(row=0, column=1, sticky="we")
self.some_title = tk.Label(self.some_title_frame, text="some title", bg="#dfdfdf")
self.some_title.pack()
self.canvas_area = tk.Canvas(self.root, width=500, height=400, background="#ffffff")
self.canvas_area.grid(row=1, column=1)
self.root.mainloop()
此设计在左侧菜单中没有内容的情况下工作。每当我在self.menu_left_upper
或self.menu_left_lower
中添加内容时,就像test
标签一样,我的设计就破了:菜单框架消失了。
此外,即使使用 columnspan ,我也必须删除状态栏,因为当它添加时,左侧的菜单再次消失。
使用包布局管理器我得到了这个:
######################################
# | some title #
# |----------------------#
# menu upper | #
# | CANVAS #
# | #
# menu lower | #
# |----------------------#
# | statusbar #
######################################
由于我希望左侧的菜单框架消耗完整的y空间,因此我使用pack(side="left", expand=True, fill="both")
增长,但此设置始终拒绝状态栏以获得整个宽度。
此外,纯包管理器代码看起来很丑陋" 。我认为使用网格管理器的设计更清晰"。因此我认为网格布局中的网格或包布局会很好?
有人可以帮忙吗?我被困在GUI-hell:/
答案 0 :(得分:11)
进行布局的关键是有条不紊,并使用正确的工具
为了工作。这也意味着有时你需要有创意。
这意味着在铺设东西时使用grid
grid
,并在从上到下排列时使用pack
左到右。
另一个关键是将布局代码组合在一起。这太多了 当它在一个块中时,更容易可视化和修改布局 代码。
在您的情况下,您似乎有三个或四个不同的区域,具体取决于
你如何计算。如果您想使用grid
,这将是最简单的
将“菜单上部”和“菜单下部”组合成一个框架,然后对待它
整个框架作为表格单元格。看起来你已经这样做了,
这很好。
所以,让我们从这些主要领域开始:
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="nsew")
self.some_title_frame.grid(row=0, column=1, sticky="ew")
self.canvas_area.grid(row=1, column=1, sticky="nsew")
# you don't have a status bar in the example code, but if you
# did, this is where you would put it
# self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")
每次使用grid
时,您都需要至少提供一行和一行
列为正权重,以便tkinter知道在哪里使用任何
未分配的空间。通常有一个小部件是“主要”
小部件。在这种情况下,它是画布。你想确保
画布的行和列的权重为1(一):
self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(1, weight=1)
注意:使用pack
代替grid
会为您节省两行代码,因为pack
不需要您按照grid
的方式设置权重。
接下来,我们需要解决菜单区域的问题。默认情况下, 框架缩小以适应其内容,这就是添加标签的原因 打破你的布局。你没有告诉tkinter如何处理额外的空间,所以框架缩小以适应,并且额外的空间未被使用。
因为你想要“menu_upper”和“menu_lower”
每个份额占该区域的50%,pack
是最简单的解决方案。您可以
使用grid
,但需要更多代码行来添加行和列权重。
self.menu_left_upper.pack(side="top", fill="both", expand=True)
self.menu_left_lower.pack(side="top", fill="both", expand=True)
这是一个正常运行的版本,带有状态栏。请注意它在调整窗口大小时的行为方式:
import Tkinter as tk
class Example():
def __init__(self):
self.root = tk.Tk()
self.root.title("some application")
# menu left
self.menu_left = tk.Frame(self.root, width=150, bg="#ababab")
self.menu_left_upper = tk.Frame(self.menu_left, width=150, height=150, bg="red")
self.menu_left_lower = tk.Frame(self.menu_left, width=150, bg="blue")
self.test = tk.Label(self.menu_left_upper, text="test")
self.test.pack()
self.menu_left_upper.pack(side="top", fill="both", expand=True)
self.menu_left_lower.pack(side="top", fill="both", expand=True)
# right area
self.some_title_frame = tk.Frame(self.root, bg="#dfdfdf")
self.some_title = tk.Label(self.some_title_frame, text="some title", bg="#dfdfdf")
self.some_title.pack()
self.canvas_area = tk.Canvas(self.root, width=500, height=400, background="#ffffff")
self.canvas_area.grid(row=1, column=1)
# status bar
self.status_frame = tk.Frame(self.root)
self.status = tk.Label(self.status_frame, text="this is the status bar")
self.status.pack(fill="both", expand=True)
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="nsew")
self.some_title_frame.grid(row=0, column=1, sticky="ew")
self.canvas_area.grid(row=1, column=1, sticky="nsew")
self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")
self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(1, weight=1)
self.root.mainloop()
Example()
在不相关的说明中:我强烈建议您不删除用户调整窗口大小的功能。他们比你更了解他们的要求。如果正确使用grid
和pack
,GUI将完美调整大小。
答案 1 :(得分:0)
在self.root.mainloop()
之前添加以下代码即可实现您正在寻找的内容
self.some_status = tk.Label(self.root, text="status bar", bg="#dfdfdf")
self.some_status.grid(row=3, column=0, columnspan=2, sticky="we")
答案 2 :(得分:0)
加入行:
menu_left_upper.grid_propagate(False)
在menu_left_upper
框架与menu_left_upper.mainloop()
这适用于:
默认情况下,容器窗口小部件会展开或折叠到足以保存其内容的大小。因此,当您调用pack时,会导致帧缩小。此功能称为几何传播。
对于绝大多数应用程序,这是您想要的行为。对于那些您想要明确设置容器大小的罕见情况,您可以关闭此功能。要将其关闭,请在容器上调用
pack_propagate
或grid_propagate
(具体取决于您是否在该容器上使用网格或包),并为其指定值False
。
See link to another question where this came from
要获取状态栏,只需实现另一个框架和网格方法:
status_bar_frame = Frame(root, bg="#dfdfdf")
status_bar_frame.grid(row=3, column=0, columnspan=2, sticky="we")
status_bar = Label(status_bar_frame, text="status bar", bg="#dfdfdf")
status_bar.pack()
然后你的计划有效。 希望它有所帮助:)
PS。还有为什么所有self
属性?
编辑: 工作你需要做:
menu_left_upper = Frame(menu_left, width=225, height=225, bg="red")
menu_left_upper.grid_propagate(False)
menu_left_upper.grid(row=0, column=0)
# this label breaks the design
test = Label(menu_left_upper, text="test", bg='red')
test.grid()