我试图建立一个GUI与几个级别的窗口。当我打开一个新的高层窗口时,我需要他的父母被抓住。为此,我在Toplevel类的开头使用grab_set()
。
新的顶层子窗口也将有一些其他功能,允许打开新的顶层子窗口。问题是,当任何顶层的孙子窗口接近时,处于抓取状态的窗口的层次被释放,然后它们又都是可操作的。
当一个grab_settle的孙子窗口被释放时,我如何保持grab_set()
函数对所有窗口都有效?
下面是一个简单的代码示例来重现此行为
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答案——
我一直在用你的答案工作,看起来工作得很好。问题是,我已经意识到,如果我试图把一个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()
之前保存具有抓取集的窗口的引用。然后在当前窗口关闭后将抓取集恢复到保存的窗口。
下面的自定义类(继承自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()