简体   繁体   English

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

[英]Tkinter: grid or pack inside a grid?

I am building a GUI for a software and want to achieve this: 我正在构建一个软件的GUI,并希望实现这一目标:

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

Menu upper has some high level functionality, menu lower is changing in dependency of user input. 菜单上部具有一些高级功能, 菜单下限根据用户输入而变化。 Statusbar changes its contents often. 状态栏经常更改其内容。


Unfortunately, Tkinter refuses to work. 不幸的是,Tkinter拒绝工作。

Using the grid layout manager I were unable to create a stable design and adding content like labels and buttons to the menu on the left side: 使用网格布局管理器我无法创建稳定的设计,并在左侧的菜单中添加标签和按钮等内容:

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

This design worked without contents in the menu on the left side . 这种设计在左侧的菜单中没有内容 Whenever I added something in self.menu_left_upper or self.menu_left_lower , like the test label, my design got broke: the menu frames disappeared. 每当我在self.menu_left_upperself.menu_left_lower添加一些东西时,就像test标签一样,我的设计破了:菜单框架消失了。 Additionally, even with columnspan , I had to remove the statusbar, because when it was added the menus on the left disappeared, again. 另外,即使有了柱子 ,我也不得不删除状态栏,因为当它被添加时,左边的菜单再次消失了。


Using pack layout manager I got this: 使用包布局管理器我得到了这个:

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

Since I wanted the menu frame on the left to consume the full y-space I made it grow with pack(side="left", expand=True, fill="both") , but this setup always denies the statusbar to go for the full width. 因为我希望左边的菜单框架消耗完整的y空间,所以我使用它来增长pack(side="left", expand=True, fill="both") ,但是这个设置总是拒绝状态栏去全宽。

Besides, the pure pack manager code looks "ugly" . 此外,纯包管理器代码看起来“丑陋” I think a design with a grid manager is "clearer". 我认为使用网格管理器的设计更“清晰”。 Therefore I thought a grid or a pack layout inside a grid layout would be nice ? 因此我认为网格布局中的网格或包布局会很好吗?


Can anyone help? 有人可以帮忙吗? I am stuck in the GUI-hell :/ 我陷入了GUI-hell:/

The key to doing layout is to be methodical, and to use the right tool for the job. 做布局的关键是有条不紊,并使用正确的工具来完成工作。 It also means that sometimes you need to be creative. 这也意味着有时你需要有创意。 That means using grid when laying things out in a grid , and using pack when laying things out top-to-bottom or left-to-right. 这意味着在grid放置东西时使用grid grid ,而在从上到下或从左到右铺设时使用pack

The other key is to group your layout code together. 另一个关键是将布局代码组合在一起。 It's much, much easier to visualize and modify the layout when it's all in one block of code. 当它在一个代码块中时,可视化和修改布局要容易得多。

In your case you seem to have three or four distinct areas, depending on how you count. 在您的情况下,您似乎有三个或四个不同的区域,具体取决于您的计算方式。 If you want to use grid , it will be easiest to combine "menu upper" and "menu lower" into a frame, and treat that whole frame as a table cell. 如果要使用grid ,最简单的方法是将“菜单上部”和“菜单下部”组合到一个框架中,并将整个框架视为表格单元格。 It looks like you're already doing that, which is good. 看起来你已经在做了,这很好。

So, let's start with those main areas: 那么,让我们从这些主要领域开始:

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

Any time you use grid , you need to give at least one row and one column a positive weight so that tkinter knows where to use any unallocated space. 无论何时使用grid ,您都需要为至少一行和一列提供正权重,以便tkinter知道在何处使用任何未分配的空间。 Usually there is one widget that is the "main" widget. 通常有一个小部件是“主”小部件。 In this case it's the canvas. 在这种情况下,它是画布。 You want to make sure that the row and column for the canvas has a weight of 1 (one): 您希望确保画布的行和列的权重为1(一):

self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(1, weight=1)

note: using pack instead of grid would save you two lines of code, since pack doesn't require you to set weights the way grid does. 注意:使用pack代替grid可以节省两行代码,因为pack不需要像grid那样设置权重。

Next, we need to solve the problem of the menu areas. 接下来,我们需要解决菜单区域的问题。 By default, frames shrink to fit their contents, which is why adding the label broke your layout. 默认情况下,框架会缩小以适合其内容,这就是添加标签会破坏布局的原因。 You weren't telling tkinter what to do with extra space, so the frames shrunk to fit, and extra space went unused. 你没有告诉tkinter如何处理额外的空间,所以框架缩小以适应,并且额外的空间未被使用。

Since you want "menu_upper" and "menu_lower" to each share 50% of that area, pack is the simplest solution. 由于您希望“menu_upper”和“menu_lower”分别占该区域的50%,因此pack是最简单的解决方案。 You can use grid , but it requires more lines of code to add the row and column weights. 您可以使用grid ,但需要更多代码行来添加行和列权重。

self.menu_left_upper.pack(side="top", fill="both", expand=True)
self.menu_left_lower.pack(side="top", fill="both", expand=True)

Here is a functioning version, with statusbar. 这是一个功能正常的版本,带有状态栏。 Notice how it behaves exactly as it should when you resize the window: 请注意它在调整窗口大小时的行为方式:

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

On an unrelated note: I would strongly encourage you to not remove the ability for the user to resize the window. 在一个不相关的说明:我强烈建议您不要删除用户调整窗口大小的能力。 They know better than you what their requirements are. 他们比你更了解他们的要求。 If you use grid and pack properly, the GUI will resize perfectly. 如果您正确使用gridpack ,GUI将完美调整大小。

Adding the following code right before self.root.mainloop() achieves what you're looking for 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")

By putting in the line: 通过加入:

menu_left_upper.grid_propagate(False) 

In between your menu_left_upper Frame and menu_left_upper.mainloop() 在你的menu_left_upper框架和menu_left_upper.mainloop()

This works as: 这适用于:

By default, a container widget expands or collapses to be just big enough to hold its contents. 默认情况下,容器窗口小部件会扩展或折叠,以便足以容纳其内容。 Thus, when you call pack, it causes the frame to shrink. 因此,当您调用pack时,会导致帧缩小。 This feature is called geometry propagation . 此功能称为几何体传播

For the vast majority of applications, this is the behavior you want. 对于绝大多数应用程序,这是您想要的行为。 For those rare times when you want to explicitly set the size of a container you can turn this feature off. 对于那些您想要明确设置容器大小的罕见情况,您可以关闭此功能。 To turn it off, call either pack_propagate or grid_propagate on the container (depending on whether you're using grid or pack on that container), giving it a value of False . 要关闭它,请在容器上调用pack_propagategrid_propagate (取决于您是否在该容器上使用grid或pack),将其赋值为False

See link to another question where this came from 请参阅另一个问题的链接

To get your status bar just implement another frame and grid method: 要获取状态栏,只需实现另一个框架和网格方法:

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

Then your plan works. 然后你的计划有效。 Hope it helps :) 希望能帮助到你 :)

PS. PS。 Also why all the self attributes? 还有为什么所有的self属性?

EDIT: TO work you need to do: 编辑:你需要做的工作:

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 我的结果

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM