【问题标题】:tkinter multi-Toplevel windows releases all windows grab_set when only one of them releases the grabbed statustkinter multi-Toplevel windows 当只有一个窗口释放抓取状态时释放所有窗口grab_set
【发布时间】:2021-09-01 09:20:29
【问题描述】:

我正在尝试使用具有多个级别窗口的 tkinter 构建 GUI。当我打开一个新的顶级窗口时,我需要抓住他的括号。为此,我在 Toplevel 类的开头使用了grab_set()

新的顶层子窗口还将具有一些其他功能,允许打开新的顶层孙子窗口。问题是,当任何顶层孙窗口关闭时,处于抓取状态的窗口的层次结构会被释放,然后它们都可以再次被操作。

当一个grab_settled 孙子窗口被释放时,我如何维护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()

【问题讨论】:

    标签: python tkinter toplevel


    【解决方案1】:

    其中一种方法是在调用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()
    

    【讨论】:

    • 看起来很不错!我已经尝试了一点,它看起来很完美。这似乎正是我想要的。明天或星期一我将尝试在我的真实代码中实现它,如果它有效,我将关闭这个问题。非常感谢你
    • 我无法让它在我的代码中工作。我想知道问题出在哪里,直到 6 小时后我才意识到,如果我为按钮的名称分配了一个“自我”,那么“__del__”就不会触发。我已更新 mi question 以扩展您的代码建议。请看一下
    • @Joracosu 我不知道为什么在这种情况下不执行__del__()。但覆盖destroy() 似乎解决了这个问题。答案已更新。
    猜你喜欢
    • 2011-09-14
    • 2017-12-23
    • 1970-01-01
    • 2018-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多