"""Sidebar dinámico de sistemas del proyecto. Lee el catálogo maestro de sistemas y muestra: - Sistemas habilitados del Project (en la parte superior, con contador de equipos) - Sistemas NO habilitados pero disponibles (al pie, en gris) En Sprint 1 es sólo visualización. En Sprint 2 se podrá toggle desde aquí. """ from __future__ import annotations from collections import Counter from PySide6.QtCore import Qt, Signal from PySide6.QtWidgets import ( QFrame, QLabel, QListWidget, QListWidgetItem, QVBoxLayout, QWidget, ) from vmssailor.core.enums import SystemId from vmssailor.core.project import Project from vmssailor.library import load_systems_catalog from vmssailor.studio.theme import C_FOG, ui_font class SystemSidebar(QWidget): """Lista lateral de sistemas. Emite `systemActivated(SystemId)` al doble-click.""" systemActivated = Signal(str) def __init__(self, parent: QWidget | None = None) -> None: super().__init__(parent) self._project: Project | None = None self._catalog = self._load_catalog_safe() layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) # Header section: wizard / overview self._wizard_label = QLabel("WIZARD") self._wizard_label.setObjectName("overline") self._wizard_label.setFont(ui_font(9)) self._wizard_label.setStyleSheet( f"color: {C_FOG}; padding: 16px 16px 6px 16px; letter-spacing: 2px;" ) layout.addWidget(self._wizard_label) self._wizard_steps = QListWidget() self._wizard_steps.setFrameShape(QFrame.Shape.NoFrame) for step in self._WIZARD_STEPS: item = QListWidgetItem(step) item.setFlags(item.flags() & ~Qt.ItemIsSelectable) self._wizard_steps.addItem(item) self._wizard_steps.setFixedHeight(self._wizard_steps.sizeHintForRow(0) * 8 + 10) layout.addWidget(self._wizard_steps) # Systems section self._systems_header = QLabel("SISTEMAS HABILITADOS") self._systems_header.setObjectName("overline") self._systems_header.setFont(ui_font(9)) self._systems_header.setStyleSheet( f"color: {C_FOG}; padding: 16px 16px 6px 16px; letter-spacing: 2px;" ) layout.addWidget(self._systems_header) self._enabled_list = QListWidget() self._enabled_list.setFrameShape(QFrame.Shape.NoFrame) self._enabled_list.itemDoubleClicked.connect(self._on_double_click_enabled) layout.addWidget(self._enabled_list, 1) self._available_header = QLabel("DISPONIBLES (NO HABILITADOS)") self._available_header.setObjectName("overline") self._available_header.setFont(ui_font(9)) self._available_header.setStyleSheet( f"color: {C_FOG}; padding: 16px 16px 6px 16px; letter-spacing: 2px;" ) layout.addWidget(self._available_header) self._available_list = QListWidget() self._available_list.setFrameShape(QFrame.Shape.NoFrame) self._available_list.setMaximumHeight(140) layout.addWidget(self._available_list) self.refresh() # ----- Const ----------------------------------------------------- _WIZARD_STEPS = ( "1 · Tipo de buque", "2 · Plantilla", "3 · Dimensiones", "4 · Sistemas", "5 · Equipos sugeridos", "6 · Refinamiento", "7 · Topología I/O", "8 · Confirmación", ) # ----- Public ---------------------------------------------------- def set_project(self, project: Project | None) -> None: self._project = project self.refresh() def refresh(self) -> None: self._enabled_list.clear() self._available_list.clear() if self._project is None: self._systems_header.setText("SISTEMAS HABILITADOS · sin proyecto") self._enabled_list.addItem("(crea o abre un proyecto)") return enabled = set(self._project.systems_enabled) equipment_count: Counter[str] = Counter() for eq in self._project.equipment: equipment_count[eq.system_id.value] += 1 self._systems_header.setText( f"SISTEMAS HABILITADOS · {len(enabled)}" ) for sys_id_str in sorted(s.value for s in enabled): name = self._system_display_name(sys_id_str) count = equipment_count.get(sys_id_str, 0) label = f"{name} · {count} eq" if count else f"{name} · —" item = QListWidgetItem(label) item.setData(Qt.UserRole, sys_id_str) self._enabled_list.addItem(item) all_known = {s.value for s in SystemId} disabled = sorted(all_known - {s.value for s in enabled}) self._available_header.setText( f"DISPONIBLES (NO HABILITADOS) · {len(disabled)}" ) for sys_id_str in disabled[:40]: item = QListWidgetItem(self._system_display_name(sys_id_str)) item.setForeground(Qt.GlobalColor.darkGray) item.setData(Qt.UserRole, sys_id_str) self._available_list.addItem(item) # ----- Internals ------------------------------------------------- def _load_catalog_safe(self) -> dict: try: return load_systems_catalog() except Exception: return {"categories": []} def _system_display_name(self, sys_id_value: str) -> str: for cat in self._catalog.get("categories", []): for s in cat.get("systems", []): if s.get("id") == sys_id_value: return s.get("name", sys_id_value) return sys_id_value.replace("_", " ").title() def _on_double_click_enabled(self, item: QListWidgetItem) -> None: sys_id = item.data(Qt.UserRole) if sys_id: self.systemActivated.emit(sys_id)