poc/project/llmclipboard/tray_test.py
2025-03-02 18:56:33 +08:00

325 lines
12 KiB
Python

#!/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()