代码之家  ›  专栏  ›  技术社区  ›  lemi57ssss

哪个tkinter属性表示tkinter是否仍在运行?

  •  -1
  • lemi57ssss  · 技术社区  · 6 年前

    Tk是否有一个明确的属性,我可以检查它是否mainloop已经停止运行,或者根窗口是否已经被破坏?

    下面的最小代码显示了一个由Tks传播python异常的明显失败引起的问题。要查看实际问题,请单击根窗口按钮,启动子窗口对话框。接下来,使用close window按钮(红色X)关闭根窗口。

    import sys
    import tkinter as tk
    
    class ProgramIsEnding(Exception):
        pass
    
    
    class UnrecognizedButtonException(Exception):
        pass
    
    
    class MainWindow(tk.Tk):
        def __init__(self):
            super().__init__()
            self.title('Root Window')
            button = tk.Button(text='Start The Child Window Dialog')
            button.configure(command=self.run_dialog)
            button.grid()
            self.protocol('WM_DELETE_WINDOW', self.delete_window_callback)
    
        def delete_window_callback(self):
            self.destroy()
            print('Root has been destroyed')
            raise ProgramIsEnding
    
        def run_dialog(self):
            try:
                button = YesNoDialog(self)()
            except ProgramIsEnding:
                print('Doing end of program stuff.')
                return
    
            print(f"Button returned is '{button}'")
            if button == 'yes':
                print("'Yes' button clicked")
            elif button == 'no':
                print("'No' button clicked")
            else:
                msg = f"button '{button}'"
                raise UnrecognizedButtonException(msg)
    
    
    class YesNoDialog:
        window: tk.Toplevel = None
        button_clicked = None
    
        def __init__(self, parent):
            self.parent = parent
    
        def __call__(self):
            self.create_window()
            return self.button_clicked
    
        def create_window(self):
            self.window = tk.Toplevel(self.parent)
            yes = tk.Button(self.window, text='Yes', command=self.yes_command)
            yes.pack(side='left')
            no = tk.Button(self.window, text='No', command=self.no_command)
            no.pack(side='left')
            self.window.wait_window()
    
        def yes_command(self):
            self.button_clicked = 'yes'
            self.window.destroy()
    
        def no_command(self):
            self.button_clicked = 'no'
            self.window.destroy()
    
    def main():
        tkroot = MainWindow()
        tkroot.mainloop()
    
    
    if __name__ == '__main__':
        sys.exit(main())
    

    如果代码按预期工作,它将在捕获异常后正确终止,即ProgramIsEnding。相反,程序会以未处理的未识别ButtonException终止。下面是完整的错误消息。请注意,尽管try/except处理程序在从Tk将控制权交还给python之后无法使用programmisending异常,但还是通过stdout报告了该异常。

    Root has been destroyed
    Exception in Tkinter callback
    Traceback (most recent call last):
      File "[…]/python3.7/tkinter/__init__.py", line 1702, in __call__
        return self.func(*args)
      File "[…]/wmdeletedemo.py", line 25, in delete_window_callback
         raise ProgramIsEnding
    ProgramIsEnding
    Exception in Tkinter callback
    Traceback (most recent call last):
      File "[…]/python3.7/tkinter/__init__.py", line 1702, in __call__
        return self.func(*args)
      File "[…]/wmdeletedemo.py", line 41, in run_dialog
        raise UnrecognizedButtonException(msg)
    UnrecognizedButtonException: button 'None'
    Button returned is 'None'
    

    一个明显的解决方法是检查按钮值是否为None,如果是,则返回。不过,对我来说,良好的实践建议我应该检查主要事件,既不依赖其次要效果,也不依赖于设置标志。

    那么,Tk或tkinter是否还有其他属性记录mainloop的结束,或者根窗口是否已被破坏?

    2 回复  |  直到 6 年前
        1
  •  0
  •   Rachit Tayal    6 年前

    可以在delete回调方法中将任何变量设置为true。然后您只需在正确的位置检查变量。像这样的。

    def _delete_window_callback(self):
        """Carry out actions needed when main window is closed."""
    
        # Save the current geometry
        geometry = self.winfo_geometry()
        config.config_dynamic.set(self.config_section, self.config_geometry, geometry)
    
        destroyed = true
    
        # Destroy all widgets and end mainloop.
        self.destroy()
    
    destroyed = false
    if destroyed:
        break
    
        2
  •  0
  •   lemi57ssss    6 年前

    简而言之,Tk不设置属性。Tkinter通过提供 <Destroy> 每个小部件的事件。

    • 装订机 <销毁> destroy_callback )将调用者和程序员与任何 孩子的窗户。因此,程序员可以自由地集中精力处理闭包的效果。

    • A TkClosedWindow 出现异常时 YesNoDialog.__call__ 对于 MainWindow.run_dialog . 如果以后编写的任何代码都无法处理异常,程序将很快失败并显示错误消息。

    • YesNoDialog.window 因为Tk/Tcl不传播python异常。

    .

    import sys
    import tkinter as tk
    
    
    class TkClosedWindow(Exception):
        pass
    
    
    class UnrecognizedButtonException(Exception):
        pass
    
    
    class MainWindow(tk.Tk):
        def __init__(self):
            super().__init__()
            self.title('Root Window')
            button = tk.Button(text='Start The Child Window Dialog')
            button.configure(command=self.run_dialog)
            button.grid()
    
        def run_dialog(self):
            try:
                button = YesNoDialog(self)()
            except TkClosedWindow:
                print('Doing end of program stuff.')
                return
    
            print(f"Button returned is '{button}'")
            if button == 'yes':
                print("Yes button clicked")
            elif button == 'no':
                print("No button clicked")
            else:
                msg = f"button '{button}'"
                raise UnrecognizedButtonException(msg)
    
    
    class YesNoDialog:
        window: tk.Toplevel = None
        button_clicked = None
        closed_by_tk = False
    
        def __init__(self, parent):
            self.parent = parent
    
        def __call__(self):
            self.create_window()
            if self.closed_by_tk:
                raise TkClosedWindow
            else:
                return self.button_clicked
    
        def create_window(self):
            self.window = tk.Toplevel(self.parent)
            self.window.bind('<Destroy>', self.destroy_callback)
            yes = tk.Button(self.window, text='Yes', command=self.yes_command)
            yes.pack(side='left')
            no = tk.Button(self.window, text='No', command=self.no_command)
            no.pack(side='left')
            self.window.wait_window()
    
        def yes_command(self):
            self.button_clicked = 'yes'
            self.window.destroy()
    
        def no_command(self):
            self.button_clicked = 'no'
            self.window.destroy()
    
        def destroy_callback(self, *args):
            self.closed_by_tk = True
    
    def main():
        tkroot = MainWindow()
        tkroot.mainloop()
    
    
    if __name__ == '__main__':
        sys.exit(main())