Files
whisper_voice/src/core/hotkey_manager.py
2026-01-24 18:29:10 +02:00

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()