EU-Utility/core/tray_icon.py

194 lines
7.0 KiB
Python

"""
EU-Utility - System Tray Icon (Debug Version)
=============================================
Simple, responsive system tray icon with debug logging.
"""
from PyQt6.QtWidgets import QSystemTrayIcon, QMenu, QApplication, QWidget
from PyQt6.QtGui import QAction, QIcon, QColor, QPainter, QFont, QPixmap
from PyQt6.QtCore import pyqtSignal, Qt, QTimer
# Import debug logger
try:
from core.debug_logger import debug_logger
DEBUG_AVAILABLE = True
except ImportError:
DEBUG_AVAILABLE = False
# Dummy logger
class DummyLogger:
def debug(self, *args): pass
def info(self, *args): pass
def warn(self, *args): pass
def error(self, *args): pass
def start_timer(self, *args): pass
def end_timer(self, *args): return 0.0
debug_logger = DummyLogger()
class TrayIcon(QWidget):
"""Simple system tray icon for EU-Utility."""
show_dashboard = pyqtSignal()
toggle_activity_bar = pyqtSignal()
open_settings = pyqtSignal()
quit_app = pyqtSignal()
def __init__(self, app: QApplication, parent=None):
debug_logger.start_timer("TrayIcon.__init__")
super().__init__(parent)
self.app = app
debug_logger.debug("TRAY", "Creating tray icon...")
# Create icon
self._create_icon()
# Create menu (but don't set it yet)
debug_logger.debug("TRAY", "Creating menu...")
self._create_menu()
# Connect signals
debug_logger.debug("TRAY", "Connecting signals...")
self.tray_icon.activated.connect(self._on_activated)
debug_logger.end_timer("TrayIcon.__init__")
debug_logger.info("TRAY", "Tray icon initialized")
def _create_icon(self):
"""Create tray icon."""
debug_logger.start_timer("TrayIcon._create_icon")
# Create simple icon
px = QPixmap(64, 64)
px.fill(QColor(255, 140, 66)) # Orange
painter = QPainter(px)
painter.setPen(QColor(255, 255, 255))
font = QFont("Segoe UI", 24, QFont.Weight.Bold)
painter.setFont(font)
painter.drawText(px.rect(), 0x84, "EU") # AlignCenter
painter.end()
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(QIcon(px))
self.tray_icon.setToolTip("EU-Utility")
self.tray_icon.show()
debug_logger.end_timer("TrayIcon._create_icon")
def _create_menu(self):
"""Create simple context menu."""
debug_logger.start_timer("TrayIcon._create_menu")
self.menu = QMenu()
# Track menu timing
self.menu.aboutToShow.connect(self._on_menu_about_to_show)
self.menu.aboutToHide.connect(self._on_menu_about_to_hide)
# Simple actions (no emojis to avoid encoding issues)
debug_logger.debug("TRAY", "Creating Dashboard action...")
self.dashboard_action = QAction("Dashboard (Click to Open)", self)
self.dashboard_action.triggered.connect(self._on_dashboard_clicked)
self.menu.addAction(self.dashboard_action)
debug_logger.debug("TRAY", "Creating Activity Bar action...")
self.activity_bar_action = QAction("Activity Bar (Ctrl+Shift+U)", self)
self.activity_bar_action.setCheckable(True)
self.activity_bar_action.triggered.connect(self._on_activity_bar_clicked)
self.menu.addAction(self.activity_bar_action)
self.menu.addSeparator()
debug_logger.debug("TRAY", "Creating Quit action...")
self.quit_action = QAction("Quit", self)
self.quit_action.triggered.connect(self._on_quit_clicked)
self.menu.addAction(self.quit_action)
# Set context menu
debug_logger.debug("TRAY", "Setting context menu...")
self.tray_icon.setContextMenu(self.menu)
debug_logger.end_timer("TrayIcon._create_menu")
def _on_menu_about_to_show(self):
"""Called when menu is about to show."""
debug_logger.start_timer("TRAY_MENU_SHOW")
debug_logger.debug("TRAY", "Menu about to show")
def _on_menu_about_to_hide(self):
"""Called when menu is about to hide."""
elapsed = debug_logger.end_timer("TRAY_MENU_SHOW")
debug_logger.debug("TRAY", f"Menu was visible for {elapsed:.2f}ms")
def _on_dashboard_clicked(self):
"""Handle dashboard click with timing."""
debug_logger.start_timer("TRAY_DASHBOARD_CLICK")
debug_logger.debug("TRAY", "Dashboard clicked - emitting signal...")
# Use single shot to not block menu
QTimer.singleShot(0, self._emit_dashboard)
def _emit_dashboard(self):
"""Emit dashboard signal."""
debug_logger.debug("TRAY", "Emitting show_dashboard signal...")
self.show_dashboard.emit()
debug_logger.end_timer("TRAY_DASHBOARD_CLICK")
def _on_activity_bar_clicked(self):
"""Handle activity bar click with timing."""
debug_logger.start_timer("TRAY_ACTIVITYBAR_CLICK")
debug_logger.debug("TRAY", "Activity Bar clicked - emitting signal...")
# Use single shot to not block menu
QTimer.singleShot(0, self._emit_activity_bar)
def _emit_activity_bar(self):
"""Emit activity bar signal."""
debug_logger.debug("TRAY", "Emitting toggle_activity_bar signal...")
self.toggle_activity_bar.emit()
debug_logger.end_timer("TRAY_ACTIVITYBAR_CLICK")
def _on_quit_clicked(self):
"""Handle quit click with timing."""
debug_logger.start_timer("TRAY_QUIT_CLICK")
debug_logger.debug("TRAY", "Quit clicked - emitting signal...")
# Use single shot to not block menu
QTimer.singleShot(0, self._emit_quit)
def _emit_quit(self):
"""Emit quit signal."""
debug_logger.debug("TRAY", "Emitting quit_app signal...")
self.quit_app.emit()
debug_logger.end_timer("TRAY_QUIT_CLICK")
def show(self):
"""Show the tray icon."""
debug_logger.debug("TRAY", "show() called")
if self.tray_icon:
self.tray_icon.show()
def hide(self):
"""Hide the tray icon."""
debug_logger.debug("TRAY", "hide() called")
if self.tray_icon:
self.tray_icon.hide()
def isVisible(self):
"""Check if tray icon is visible."""
return self.tray_icon.isVisible() if self.tray_icon else False
def _on_activated(self, reason):
"""Handle double-click."""
debug_logger.debug("TRAY", f"Activated with reason: {reason}")
if reason == QSystemTrayIcon.ActivationReason.DoubleClick:
debug_logger.debug("TRAY", "Double-click detected - showing dashboard")
self.show_dashboard.emit()
def set_activity_bar_checked(self, checked: bool):
"""Update checkbox."""
debug_logger.debug("TRAY", f"Setting Activity Bar checked: {checked}")
self.activity_bar_action.setChecked(checked)