Tkinter:网格内的网格或包装?

时间:2016-04-08 17:50:16

标签: python layout tkinter

我正在为软件构建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_upperself.menu_left_lower中添加内容时,就像test标签一样,我的设计就破了:菜单框架消失了。 此外,即使使用 columnspan ,我也必须删除状态栏,因为当它添加时,左侧的菜单再次消失。

使用包布局管理器我得到了这个:

######################################
#             |      some title      #
#             |----------------------#
# menu upper  |                      #
#             |         CANVAS       #
#             |                      #
# menu lower  |                      #
#             |----------------------#
#             |       statusbar      #
######################################

由于我希望左侧的菜单框架消耗完整的y空间,因此我使用pack(side="left", expand=True, fill="both")增长,但此设置始终拒绝状态栏以获得整个宽度。

此外,纯包管理器代码看起来很丑陋" 。我认为使用网格管理器的设计更清晰"。因此我认为网格布局中的网格或包布局会很好

有人可以帮忙吗?我被困在GUI-hell:/

3 个答案:

答案 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()

在不相关的说明中:我强烈建议您删除用户调整窗口大小的功能。他们比你更了解他们的要求。如果正确使用gridpack,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_propagategrid_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()

My result