使用qasync与aiohttp时QSystemTrayIcon无法显示,求可行解决方案
问题根因
web.run_app()是aiohttp的阻塞式启动方法,调用后会独占当前线程,后续Qt的界面事件循环完全没有运行机会,因此系统托盘图标不会被渲染显示。- 你已经初始化了
qasync.QEventLoop但没有显式启动该事件循环,程序执行到trayIcon.startServer()后就直接阻塞,后续逻辑全部无法执行。 - 额外注意:部分桌面环境要求
QSystemTrayIcon必须显式设置图标才会显示,未设置图标时也可能出现托盘不显示的问题。
修复步骤
- 替换阻塞的
web.run_app调用,改用aiohttp的非阻塞API手动启动服务,共用qasync的事件循环。 - 显式启动qasync的事件循环,将aiohttp服务启动逻辑作为协程提交到事件循环中运行。
- 为
QSystemTrayIcon显式设置图标,兼容不同桌面环境的显示要求。
完整修复后代码
import asyncio import sys import datetime from functools import cached_property from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui import QIcon import qasync from aiohttp import web class TrojanGUI(QSystemTrayIcon): def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) # 显式设置托盘图标,这里用Qt内置图标,可替换为自定义图标路径 self.setIcon(QApplication.style().standardIcon(QStyle.SP_ComputerIcon)) self.setup_server() # 可选:添加右键菜单方便退出程序 menu = QMenu() exit_action = menu.addAction("退出") exit_action.triggered.connect(QApplication.quit) self.setContextMenu(menu) def setup_server(self): self.app.add_routes([web.get("/proxy.pac", self.hello)]) @cached_property def app(self): return web.Application() async def hello(self, request): print(datetime.datetime.now().strftime("%H:%M:%S")) # 这里为示例pac内容,可替换为你的实际逻辑 self.pacContent = "function FindProxyForURL(url, host) { return 'PROXY 127.0.0.1:8080'; }" return web.Response(text=self.pacContent) # 改为异步非阻塞启动方法 async def startServer(self): runner = web.AppRunner(self.app) await runner.setup() site = web.TCPSite(runner, port=23336) await site.start() print("服务已启动,监听端口23336") # 保持服务持续运行 while True: await asyncio.sleep(3600) def main(): QApplication.setQuitOnLastWindowClosed(False) app = QApplication(sys.argv) loop = qasync.QEventLoop(app) asyncio.set_event_loop(loop) trayIcon = TrojanGUI() trayIcon.show() # 整合服务启动和Qt事件循环运行 with loop: loop.create_task(trayIcon.startServer()) loop.run_forever() if __name__ == "__main__": main()
内容的提问来源于stack exchange,提问作者iMath
相关产品推荐
相关产品推荐

