97 lines
3.1 KiB
Python
97 lines
3.1 KiB
Python
"""
|
|
Hotkey Manager Module.
|
|
======================
|
|
|
|
This module wraps the `keyboard` library to provide Global Hotkey functionality.
|
|
It allows the application to respond to key presses even when it is not in focus
|
|
(background operation).
|
|
|
|
Classes:
|
|
HotkeyManager: Qt-compatible wrapper for keyboard hooks.
|
|
"""
|
|
|
|
import keyboard
|
|
import logging
|
|
from PySide6.QtCore import QObject, Signal
|
|
from typing import Optional
|
|
|
|
class HotkeyManager(QObject):
|
|
"""
|
|
Manages global keyboard shortcuts using the `keyboard` library.
|
|
inherits from QObject to allow Signal/Slot integration with PySide6.
|
|
|
|
Signals:
|
|
triggered: Emitted when the hotkey is pressed.
|
|
|
|
Attributes:
|
|
hotkey (str): The key combination as a string (e.g. "f8", "ctrl+alt+r").
|
|
is_listening (bool): State of the listener.
|
|
"""
|
|
|
|
triggered = Signal()
|
|
|
|
def __init__(self, config_key: str = "hotkey"):
|
|
"""
|
|
Initialize the HotkeyManager.
|
|
|
|
Args:
|
|
config_key (str): The configuration key to look up (e.g. "hotkey").
|
|
"""
|
|
super().__init__()
|
|
self.config_key = config_key
|
|
self.hotkey = "f8" # Placeholder
|
|
self.is_listening = False
|
|
self._enabled = True
|
|
|
|
def set_enabled(self, enabled: bool):
|
|
"""Enable or disable the hotkey trigger without unhooking."""
|
|
self._enabled = enabled
|
|
logging.info(f"Hotkey listener {'enabled' if enabled else 'suspended'}")
|
|
|
|
def start(self):
|
|
"""Start listening for the hotkey."""
|
|
self.reload_hotkey()
|
|
|
|
def reload_hotkey(self):
|
|
"""Unregister old hotkey and register new one from Config."""
|
|
if self.is_listening:
|
|
self.stop()
|
|
|
|
from src.core.config import ConfigManager
|
|
config = ConfigManager()
|
|
self.hotkey = config.get(self.config_key)
|
|
|
|
logging.info(f"Registering global hotkey ({self.config_key}): {self.hotkey}")
|
|
try:
|
|
# We don't suppress=True here because we want the app to see keys during recording
|
|
# (Wait, actually if we are recording we WANT keyboard to see it,
|
|
# but usually global hotkeys should be suppressed if we don't want them leaking to other apps)
|
|
# However, the user is fixing the internal collision.
|
|
keyboard.add_hotkey(self.hotkey, self.on_press, suppress=False)
|
|
self.is_listening = True
|
|
except Exception as e:
|
|
logging.error(f"Failed to bind hotkey: {e}")
|
|
|
|
def stop(self):
|
|
"""
|
|
Stop listening and unregister the hook.
|
|
Safe to call even if not listening.
|
|
"""
|
|
if self.is_listening:
|
|
try:
|
|
keyboard.remove_hotkey(self.hotkey)
|
|
except:
|
|
pass
|
|
self.is_listening = False
|
|
logging.info(f"Unregistered global hotkey: {self.hotkey}")
|
|
|
|
def on_press(self):
|
|
"""
|
|
Callback triggered internally by the keyboard library when the key is pressed.
|
|
Emits the Qt `triggered` signal.
|
|
"""
|
|
if not self._enabled:
|
|
return
|
|
logging.info(f"Hotkey {self.hotkey} detected.")
|
|
self.triggered.emit()
|