有没有办法测试/检查tkinter mainloop是否正在运行?

2024-10-03 04:34:26 发布

您现在位置:Python中文网/ 问答频道 /正文

我想知道是否有办法测试/检查tkintermainloop是否以某种方式运行?发出多个tk.Tk().mainloop()命令将破坏GUI,但是我还没有找到一种方法来检查mainloop是否已经为tk._default_root启动了


Tags: 方法命令default方式guiroottk办法
3条回答

matplotlib源代码发布基于线程的解决方案:

lib/matplotlib/backends/_backend_tk.py

def is_tk_mainloop_running():
    import threading
    dispatching = sentinel = object()

    def target():
        nonlocal dispatching
        try:
            tk._default_root.call('while', '0', '{}')
        except RuntimeError as e:
            if str(e) == "main thread is not in main loop":
                print('not dispatching')
                dispatching = False
            else:
                raise
        except BaseException as e:
            print(e)
            raise
        else:
            dispatching = True
            print('dispatching')

    t = threading.Thread(target=target, daemon=True)
    t.start()
    tk._default_root.update()
    t.join()

    if dispatching is sentinel:
        raise RuntimeError('thread failed to determine dispatching value')

    return dispatching

令人烦恼的是,虽然Tk().willdispatch()存在(尽管它似乎没有文档记录)撒谎说mainloop正在运行,而它没有运行(我使用它来允许asyncio任务将asyncio事件与tkinter事件交错,但它设置的底层标志(C级结构成员dispatching)没有Python级API

测试标志的唯一地方是在自动封送从非主线程到mainloop线程的调用的函数中,而将其用作检测机制并不实际(它只需要生成线程来执行测试,涉及超时,并在失败时抛出异常,即使您可以让它工作,也会让它变得丑陋)

简言之,它将由您负责。您可能使用的解决方案是创建一个子类,该子类拦截对mainloop的调用并记录已调用的调用:

class CheckableTk(Tk):
    def __init__(self, *args, **kwargs):
        self.running = False
        super().__init__(*args, **kwargs)

    def mainloop(self, *args, **kwargs):
        self.running = True
        try:
            return super().mainloop(*args, **kwargs)
        finally:
            self.running = False

    def willdispatch(self, *args, **kwargs):
        self.running = True  # Lie just like willdispatch lies
        return super().willdispatch(*args, **kwargs)

这不是一个很好的解决方案,我一般不鼓励这样做。真正的答案是,有一个,而且只有一个,运行mainloop的单一位置,而不是将可能的启动点分散到整个程序中

如果您知道进程名称,在Windows中,可以使用psutil检查进程是否正在运行

导入PSUTIL:

import psutil

生成一个函数以检查进程是否正在运行:

def is_running(name: str) -> bool:
    name = name if name.endswith('.exe') else name + '.exe'

    return name in [process.name() for process in psutil.process_iter()]

或用于终止进程的函数:

def kill(name: str) -> bool:
    name = name if name.endswith('.exe') else name + '.exe'

    for process in psutil.process_iter():
        if process.name() == name:
            process.kill()
            return True

    return False

然后你可以像这样简单地使用它们

if __name__ == "__main__":
    app_name = 'chrome'

    if is_running(name=app_name):
        if kill(name=app_name):
            print(f'{app_name} killed!')

    else:
        print(f'{app_name} is not running!')

相关问题 更多 >