使用psgtray SystemTray时调用sg.popup触发AttributeError错误求助
问题:PySimpleGUI结合psgtray开发托盘应用时点击菜单触发AttributeError
点击托盘菜单的“Show Audio Devices”选项调用sg.popup时,触发以下错误:
An error occurred when calling message handler Traceback (most recent call last): File "C:\Users\willwade\.pyenv\pyenv-win\versions\3.11.4\Lib\site-packages\pystray\_win32.py", line 398, in _dispatcher return int(icon._message_handlers.get( ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\willwade\.pyenv\pyenv-win\versions\3.11.4\Lib\site-packages\pystray\_win32.py", line 210, in _on_notify descriptors[index - 1](self) File "C:\Users\willwade\.pyenv\pyenv-win\versions\3.11.4\Lib\site-packages\pystray\_base.py", line 308, in inner callback(self) File "C:\Users\willwade\.pyenv\pyenv-win\versions\3.11.4\Lib\site-packages\pystray\_base.py", line 434, in __call__ return self._action(icon, self) ^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\willwade\.pyenv\pyenv-win\versions\3.11.4\Lib\site-packages\psgtray\psgtray.py", line 138, in _on_clicked self.window.write_event_value(self.key, item.text) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'write_event_value'
原代码如下:
import PySimpleGUI as sg from psgtray import SystemTray import pyaudio import threading def findAudioDevices(): p = pyaudio.PyAudio() info = p.get_host_api_info_by_index(0) numdevices = info.get('deviceCount') device_info = "" for i in range(0, numdevices): if (p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0: device_info += f"Input Device ID {i} - {p.get_device_info_by_host_api_device_index(0, i).get('name')}\n" sg.popup('Audio Devices', device_info) # PySimpleGUI setup menu_def = ['File', ['Show Audio Devices', 'Open Config', 'Exit']] tooltip = 'Tooltip' # Initialize the tray tray = SystemTray(menu=menu_def, single_click_events=False, tooltip=tooltip, icon=r'Icon.png') tray.show_message('Sound Switch', 'Sound Switchs Started!') # Event loop while True: menu_item = tray.key if menu_item == 'Exit': break elif menu_item == 'Show Audio Devices': findAudioDevices() # This function should probably be modified to display a popup instead of printing to console elif menu_item == 'Open Config': os.system(f"notepad {config_path}") tray.close()
解决方案
错误原因
SystemTray初始化时未关联PySimpleGUI窗口,导致内部self.window为None,触发write_event_value调用失败。- 事件循环写法错误:直接读取
tray.key不是psgtray的正确事件处理方式,必须依托PySimpleGUI的窗口事件循环。
修正后的代码
import PySimpleGUI as sg from psgtray import SystemTray import pyaudio import os # 补充原代码遗漏的模块导入 def findAudioDevices(): p = pyaudio.PyAudio() info = p.get_host_api_info_by_index(0) numdevices = info.get('deviceCount') device_info = "" for i in range(numdevices): device = p.get_device_info_by_host_api_device_index(0, i) if device.get('maxInputChannels') > 0: device_info += f"Input Device ID {i} - {device.get('name')}\n" sg.popup('Audio Devices', device_info) p.terminate() # 释放pyaudio资源,避免内存泄漏 # 创建隐藏的主窗口(托盘应用必备,用于绑定事件循环) layout = [[sg.Text("Sound Switch", visible=False)]] window = sg.Window("Sound Switch", layout, finalize=True, visible=False) # 初始化托盘,关联主窗口 menu_def = ['File', ['Show Audio Devices', 'Open Config', 'Exit']] tooltip = 'Tooltip' tray = SystemTray(menu=menu_def, single_click_events=False, tooltip=tooltip, icon=r'Icon.png', window=window) tray.show_message('Sound Switch', 'Sound Switch Started!') config_path = "config.ini" # 补充原代码未定义的变量 # 正确的事件循环 while True: event, values = window.read() # 处理托盘菜单事件 if event == tray.key: event = values[event] # 获取点击的菜单项文本 if event in (sg.WIN_CLOSED, 'Exit'): break elif event == 'Show Audio Devices': findAudioDevices() elif event == 'Open Config': os.system(f"notepad {config_path}") # 清理资源 tray.close() window.close()
关键修改点
- 创建隐藏的PySimpleGUI窗口并传给
SystemTray的window参数,解决self.window为None的核心问题。 - 使用
window.read()处理事件,通过tray.key识别托盘事件,再从values中获取具体菜单项文本。 - 补充原代码遗漏的
os模块导入和config_path变量定义。 - 在音频设备查询函数中添加资源释放逻辑,避免内存泄漏。
内容的提问来源于stack exchange,提问作者willwade
相关产品推荐
相关产品推荐

