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:
parent
eba2e029e3
commit
0d2494abd7
|
|
@ -1,32 +1,17 @@
|
||||||
"""
|
"""
|
||||||
EU-Utility - System Tray Icon
|
EU-Utility - System Tray Icon (Simplified)
|
||||||
=============================
|
==========================================
|
||||||
|
|
||||||
System tray implementation with right-click menu.
|
Simple, responsive system tray icon.
|
||||||
Replaces the floating icon with a proper tray icon.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from PyQt6.QtWidgets import QSystemTrayIcon, QMenu, QApplication
|
from PyQt6.QtWidgets import QSystemTrayIcon, QMenu, QApplication, QWidget
|
||||||
from PyQt6.QtGui import QAction
|
from PyQt6.QtGui import QAction, QIcon, QColor, QPainter, QFont
|
||||||
from PyQt6.QtCore import QTimer, pyqtSignal, QObject
|
from PyQt6.QtCore import pyqtSignal
|
||||||
from PyQt6.QtGui import QIcon, QColor, QPainter, QFont, QFontMetrics
|
|
||||||
|
|
||||||
from core.logger import get_logger
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class TrayIcon(QObject):
|
class TrayIcon(QWidget):
|
||||||
"""
|
"""Simple system tray icon for EU-Utility."""
|
||||||
System tray icon for EU-Utility.
|
|
||||||
|
|
||||||
Features:
|
|
||||||
- Right-click context menu
|
|
||||||
- Show/hide dashboard
|
|
||||||
- Toggle activity bar
|
|
||||||
- Settings access
|
|
||||||
- Quit option
|
|
||||||
"""
|
|
||||||
|
|
||||||
show_dashboard = pyqtSignal()
|
show_dashboard = pyqtSignal()
|
||||||
toggle_activity_bar = pyqtSignal()
|
toggle_activity_bar = pyqtSignal()
|
||||||
|
|
@ -36,126 +21,62 @@ class TrayIcon(QObject):
|
||||||
def __init__(self, app: QApplication, parent=None):
|
def __init__(self, app: QApplication, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.tray_icon = None
|
|
||||||
self.menu = None
|
|
||||||
|
|
||||||
|
# Create icon
|
||||||
self._create_icon()
|
self._create_icon()
|
||||||
self._setup_menu()
|
|
||||||
self._setup_visibility_timer()
|
# Create menu (but don't set it yet)
|
||||||
|
self._create_menu()
|
||||||
|
|
||||||
|
# Connect signals
|
||||||
|
self.tray_icon.activated.connect(self._on_activated)
|
||||||
|
|
||||||
def _create_icon(self):
|
def _create_icon(self):
|
||||||
"""Create the tray icon with EU logo."""
|
"""Create tray icon."""
|
||||||
# Create a simple colored circle icon with "EU" text
|
# Create simple icon
|
||||||
pixmap = QIcon.fromTheme("applications-system")
|
|
||||||
|
|
||||||
# If no system icon, create custom
|
|
||||||
if pixmap.isNull():
|
|
||||||
from PyQt6.QtGui import QPixmap, QPainter, QColor, QFont
|
|
||||||
px = QPixmap(64, 64)
|
px = QPixmap(64, 64)
|
||||||
px.fill(QColor(255, 140, 66)) # Orange background
|
px.fill(QColor(255, 140, 66)) # Orange
|
||||||
|
|
||||||
painter = QPainter(px)
|
painter = QPainter(px)
|
||||||
painter.setPen(QColor(255, 255, 255))
|
painter.setPen(QColor(255, 255, 255))
|
||||||
font = QFont("Segoe UI", 24, QFont.Weight.Bold)
|
font = QFont("Segoe UI", 24, QFont.Weight.Bold)
|
||||||
painter.setFont(font)
|
painter.setFont(font)
|
||||||
painter.drawText(px.rect(), Qt.AlignmentFlag.AlignCenter, "EU")
|
painter.drawText(px.rect(), 0x84, "EU") # AlignCenter
|
||||||
painter.end()
|
painter.end()
|
||||||
|
|
||||||
pixmap = QIcon(px)
|
|
||||||
|
|
||||||
self.tray_icon = QSystemTrayIcon(self)
|
self.tray_icon = QSystemTrayIcon(self)
|
||||||
self.tray_icon.setIcon(pixmap)
|
self.tray_icon.setIcon(QIcon(px))
|
||||||
self.tray_icon.setToolTip("EU-Utility")
|
self.tray_icon.setToolTip("EU-Utility")
|
||||||
|
|
||||||
# Show the icon
|
|
||||||
self.tray_icon.show()
|
self.tray_icon.show()
|
||||||
|
|
||||||
# Connect activation (double-click)
|
def _create_menu(self):
|
||||||
self.tray_icon.activated.connect(self._on_activated)
|
"""Create simple context menu."""
|
||||||
|
|
||||||
def _setup_menu(self):
|
|
||||||
"""Setup the right-click context menu."""
|
|
||||||
self.menu = QMenu()
|
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;
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
|
|
||||||
# Dashboard action
|
# Simple actions (no emojis to avoid encoding issues)
|
||||||
self.dashboard_action = QAction("📊 Dashboard", self)
|
self.dashboard_action = QAction("Dashboard", self)
|
||||||
self.dashboard_action.triggered.connect(self.show_dashboard)
|
self.dashboard_action.triggered.connect(self.show_dashboard.emit)
|
||||||
self.menu.addAction(self.dashboard_action)
|
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.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.addAction(self.activity_bar_action)
|
||||||
|
|
||||||
self.menu.addSeparator()
|
self.menu.addSeparator()
|
||||||
|
|
||||||
# Settings
|
self.quit_action = QAction("Quit", self)
|
||||||
self.settings_action = QAction("⚙️ Settings", self)
|
self.quit_action.triggered.connect(self.quit_app.emit)
|
||||||
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.menu.addAction(self.quit_action)
|
self.menu.addAction(self.quit_action)
|
||||||
|
|
||||||
# Set menu
|
# Set context menu
|
||||||
self.tray_icon.setContextMenu(self.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):
|
def _on_activated(self, reason):
|
||||||
"""Handle tray icon activation."""
|
"""Handle double-click."""
|
||||||
if reason == QSystemTrayIcon.ActivationReason.DoubleClick:
|
if reason == QSystemTrayIcon.ActivationReason.DoubleClick:
|
||||||
self.show_dashboard.emit()
|
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):
|
def set_activity_bar_checked(self, checked: bool):
|
||||||
"""Update activity bar menu item state."""
|
"""Update checkbox."""
|
||||||
self.activity_bar_action.setChecked(checked)
|
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()
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue