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

时间:2018-09-10 12:03:55

标签: python tkinter

是否有Tk的确定属性可以检查主循环是否已停止运行或根窗口是否已被破坏?

下面的最小代码显示了由于Tk显然无法传播python异常而导致的问题。要查看实际问题,请单击根窗口按钮“启动子窗口对话框”。接下来,使用其关闭窗口按钮(红色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”即可正确终止。相反,程序以未处理的“ U​​nrecognizedButtonException”终止。完整的错误消息如下。请注意,即使已将控制权从Tk交还给python,“ try / except处理程序”也无法通过stdout报告“ ProgramIsEnding”异常。

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是否还有其他一些属性可以记录主循环的结束或根窗口是否已被破坏?

2 个答案:

答案 0 :(得分:0)

您可以在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

答案 1 :(得分:0)

简短的答案是Tk没有设置属性。 Tkinter通过为每个小部件提供一个Thread number 140672336922368 addr(errno):0x7ff0d4ac0698 Thread number 140672345315072 addr(errno):0x7ff0d52c1698 Thread number 140672328529664 addr(errno):0x7ff0d42bf698 Thread number 140672320136960 addr(errno):0x7ff0d3abe698 Thread number 140672311744256 addr(errno):0x7ff0d32bd698 事件来处理此问题。

下面的更正代码具有以下三个功能:

  • 将tkinter的<Destroy>事件绑定到子窗口的处理程序 (<Destroy>)将调用者和程序员与任何其他人隔离 需要了解可能导致Tk关闭的各种事情 子窗口。因此,程序员可以自由地专注于处理关闭的影响。

  • destroy_callback引发TkClosedWindow异常 退出。可以在呼叫链中的任何适当位置进行处理 YesNoDialog.__call__。如果以后编写的任何代码均无法处理该异常,则程序将快速失败并显示一条明确的错误消息。

  • 事件处理程序设置一个标志来指示MainWindow.run_dialog 已被关闭。无法在此内部引发python异常 处理程序,因为Tk / Tcl不会传播python异常。

YesNoDialog.window