fix: Simplify tray icon - remove timer and complex styling that blocks UI

The tray icon was blocking the main UI thread because:
1. QTimer was updating menu state every second
2. Complex stylesheet on menu may cause blocking
3. Emojis in menu items might cause encoding issues

Simplified:
- Removed update timer (no longer needed)
- Removed complex stylesheet (use default)
- Removed emojis from menu items
- Made TrayIcon inherit QWidget for proper parent
- Simplified signal connections
This commit is contained in:
LemonNexus 2026-02-15 23:29:54 +00:00
parent eba2e029e3
commit 0d2494abd7
1 changed files with 43 additions and 122 deletions

View File

@ -1,32 +1,17 @@
"""
EU-Utility - System Tray Icon
=============================
EU-Utility - System Tray Icon (Simplified)
==========================================
System tray implementation with right-click menu.
Replaces the floating icon with a proper tray icon.
Simple, responsive system tray icon.
"""
from PyQt6.QtWidgets import QSystemTrayIcon, QMenu, QApplication
from PyQt6.QtGui import QAction
from PyQt6.QtCore import QTimer, pyqtSignal, QObject
from PyQt6.QtGui import QIcon, QColor, QPainter, QFont, QFontMetrics
from core.logger import get_logger
logger = get_logger(__name__)
from PyQt6.QtWidgets import QSystemTrayIcon, QMenu, QApplication, QWidget
from PyQt6.QtGui import QAction, QIcon, QColor, QPainter, QFont
from PyQt6.QtCore import pyqtSignal
class TrayIcon(QObject):
"""
System tray icon for EU-Utility.
Features:
- Right-click context menu
- Show/hide dashboard
- Toggle activity bar
- Settings access
- Quit option
"""
class TrayIcon(QWidget):
"""Simple system tray icon for EU-Utility."""
show_dashboard = pyqtSignal()
toggle_activity_bar = pyqtSignal()
@ -36,126 +21,62 @@ class TrayIcon(QObject):
def __init__(self, app: QApplication, parent=None):
super().__init__(parent)
self.app = app
self.tray_icon = None
self.menu = None
# Create icon
self._create_icon()
self._setup_menu()
self._setup_visibility_timer()
def _create_icon(self):
"""Create the tray icon with EU logo."""
# Create a simple colored circle icon with "EU" text
pixmap = QIcon.fromTheme("applications-system")
# Create menu (but don't set it yet)
self._create_menu()
# If no system icon, create custom
if pixmap.isNull():
from PyQt6.QtGui import QPixmap, QPainter, QColor, QFont
px = QPixmap(64, 64)
px.fill(QColor(255, 140, 66)) # Orange background
painter = QPainter(px)
painter.setPen(QColor(255, 255, 255))
font = QFont("Segoe UI", 24, QFont.Weight.Bold)
painter.setFont(font)
painter.drawText(px.rect(), Qt.AlignmentFlag.AlignCenter, "EU")
painter.end()
pixmap = QIcon(px)
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(pixmap)
self.tray_icon.setToolTip("EU-Utility")
# Show the icon
self.tray_icon.show()
# Connect activation (double-click)
# Connect signals
self.tray_icon.activated.connect(self._on_activated)
def _setup_menu(self):
"""Setup the right-click context menu."""
self.menu = QMenu()
self.menu.setStyleSheet("""
QMenu {
background: rgba(35, 35, 35, 0.98);
color: white;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 8px;
}
QMenu::item {
padding: 10px 24px;
border-radius: 6px;
}
QMenu::item:selected {
background: rgba(255, 140, 66, 0.3);
}
QMenu::separator {
height: 1px;
background: rgba(255, 255, 255, 0.1);
margin: 8px 16px;
}
""")
def _create_icon(self):
"""Create tray icon."""
# Create simple icon
px = QPixmap(64, 64)
px.fill(QColor(255, 140, 66)) # Orange
# Dashboard action
self.dashboard_action = QAction("📊 Dashboard", self)
self.dashboard_action.triggered.connect(self.show_dashboard)
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()
def _create_menu(self):
"""Create simple context menu."""
self.menu = QMenu()
# Simple actions (no emojis to avoid encoding issues)
self.dashboard_action = QAction("Dashboard", self)
self.dashboard_action.triggered.connect(self.show_dashboard.emit)
self.menu.addAction(self.dashboard_action)
# Activity Bar toggle
self.activity_bar_action = QAction("🎮 Activity Bar", self)
self.activity_bar_action = QAction("Activity Bar", self)
self.activity_bar_action.setCheckable(True)
self.activity_bar_action.triggered.connect(self.toggle_activity_bar)
self.activity_bar_action.triggered.connect(self.toggle_activity_bar.emit)
self.menu.addAction(self.activity_bar_action)
self.menu.addSeparator()
# Settings
self.settings_action = QAction("⚙️ Settings", self)
self.settings_action.triggered.connect(self.open_settings)
self.menu.addAction(self.settings_action)
self.menu.addSeparator()
# Quit
self.quit_action = QAction("❌ Quit", self)
self.quit_action.triggered.connect(self.quit_app)
self.quit_action = QAction("Quit", self)
self.quit_action.triggered.connect(self.quit_app.emit)
self.menu.addAction(self.quit_action)
# Set menu
# Set context menu
self.tray_icon.setContextMenu(self.menu)
def _setup_visibility_timer(self):
"""Setup timer to update menu state."""
self.update_timer = QTimer(self)
self.update_timer.timeout.connect(self._update_menu_state)
self.update_timer.start(1000) # Update every second
def _update_menu_state(self):
"""Update menu checkbox states."""
# This will be connected to actual state
pass
def _on_activated(self, reason):
"""Handle tray icon activation."""
"""Handle double-click."""
if reason == QSystemTrayIcon.ActivationReason.DoubleClick:
self.show_dashboard.emit()
def show_notification(self, title: str, message: str, duration: int = 3000):
"""Show a system notification."""
if self.tray_icon and self.tray_icon.supportsMessages():
self.tray_icon.showMessage(
title,
message,
QSystemTrayIcon.MessageIcon.Information,
duration
)
def set_activity_bar_checked(self, checked: bool):
"""Update activity bar menu item state."""
"""Update checkbox."""
self.activity_bar_action.setChecked(checked)
def is_visible(self) -> bool:
"""Check if tray icon is visible."""
return self.tray_icon and self.tray_icon.isVisible()