20

我正在尝试创建两个 Label 小部件,它们位于我的测试 UI 的左上角和右上角。问题是小部件粘在一起,我希望它们之间有空间。

在我的研究中,我遇到了使用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)
4

5 回答 5

71

您需要使用grid_columnconfigure给中间的列一些重量。由于重量比其他柱子更大,它们会膨胀和收缩以填充您拥有的任何额外空间。但是,在您的情况下,确实不需要使用网格。GUI 中的所有内容都沿着pack更自然的特定侧对齐。

我将展示如何同时使用 pack 和 grid,从 pack 开始,因为它是最简单的。即使您坚持使用grid,请阅读下一节以了解如何将一个大布局问题分解为许多较小的布局问题。

分而治之

在 Tkinter 中进行布局的最佳方式是“分而治之”。从最外面的小部件开始,以您想要的方式获得它们。然后,一次处理其中的每一个。

在您的情况下,您有一个最外层的小部件 - 主容器。由于它是窗口中唯一的小部件,因此 pack 是让它填满整个容器的最简单方法。您也可以使用网格,但它需要一些额外的工作:

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

它有助于暂时为您的框架提供独特的颜色,以便您可以在开发时将它们可视化。只需将上面的代码添加到您的__init__,运行您的应用程序,然后调整窗口大小以查看主框架是否正确增长和缩小。

接下来,您有两个框架——top_frame 和bottom_frame。从他们的名字和你尝试使用网格的方式来看,我认为他们应该在 x 方向填充 GUI。另外,我猜顶部框架是某种工具栏,而底部框架是 GUI 的真正“主要”部分。因此,让我们让底部小部件占据所有额外空间。

由于这些是相互堆叠的,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?

使用 时pack,所有配置选项都可以包含在一个调用中。除了grid将小部件放入它们的容器中之外,您还必须注意为各种列和行赋予“权重”,以便它们正确调整大小。当您只是将小部件堆叠在一起或将它们排成一排时,pack使用起来会容易得多。

在我的 GUI 中,我几乎总是使用grid和的组合pack。他们都很强大,擅长不同的事情。唯一要记住的——这是至关重要的——是你不能在同一个父级中使用它们。以您的代码为例,您不能使用packtop_left 框架和gridtop_right 框架,因为它们都共享同一个父级。但是,您可以在同一个应用程序中混合使用它们。

再一次,使用网格

好的,所以也许您真的想使用grid:也许这是一项学校作业,或者您可能只想一次专注于一个几何管理器。这很酷。这就是我将如何做到的。同样,您必须分而治之:

从主框架开始。用以下几行替换我们打包主容器的一个语句。请注意,我们必须在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)

好的,现在是顶部和底部框架。删除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)

接下来,他们框架中的标签。由于我们不需要它们扩展,我们可以保持默认的权重为零。您可能会觉得这两个标签都在第 0 列很奇怪。请记住,它们位于不同的父级中,并且它们是父级中唯一的小部件,因此每个小部件中只有一列:

    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使用起来会更容易一些,因为您不必担心行和列的权重。

于 2013-02-19T00:12:11.667 回答
1

如果您同时添加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()
于 2013-02-19T00:12:22.723 回答
1

这是一个简单的方法:

  • 首先在纸上设计你的 gui 或使用任何适合你的工具
  • 使用网格布局管理器
  • 无论您想在哪里创建空白空间,都可以使用布局的 columnspan 或 rowspan 属性,结合粘性属性 columnspan 或 rowspan 可以让您将网格的多个单元格分配给一个 gui 组件,而 sticky 属性可以让您强制该元素坚持一侧并在另一侧留出空间
于 2015-07-16T18:32:57.217 回答
1

迟到总比不到好 ;)

Tkinter 的“网格”想要把所有东西放在一起。这就是为什么你不能跳过单元​​格。您必须指定宽度,然后锚定文本。我添加了颜色以帮助显示单元格。我把bd放在框架里。然后我必须给左右单元格设置宽度,这样网格就会给它们带来重量。然后锚文本 West 或 East。我不确定为什么必须为每个单元格添加额外的 2 个空格,但我认为这是字体问题。

我相信 Rodas 更干净、更简单,但我试图保持在您询问的参数范围内。

对于小东西,Pack 更容易、更快捷。

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()
于 2015-10-07T23:39:18.320 回答
0

如果您需要完全控制 gui 组件的放置,请尝试放置布局;一种高度可管理的方法是将您的组件组织在几个框架中,每个框架都有网格或包布局,然后使用布局布局组织所有这些框架。

于 2015-07-16T20:21:38.780 回答