Tkinter网格:如何使控件不紧贴在一起

22 投票
5 回答
84294 浏览
提问于 2025-04-17 16:18

我正在尝试创建两个标签(Label)控件,分别放在我的测试界面的左上角和右上角。问题是这两个控件紧挨在一起,我希望它们之间有一些空隙。

在我的研究中,我发现有人建议使用sticky、padx和pady这些选项。但是无论我在.grid()中传入什么参数,我似乎都无法在控件之间创造出空隙。我明白了,不管两个控件之间有多少行和列,如果这些行/列是空的,就好像它们不存在一样,控件就会看起来粘在一起。

使用.grid()方法,我该如何放置控件才能让它们不再粘在一起呢?

这是我目前的代码:

#!/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()

~~更新~~

我尝试了以下方法,但没有成功:

    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)

5 个回答

1

这里有一个简单的方法:

  • 首先,可以在纸上或者用你喜欢的工具设计你的界面。
  • 使用网格布局管理器。
  • 如果你想在某个地方留空白,可以使用布局中的columnspan或rowspan属性,配合sticky属性。columnspan或rowspan可以让你把网格中的多个单元格分配给一个界面组件,而sticky属性则可以让这个组件贴在某一侧,另一侧留空。
1

如果你在 top_lefttop_right 的构造函数里都加上 bg = "red",你会发现这些 Frames 被固定在中间,即使你使用了 sticky 选项。如果它们只会放一个小部件,我建议你不要使用这些 Frames

#!/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()
75

你需要使用 grid_columnconfigure 来给中间的列一些“权重”。这样,它们就会比其他列更容易扩展和收缩,以填充你多出来的空间。不过,在你的情况下,其实没必要使用 grid。因为你界面上的所有东西都是沿着特定的边对齐的,使用 pack 更自然。

我会先介绍如何使用 packgrid,先从 pack 开始,因为它是最简单的。即使你坚持要使用 grid,也请先看看下一部分,了解如何把一个大布局问题拆分成多个小问题。

分而治之

在 Tkinter 中,布局的最佳方法是“分而治之”。从最外层的控件开始,按照你想要的样子调整它们。然后,逐个解决这些控件。

在你的情况下,最外层的控件是主容器。因为它是窗口中唯一的控件,所以使用 pack 是让它填满整个容器的最简单方法。你也可以使用 grid,但这需要多一点工作:

self.main_container = Frame(parent. background="bisque")
self.main_container.pack(side="top", fill="both", expand=True)

为了帮助你在开发时更好地可视化,可以暂时给你的框架设置不同的颜色。把上面的代码加到你的 __init__ 方法中,运行你的应用程序,然后调整窗口大小,看看主框架是否能正确地扩展和收缩。

接下来,你有两个框架——top_frame 和 bottom_frame。根据它们的名字和你尝试使用 grid 的方式,我猜它们应该在 x 方向上填满界面。而且,我猜 top_frame 是某种工具栏,bottom_frame 是你界面的“主”部分。因此,让我们让底部的控件占据所有的额外空间。

因为这些框架是上下堆叠的,所以 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)

接下来是标签的框架。它们同样占据了容器边缘的空间,所以使用 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)

接下来是你的“角落”标签。由于容器只有一行控件,使用 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")

最后,你有文本控件。它填满了整个底部框架,所以再次使用 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 还是 grid?

你在原始代码中使用了 grid,并询问如何修复它。那我为什么在我的例子中使用 pack 呢?

使用 pack 时,所有的配置选项都可以在一个调用中完成。而使用 grid 时,除了把控件放入容器外,你还必须为不同的列和行设置“权重”,以便它们能够正确调整大小。当你只是把控件堆叠在一起或在一行中对齐时,pack 更容易使用。

在我的 GUI 中,我几乎总是结合使用 gridpack。它们都很强大,并且在不同的方面表现出色。唯一需要记住的——这非常重要——是你不能在同一个父容器中同时使用它们。以你的代码为例,你不能在 top_left 框架中使用 pack,在 top_right 框架中使用 grid,因为它们共享同一个父容器。不过,你可以在同一个应用程序中混合使用它们。

再次使用 grid

好吧,也许你真的想使用 grid:也许这是一个学校作业,或者你只是想一次专注于一个几何管理器。没问题。以下是我会怎么做。同样,你必须分而治之:

从主框架开始。用以下几行代码替换我们打包主容器的那一行。注意,我们必须在父容器上配置行和列,而不是我们创建的框架上。

    self.main_container.grid(row=0, column=0, sticky="nsew")
    self.myParent.grid_rowconfigure(0, weight=1)
    self.myParent.grid_columnconfigure(0, weight=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)

角落框架——终于,一个有多于一列的容器!让我们在中间创建第三列来填补所有的空隙。用以下代码替换 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)

接下来是它们框架中的标签。因为我们不需要它们扩展,所以可以保持默认的零权重。你可能会觉得两个标签都在第零列有点奇怪。记住,它们在不同的父容器中,而且它们是各自父容器中唯一的控件,所以每个父容器中只有一列:

    self.top_left_label.grid(row=0, column=0, sticky="w")
    self.top_right_label.grid(row=0, column=0, sticky="e")

最后,我们有文本控件,它是底部框架中唯一的控件。用以下代码替换最后的 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)

总结

布局控件很简单,但你必须有条理。考虑你在做什么,把控件组织成组,然后逐个解决这些组。当你这样做时,应该能清楚地看出应该使用哪个几何管理器。

如你所见,grid 需要更多的代码行,但当你确实有一个网格时,它是正确的选择。在你的情况下,你已经把 GUI 分成了几个部分,所以即使最终结果是一个网格,每个部分都是堆叠在一起或靠左或靠右边缘的。在这些情况下,pack 更容易使用,因为你不需要担心行和列的权重。

撰写回答