【问题标题】:Tkinter: grid or pack inside a grid?Tkinter:网格还是网格内的包?
【发布时间】:2016-07-30 02:29:43
【问题描述】:

我正在为软件构建 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,我也必须删除状态栏,因为添加后左侧的菜单又消失了。


使用 pack layout manager 我得到了这个:

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

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

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


有人可以帮忙吗?我被困在 GUI 地狱中:/

【问题讨论】:

  • 您是在寻求使用 grid 的帮助,还是寻求使用 pack 的帮助,或者您不在乎使用什么,只要它提供所需的外观? pack 代码怎么看起来很难看?这只是代码。
  • 我不在乎正在使用什么,只要它在工作。但是,我更喜欢网格管理器,因为网格布局对我来说更有意义。它易于理解和扩展,而使用大量具有不同 side=... 属性的 pack() 让我感到困惑。
  • 你想让“menu upper”和“menu lower”各占50%的面积吗?
  • 最好是。我尝试使用 weight=... 属性,但在我的尝试中这也不起作用。

标签: python layout tkinter


【解决方案1】:

做布局的关键是有条不紊,使用正确的工具 为了工作。这也意味着有时你需要有创造力。 这意味着在布局时使用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 将完美调整大小。

【讨论】:

  • 多么好的教程。这可能是 Tkinter 官方文档的一部分。
【解决方案2】:

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")

【讨论】:

  • 是的,但它不能解决当我在那里添加内容时左侧菜单栏消失的奇怪错误。如果我这样做self.test = tk.Label(self.menu_left_upper, text="test")self.test.pack()self.test.grid() 左侧的上/下菜单消失。
  • 您是否尝试给self.test 一些填充(padxpady)?
【解决方案3】:

通过输入:

menu_left_upper.grid_propagate(False) 

在您的 menu_left_upper 框架和 menu_left_upper.mainloop() 之间

这样工作:

默认情况下,容器小部件会展开或折叠到刚好足以容纳其内容的大小。因此,当您调用 pack 时,它会导致框架缩小。此功能称为几何传播

对于绝大多数应用程序,这是您想要的行为。在极少数情况下,当您想明确设置容器的大小时,您可以关闭此功能。要关闭它,请在容器上调用pack_propagategrid_propagate(取决于您是在该容器上使用grid 还是pack),并为其赋值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()

那么你的计划就奏效了。 希望对你有帮助:)

附言。还有为什么所有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

【讨论】:

  • 为什么投反对票?有点困惑为什么有人这样做......回答问题并获得计划:(
  • 谢谢,将对此进行测试。反对票的不是我。那么用网格管​​理器在顶层构建框架并管理它们,同时用包管理器管理框架的内容,是合法的吗?
  • 我尝试了.grid_propagate(False) 的所有可能组合:仅将其分配给menu_left,仅分配给menu_left_upper,仅分配给menu_left_lower 或这些组合。没有一个有效。菜单和子框架正确显示没有内容,但只要例如test 标签添加到 menu_left_upper 框架消失。
  • 格式很垃圾,请参阅编辑。你需要把它放在menu_left_upper.grid方法之前
  • @ascenator: “那么用网格管​​理器在顶层构建框架并管理它们,同时用包管理器管理框架的内容是合法的吗?” 不仅合法, 首选。或者更准确地说,为工作使用正确的工具。当pack 有意义时使用pack,当grid 有意义时使用grid。唯一的关键是您不能将它们一起用于共享一个共同父级的小部件。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-29
  • 2014-10-31
相关资源
最近更新 更多