tkinter multi-Toplevel windows 释放所有 windows grab_set 当其中只有一个释放抓取状态
tkinter multi-Toplevel windows releases all windows grab_set when only one of them releases the grabbed status
我正在尝试使用具有多个级别的 tkinter 构建 GUI windows。当我打开一个新的 Toplevel window 时,我需要他的 parens 被抓住。为此,我在 Toplevel class.
的开头使用 grab_set()
新的 Toplevel child window 还将具有一些其他功能,允许打开新的 Toplevel grandchild windows。问题是,当任何 Toplevel grandchild window 接近时,处于抓取状态的 windows 的层次结构将获得自由,然后所有这些都可以再次被操纵。
当 grab_settled grandchild window 发布时,如何保持 grab_set()
功能对所有 windows 有效?
这是重现此行为的简单代码示例
from tkinter import *
class GUI(Frame):
def __init__(self, master, *args, **kwargs):
Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.my_frame = Frame(self.master)
self.my_frame.pack()
self.button1 = Button(self.master, text="Open New Window", command=OpenFirstToplevelWindow)
self.button1.pack()
self.text = Text(self.master, width=30, height=3)
self.text.pack()
self.text.insert(END, "Some text")
class OpenFirstToplevelWindow(Toplevel):
def __init__(self, *args, **kwargs):
Toplevel.__init__(self, *args, **kwargs)
self.grab_set()
top_button = Button(self, text="Open a second window", command=OpenSecondToplevelWindow)
top_button.pack()
app.text.delete(1.0, END)
app.text.insert(END, "Text edition should be inhibited")
# Toplevel.wait_window(self)
class OpenSecondToplevelWindow(Toplevel):
def __init__(self, *args, **kwargs):
Toplevel.__init__(self, *args, **kwargs)
self.grab_set()
top_button = Button(self, text="close second window", command=self.close_window)
top_button.pack()
app.text.delete(1.0, END)
app.text.insert(END, "Text edition still inhibited")
def close_window(self):
app.text.delete(1.0, END)
app.text.insert(END, "Text edition should keep inhibited but it IS NOT")
self.destroy()
if __name__ == "__main__":
root = Tk()
app = GUI(root)
root.mainloop()
--- 编辑扩展 @acw1668 答案 ---
我一直在处理您的答案,看起来效果很好。问题是我已经意识到,如果我尝试在最后一个 window 中的关闭按钮名称中添加一个 self
以供进一步使用,那么该方法将不起作用:
class OpenSecondToplevelWindow(MyToplevel):
def __init__(self, *args, **kwargs):
MyToplevel.__init__(self, *args, **kwargs)
enable_button = Button(self, text="Enable close button", command=self.enable_click)
enable_button.pack()
self.low_button = Button(self, text="Close second window", command=self.close_window, state=DISABLED)
self.low_button.pack()
app.text.delete(1.0, END)
app.text.insert(END, "Text edition still inhibited")
def enable_click(self):
self.low_button["state"] = NORMAL
def close_window(self):
app.text.delete(1.0, END)
app.text.insert(END, "Text edition should keep inhibited but it IS NOT")
self.destroy()
一种方法是在调用 grab_set()
之前保存已设置抓取的 window 的引用(如果有的话)。然后在当前window关闭后恢复抓包到保存的window
以下习俗class(继承自Toplevel
)将具有上述特征:
class MyToplevel(Toplevel):
def __init__(self, master=None, **kw):
super().__init__(master, **kw)
# save the reference of current window having the grab set
self._last_grab_set_win = self.grab_current()
self.grab_set()
def __del__(self):
if self._last_grab_set_win:
# restore the grab set to saved window
self._last_grab_set_win.grab_set()
然后在您的代码中使用上面的自定义 MyToplevel
而不是 Toplevel
:
class OpenFirstToplevelWindow(MyToplevel):
def __init__(self, *args, **kwargs):
MyToplevel.__init__(self, *args, **kwargs)
top_button = Button(self, text="Open a second window", command=OpenSecondToplevelWindow)
top_button.pack()
app.text.delete(1.0, END)
app.text.insert(END, "Text edition should be inhibited")
# Toplevel.wait_window(self)
class OpenSecondToplevelWindow(MyToplevel):
def __init__(self, *args, **kwargs):
MyToplevel.__init__(self, *args, **kwargs)
top_button = Button(self, text="close second window", command=self.close_window)
top_button.pack()
app.text.delete(1.0, END)
app.text.insert(END, "Text edition still inhibited")
...
更新:我不知道如果使用实例小部件而不是本地小部件,为什么不执行__del__()
。但是,覆盖 destroy()
似乎可以解决问题:
class MyToplevel(Toplevel):
def __init__(self, master=None, **kw):
super().__init__(master, **kw)
# save the reference of current window having the grab set
self._last_grab_set_win = self.grab_current()
self.grab_set()
# override destroy()
def destroy(self):
if self._last_grab_set_win:
# restore the grab set to saved window
self._last_grab_set_win.grab_set()
# call Toplevel.destroy()
super().destroy()
我正在尝试使用具有多个级别的 tkinter 构建 GUI windows。当我打开一个新的 Toplevel window 时,我需要他的 parens 被抓住。为此,我在 Toplevel class.
的开头使用grab_set()
新的 Toplevel child window 还将具有一些其他功能,允许打开新的 Toplevel grandchild windows。问题是,当任何 Toplevel grandchild window 接近时,处于抓取状态的 windows 的层次结构将获得自由,然后所有这些都可以再次被操纵。
当 grab_settled grandchild window 发布时,如何保持 grab_set()
功能对所有 windows 有效?
这是重现此行为的简单代码示例
from tkinter import *
class GUI(Frame):
def __init__(self, master, *args, **kwargs):
Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.my_frame = Frame(self.master)
self.my_frame.pack()
self.button1 = Button(self.master, text="Open New Window", command=OpenFirstToplevelWindow)
self.button1.pack()
self.text = Text(self.master, width=30, height=3)
self.text.pack()
self.text.insert(END, "Some text")
class OpenFirstToplevelWindow(Toplevel):
def __init__(self, *args, **kwargs):
Toplevel.__init__(self, *args, **kwargs)
self.grab_set()
top_button = Button(self, text="Open a second window", command=OpenSecondToplevelWindow)
top_button.pack()
app.text.delete(1.0, END)
app.text.insert(END, "Text edition should be inhibited")
# Toplevel.wait_window(self)
class OpenSecondToplevelWindow(Toplevel):
def __init__(self, *args, **kwargs):
Toplevel.__init__(self, *args, **kwargs)
self.grab_set()
top_button = Button(self, text="close second window", command=self.close_window)
top_button.pack()
app.text.delete(1.0, END)
app.text.insert(END, "Text edition still inhibited")
def close_window(self):
app.text.delete(1.0, END)
app.text.insert(END, "Text edition should keep inhibited but it IS NOT")
self.destroy()
if __name__ == "__main__":
root = Tk()
app = GUI(root)
root.mainloop()
--- 编辑扩展 @acw1668 答案 ---
我一直在处理您的答案,看起来效果很好。问题是我已经意识到,如果我尝试在最后一个 window 中的关闭按钮名称中添加一个 self
以供进一步使用,那么该方法将不起作用:
class OpenSecondToplevelWindow(MyToplevel):
def __init__(self, *args, **kwargs):
MyToplevel.__init__(self, *args, **kwargs)
enable_button = Button(self, text="Enable close button", command=self.enable_click)
enable_button.pack()
self.low_button = Button(self, text="Close second window", command=self.close_window, state=DISABLED)
self.low_button.pack()
app.text.delete(1.0, END)
app.text.insert(END, "Text edition still inhibited")
def enable_click(self):
self.low_button["state"] = NORMAL
def close_window(self):
app.text.delete(1.0, END)
app.text.insert(END, "Text edition should keep inhibited but it IS NOT")
self.destroy()
一种方法是在调用 grab_set()
之前保存已设置抓取的 window 的引用(如果有的话)。然后在当前window关闭后恢复抓包到保存的window
以下习俗class(继承自Toplevel
)将具有上述特征:
class MyToplevel(Toplevel):
def __init__(self, master=None, **kw):
super().__init__(master, **kw)
# save the reference of current window having the grab set
self._last_grab_set_win = self.grab_current()
self.grab_set()
def __del__(self):
if self._last_grab_set_win:
# restore the grab set to saved window
self._last_grab_set_win.grab_set()
然后在您的代码中使用上面的自定义 MyToplevel
而不是 Toplevel
:
class OpenFirstToplevelWindow(MyToplevel):
def __init__(self, *args, **kwargs):
MyToplevel.__init__(self, *args, **kwargs)
top_button = Button(self, text="Open a second window", command=OpenSecondToplevelWindow)
top_button.pack()
app.text.delete(1.0, END)
app.text.insert(END, "Text edition should be inhibited")
# Toplevel.wait_window(self)
class OpenSecondToplevelWindow(MyToplevel):
def __init__(self, *args, **kwargs):
MyToplevel.__init__(self, *args, **kwargs)
top_button = Button(self, text="close second window", command=self.close_window)
top_button.pack()
app.text.delete(1.0, END)
app.text.insert(END, "Text edition still inhibited")
...
更新:我不知道如果使用实例小部件而不是本地小部件,为什么不执行__del__()
。但是,覆盖 destroy()
似乎可以解决问题:
class MyToplevel(Toplevel):
def __init__(self, master=None, **kw):
super().__init__(master, **kw)
# save the reference of current window having the grab set
self._last_grab_set_win = self.grab_current()
self.grab_set()
# override destroy()
def destroy(self):
if self._last_grab_set_win:
# restore the grab set to saved window
self._last_grab_set_win.grab_set()
# call Toplevel.destroy()
super().destroy()