删除内容后如何在 tkinter 中缩小框架?

How to shrink a frame in tkinter after removing contents?

我遇到的大多数主题都涉及如何 缩小 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()

到目前为止,我应该在 window:

中看到这个
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. Re-运行 frm.pack(): 但这破坏了我的主要小部件的顺序。
  3. .geometry(''):这仅适用于 root window - Frame 不存在。
  4. frm.config(height=0):奇怪的是,这似乎根本没有改变任何东西。
  5. frm.pack_forget()From this answer,但它并没有把它带回来。

它留给我的唯一选择是使用 grid 管理器,我想它可以工作,但不是我正在寻找的东西...所以我想知道是否有其他方法可以做到这一点。

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

一个简单的解决方法是在框架中添加一个小的 1 x 1 像素 window,这样 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()

Question: Shrink a Frame after removing the last widget?

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


参考:

  • Expose

    An Expose event is generated whenever all or part of a widget should be redrawn


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

Question: Re-run frm.pack(): but this ruins the order of my main widgets.
frm.pack_forget(), however it doesn't bring it back.

Pack 有选项 before=after。这允许相对于其他小部件打包小部件。


参考:

  • -before

    Use its master as the master for the slaves, and insert the slaves just before other in the packing order.


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

Note: I show only the relevant parts!


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