为什么我在使用 pystray 时无法关闭?

问题描述

我使用 tkinter 编写了一个程序,当主窗口关闭时,它应该最小化到系统托盘。但是当我尝试退出程序时,单击托盘中的“关闭”会触发以下功能

def quit_window(icon,item):
    icon.stop() # Удаление иконки из трея
    sys.exit(0) # Завершение программы

但它不起作用并抛出以下异常:

An error occurred when calling message handler
Traceback (most recent call last):
  File "C:\Users\a-par\mini_library_2020\env\lib\site-packages\pystray\_win32.py",line 386,in _dispatcher
    return int(icon._message_handlers.get(
  File "C:\Users\a-par\mini_library_2020\env\lib\site-packages\pystray\_win32.py",line 207,in _on_notify 
    descriptors[index - 1](self)
  File "C:\Users\a-par\mini_library_2020\env\lib\site-packages\pystray\_base.py",line 267,in inner
    callback(self)
  File "C:\Users\a-par\mini_library_2020\env\lib\site-packages\pystray\_base.py",line 368,in __call__
    return self._action(icon,self)
  File "c:/Users/a-par/mini_library_2020/LC.pyw",line 2976,in quit_window
    sys.exit(0)
SystemExit: 0

程序中还有一个 VK bot,它应该在程序最小化时工作(这是实际最小化到托盘的原因)。该机器人在不同于 GUI 线程中工作。我试图完全删除机器人,但没有任何帮助。也许问题是线程,但我不这么认为......

最少可重现的非工作代码

import pystray
import sys
import time
from PIL import Image
from pystray import Menu,MenuItem


def exit_action(icon):
    sys.exit(0)


def setup(icon):
    icon.visible = True
    
    i = 0
    while icon.visible:
        # Some payload code
        print(i)
        i += 1
        
        time.sleep(5)


def init_icon():
    icon = pystray.Icon('mon')
    icon.menu = Menu(
        MenuItem('Exit',lambda : exit_action(icon)),)
    icon.icon = Image.open('C:/Users/a-par/mini_library_2020/logo.ico')
    icon.title = 'tooltip'

    icon.run(setup)

init_icon()

Video

解决方法

sys.exit() 不会工作,因为它不是从主线程或其他东西执行的。 需要使用icon.stop()关闭pystray的事件循环,从而停止整个程序。