tkinter - 取消停靠框架并处理 window 关闭事件

tkinter - undock frame and handle window close event

基于this question,我写了如下mwe:

import tkinter as tk


class BaseFrame(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.parent = parent
      
        self.bmanage = tk.Button(self, text='undock', command = self.undock)
        self.bforget = tk.Button(self, text='dock', command = self.dock)

        self.bmanage.grid(row=0, column=0, padx=20, pady=20, sticky='nsew')
        self.bforget.grid(row=0, column=1, padx=20, pady=20, sticky='nsew')

        self.dockable_frame = tk.Frame(self, bg="red", height=100)
        self.dockable_frame.grid(row=1, column=0, padx=20, pady=20, columnspan=2, sticky='nsew')
        self.label = tk.Label(self.dockable_frame, text="hi")
        self.label.grid(row=0, column=0, padx=150, pady=20, sticky='nsew')

    def undock(self):
        self.parent.wm_manage(self.dockable_frame)
        self.dockable_frame.configure(bg='blue')

        print(type(self.dockable_frame))

    def dock(self):
        self.parent.wm_forget(self.dockable_frame)
        self.dockable_frame.grid()


if __name__ == "__main__":
    root = tk.Tk()

    base_frame = BaseFrame(root)
    base_frame.grid(row=0, column=0, padx=20, pady=20, sticky='nsew')
    
    root.mainloop() 

单击 取消停靠 按钮,取消停靠红框,单击 停靠 按钮,再次停靠红框。我有两个问题:

  1. 为什么 self.dockable_frame 的类型是 而不是 TopLevel 因为 wm manage documentation 说: 指定的小部件将成为独立的顶级window?
  2. 如何处理 window 关闭事件,因为 self.dockable_frame.protocol("WM_DELETE_WINDOW", insert_function_here)在我的 Windows 电脑上出现错误?

错误是:
AttributeError: 'Frame' 对象没有属性 'protocol'
我理解错误,但如何处理 window 关闭事件?

Why is the type of self.dockable_frame a <class 'tkinter.Frame'> and not a TopLevel since the wm manage documentation says: The widget specified will become a stand alone top-level window?

我认为这是因为 self.dockable_frame 是 python class 并且不知道底层小部件已更改。可以说这是 wm_manage.

中的错误

How can I handle the window close event since self.dockable_frame.protocol("WM_DELETE_WINDOW", insert_function_here) gives an error on my Windows PC?

最简单的方法是直接从tk.Wm class调用方法。它看起来像这样:

tk.Wm.protocol(self.dockable_frame, "WM_DELETE_WINDOW", self.whatever)

文档具有误导性。 我也是这么想的,框架变成了window。事实上,这不是真的,我可以通过下面的代码证明这一点。

真正发生的是,至少在 MS-Windows 下,但我希望在其他 os 下具有相同的功能,即帧将被打包到由 [= 创建的不同顶层上46=]为此。

当 tkinter 定义一个 Window/Toplevel it always build a child window (frame) for the client area which you will work with. Thats why you need to call the win32gui.GetParent method when you will change your window style.

代码:

import tkinter as tk
import win32gui

def info():
    print(f'python id: {id(frame)}')
    print(f'tkinterID: {frame.winfo_id()}')
    print(f'parent id: {win32gui.GetParent(frame.winfo_id())}')
def undock():
    root.wm_manage(frame)
def forget():
    root.wm_forget(frame)
    frame.pack()

root = tk.Tk()
frame= tk.Frame()
frame.pack()

b1 = tk.Button(frame,text='INFO',command=info)
b2 = tk.Button(frame,text='mnge',command=undock)
b3 = tk.Button(frame,text='nrml',command=forget)
b1.pack()
b2.pack()
b3.pack()

root.mainloop()

输出:

第一次出现:

python id: 67118160
tkinterID: 3412074
parent id: 7867926

取消停靠后

python id: 67118160
tkinterID: 3412074
parent id: 15666896

后忘记

python id: 67118160
tkinterID: 3412074
parent id: 7867926

reference:

In Tk, Toplevel windows are basically a special form of a Frame which are managed by the window manager. The proposal is to add the commands wm manage and wm forget which will take an arbitrary Frame and allow it to be managed by the window manager, making it a Toplevel window.