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

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