You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tkinter多级Toplevel窗口关闭孙级窗口后父窗口grab_set失效如何解决?

问题根因

tkinter的全局grab同一时间仅允许一个窗口持有:当孙级窗口调用grab_set()时,子窗口的grab状态会被直接覆盖。孙级窗口销毁时,tkinter默认直接释放全局grab,不会自动恢复上一级窗口的grab状态,因此所有窗口都会恢复可操作状态。

解决方案

自定义支持层级grab自动恢复的Toplevel基类,在窗口初始化时记录前一个持有grab的窗口,重写destroy方法,销毁当前窗口前自动为上一级窗口恢复grab,同时绑定窗口右上角关闭事件走相同逻辑,避免漏处理场景。

完整可运行代码
from tkinter import *

# 自定义层级grab恢复基类
class MyToplevel(Toplevel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 记录当前持有grab的父级窗口
        self.prev_grab = self.grab_current()
        # 当前窗口抢占grab
        self.grab_set()
        # 绑定右上角关闭按钮走自定义销毁逻辑
        self.protocol("WM_DELETE_WINDOW", self.destroy)
    
    def destroy(self):
        # 销毁前恢复上一级窗口的grab,仅在上一级窗口还存在时执行
        if self.prev_grab and self.prev_grab.winfo_exists():
            self.prev_grab.grab_set()
        super().destroy()

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=60, height=3)
        self.text.pack()
        self.text.insert(END, "Some text")

class OpenFirstToplevelWindow(MyToplevel):
    def __init__(self, *args, **kwargs):
        super().__init__(*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")

class OpenSecondToplevelWindow(MyToplevel):
    def __init__(self, *args, **kwargs):
        super().__init__(*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 and it IS now")
        self.destroy()

if __name__ == "__main__":
    root = Tk()
    app = GUI(root)
    root.mainloop()
效果说明

该方案适配所有关闭场景:

  • 常规按钮关闭、右上角叉号关闭都会自动恢复上一级窗口的grab状态
  • 支持带前置状态判断的关闭逻辑(如先点击启用按钮才能关闭),无论关闭按钮是否加self前缀,只要最终调用self.destroy()就会触发恢复逻辑
  • 支持任意层级的嵌套窗口,grab状态会逐层自动恢复

内容的提问来源于stack exchange,提问作者Joracosu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 23:57:04