为什么 TKinter OptionMenu 会更改其父项的大小?

Why is TKinter OptionMenu changing the size of it's parent?

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

可以看到随着OptionMenu的尺寸变大,紫色、蓝色、红色的框也变大了。

将window拆分为左右两个Frames,采用Grid Layout,权重分别为3和2。两个框架里面是每个彩色面板的Pack布局,设置为填充X。紫色里面,我设置了这个optionMenu,并把它打包到面板的左边。

当它更改其内容时,它会调整整个右侧框架的大小,忽略网格权重并破坏 GUI 的平衡。如果我为 OptionMenu 设置固定宽度,它不会调整大小,但仍然会增加框架,使其与 GridLayout 不对齐。我怎样才能让框架不根据这个元素的宽度调整大小,而只是将元素放在框架内,框架有足够的空间来处理它,即使是最宽的?

这是我正在使用的 GUI 代码的精简版,完整代码有点太长,因为每个面板都分为 类 以供将来使用:

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

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

小部件和列的自然大小

因为 none 的框架有明确的宽度,它们默认为宽度 0(零)。 optionmenu 的行为是将其大小调整到足以显示值(我认为 Mac 除外)。它是唯一具有非零请求(或 "natural")大小的小部件。

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

处理额外的 space

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

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

现在,您更改选项菜单的值,使其变大。假设选项菜单的新宽度为 440 像素。左边还是要0,但是现在右边要440。也就是说只有200个额外的像素space:左边120个,右边80个。请记住,左列的自然大小为零,因此零加 120 意味着左列的宽度为 120 像素。在右侧,自然大小为 440。440 加上额外的 80 意味着右侧现在为 520 像素宽。

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

如何获得统一的列宽

你没有问,但我假设后续问题是 "how do I make the two columns the same size?" 答案是使用带有网格的 uniform 选项。告诉网格您希望两列具有统一的大小,如下所示:

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

uniform 接受任何字符串作为参数。所有具有相同值的列(或行)都被视为 "uniform group".

请注意,对于 uniform,网格仍然遵循权重。只是 uniform 保证列严格遵循列权重。在您的情况下,这意味着您的左右宽度

之间的比率为 3:2

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

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