Tkinter中创建有效模态对话框求助:grab_set()方法失效问题
解决Tkinter中
grab_set()无法实现有效模态对话框的问题 我明白你遇到的困扰了——明明按照常规方法用Toplevel加grab_set()创建模态对话框,但在Windows 7或Ubuntu 16.04系统上,用户还是能操作父窗口,完全没达到预期的模态效果。而且grab_set_global()又太极端,会阻塞整个系统的窗口,根本不能作为可行方案。
问题根源
你的代码里漏掉了关键的一步:让父窗口的事件循环等待对话框关闭。grab_set()只是把事件抓取到对话框本身,但如果不暂停父窗口的事件处理流程,父窗口依然会响应用户的交互操作。加上你的窗口层级中,root被隐藏,MyToplevel作为root的子窗口,MyDialog作为MyToplevel的子窗口,必须明确让父窗口等待对话框销毁后才能继续工作。
修正后的代码
下面是调整后的最简示例,能实现真正有效的模态对话框:
import os try: import Tkinter as tkinter except ImportError: import tkinter class MyToplevel(tkinter.Toplevel): def __init__(self, parent): tkinter.Toplevel.__init__(self, parent) self.title("Main window") # 创建对话框后,等待它关闭再恢复父窗口的交互 dialog = MyDialog(self) self.wait_window(dialog) self.protocol("WM_DELETE_WINDOW", parent.destroy) class MyDialog(tkinter.Toplevel): def __init__(self, parent): tkinter.Toplevel.__init__(self, parent) self.transient(parent) self.title("Dialog") # 抓取所有指向父窗口的事件 self.grab_set() self.protocol("WM_DELETE_WINDOW", self.destroy) if __name__ == "__main__": root = tkinter.Tk() root.withdraw() app = MyToplevel(root) app.mainloop()
关键改动说明
- 在
MyToplevel的初始化方法中,创建MyDialog实例后调用self.wait_window(dialog):这会暂停父窗口的事件循环,直到对话框被销毁,在此期间用户无法对父窗口进行任何操作。 grab_set()配合transient(parent):transient()确保对话框始终依附于父窗口,grab_set()则将所有原本要发送给父窗口的事件都重定向到对话框,双重保障模态效果。
这个方案在Windows和Linux系统上都能稳定工作,既实现了对话框的模态特性,又不会影响系统中其他应用的窗口操作。
内容的提问来源于stack exchange,提问作者std_answ




