为什么" wm_protocol"打破Python3 / tkinter中的正常窗口管理?

时间:2017-04-19 13:57:28

标签: python oop tkinter window-management

我正在测试一个有点大的Python 3.6项目的tkinter窗口管理,有一件事我似乎无法正确或甚至不能理解。在下面的代码中,窗口按预期打开和关闭(我的意思是,通过单击红色' x'按钮或按OS X中的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()

似乎当我对行self.newDetailsWindow.wm_protocol("WM_DELETE_WINDOW", lambda: self.close_detail_window())取消注释时,窗口管理会被破坏。行self.newDetailsWindow.destroy()是不是足以简单地关闭辅助窗口?...我在实例化对象的方式上做错了吗?

3 个答案:

答案 0 :(得分:2)

我对您的代码进行了多次调整。它现在应该工作。基本上,您的方法X每次调用时都会重新分配属性<TextBox Style="{StaticResource MaterialDesignFloatingHintTextBox}" materialDesign:HintAssist.Hint="test" materialDesign:HintAssist.IsFloating="True" materialDesign:HintAssist.FloatingOffset="-15, 0" Text="30.03.17" Padding="15 0 0 0"> ,这就是&#39; x&#39;按钮将被发送到错误的窗口。我使用app.create_detail_window来存储您创建的所有self.newDetailWindow

dict

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

如评论中所述,在您的情况下,以下代码仍然有效。

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))
        self.btn.pack()
        self.windows_count=0

    def create_detail_window(self, *event):
        self.windows_count+=1
        newDetailsWindow=tk.Toplevel()
        newDetailsWindow.geometry('900x600+80+130')
        newDetailsWindow.title(f'Detail: {self.windows_count}')
        newDetailsWindow.wm_protocol("WM_DELETE_WINDOW", newDetailsWindow.destroy)
        self.detail_window = detailWindow(newDetailsWindow, self.windows_count)
        newDetailsWindow.focus()


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()