You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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()

解决方案

错误原因

  1. SystemTray初始化时未关联PySimpleGUI窗口,导致内部self.window为None,触发write_event_value调用失败。
  2. 事件循环写法错误:直接读取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 01:57:45