Python Tkinter Grid:如何定位小部件,使它们不会粘在一起
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14946963/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Tkinter Grid: How to position widgets so they are not stuck together
提问by Dirty Penguin
I'm trying to create two Label widgets that are in the top left and top right corners of my test UI. The problem is that the widgets are stuck together and I'd like there to be space between them.
我正在尝试在我的测试 UI 的左上角和右上角创建两个 Label 小部件。问题是小部件粘在一起,我希望它们之间有空间。
In my research, I came across suggestions to use the sticky, padx, and pady options. But no matter what arguments I pass to .grid() , I can't seem to be able to create space between my widgets. I understand that regardless of the number of columns and rows between two widgets, if said rows/columns are empty, then its as if they didn't exist and the widgets appear glued together.
在我的研究中,我遇到了使用粘性、padx 和 pady 选项的建议。但是无论我传递给 .grid() 什么参数,我似乎都无法在我的小部件之间创建空间。我知道无论两个小部件之间的列数和行数如何,如果所述行/列是空的,那么就好像它们不存在并且小部件看起来粘在一起。
Using the .grid() method, how can I position widgets so that they aren't stuck together?
使用 .grid() 方法,我如何定位小部件以使它们不会粘在一起?
Here is my code so far:
到目前为止,这是我的代码:
#!/usr/bin/python
from Tkinter import *
class MyApp:
def __init__(self, parent):
self.myParent = parent
self.main_container = Frame(parent)
self.main_container.grid(row=0, rowspan=2, column=0, columnspan=4)
self.top_frame = Frame(self.main_container)
self.top_frame.grid(row=0, column=0, columnspan=4)
self.top_left = Frame(self.top_frame)
self.top_left.grid(row=0, column=0, columnspan=2)
self.top_right = Frame(self.top_frame)
self.top_right.grid(row=0, column=2, columnspan=2)
self.bottom_frame = Frame(self.main_container)
self.bottom_frame.grid(row=2, column=0, columnspan=4)
self.top_left_label = Label(self.top_left, text="Top Left")
self.top_left_label.grid(row=0, column=0, sticky='W', padx=2, pady=2)
self.top_right_label = Label(self.top_right, text="Top Right")
self.top_right_label.grid(row=0, column=4, sticky='E', padx=2, pady=2)
self.text_box = Text(self.bottom_frame, height=5, width=40)
self.text_box.grid(row=0, column=0)
root = Tk()
root.title("Test UI")
myapp = MyApp(root)
root.mainloop()
~~Update~~
〜〜更新〜〜
I tried the following but it did not work:
我尝试了以下但没有奏效:
self.top_left = Frame(self.top_frame)
self.top_left.grid(row=0, column=0, columnspan=2)
for c in range(2):
self.top_left.columnconfigure(c, weight=2)
self.top_right = Frame(self.top_frame)
self.top_right.grid(row=0, column=2, columnspan=2)
for c in range(2):
self.top_right.columnconfigure(c, weight=2)
采纳答案by Bryan Oakley
You need to use grid_columnconfigureto give the columns in the middle some weight. With more weight than the other columns, they will expand and contract to fill any extra space you have. However, in your case there's really no need to use grid. Everything in your GUI is aligned along particular sides for which packis a more natural fit.
您需要使用grid_columnconfigure给中间的列一些重量。与其他列相比,它们的重量更大,它们会膨胀和收缩以填充您拥有的任何额外空间。但是,在您的情况下,确实没有必要使用网格。GUI 中的所有内容都沿着特定的边对齐,这样pack更自然。
I'll show how to use both pack and grid, starting with pack since it's the easiest. Even if you insist on using grid, read through the next section to understand how to break one big layout problem into many smaller layout problems.
我将展示如何同时使用 pack 和 grid,从 pack 开始,因为它是最简单的。即使您坚持使用grid,请通读下一节以了解如何将一个大的布局问题分解为许多较小的布局问题。
Divide and conquer
分而治之
The best way to do layout in Tkinter is "divide and conquer". Start with the outermost widgets and get them exactly the way you want. Then, tackle each of these one at a time.
在 Tkinter 中进行布局的最佳方式是“分而治之”。从最外面的小部件开始,按照您想要的方式获取它们。然后,一次处理每一个。
In your case you have one outermost widget - the main container. Since it is the only widget in the window, pack is the simplest way to get it to fill the whole container. You can use grid as well, but it requires a little extra work:
在您的情况下,您有一个最外面的小部件 - 主容器。由于它是窗口中唯一的小部件,因此 pack 是让它填满整个容器的最简单方法。您也可以使用 grid ,但它需要一些额外的工作:
self.main_container = Frame(parent. background="bisque")
self.main_container.pack(side="top", fill="both", expand=True)
It helps to temporarily give your frames distinctive colors so you can visualize them as you develop. Add just the above code to your __init__, run your app, then resize the window to see that the main frame properly grows and shrinks.
它有助于暂时为您的框架赋予独特的颜色,以便您可以在开发过程中将它们可视化。将上面的代码添加到您的__init__,运行您的应用程序,然后调整窗口大小以查看主框架是否正确增长和缩小。
Next, you have two frames -- top_frame and bottom_frame. By their names and judging by how you've attempted to use grid, I assume they should fill the GUI in the x direction. Also, I'm guessing the top frame is some sort of toolbar, and the bottom frame is the real "main" part of your GUI. Thus, let's make the bottom widget take up all the extra space.
接下来,您有两个框架——top_frame 和bottom_frame。根据他们的名字和你尝试使用网格的方式判断,我认为他们应该在 x 方向填充 GUI。另外,我猜顶部框架是某种工具栏,底部框架是 GUI 的真正“主要”部分。因此,让我们让底部小部件占据所有额外的空间。
Since these are stacked on top of each other, packis again the natural choice. Add the following code -- and only the following code -- to make sure these areas occupy the parts of the window that you expect, and that they have the proper resize behavior.
由于这些堆叠在一起,pack再次是自然的选择。添加以下代码——并且仅添加以下代码——以确保这些区域占据您期望的窗口部分,并且它们具有适当的调整大小行为。
self.top_frame = Frame(self.main_container, background="green")
self.bottom_frame = Frame(self.main_container, background="yellow")
self.top_frame.pack(side="top", fill="x", expand=False)
self.bottom_frame.pack(side="bottom", fill="both", expand=True)
Next comes the frames for the labels. Again, these occupy space along the edges of their container so packmakes the most sense. And again, add just the following bit of code, run your program, and make sure things are still resizing properly and filling the right parts of the window:
接下来是标签的框架。同样,这些沿着容器边缘占据空间,因此pack最有意义。再一次,只添加以下代码,运行你的程序,并确保事情仍然正确调整大小并填充窗口的正确部分:
self.top_left = Frame(self.top_frame, background="pink")
self.top_right = Frame(self.top_frame, background="blue")
self.top_left.pack(side="left", fill="x", expand=True)
self.top_right.pack(side="right", fill="x", expand=True)
Next, you have your "corner" labels. Again, since the container is but a single row of widgets, packmakes it easy. Since you want the labels in the corners, we'll set the stickyattribute a little different for each:
接下来,你有你的“角落”标签。同样,由于容器只是一行小部件,pack因此很容易。由于您想要角落中的标签,我们将为sticky每个设置稍微不同的属性:
self.top_left_label = Label(self.top_left, text="Top Left")
self.top_right_label = Label(self.top_right, text="Top Right")
self.top_left_label.pack(side="left")
self.top_right_label.pack(side="right")
Finally, you have the text widget. It fills the entire bottom frame, so once again packis our friend:
最后,您有文本小部件。它填满了整个底部框架,所以再次pack是我们的朋友:
self.text_box = Text(self.bottom_frame, height=5, width=40, background="gray")
self.text_box.pack(side="top", fill="both", expand=True)
pack or grid?
包还是网格?
You used grid for your original code and asked how to fix it. Why did I use pack for my examples?
您将 grid 用于原始代码并询问如何修复它。为什么我在我的例子中使用了 pack?
When you use pack, all of the configuration options can be wrapped up in a single call. With gridalong with putting the widgets in their containers you must also take care to give your various columns and rows "weight" so that they resize properly. When you are simply stacking widgets on top of each other or aligning them in a row, packis much easier to use.
当您使用 时pack,所有配置选项都可以包含在一个调用中。除了grid将小部件放入其容器中外,您还必须注意给各个列和行赋予“权重”,以便它们正确调整大小。当您只是将小部件堆叠在一起或将它们排成一行时,pack使用起来要容易得多。
In my GUIs, I almost always use a combination of gridand pack. They are both powerful, and excel at different things. The only thing to remember -- and this is crucial-- is that you can't use them in the same parent. Using your code as an example, you can't use packfor the top_left frame and gridfor the top_right frame, since they both share the same parent. You can, however, mix them within the same application.
在我的图形用户界面,我几乎总是使用的组合grid和pack。他们都很强大,并且擅长不同的事情。唯一要记住的——这很关键——是你不能在同一个父级中使用它们。以您的代码为例,您不能pack用于 top_left 框架和gridtop_right 框架,因为它们共享同一个父级。但是,您可以在同一个应用程序中混合使用它们。
Once more, using grid
再次,使用网格
Ok, so maybe you really want to use grid: maybe this is a school assignment, or maybe you just want to focus on one geometry manager at a time. That's cool. Here's how I would do it. Again, you must divide and conquer:
好的,所以也许您真的想使用grid:也许这是学校作业,或者您只想一次专注于一个几何经理。这很酷。这是我将如何做到的。同样,你必须分而治之:
Start with the main frame. Replace the one statement where we pack the main container with the following lines. Notice that we have to configure the rows and columns on the parent, not the frame that we created.
从主框架开始。用以下几行替换我们打包主容器的 one 语句。请注意,我们必须在parent上配置行和列,而不是我们创建的框架。
self.main_container.grid(row=0, column=0, sticky="nsew")
self.myParent.grid_rowconfigure(0, weight=1)
self.myParent.grid_columnconfigure(0, weight=1)
Ok, now for the top and bottom frames. Remove the pack, and add the following lines. You still only have a single column, but this time there are a couple of rows. Notice which row gets a weight of 1:
好的,现在是顶部和底部框架。删除pack, 并添加以下行。您仍然只有一列,但这次有几行。注意哪一行的权重为 1:
self.top_frame.grid(row=0, column=0, sticky="ew")
self.bottom_frame.grid(row=1, column=0,sticky="nsew")
self.main_container.grid_rowconfigure(1, weight=1)
self.main_container.grid_columnconfigure(0, weight=1)
The corner frames -- at last, a container with more than one column! Let's create a third column in the middle to take up all the slack. Replace the packstatements with the following, paying close attention to what is given "weight":
角框——终于,一个不止一列的容器!让我们在中间创建第三列来填补所有松弛。将这些pack陈述替换为以下内容,并密切注意给出的“权重”:
self.top_left.grid(row=0, column=0, sticky="w")
self.top_right.grid(row=0, column=2, sticky="e")
self.top_frame.grid_columnconfigure(1, weight=1)
Next, the labels in their frames. Since we don't need them to expand, we can keep the default weight of zero. You may think it odd that both labels are in column zero. Remember that they are in different parents, and they are the only widgets in their parents so there's only one column in each:
接下来,它们框架中的标签。由于我们不需要它们展开,我们可以保持默认权重为零。您可能认为两个标签都在第 0 列中很奇怪。请记住,它们在不同的父级中,并且它们是父级中唯一的小部件,因此每个小部件中只有一列:
self.top_left_label.grid(row=0, column=0, sticky="w")
self.top_right_label.grid(row=0, column=0, sticky="e")
Finally we have the text widget which is the only widget in the bottom frame. Replace the final packstatements with the following:
最后,我们有文本小部件,它是底部框架中唯一的小部件。将最后的pack声明替换为以下内容:
self.text_box.grid(row=0, column=0, sticky="nsew")
self.bottom_frame.grid_rowconfigure(0, weight=1)
self.bottom_frame.grid_columnconfigure(0, weight=1)
Conclusion
结论
Laying out widgets is easy, but you have to be systematic about it. Think about what you are doing, organize your widgets into groups, and then tackle the groups one at a time. When you do that, it should become apparent which geometry manager you should use.
布置小部件很容易,但您必须对其进行系统化。想想你在做什么,将你的小部件组织成组,然后一次处理一个组。当你这样做时,你应该使用哪个几何管理器应该变得很明显。
As you can see, gridrequires a few more lines of code but it's the right choice when you really do have a grid. In your case you had already sub-divided your GUI into sections, and so even though the end result was a grid, each section was either packed on top or below another, or to the left or right edges. In these cases, packis a bit easier to use since you don't have to worry about row and column weights.
如您所见,grid需要多几行代码,但当您确实拥有网格时,这是正确的选择。在您的情况下,您已经将您的 GUI 细分为多个部分,因此即使最终结果是一个网格,每个部分要么被打包在另一个部分的顶部或下方,要么位于左侧或右侧边缘。在这些情况下,pack使用起来更容易一些,因为您不必担心行和列的权重。
回答by A. Rodas
If you add bg = "red"to both top_leftand top_rightconstructors, you will see how the Framesare stuck on the centre, even using the stickyoption. If they are not going to contain anything else than a single widget, I recommend not to use them.
如果bg = "red"同时添加top_left和top_right构造函数Frames,即使使用该sticky选项,您也会看到它们是如何卡在中心的。如果它们不打算包含一个小部件以外的任何东西,我建议不要使用它们。
#!/usr/bin/python
from Tkinter import *
class MyApp:
def __init__(self, parent):
self.top_left_label = Label(parent, text="Top Left")
self.top_left_label.grid(row=0, column=0, padx=2, pady=2, sticky=N+S+W)
self.top_right_label = Label(parent, text="Top Right")
self.top_right_label.grid(row=0, column=1, padx=2, pady=2, sticky=N+S+E)
self.text_box = Text(parent, height=5, width=40)
self.text_box.grid(row=1, column=0, columnspan=2)
root = Tk()
root.title("Test UI")
myapp = MyApp(root)
root.mainloop()
回答by Farhad Maleki
Here is an easy way to do it:
这是一个简单的方法:
- First design your gui on paper or using any tool that works for you
- Use grid layout manager
- Wherever you want to create empty space, use columnspan or rowspan property of layout, in combination of sticky property columnspan or rowspan let you assign more than one cell of the grid to a gui component, and sticky property let you force that element to stick to one side(s) and leave empty space on the other side(s)
- 首先在纸上或使用任何适合您的工具设计您的 gui
- 使用网格布局管理器
- 无论您想在何处创建空白空间,都可以使用布局的 columnspan 或 rowspan 属性,结合粘性属性 columnspan 或 rowspan 可让您将网格的多个单元格分配给 gui 组件,而粘性属性可让您强制该元素坚持一侧并在另一侧留空
回答by Farhad Maleki
If you need to have full control on the placement of your gui component, try place layout; a highly manageable approach is to organize your components in several frame, each with grid or pack layout, and then organizing all those frames using place layout.
如果您需要完全控制 gui 组件的放置,请尝试放置布局;一种高度易于管理的方法是将您的组件组织在几个框架中,每个框架都具有网格或包布局,然后使用位置布局组织所有这些框架。
回答by James
Better late than never ;)
迟到总比不到好 ;)
Tkinter's "grid" wants to put everything together. Thats why you are not able to skip cells. You have to specify the widths and then anchor the text. I added color to help show cells. I put the bd in the frame. Then I had to give width to the cells left and right so grid would give weight to them. Then anchor text West or East. Im not sure why I had to add extra 2 spaces for each cell but I figured it was a font issue.
Tkinter 的“网格”想把所有东西放在一起。这就是您无法跳过单元格的原因。您必须指定宽度,然后锚定文本。我添加了颜色来帮助显示单元格。我把 bd 放在框架里。然后我不得不为左右单元格赋予宽度,这样网格就会赋予它们权重。然后锚文本西方或东方。我不确定为什么我必须为每个单元格添加额外的 2 个空格,但我认为这是字体问题。
I believe Rodas is cleaner and simpler but I tried to stay within the parameters you asking about.
我相信 Rodas 更干净、更简单,但我试图保持在您询问的参数范围内。
Pack is easier and faster for small stuff.
小件物品的打包更容易、更快捷。
from tkinter import *
class MyApp:
def __init__(self, parent):
self.myParent = parent
self.main_container = Frame(parent, bg="green")
self.main_container.grid()
self.top_frame = Frame(self.main_container)
self.top_frame.grid()
self.top_left = Frame(self.top_frame, bd=2)
self.top_left.grid(row=0, column=0)
self.top_right = Frame(self.top_frame, bd=2)
self.top_right.grid(row=0, column=2)
self.top_left_label = Label(self.top_left, bd=2, bg="red", text="Top Left", width=22, anchor=W)
self.top_left_label.grid(row=0, column=0)
self.top_right_label = Label(self.top_right, bd=2, bg="blue", text="Top Right", width=22, anchor=E)
self.top_right_label.grid(row=0, column=0)
self.bottom_frame = Frame(self.main_container, bd=2)
self.bottom_frame.grid(row=2, column=0)
self.text_box = Text(self.bottom_frame, width=40, height=5)
self.text_box.grid(row=0, column=0)
root = Tk()
root.title("Test UI")
myapp = MyApp(root)
root.mainloop()

