“为什么?”;wm“U协议”;在Python3/tkinter中破坏正常的窗口管理?

“为什么?”;wm“U协议”;在Python3/tkinter中破坏正常的窗口管理?,python,oop,tkinter,window-management,Python,Oop,Tkinter,Window Management,我正在为一个较大的Python3.6项目测试tkinter窗口管理,有一件事我似乎不能正确理解,甚至不能很好地理解。在下面的代码中,窗口按预期打开和关闭(我的意思是,单击红色的“x”按钮或按OSX中的Command-W)。但是,当我试图为第二个窗口关闭事件添加回调时,事情变得一团糟。例如,如果我有多个辅助窗口,则键盘快捷键甚至按钮并不总是关闭活动窗口。你知道这里出了什么问题吗 以下是我当前的测试代码: #!/usr/bin/env python3.6 # encoding: utf-8 imp

我正在为一个较大的Python3.6项目测试tkinter窗口管理,有一件事我似乎不能正确理解,甚至不能很好地理解。在下面的代码中,窗口按预期打开和关闭(我的意思是,单击红色的“x”按钮或按OSX中的Command-W)。但是,当我试图为第二个窗口关闭事件添加回调时,事情变得一团糟。例如,如果我有多个辅助窗口,则键盘快捷键甚至按钮并不总是关闭活动窗口。你知道这里出了什么问题吗

以下是我当前的测试代码:

#!/usr/bin/env python3.6
# encoding: utf-8

import tkinter as tk
import tkinter.font
from tkinter import ttk


class baseApp(ttk.Frame):
    """
    Parent classe for main app window (will include some aditional methods and properties).
    """
    def __init__(self, master, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.master = master
        self.mainframe = ttk.Frame(master)
        self.mainframe.pack()


class App(baseApp):
    """ Base class for the main application window """
    def __init__(self, master, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.master = master
        self.lbl_text = ttk.Label(self.mainframe, text="This is the Main Window")
        self.lbl_text.pack()
        self.btn = ttk.Button(self.mainframe, text="Open Second window",
                              command=lambda: self.create_detail_window(self, number=0))
        self.btn.pack()

    def create_detail_window(self, *event, number=None):
        self.newDetailsWindow = tk.Toplevel(self.master)
        self.newDetailsWindow.geometry('900x600+80+130')
        self.newDetailsWindow.title(f'Detail: {number}')
        self.newDetailsWindow.wm_protocol("WM_DELETE_WINDOW", lambda: self.close_detail_window()) # This line breaks window management!...
        self.detail_window = detailWindow(self.newDetailsWindow, 0)
        self.newDetailsWindow.focus()

    def close_detail_window(self, *event):
        """ will test for some condition before closing, save if necessary and
            then call destroy()
        """
        self.newDetailsWindow.destroy() # Shouldn't this be enough to close the secondary window?...


class detailWindow(ttk.Frame):
    """ Base class for secondary windows """
    def __init__(self, master, rep_num, *args,**kwargs):
        super().__init__(master,*args,**kwargs)
        self.num_rep = rep_num
        self.master.minsize(900, 600)
        self.master.maxsize(900, 600)
        print(f"Showing details about nr. {self.num_rep}")
        self.mainframe = ttk.Frame(master)
        self.mainframe.pack()

        self.lbl_text = ttk.Label(self.mainframe,
                                  text=f"Showing details about nr. {self.num_rep}")
        self.lbl_text.pack()


if __name__ == "__main__":
    root = tk.Tk()
    janela_principal = App(root)
    root.title('Main Window')
    root.bind_all("<Mod2-q>", exit)
    root.mainloop()
#/usr/bin/env蟒蛇3.6
#编码:utf-8
将tkinter作为tk导入
导入tkinter.font
从tkinter导入ttk
类baseApp(ttk.Frame):
"""
主应用程序窗口的父类(将包括一些传统方法和属性)。
"""
定义初始值(self、master、*args、**kwargs):
super()。\uuuuu init\uuuuu(主参数,*args,**kwargs)
self.master=master
self.mainframe=ttk.Frame(主)
self.mainframe.pack()
类应用程序(baseApp):
“”“主应用程序窗口的基类”“”
定义初始值(self、master、*args、**kwargs):
super()。\uuuuu init\uuuuu(主参数,*args,**kwargs)
self.master=master
self.lbl_text=ttk.Label(self.mainframe,text=“这是主窗口”)
self.lbl_text.pack()
self.btn=ttk.Button(self.mainframe,text=“打开第二个窗口”,
command=lambda:self.create_detail_window(self,number=0))
self.btn.pack()
def创建详细信息窗口(自,*事件,编号=无):
self.newDetailsWindow=tk.Toplevel(self.master)
self.newDetailsWindow.geometry('900x600+80+130')
self.newDetailsWindow.title(f'Detail:{number})
self.newDetailsWindow.wm_协议(“wm_DELETE_WINDOW”,lambda:self.close_detail_WINDOW())#此行中断了窗口管理!。。。
self.detail\u window=detailWindow(self.newDetailsWindow,0)
self.newDetailsWindow.focus()
def关闭详细信息窗口(自身,*事件):
“”将在关闭前测试某些条件,如有必要请保存,然后重试
然后调用destroy()
"""
self.newDetailsWindow.destroy()#这是否足以关闭辅助窗口?。。。
类详细信息窗口(ttk.Frame):
“”“辅助窗口的基类”“”
定义初始值(self、master、rep_num、*args、**kwargs):
super()。\uuuuu init\uuuuu(主参数,*args,**kwargs)
self.num\u rep=rep\u num
self.master.minsize(900600)
自身主控最大尺寸(900600)
打印(f“显示有关编号{self.num_rep}的详细信息”)
self.mainframe=ttk.Frame(主)
self.mainframe.pack()
self.lbl_text=ttk.Label(self.mainframe,
text=f“显示有关编号{self.num_rep}的详细信息”)
self.lbl_text.pack()
如果名称=“\uuuuu main\uuuuuuuu”:
root=tk.tk()
janela_principal=App(根)
root.title('主窗口')
root.bind_all(“,退出)
root.mainloop()

当我对行
self.newDetailsWindow.wm_协议(“wm_DELETE_WINDOW”,lambda:self.close_detail_WINDOW())进行注释时,
窗口管理被破坏。行
self.newDetailsWindow.destroy()
是否足以简单地关闭辅助窗口?。。。我实例化对象的方式是否有任何错误?

看起来像是使用
self.newDetailsWindow
创建新的顶级垃圾收集现有的顶级垃圾。我在
App
中添加了一个list类变量,它是顶级的列表

class App(baseApp):
     """ Base class for the main application window """
    def __init__(self, master, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.master = master
        self.lbl_text = ttk.Label(self.mainframe, text="This is the Main Window")
        self.lbl_text.pack()
        self.btn = ttk.Button(self.mainframe, text="Open Second window",
                          command=lambda: self.create_detail_window(self, number=0))
        self.btn.pack()
        self.windows = [] #This is a list of the created windows instances.

    def create_detail_window(self, *event, number=None):
        newDetailsWindow = tk.Toplevel(self.master)
        self.windows.append(newDetailsWindow)

        newDetailsWindow.geometry('900x600+80+130')
        newDetailsWindow.title(f'Detail: {number}')
        newDetailsWindow.wm_protocol("WM_DELETE_WINDOW", lambda: 
        self.close_detail_window(newDetailsWindow)) # This line breaks window management!...
        detail_window = detailWindow(newDetailsWindow, 0)
        newDetailsWindow.focus()


    def close_detail_window(self, window):
        """ will test for some condition before closing, save if necessary and
        then call destroy()
        """
        self.windows.remove(window)
        window.destroy() # destroy the specific instance in self.windows

看起来像是使用
self.newDetailsWindow
创建一个新的顶级垃圾收集现有的顶级垃圾。我在
App
中添加了一个list类变量,它是顶级的列表

class App(baseApp):
     """ Base class for the main application window """
    def __init__(self, master, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.master = master
        self.lbl_text = ttk.Label(self.mainframe, text="This is the Main Window")
        self.lbl_text.pack()
        self.btn = ttk.Button(self.mainframe, text="Open Second window",
                          command=lambda: self.create_detail_window(self, number=0))
        self.btn.pack()
        self.windows = [] #This is a list of the created windows instances.

    def create_detail_window(self, *event, number=None):
        newDetailsWindow = tk.Toplevel(self.master)
        self.windows.append(newDetailsWindow)

        newDetailsWindow.geometry('900x600+80+130')
        newDetailsWindow.title(f'Detail: {number}')
        newDetailsWindow.wm_protocol("WM_DELETE_WINDOW", lambda: 
        self.close_detail_window(newDetailsWindow)) # This line breaks window management!...
        detail_window = detailWindow(newDetailsWindow, 0)
        newDetailsWindow.focus()


    def close_detail_window(self, window):
        """ will test for some condition before closing, save if necessary and
        then call destroy()
        """
        self.windows.remove(window)
        window.destroy() # destroy the specific instance in self.windows

我对你的代码做了一些调整。现在应该可以用了。基本上,您的方法
app.create_detail_window
每次调用时都会重新分配属性
self.newDetailWindow
,这就是为什么“x”按钮会发送到错误的窗口。我使用了一个
dict
来存储您创建的所有
Toplevel
s

#!/usr/bin/env python3.6
# encoding: utf-8

import tkinter as tk
import tkinter.font
from tkinter import ttk


class baseApp(ttk.Frame):
    """
    Parent classe for main app window (will include some aditional methods and properties).
    """
    def __init__(self, master, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.master = master
        self.mainframe = ttk.Frame(master)
        self.mainframe.pack()


class App(baseApp):
    """ Base class for the main application window """
    def __init__(self, master, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.master = master
        self.lbl_text = ttk.Label(self.mainframe, text="This is the Main Window")
        self.lbl_text.pack()
        self.btn = ttk.Button(self.mainframe, text="Open Second window",
                              command=lambda: self.create_detail_window(self, number=0))
        self.btn.pack()
        self.newDetailsWindow = {}
        self.windows_count=0

    def create_detail_window(self, *event, number=None):
        self.windows_count+=1
        self.newDetailsWindow[self.windows_count]=tk.Toplevel(self.master)
        self.newDetailsWindow[self.windows_count].geometry('900x600+80+130')
        self.newDetailsWindow[self.windows_count].title(f'Detail: {self.windows_count}')

        self.newDetailsWindow[self.windows_count].wm_protocol("WM_DELETE_WINDOW", self.newDetailsWindow[self.windows_count].destroy)
        #self.newDetailsWindow[self.windows_count].bind("Command-w", lambda event: self.newDetailsWindow[-1].destroy())

        self.detail_window = detailWindow(self.newDetailsWindow[self.windows_count], self.windows_count)
        self.newDetailsWindow[self.windows_count].focus()
        print(self.newDetailsWindow)

    def close_detail_window(self, *event):
        """ will test for some condition before closing, save if necessary and
            then call destroy()
        """
        pass
        #self.newDetailsWindow.destroy() # Shouldn't this be enough to close the secondary window?...


class detailWindow(ttk.Frame):
    """ Base class for secondary windows """
    def __init__(self, master, rep_num, *args,**kwargs):
        super().__init__(master,*args,**kwargs)
        self.num_rep = rep_num
        self.master.minsize(900, 600)
        self.master.maxsize(900, 600)
        print(f"Showing details about nr. {self.num_rep}")
        self.mainframe = ttk.Frame(master)
        self.mainframe.pack()

        self.lbl_text = ttk.Label(self.mainframe,
                                  text=f"Showing details about nr. {self.num_rep}")
        self.lbl_text.pack()


if __name__ == "__main__":
    root = tk.Tk()
    janela_principal = App(root)
    root.title('Main Window')
    root.bind_all("<Mod2-q>", exit)
    root.mainloop()
#/usr/bin/env蟒蛇3.6
#编码:utf-8
将tkinter作为tk导入
导入tkinter.font
从tkinter导入ttk
类baseApp(ttk.Frame):
"""
主应用程序窗口的父类(将包括一些传统方法和属性)。
"""
定义初始值(self、master、*args、**kwargs):
super()。\uuuuu init\uuuuu(主参数,*args,**kwargs)
self.master=master
self.mainframe=ttk.Frame(主)
self.mainframe.pack()
类应用程序(baseApp):
“”“主应用程序窗口的基类”“”
定义初始值(self、master、*args、**kwargs):
super()。\uuuuu init\uuuuu(主参数,*args,**kwargs)
self.master=master
self.lbl_text=ttk.Label(self.mainframe,text=“这是主窗口”)
self.lbl_text.pack()
self.btn=ttk.Button(self.mainframe,text=“打开第二个窗口”,
command=lambda:self.create_detail_window(self,number=0))
self.btn.pack()
self.newDetailsWindow={}
self.windows\u计数=0
def创建详细信息窗口(自,*事件,编号=无):
self.windows\u计数+=1
self.newDetailsWindow[self.windows\u count]=tk.Toplevel(self.master)
self.newDetailsWindow[self.windows\u count].geometry('900x600+80+130'))
self.newDetailsWindow[self.windows\u count].title(f'Detail:{self.windows\u count})
self.newDetailsWindow[self.windows\u count].wm\u协议(“wm\u DELETE\u WINDOW”,self.newDetailsWindow[self.windows\u count].destroy)
#self.newDetailsWindow[self.windows\u count].bind(“命令-w”,lambda事件:self.newDetailsWindow[-1].destroy())
self.detail\u window=detailWindow(self.newDetailsWindow[self.windows\u count],self.windows\u count)
self.ne