Python3适配SysTrayIcon.py后win32gui.PumpMessages阻塞后台代码问题
问题原因
win32gui.PumpMessages()是Windows提供的消息泵函数,内部是永久运行的阻塞循环,专门负责分发处理窗口系统消息,直到接收到退出消息才会返回。你把它放在SysTrayIcon的__init__方法末尾,实例化这个类的时候代码就会直接卡在这里,永远不会执行到后面的自定义while循环。
解决方案
方案1:业务逻辑放入独立子线程运行(改動最小,推荐)
不需要修改SysTrayIcon原有代码,只要把LED控制的业务逻辑放到单独的子线程启动,再初始化托盘图标即可:
import threading class Main(): def __init__(self): self.OnSwitch = True hover_text = "Screen-LED-Imitator" menu_options = ( ("De/Activate", "H:\Programs\Scripts\ScreenToLED\Apps-colors-icon_31805.ico", self.dexactivate), ("Reset", "H:\Programs\Scripts\ScreenToLED\Apps-colors-icon_31805.ico", self.reset) ) # 先启动业务逻辑子线程,daemon参数保证主线程退出时子线程自动销毁 self.worker_thread = threading.Thread(target=self.run_led_logic, daemon=True) self.worker_thread.start() # 再启动托盘图标(会阻塞主线程,但子线程会正常运行) SysTrayIcon("H:\Programs\Scripts\ScreenToLED\Apps-colors-icon_31805.ico", hover_text, menu_options, on_quit=self.bye, default_menu_index=1) def run_led_logic(self): while True: if self.OnSwitch: self.average_image_color() self.ColorToLed()
方案2:修改SysTrayIcon源码,替换阻塞消息泵
如果不想用多线程,可以把原有阻塞的消息泵替换成自定义循环,每次消息处理空闲时执行你的业务逻辑:
- 先修改SysTrayIcon类的代码,把
__init__末尾的win32gui.PumpMessages()注释掉,新增start方法:
# 注释掉原来的阻塞调用 # win32gui.PumpMessages() # 新增自定义消息循环方法 def start(self, idle_callback=None): import time while True: # 处理待处理的系统消息 if win32gui.PeekMessage(None, 0, 0, win32con.PM_REMOVE): win32gui.TranslateMessage(None) win32gui.DispatchMessage(None) # 收到退出消息就终止循环 if getattr(None, 'message', 0) == win32con.WM_QUIT: break else: # 无消息时执行传入的空闲业务逻辑 if callable(idle_callback): idle_callback() # 加短延时避免占用满CPU time.sleep(0.01)
- 再修改你的Main类调用逻辑:
class Main(): def __init__(self): self.OnSwitch = True hover_text = "Screen-LED-Imitator" menu_options = ( ("De/Activate", "H:\Programs\Scripts\ScreenToLED\Apps-colors-icon_31805.ico", self.dexactivate), ("Reset", "H:\Programs\Scripts\ScreenToLED\Apps-colors-icon_31805.ico", self.reset) ) # 实例化托盘类不会阻塞 self.tray = SysTrayIcon("H:\Programs\Scripts\ScreenToLED\Apps-colors-icon_31805.ico", hover_text, menu_options, on_quit=self.bye, default_menu_index=1) # 启动消息循环,传入空闲时执行的业务逻辑 self.tray.start(idle_callback=self.led_logic_step) def led_logic_step(self): if self.OnSwitch: self.average_image_color() self.ColorToLed()
注意事项
- 多线程方案下如果在菜单回调里修改
self.OnSwitch这类共享变量,复杂逻辑下建议加线程锁避免读写冲突,简单场景Python的GIL可以保证基础的原子性。 - 两种方案都不会影响托盘图标的原有功能,菜单点击、消息响应逻辑和原来完全一致。
内容的提问来源于stack exchange,提问作者JakoLex
相关产品推荐
相关产品推荐

