71 lines
2.0 KiB
Python
71 lines
2.0 KiB
Python
"""Tray-App und App-Zustände für whisper-local."""
|
|
|
|
import enum
|
|
import threading
|
|
from typing import Callable
|
|
|
|
|
|
class AppState(enum.Enum):
|
|
WAITING = "waiting"
|
|
RECORDING = "recording"
|
|
TRANSCRIBING = "transcribing"
|
|
|
|
|
|
class PystrayApp:
|
|
"""Tray-Icon via pystray — cross-platform (Windows + Linux)."""
|
|
|
|
def __init__(self, on_settings: Callable[[], None], on_quit: Callable[[], None]):
|
|
self._on_settings = on_settings
|
|
self._on_quit = on_quit
|
|
self._icon = None
|
|
|
|
def start(self) -> None:
|
|
"""Startet pystray in einem Daemon-Thread."""
|
|
import pystray
|
|
from whisper_local.tray._icon import create_icon
|
|
|
|
menu = pystray.Menu(
|
|
pystray.MenuItem("Einstellungen", self._menu_settings),
|
|
pystray.MenuItem("Beenden", self._menu_quit),
|
|
)
|
|
self._icon = pystray.Icon(
|
|
"whisper-local",
|
|
create_icon(AppState.WAITING),
|
|
"whisper-local",
|
|
menu,
|
|
)
|
|
thread = threading.Thread(target=self._icon.run, daemon=True)
|
|
thread.start()
|
|
|
|
def set_state(self, state: AppState) -> None:
|
|
"""Tauscht das Icon aus (thread-sicher)."""
|
|
if self._icon is not None:
|
|
from whisper_local.tray._icon import create_icon
|
|
self._icon.icon = create_icon(state)
|
|
|
|
def set_warning(self, msg: str | None) -> None:
|
|
"""Setzt Tray-Titel auf Warnung oder zurück auf normal (thread-sicher)."""
|
|
if self._icon is not None:
|
|
self._icon.title = "whisper-local" if msg is None else f"whisper-local ⚠ {msg}"
|
|
|
|
def _menu_settings(self, icon, item) -> None:
|
|
self._on_settings()
|
|
|
|
def _menu_quit(self, icon, item) -> None:
|
|
if self._icon is not None:
|
|
self._icon.stop()
|
|
self._on_quit()
|
|
|
|
|
|
class NoOpTray:
|
|
"""Platzhalter-Implementierung für nicht-Windows-Plattformen."""
|
|
|
|
def start(self) -> None:
|
|
pass
|
|
|
|
def set_state(self, state: AppState) -> None:
|
|
pass
|
|
|
|
def set_warning(self, msg: str | None) -> None:
|
|
pass
|