"""Ventana principal de la aplicación.""" from PySide6.QtWidgets import ( QMainWindow, QWidget, QVBoxLayout, QTabWidget, QSystemTrayIcon, QMenu ) from PySide6.QtCore import Qt, QTimer, Signal from PySide6.QtGui import QIcon, QCloseEvent, QAction from .dashboard_tab import DashboardTab from .jobs_tab import JobsTab from .nodes_tab import NodesTab from .config_tab import ConfigTab from .logs_tab import LogsTab from ..engine.engine import RestoreEngine from ..utils.logger import app_logger class MainWindow(QMainWindow): """Ventana principal de la aplicación.""" # Señal para minimizar a tray minimize_to_tray = Signal() def __init__(self): """Inicializa la ventana principal.""" super().__init__() # Motor de restauración self.engine = RestoreEngine() # Configurar ventana self.setWindowTitle("CloudRestoreAS - Restauración Automática SQL Server") self.setMinimumSize(1200, 800) # System tray self.tray_icon: QSystemTrayIcon = None self._setup_tray() # UI self._setup_ui() # Conectar señales del motor self._connect_engine_signals() # Timer para actualizar stats self.stats_timer = QTimer() self.stats_timer.timeout.connect(self._update_stats) self.stats_timer.start(5000) # Cada 5 segundos app_logger.info("Ventana principal inicializada") def _setup_ui(self): """Configura la interfaz de usuario.""" # Widget central central_widget = QWidget() self.setCentralWidget(central_widget) # Layout principal layout = QVBoxLayout(central_widget) layout.setContentsMargins(10, 10, 10, 10) # Tabs self.tabs = QTabWidget() # Crear tabs self.dashboard_tab = DashboardTab(self.engine) self.jobs_tab = JobsTab(self.engine) self.nodes_tab = NodesTab(self.engine) self.config_tab = ConfigTab(self.engine) self.logs_tab = LogsTab(self.engine) # Agregar tabs self.tabs.addTab(self.dashboard_tab, "📊 Dashboard") self.tabs.addTab(self.jobs_tab, "📋 Jobs") self.tabs.addTab(self.nodes_tab, "🔗 Nodos") self.tabs.addTab(self.config_tab, "⚙️ Configuración") self.tabs.addTab(self.logs_tab, "📝 Logs") layout.addWidget(self.tabs) # Menu bar self._setup_menu() def _setup_menu(self): """Configura el menú.""" menubar = self.menuBar() # Menú Motor motor_menu = menubar.addMenu("Motor") self.start_action = QAction("▶️ Iniciar", self) self.start_action.triggered.connect(self._start_engine) motor_menu.addAction(self.start_action) self.pause_action = QAction("⏸️ Pausar", self) self.pause_action.triggered.connect(self._pause_engine) self.pause_action.setEnabled(False) motor_menu.addAction(self.pause_action) self.resume_action = QAction("▶️ Continuar", self) self.resume_action.triggered.connect(self._resume_engine) self.resume_action.setEnabled(False) motor_menu.addAction(self.resume_action) self.stop_action = QAction("⏹️ Detener", self) self.stop_action.triggered.connect(self._stop_engine) self.stop_action.setEnabled(False) motor_menu.addAction(self.stop_action) motor_menu.addSeparator() self.scan_action = QAction("🔍 Escanear Ahora", self) self.scan_action.triggered.connect(self._scan_now) motor_menu.addAction(self.scan_action) # Menú Ayuda help_menu = menubar.addMenu("Ayuda") about_action = QAction("Acerca de", self) about_action.triggered.connect(self._show_about) help_menu.addAction(about_action) def _setup_tray(self): """Configura el icono de bandeja del sistema.""" # Crear icono (usar un icono por defecto o crear uno simple) self.tray_icon = QSystemTrayIcon(self) self.tray_icon.setToolTip("CloudRestoreAS") # Menú del tray tray_menu = QMenu() show_action = QAction("Mostrar", self) show_action.triggered.connect(self._show_window) tray_menu.addAction(show_action) hide_action = QAction("Ocultar", self) hide_action.triggered.connect(self.hide) tray_menu.addAction(hide_action) tray_menu.addSeparator() pause_tray_action = QAction("Pausar", self) pause_tray_action.triggered.connect(self._pause_engine) tray_menu.addAction(pause_tray_action) resume_tray_action = QAction("Continuar", self) resume_tray_action.triggered.connect(self._resume_engine) tray_menu.addAction(resume_tray_action) tray_menu.addSeparator() quit_action = QAction("Salir", self) quit_action.triggered.connect(self._quit_application) tray_menu.addAction(quit_action) self.tray_icon.setContextMenu(tray_menu) # Doble clic en tray para mostrar/ocultar self.tray_icon.activated.connect(self._on_tray_activated) # Mostrar tray self.tray_icon.show() def _connect_engine_signals(self): """Conecta las señales del motor.""" self.engine.signals.job_created.connect(self._on_job_created) self.engine.signals.stats_updated.connect(self._on_stats_updated) self.engine.signals.config_loaded.connect(self._on_config_loaded) def closeEvent(self, event: QCloseEvent): """Maneja el evento de cierre de ventana (minimizar a tray).""" if self.tray_icon.isVisible(): self.hide() event.ignore() app_logger.info("Ventana minimizada a bandeja del sistema") else: event.accept() def _on_tray_activated(self, reason): """Maneja la activación del icono de tray.""" if reason == QSystemTrayIcon.ActivationReason.DoubleClick: if self.isVisible(): self.hide() else: self._show_window() def _show_window(self): """Muestra la ventana.""" self.show() self.activateWindow() self.raise_() def _start_engine(self): """Inicia el motor.""" self.engine.start() self.start_action.setEnabled(False) self.pause_action.setEnabled(True) self.stop_action.setEnabled(True) app_logger.info("Motor iniciado desde UI") def _pause_engine(self): """Pausa el motor.""" self.engine.pause() self.pause_action.setEnabled(False) self.resume_action.setEnabled(True) app_logger.info("Motor pausado desde UI") def _resume_engine(self): """Reanuda el motor.""" self.engine.resume() self.resume_action.setEnabled(False) self.pause_action.setEnabled(True) app_logger.info("Motor reanudado desde UI") def _stop_engine(self): """Detiene el motor.""" self.engine.stop() self.stop_action.setEnabled(False) self.pause_action.setEnabled(False) self.resume_action.setEnabled(False) self.start_action.setEnabled(True) app_logger.info("Motor detenido desde UI") def _scan_now(self): """Fuerza un escaneo manual.""" self.engine.scan_now() app_logger.info("Escaneo manual solicitado desde UI") def _update_stats(self): """Actualiza las estadísticas.""" if self.engine.is_running(): stats = self.engine.get_stats() self.dashboard_tab.update_stats(stats) def _on_job_created(self, job_id: str): """Maneja la creación de un nuevo job.""" self.jobs_tab.refresh_jobs() self._update_stats() def _on_stats_updated(self, stats: dict): """Maneja la actualización de estadísticas.""" self.dashboard_tab.update_stats(stats) def _on_config_loaded(self, config: dict): """Maneja la carga de configuración.""" self.config_tab.load_config(config) def _show_about(self): """Muestra el diálogo Acerca de.""" from PySide6.QtWidgets import QMessageBox QMessageBox.about( self, "Acerca de CloudRestoreAS", "CloudRestoreAS v1.0.0\n\n" "Aplicación de restauración automática de bases de datos SQL Server.\n\n" "© 2026 Aduanasoft" ) def _quit_application(self): """Sale de la aplicación completamente.""" app_logger.info("Saliendo de la aplicación") self.engine.stop() self.tray_icon.hide() self.close() from PySide6.QtWidgets import QApplication QApplication.quit()