Based on this question, I wrote the following 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()
By clicking the undock button, the red frame is undocked and by clicking the dock button, the frame is docked again. I have two questions:
- 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?
- 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?
The error is:
AttributeError: 'Frame' object has no attribute 'protocol'
I understand the error but how to handle the window close event?
CodePudding user response:
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?
I think it is because self.dockable_frame
is a python class and doesn't know that the underlying widget has been changed. Arguably this is a bug in 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?
The simplest way is to call the method directly from the tk.Wm
class. It would look like this:
tk.Wm.protocol(self.dockable_frame, "WM_DELETE_WINDOW", self.whatever)
CodePudding user response:
The documentation is missleading. As I discovered this feature I thought simliar, the frame becomes a window. In fact that isnt really true which I can proof by my code below.
What really happens, at least under MS-Windows but I expect same functionality under other os, is that frames will be just packed on a different toplevel that will be created by wm_mange for this.
When tkinter defines a 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.
Code:
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()
Output:
By first appearance:
python id: 67118160
tkinterID: 3412074
parent id: 7867926
after undock
python id: 67118160
tkinterID: 3412074
parent id: 15666896
after forget
python id: 67118160
tkinterID: 3412074
parent id: 7867926
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.