# agent_v0/lea_ui/launcher.py """ Point d'entree pour le panneau Lea. Lancement autonome : python -m agent_v0.lea_ui.launcher Ou integre dans agent_v0/agent_v1/main.py avec flag --ui lea. Ce module : 1. Cree l'application Qt 2. Instancie LeaServerClient 3. Instancie LeaMainWindow 4. Enregistre un raccourci global (Ctrl+Shift+L) via keyboard hook 5. Lance la boucle Qt """ from __future__ import annotations import argparse import logging import os import sys from typing import Optional logger = logging.getLogger("lea_ui.launcher") def _setup_logging(verbose: bool = False) -> None: """Configurer le logging pour le panneau Lea.""" level = logging.DEBUG if verbose else logging.INFO logging.basicConfig( level=level, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", datefmt="%H:%M:%S", ) def _setup_global_hotkey(window) -> Optional[object]: """Enregistrer le raccourci global Ctrl+Shift+L pour afficher/cacher le panneau. Utilise la librairie keyboard si disponible (Windows/Linux). Retourne le hook pour pouvoir le desinscrire a l'arret. """ try: import keyboard def on_hotkey(): # Appeler toggle_visibility dans le thread Qt from PyQt5.QtCore import QTimer QTimer.singleShot(0, window.toggle_visibility) keyboard.add_hotkey("ctrl+shift+l", on_hotkey) logger.info("Raccourci global Ctrl+Shift+L enregistre") return True except ImportError: logger.info( "Librairie 'keyboard' non disponible — " "raccourci global Ctrl+Shift+L non enregistre. " "Installez-la avec: pip install keyboard" ) return None except Exception as e: logger.warning("Impossible d'enregistrer le raccourci global : %s", e) return None def _load_environment() -> None: """Charger les variables d'environnement depuis .env.local.""" env_paths = [ os.path.join(os.path.dirname(__file__), "..", "..", ".env.local"), os.path.join(os.path.dirname(__file__), "..", ".env.local"), ] for env_path in env_paths: env_path = os.path.abspath(env_path) if os.path.exists(env_path): try: from dotenv import load_dotenv load_dotenv(env_path) logger.info("Variables d'environnement chargees depuis %s", env_path) return except ImportError: # Fallback : chargement manuel with open(env_path, "r", encoding="utf-8") as f: for line in f: line = line.strip() if line and not line.startswith("#") and "=" in line: key, value = line.split("=", 1) value = value.strip("\"'") os.environ[key.strip()] = value logger.info("Variables chargees manuellement depuis %s", env_path) return def launch_lea( server_host: Optional[str] = None, chat_port: int = 5004, stream_port: int = 5005, verbose: bool = False, session_id: Optional[str] = None, ) -> None: """Lancer le panneau Lea. Args: server_host: adresse du serveur Linux (None = auto-detection) chat_port: port du serveur chat stream_port: port du serveur streaming verbose: mode debug session_id: identifiant de session pour le polling replay """ _setup_logging(verbose) _load_environment() # Import PyQt5 ici pour un message d'erreur clair si absent try: from PyQt5.QtWidgets import QApplication from PyQt5.QtCore import Qt except ImportError: logger.error( "PyQt5 n'est pas installe. Installez-le avec :\n" " pip install PyQt5" ) sys.exit(1) from .server_client import LeaServerClient from .main_window import LeaMainWindow # Creer ou recuperer l'application Qt app = QApplication.instance() if app is None: app = QApplication(sys.argv) app.setQuitOnLastWindowClosed(False) # Client serveur client = LeaServerClient( server_host=server_host, chat_port=chat_port, stream_port=stream_port, ) # Fenetre principale window = LeaMainWindow(server_client=client) window.show() # Raccourci global hotkey = _setup_global_hotkey(window) # Polling replay (si session_id fourni) if session_id: client.start_polling(session_id) logger.info( "Panneau Lea demarre — serveur=%s, chat_port=%d, stream_port=%d", client.server_host, chat_port, stream_port, ) # Boucle Qt try: exit_code = app.exec_() finally: window.shutdown() if hotkey: try: import keyboard keyboard.unhook_all() except Exception: pass sys.exit(exit_code) def main() -> None: """Point d'entree CLI.""" parser = argparse.ArgumentParser( description="Panneau Lea — Interface utilisateur RPA Vision V3", ) parser.add_argument( "--server", "-s", dest="server_host", default=None, help="Adresse du serveur Linux (defaut: RPA_SERVER_HOST ou localhost)", ) parser.add_argument( "--chat-port", type=int, default=5004, help="Port du serveur chat (defaut: 5004)", ) parser.add_argument( "--stream-port", type=int, default=5005, help="Port du serveur streaming (defaut: 5005)", ) parser.add_argument( "--session-id", default=None, help="Identifiant de session pour le polling replay", ) parser.add_argument( "--verbose", "-v", action="store_true", help="Mode debug (logs verbeux)", ) args = parser.parse_args() launch_lea( server_host=args.server_host, chat_port=args.chat_port, stream_port=args.stream_port, verbose=args.verbose, session_id=args.session_id, ) if __name__ == "__main__": main()