【问题标题】:How to shrink a frame in tkinter after removing contents?删除内容后如何在 tkinter 中缩小框架?
【发布时间】:2020-01-03 20:18:04
【问题描述】:

我遇到的大多数主题都涉及如何缩小 Frame 的内容,但我有兴趣在销毁所述内容后将其缩小。这是一个例子:

import tkinter as tk
root = tk.Tk()
lbl1 = tk.Label(root, text='Hello!')
lbl1.pack()
frm = tk.Frame(root, bg='black')
frm.pack()
lbl3 = tk.Label(root, text='Bye!')
lbl3.pack()
lbl2 = tk.Label(frm, text='My name is Foo')
lbl2.pack()

到目前为止,我应该在我的窗口中看到这个:

Hello!
My name is Foo
Bye!

这很好,但我希望根据需要保持中间层可互换和隐藏。所以如果我销毁里面的lbl2

lbl2.destroy()

我想看看:

Hello!
Bye!

但我看到的是:

Hello!
███████
Bye!

我想将frm 缩小到基本上不存在的状态,因为我想保持主要小部件的顺序不变。理想情况下,我想运行 frm.pack(fill=tk.BOTH, expand=True) 以便我的内部小部件可以相应地缩放。但是,如果这干扰了缩小,我可以不用fill/expand

我尝试了以下方法:

  1. pack_propagate(0):这实际上并没有扩展框架超过pack()
  2. 重新运行frm.pack():但这会破坏我主要小部件的顺序。
  3. .geometry(''):这仅适用于 root 窗口 - 对于 Frames 不存在。
  4. frm.config(height=0):奇怪的是,这似乎并没有改变任何东西。
  5. frm.pack_forget(): From this answer,但它并没有把它带回来。

它留给我的唯一选择是使用grid 管理器,我想这可行,但不完全是我想要的......所以我很想知道是否有其他方法可以实现这一目标。

【问题讨论】:

  • @stovfl 感谢您的建议,但这只是我的minimal reproducible example。我的真实示例在 frm 中有一堆不同的小部件,需要更多控件。有些是Labels、LabelFrames、Buttons……等等……
  • 您问题中的代码不是 minimal reproducible example
  • @martineau 你能澄清一下吗?我的问题中的代码产生了我想要更改的结果。如果您的意思是我应该在其中包含所有其他小部件,那可不是一个简单的例子?
  • “和Label要求的高度一样”:自己数,循环frm.children ; <child ref>.winfo_height()
  • @r.ook "在lbl2.destroy() 之后,什么都没有":是的,在你的例子中,它什么都没有导致height == 0。但是您声称您还有其他可以计算的小部件。

标签: python python-3.x tkinter tkinter-layout


【解决方案1】:

当您销毁框架中的最后一个小部件时,框架大小不再由packgrid 管理。因此,packgrid 都不知道它应该缩小框架。

一个简单的解决方法是在框架中添加一个 1 像素 x 1 像素的小窗口,以便 pack 仍然认为它是框架大小的原因。

这是一个基于问题中代码的示例:

import tkinter as tk
root = tk.Tk()
lbl1 = tk.Label(root, text='Hello!')
lbl1.pack()
frm = tk.Frame(root, bg='black')
frm.pack()
lbl3 = tk.Label(root, text='Bye!')
lbl3.pack()
lbl2 = tk.Label(frm, text='My name is Foo')
lbl2.pack()

def delete_the_label():
    lbl2.destroy()
    if len(frm.winfo_children()) == 0:
        tmp = tk.Frame(frm, width=1, height=1, borderwidth=0, highlightthickness=0)
        tmp.pack()
        root.update_idletasks()
        tmp.destroy()

button = tk.Button(root, text="Delete the label", command=delete_the_label)
button.pack()


root.mainloop()

【讨论】:

  • 有没有办法让帧0乘0?
  • @GT77:不,我认为不可能创建一个零宽度的框架。
【解决方案2】:

问题:删除最后一个小部件后缩小Frame

绑定到<'Expose'> 事件和.configure(height=1)(如果没有子级)。


参考

  • Expose

    每当需要重绘小部件的全部或部分时,都会产生一个 Expose 事件


import tkinter as tk

class App(tk.Tk):
    def __init__(self):
        super().__init__()

        tk.Label(self, text='Hello!').pack()
        self.frm = frm = tk.Frame(self, bg='black')
        frm.pack()
        tk.Label(self, text='Bye!').pack()
        tk.Label(frm, text='My name is Foo').pack()

        self.menubar = tk.Menu()
        self.config(menu=self.menubar)
        self.menubar.add_command(label='delete', command=self.do_destroy)
        self.menubar.add_command(label='add', command=self.do_add)

        frm.bind('<Expose>', self.on_expose)

    def do_add(self):
        tk.Label(self.frm, text='My name is Foo').pack()
        
    def do_destroy(self):
        w = self.frm
        if w.children:
            child = list(w.children).pop(0)
            w.children[child].destroy()

    def on_expose(self, event):
        w = event.widget
        if not w.children:
            w.configure(height=1)
        
                            
if __name__ == "__main__":
    App().mainloop()

问题:重新运行frm.pack():但这会破坏我的主要小部件的顺序。
frm.pack_forget(),但它并没有恢复。

Packbefore=after 选项。这允许相对于其他小部件打包一个小部件。


参考

  • -before

    使用它的master作为slave的master,并在打包顺序中将slave插入到其他之前。


使用before=self.lbl3 作为锚点的示例。如果没有孩子,Frame 将使用 .pack_forget() 删除,并在包装​​订单中的同一位置重新包装。

注意:我只展示相关部分!


class App(tk.Tk):
    def __init__(self):
        ...
        self.frm = frm = tk.Frame(self, bg='black')
        frm.pack()
        self.lbl3 = tk.Label(self, text='Bye!')
        self.lbl3.pack()
        ...

    def on_add(self):
        try:
            self.frm.pack_info()
        except:
            self.frm.pack(before=self.lbl3, fill=tk.BOTH, expand=True)

        tk.Label(self.frm, text='My name is Foo').pack()

    def on_expose(self, event):
        w = event.widget
        if not w.children:
            w.pack_forget()

用 Python 测试:3.5 - 'TclVersion':8.6 'TkVersion':8.6

【讨论】:

  • 此代码不会像发布的那样运行 - NameError: name 'Application' is not defined。此外,在 OSX 上,您不能将命令直接放在菜单栏上。否则,这是一个很酷的解决方案。
  • @BryanOakley:代码不会运行。已修复,import ... 也不见了。 * 将命令直接放在菜单栏上*:在 OSX 上看起来如何?
  • 在 OSX 上,命令根本不显示。 OSX 默默地忽略它们。所有命令都必须在下拉菜单中。
猜你喜欢
  • 2021-12-03
  • 2015-03-14
  • 1970-01-01
  • 2015-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-26
  • 1970-01-01
相关资源
最近更新 更多