# 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)