python - 为什么 TKinter OptionMenu 改变其父级的大小?

标签 python tkinter grid-layout optionmenu

我有这个 GUI 程序,其中一个框架中有一个选项菜单。每当我在 OptionMenu 中选择某些内容时,它都会调整父框架的大小,即使它有足够的空间来容纳当前面板。很难用语言来描述,所以这是我的意思的图像:

You can see that the purple, blue, and red frames have expanded with the size increase of the OptionMenu

您可以看到紫色、蓝色和红色框随着 OptionMenu 大小的增加而扩大。

窗口被分成左右两个 Frame,采用 Grid 布局,权重分别为 3 和 2。两个框架的内部是每个彩色面板的 Pack 布局,设置为填充 X。在 Purple 内部,我设置了此选项菜单,并将其打包到面板的左侧。

当它改变内容时,它会调整整个右框架的大小,忽略网格权重并破坏 GUI 的平衡。如果我为 OptionMenu 设置固定宽度,它不会调整大小,但仍然会使框架与 GridLayout 不对齐。如何使框架不根据该元素的宽度调整大小,而只需将元素放置在框架内部,即使在最宽的情况下,框架也有足够的空间来处理它?<​​/p>

这是我正在使用的 GUI 代码的精简版本,完整的代码有点太长,因为每个面板都被分解为 future 功能的类:

root = Tk()
root.geometry('640x480')

#Begin defining left frame areas
leftFrame = Frame(root)

grayPanel = Frame(leftFrame,bg="gray")
whitePanel = Frame(leftFrame,bg="white", height=50)
grayPanel.pack(expand=True,fill=BOTH)
whitePanel.pack(fill=X)

#Begin defining right frame areas
rightFrame = Frame(root)

purplePanel = Frame(rightFrame,bg="purple", height=50)
bluePanel = Frame(rightFrame,bg="blue")
redPanel = Frame(rightFrame,bg="red", height=100)

purplePanel.pack(fill=X)
bluePanel.pack(expand=True,fill=BOTH)
redPanel.pack(fill=X)

#create the options dropdown for purple
currentOption = StringVar(purplePanel)
currentOption.set("None")
optList = ["None","Some incredibly long string that breaks everything"]
options = OptionMenu(purplePanel,currentOption,*optList)
options.pack(side=LEFT)


leftFrame.grid(row=0,column=0,sticky=N+S+E+W)
rightFrame.grid(row=0,column=1,sticky=N+S+E+W)

root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=3)
root.grid_columnconfigure(1, weight=2)

root.mainloop()

最佳答案

问题的根源在于您没有为任何小部件指定大小,并且所有框架都是空的。以下描述可能听起来令人困惑,但实际上非常合乎逻辑、具有确定性,并且与记录的行为一致。

小部件和列的自然大小

由于所有框架都没有明确的宽度,因此它们的宽度默认为 0(零)。选项菜单具有将其大小调整为足够大以显示值的行为(我认为 Mac 上除外)。它是唯一具有非零请求(或“自然”)大小的小部件。

网格负责左侧和右侧。左侧尺寸为零宽度,右侧是选项菜单的宽度。为了便于讨论,我们假设它的宽度为 140 像素,只是为了让数学变得简单;实际尺寸可能会更大或更小,具体取决于您的字体,并且我们忽略边框以保持数学简单。因此,grid 认为左侧希望为零,右侧希望为 140。当它尝试使所有内容都适合时,这是它开始的唯一信息。

处理额外空间

您强制窗口宽度为 640 像素,但其子窗口仅需要 140 像素,因此它有 500 额外像素。您在左侧使用列权重为 3,在右侧使用列权重为 2,因此对于每五个像素的额外空间,三个将位于左侧,两个将位于右侧。这意味着左列将获得 300 像素的额外空间,右列将获得 200 像素的额外空间。这使得左列 300 像素 (0+300),右列 340 (140+200)。这就是为什么它们在启动时看起来大小大致相同。

更改选项菜单的大小时会发生什么

现在,您更改选项菜单的值,使其增大。假设选项菜单的新宽度是 440 像素。左侧仍希望为零,但现在右侧希望为 440。这意味着只有 200 像素的额外空间:左侧 120,右侧 80。请记住,左列的自然大小为零,因此 0 加 120 意味着左列的宽度为 120 像素。右侧的自然尺寸为 440。440 加上额外的 80 意味着右侧现在的宽度为 520 像素。

这就是为什么当选项菜单很长时,左侧会缩小,右侧会有额外的空间。您要求左侧为零,因此只有在布局了所有其他具有实际大小的小部件后,它才可以使用额外的空间。右侧具有非零的自然大小,但网格算法有剩余空间,必须分配给右侧。

如何获得统一的列宽

您没有问,但我假设后续问题是“如何使两列的大小相同?”答案是使用带有网格的uniform选项。告诉网格您希望两列具有统一的大小,如下所示:

root.grid_columnconfigure(0, weight=3, uniform="column")
root.grid_columnconfigure(1, weight=2, uniform="column")

uniform 接受任何字符串作为参数。所有具有相同值的列(或行)都被视为“统一组”。

请注意,使用uniform,网格仍然遵循权重。只是使用 uniform 可以保证列严格遵循列权重。在您的情况下,这意味着左右宽度的比例为 3:2

有关网格和包算法的规范信息

Tkinter 是带有 tk 工具包的 tcl 解释器的包装器。虽然语法与 python 不同,但将 tcl 文档翻译成 python 很容易。以下是有关 gridpack 如何工作的描述的链接:

关于python - 为什么 TKinter OptionMenu 改变其父级的大小?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37870982/

相关文章:

python - lambda 函数用 name_with_spaces 替换列名

python - 如何创建一个 tzinfo 设置为 'UTC' 的日期时间对象?

python - 在 Flask 中将数组处理为表单名称

python tkinter 使用 PIL 显示动画 GIF

python - Python tkinter 时钟的白天主题和夜间主题

python - 是否可以使用 Scrapy 通过代理访问 https 页面?

python - 尝试通过运行 Tkinter 的发送进程在进程之间通过管道发送任何内容时发生管道错误

Java - 从 9x9 网格中的每个方格中选择一个数字

Java:使用 If-Else 语句为网格列着色

ios - 如何在IOS平台Nativescript中使用GridLayout codrrect