fix: thread-safe hotkey handling

- Add HotkeyHandler class with pyqtSignal
- Fix QMetaObject.invokeMethod error
- Proper thread safety for keyboard hotkeys
This commit is contained in:
LemonNexus 2026-02-12 18:56:53 +00:00
parent d387a4714a
commit fa0b0c87b5
1 changed files with 23 additions and 11 deletions

View File

@ -15,7 +15,7 @@ if str(project_root) not in sys.path:
try: try:
from PyQt6.QtWidgets import QApplication from PyQt6.QtWidgets import QApplication
from PyQt6.QtCore import Qt from PyQt6.QtCore import Qt, QObject, pyqtSignal
PYQT_AVAILABLE = True PYQT_AVAILABLE = True
except ImportError: except ImportError:
PYQT_AVAILABLE = False PYQT_AVAILABLE = False
@ -35,6 +35,11 @@ from core.plugin_manager import PluginManager
from core.overlay_window import OverlayWindow from core.overlay_window import OverlayWindow
class HotkeyHandler(QObject):
"""Signal bridge for thread-safe hotkey handling."""
toggle_signal = pyqtSignal()
class EUUtilityApp: class EUUtilityApp:
"""Main application controller.""" """Main application controller."""
@ -42,6 +47,7 @@ class EUUtilityApp:
self.app = None self.app = None
self.overlay = None self.overlay = None
self.plugin_manager = None self.plugin_manager = None
self.hotkey_handler = None
def run(self): def run(self):
"""Start the application.""" """Start the application."""
@ -53,6 +59,9 @@ class EUUtilityApp:
if hasattr(Qt, 'AA_EnableHighDpiScaling'): if hasattr(Qt, 'AA_EnableHighDpiScaling'):
self.app.setAttribute(Qt.ApplicationAttribute.AA_EnableHighDpiScaling) self.app.setAttribute(Qt.ApplicationAttribute.AA_EnableHighDpiScaling)
# Create hotkey handler (must be in main thread)
self.hotkey_handler = HotkeyHandler()
# Initialize plugin manager # Initialize plugin manager
print("Loading plugins...") print("Loading plugins...")
self.plugin_manager = PluginManager(None) self.plugin_manager = PluginManager(None)
@ -62,6 +71,9 @@ class EUUtilityApp:
self.overlay = OverlayWindow(self.plugin_manager) self.overlay = OverlayWindow(self.plugin_manager)
self.plugin_manager.overlay = self.overlay self.plugin_manager.overlay = self.overlay
# Connect hotkey signal
self.hotkey_handler.toggle_signal.connect(self._on_toggle_signal)
# Setup global hotkey # Setup global hotkey
self._setup_hotkey() self._setup_hotkey()
@ -75,20 +87,20 @@ class EUUtilityApp:
"""Setup global hotkey.""" """Setup global hotkey."""
if KEYBOARD_AVAILABLE: if KEYBOARD_AVAILABLE:
try: try:
keyboard.add_hotkey('ctrl+shift+u', self._toggle_overlay) keyboard.add_hotkey('ctrl+shift+u', self._on_hotkey_pressed)
except Exception as e: except Exception as e:
print(f"Failed to register hotkey: {e}") print(f"Failed to register hotkey: {e}")
def _toggle_overlay(self): def _on_hotkey_pressed(self):
"""Toggle overlay visibility.""" """Called when hotkey is pressed (from keyboard thread)."""
# Emit signal to main thread
if self.hotkey_handler:
self.hotkey_handler.toggle_signal.emit()
def _on_toggle_signal(self):
"""Handle toggle signal in main thread."""
if self.overlay: if self.overlay:
# Use Qt's thread-safe method self.overlay.toggle_overlay()
from PyQt6.QtCore import QMetaObject, Qt
QMetaObject.invokeMethod(
self.overlay,
"toggle_overlay",
Qt.ConnectionType.QueuedConnection
)
def main(): def main():