#!/usr/bin/env python # -*- coding: utf-8 -*- """ 系统托盘功能测试脚本 """ import sys import os import logging from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QWidget, QPushButton, QVBoxLayout, QLabel from PyQt6.QtGui import QIcon from PyQt6.QtCore import Qt # 配置日志 logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(), logging.FileHandler('tray_test.log', encoding='utf-8') ] ) logger = logging.getLogger('TrayTest') class TrayTestWindow(QWidget): def __init__(self): super().__init__() self.init_ui() self.create_tray_icon() def init_ui(self): """初始化UI""" self.setWindowTitle('系统托盘测试') self.setGeometry(300, 300, 400, 300) layout = QVBoxLayout() # 添加说明标签 label = QLabel('这是一个系统托盘功能测试程序。关闭窗口后,应用程序将最小化到系统托盘。') label.setWordWrap(True) layout.addWidget(label) # 添加测试按钮 self.show_message_button = QPushButton('显示托盘消息') self.show_message_button.clicked.connect(self.test_show_message) layout.addWidget(self.show_message_button) self.toggle_icon_button = QPushButton('切换托盘图标') self.toggle_icon_button.clicked.connect(self.test_toggle_icon) layout.addWidget(self.toggle_icon_button) self.quit_button = QPushButton('退出应用程序') self.quit_button.clicked.connect(self.quit_application) layout.addWidget(self.quit_button) self.setLayout(layout) def create_tray_icon(self): """创建系统托盘图标""" logger.info("创建系统托盘图标") # 检查系统是否支持系统托盘 if not QSystemTrayIcon.isSystemTrayAvailable(): logger.error("系统不支持系统托盘功能") return False try: # 创建一个永久存在的QWidget作为托盘图标的父对象 self._tray_parent = QWidget() self._tray_parent.setVisible(False) # 创建系统托盘图标 self.tray_icon = QSystemTrayIcon(self._tray_parent) # 创建托盘菜单 self.tray_menu = QMenu() # 添加菜单项 self.show_action = self.tray_menu.addAction("显示窗口") self.show_action.triggered.connect(self.show_and_activate) self.tray_menu.addSeparator() self.test_message_action = self.tray_menu.addAction("测试托盘消息") self.test_message_action.triggered.connect(self.test_show_message) self.test_icon_action = self.tray_menu.addAction("测试切换图标") self.test_icon_action.triggered.connect(self.test_toggle_icon) self.tray_menu.addSeparator() self.quit_action = self.tray_menu.addAction("退出") self.quit_action.triggered.connect(self.quit_application) # 设置托盘菜单 self.tray_icon.setContextMenu(self.tray_menu) # 设置图标 # 尝试使用系统图标 self.icon1 = QIcon.fromTheme("dialog-information") self.icon2 = QIcon.fromTheme("dialog-warning") # 如果系统图标不可用,使用标准图标 if self.icon1.isNull(): self.icon1 = self.style().standardIcon(self.style().StandardPixmap.SP_DialogApplyButton) if self.icon2.isNull(): self.icon2 = self.style().standardIcon(self.style().StandardPixmap.SP_DialogWarningButton) # 设置初始图标 self.tray_icon.setIcon(self.icon1) self.current_icon = 1 # 连接托盘图标的激活信号 self.tray_icon.activated.connect(self.on_tray_activated) # 显示托盘图标 self.tray_icon.show() # 显示启动消息 self.show_tray_message("系统托盘测试", "应用程序已启动并显示在系统托盘") logger.info("系统托盘图标创建完成") return True except Exception as e: logger.exception(f"创建系统托盘图标失败: {str(e)}") return False def show_tray_message(self, title, message, icon_type="info", duration=2000): """显示系统托盘消息""" if not hasattr(self, 'tray_icon') or self.tray_icon is None: logger.error(f"无法显示托盘消息: {title} - {message}") return False logger.info(f"显示托盘消息: {title} - {message}") # 设置图标类型 icon = None try: # 尝试使用枚举值 (PyQt6 6.0.0+) if icon_type == "info": icon = QSystemTrayIcon.MessageIcon.Information elif icon_type == "warning": icon = QSystemTrayIcon.MessageIcon.Warning elif icon_type == "error": icon = QSystemTrayIcon.MessageIcon.Critical else: icon = QSystemTrayIcon.MessageIcon.Information except: # 尝试使用整数值 (旧版PyQt6) try: if icon_type == "info": icon = QSystemTrayIcon.Information elif icon_type == "warning": icon = QSystemTrayIcon.Warning elif icon_type == "error": icon = QSystemTrayIcon.Critical else: icon = QSystemTrayIcon.Information except: # 使用整数值 if icon_type == "info": icon = 1 # Information elif icon_type == "warning": icon = 2 # Warning elif icon_type == "error": icon = 3 # Critical else: icon = 1 # Information # 尝试显示消息 try: self.tray_icon.showMessage(title, message, icon, duration) return True except Exception as e: logger.exception(f"显示托盘消息失败: {str(e)}") # 尝试使用整数值作为图标类型 try: # 将枚举转换为整数 if isinstance(icon, object) and hasattr(icon, "value"): icon_int = icon.value else: icon_int = 1 # 默认为Information self.tray_icon.showMessage(title, message, icon_int, duration) return True except Exception as e2: logger.exception(f"使用整数值显示托盘消息也失败: {str(e2)}") return False def on_tray_activated(self, reason): """处理托盘图标激活事件""" logger.info(f"托盘图标被激活,原因: {reason}") try: # 获取激活原因的枚举值 try: # 尝试使用枚举值 (PyQt6 6.0.0+) double_click = QSystemTrayIcon.ActivationReason.DoubleClick trigger = QSystemTrayIcon.ActivationReason.Trigger except: # 尝试使用整数值 (旧版PyQt6) try: double_click = QSystemTrayIcon.DoubleClick trigger = QSystemTrayIcon.Trigger except: # 使用整数值 double_click = 2 # DoubleClick trigger = 3 # Trigger # 处理双击事件 if reason == double_click: logger.info("托盘图标被双击,显示主窗口") self.show_and_activate() # 处理单击事件 elif reason == trigger: logger.info("托盘图标被单击") # 在某些系统上,单击可能不会自动显示上下文菜单 except Exception as e: logger.exception(f"处理托盘图标激活事件失败: {str(e)}") def show_and_activate(self): """显示并激活窗口""" logger.info("显示并激活窗口") self.show() self.setWindowState(self.windowState() & ~Qt.WindowState.WindowMinimized) self.activateWindow() def test_show_message(self): """测试显示托盘消息""" logger.info("测试显示托盘消息") self.show_tray_message( "测试消息", "这是一条测试消息,用于验证系统托盘通知功能是否正常工作。", "info" ) def test_toggle_icon(self): """测试切换托盘图标""" logger.info("测试切换托盘图标") if not hasattr(self, 'tray_icon') or self.tray_icon is None: logger.error("托盘图标不存在,无法切换图标") return try: if self.current_icon == 1: self.tray_icon.setIcon(self.icon2) self.current_icon = 2 self.show_tray_message("图标已切换", "当前使用警告图标") else: self.tray_icon.setIcon(self.icon1) self.current_icon = 1 self.show_tray_message("图标已切换", "当前使用信息图标") except Exception as e: logger.exception(f"切换托盘图标失败: {str(e)}") def quit_application(self): """退出应用程序""" logger.info("退出应用程序") try: # 隐藏托盘图标 if hasattr(self, 'tray_icon') and self.tray_icon is not None: logger.info("隐藏托盘图标") self.tray_icon.hide() # 退出应用程序 logger.info("调用QApplication.quit()") QApplication.quit() except Exception as e: logger.exception(f"退出应用程序失败: {str(e)}") # 强制退出 QApplication.exit(1) def closeEvent(self, event): """处理窗口关闭事件""" logger.info("处理窗口关闭事件") try: # 如果系统托盘可用且托盘图标已创建,则最小化到系统托盘 if (QSystemTrayIcon.isSystemTrayAvailable() and hasattr(self, 'tray_icon') and self.tray_icon is not None and self.tray_icon.isVisible()): logger.info("最小化到系统托盘") # 显示提示消息 self.show_tray_message( "系统托盘测试", "应用程序已最小化到系统托盘,双击托盘图标可以重新显示窗口" ) # 隐藏主窗口 self.hide() # 忽略关闭事件,防止应用程序退出 event.ignore() else: # 系统托盘不可用,正常关闭应用程序 logger.info("系统托盘不可用,正常关闭应用程序") event.accept() except Exception as e: logger.exception(f"处理窗口关闭事件失败: {str(e)}") # 出现异常时,默认接受关闭事件 event.accept() def main(): """主函数""" logger.info("应用程序启动") app = QApplication(sys.argv) # 设置应用程序不会在最后一个窗口关闭时退出 app.setQuitOnLastWindowClosed(False) window = TrayTestWindow() window.show() sys.exit(app.exec()) if __name__ == "__main__": main()