You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
5.4 KiB
180 lines
5.4 KiB
# P2P Network Communication - System Tray Manager
|
|
"""
|
|
系统托盘管理器
|
|
实现系统托盘图标、新消息通知和后台运行
|
|
|
|
需求: 9.3, 9.5
|
|
"""
|
|
|
|
import logging
|
|
from typing import Optional, Callable
|
|
|
|
from PyQt6.QtWidgets import QSystemTrayIcon, QMenu, QApplication
|
|
from PyQt6.QtGui import QIcon, QAction
|
|
from PyQt6.QtCore import pyqtSignal, QObject
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class SystemTrayManager(QObject):
|
|
"""
|
|
系统托盘管理器
|
|
|
|
实现系统托盘图标 (需求 9.3)
|
|
实现新消息通知 (需求 9.3)
|
|
实现后台运行 (需求 9.5)
|
|
"""
|
|
|
|
# 信号定义
|
|
show_window_requested = pyqtSignal()
|
|
quit_requested = pyqtSignal()
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
|
|
self._tray_icon: Optional[QSystemTrayIcon] = None
|
|
self._menu: Optional[QMenu] = None
|
|
self._unread_count: int = 0
|
|
|
|
self._setup_tray()
|
|
|
|
logger.info("SystemTrayManager initialized")
|
|
|
|
def _setup_tray(self) -> None:
|
|
"""设置系统托盘"""
|
|
if not QSystemTrayIcon.isSystemTrayAvailable():
|
|
logger.warning("System tray is not available")
|
|
return
|
|
|
|
self._tray_icon = QSystemTrayIcon(self.parent())
|
|
|
|
# 设置默认图标(使用应用程序图标或创建简单图标)
|
|
app = QApplication.instance()
|
|
if app and not app.windowIcon().isNull():
|
|
self._tray_icon.setIcon(app.windowIcon())
|
|
else:
|
|
# 创建一个简单的默认图标
|
|
from PyQt6.QtGui import QPixmap, QPainter, QColor
|
|
pixmap = QPixmap(32, 32)
|
|
pixmap.fill(QColor("#4a90d9"))
|
|
painter = QPainter(pixmap)
|
|
painter.setPen(QColor("white"))
|
|
painter.drawText(pixmap.rect(), 0x0084, "P2P") # AlignCenter
|
|
painter.end()
|
|
self._tray_icon.setIcon(QIcon(pixmap))
|
|
|
|
self._tray_icon.setToolTip("P2P 通信应用")
|
|
|
|
# 创建菜单
|
|
self._menu = QMenu()
|
|
|
|
show_action = QAction("显示主窗口", self._menu)
|
|
show_action.triggered.connect(self._on_show_window)
|
|
self._menu.addAction(show_action)
|
|
|
|
self._menu.addSeparator()
|
|
|
|
self._status_action = QAction("状态: 在线", self._menu)
|
|
self._status_action.setEnabled(False)
|
|
self._menu.addAction(self._status_action)
|
|
|
|
self._menu.addSeparator()
|
|
|
|
quit_action = QAction("退出", self._menu)
|
|
quit_action.triggered.connect(self._on_quit)
|
|
self._menu.addAction(quit_action)
|
|
|
|
self._tray_icon.setContextMenu(self._menu)
|
|
|
|
# 连接信号
|
|
self._tray_icon.activated.connect(self._on_tray_activated)
|
|
|
|
def _on_show_window(self) -> None:
|
|
"""显示主窗口"""
|
|
self.show_window_requested.emit()
|
|
|
|
def _on_quit(self) -> None:
|
|
"""退出应用"""
|
|
self.quit_requested.emit()
|
|
|
|
def _on_tray_activated(self, reason: QSystemTrayIcon.ActivationReason) -> None:
|
|
"""托盘图标激活"""
|
|
if reason == QSystemTrayIcon.ActivationReason.DoubleClick:
|
|
self.show_window_requested.emit()
|
|
|
|
# ==================== 公共方法 ====================
|
|
|
|
def is_available(self) -> bool:
|
|
"""
|
|
检查系统托盘是否可用
|
|
|
|
Returns:
|
|
是否可用
|
|
"""
|
|
return self._tray_icon is not None
|
|
|
|
def show(self) -> None:
|
|
"""显示托盘图标"""
|
|
if self._tray_icon:
|
|
self._tray_icon.show()
|
|
|
|
def hide(self) -> None:
|
|
"""隐藏托盘图标"""
|
|
if self._tray_icon:
|
|
self._tray_icon.hide()
|
|
|
|
def show_message(self, title: str, message: str,
|
|
icon: QSystemTrayIcon.MessageIcon = QSystemTrayIcon.MessageIcon.Information,
|
|
duration: int = 3000) -> None:
|
|
"""
|
|
显示通知消息
|
|
|
|
实现新消息通知 (需求 9.3)
|
|
WHEN 有新消息到达 THEN P2P_Client SHALL 在系统托盘显示通知提醒
|
|
|
|
Args:
|
|
title: 通知标题
|
|
message: 通知内容
|
|
icon: 图标类型
|
|
duration: 显示时长(毫秒)
|
|
"""
|
|
if self._tray_icon:
|
|
self._tray_icon.showMessage(title, message, icon, duration)
|
|
logger.debug(f"Tray notification: {title} - {message}")
|
|
|
|
def set_status(self, status: str) -> None:
|
|
"""
|
|
设置状态显示
|
|
|
|
Args:
|
|
status: 状态文本
|
|
"""
|
|
if self._status_action:
|
|
self._status_action.setText(f"状态: {status}")
|
|
|
|
if self._tray_icon:
|
|
self._tray_icon.setToolTip(f"P2P 通信应用 - {status}")
|
|
|
|
def set_unread_count(self, count: int) -> None:
|
|
"""
|
|
设置未读消息数
|
|
|
|
Args:
|
|
count: 未读消息数
|
|
"""
|
|
self._unread_count = count
|
|
|
|
if self._tray_icon:
|
|
if count > 0:
|
|
self._tray_icon.setToolTip(f"P2P 通信应用 - {count} 条未读消息")
|
|
else:
|
|
self._tray_icon.setToolTip("P2P 通信应用")
|
|
|
|
def increment_unread(self) -> None:
|
|
"""增加未读消息数"""
|
|
self.set_unread_count(self._unread_count + 1)
|
|
|
|
def clear_unread(self) -> None:
|
|
"""清除未读消息数"""
|
|
self.set_unread_count(0)
|