sprint-3: editores de mimicos + tags + alarmas

3 editores nuevos integrados como tabs en MainWindow.

vmssailor/studio/editors/symbols.py
- 8 simbolos navales: motor, pump, valve, tank, sensor, indicator, line, label
- _BaseSymbol QGraphicsItemGroup arrastrable + seleccionable
- TankSymbol con fill animable por porcentaje
- SymbolSpec dataclass serializable a JSON

vmssailor/studio/editors/mimic_editor.py
- Paleta lateral con tipos de simbolos
- Doble-click crea simbolo en canvas
- Selector de sistema (de los habilitados en el proyecto)
- Boton 'Cargar demo' inserta P&ID demo (tanque-valvula-bomba-motor-sensor)
- serialize_to_project() para guardar en .vmsproj

vmssailor/studio/editors/tag_editor.py
- Tabla CRUD con 13 columnas
- Edicion inline de description, unit_si, range, control_mode
- Validacion via Pydantic en cada edit
- Filtro por texto en vivo
- Contador de tags

vmssailor/studio/editors/alarm_editor.py
- Tabla aplanada de todas las alarmas configuradas
- Counts por prioridad coloreados
- Dialog modal para agregar AlarmConfig nueva
- Edicion inline de threshold, operator, priority, hysteresis, delay, message
- Eliminacion individual

vmssailor/studio/main_window.py
- Tabs 'Equipos', 'Mimicos', 'Tags', 'Alarmas' en panel derecho
- Todos los editores reciben set_project y emiten projectMutated

Tests (tests/studio/test_editors.py, 6 nuevos, total 126/126):
- Symbol factory para los 8 tipos
- MimicEditor con demo
- TagEditor render
- AlarmEditor priority counts + empty state
- MainWindow tabs presentes

126/126 verde, ruff clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-17 18:34:30 -04:00
parent 6ad76a89fa
commit fbce1ecb42
7 changed files with 1233 additions and 4 deletions
+21 -3
View File
@@ -21,6 +21,7 @@ from PySide6.QtWidgets import (
QPushButton,
QSplitter,
QStatusBar,
QTabWidget,
QToolBar,
QVBoxLayout,
QWidget,
@@ -28,7 +29,10 @@ from PySide6.QtWidgets import (
from vmssailor.core.persistence import load_project, save_project
from vmssailor.core.project import Project
from vmssailor.studio.editors.alarm_editor import AlarmEditor
from vmssailor.studio.editors.equipment_editor import EquipmentEditor
from vmssailor.studio.editors.mimic_editor import MimicEditor
from vmssailor.studio.editors.tag_editor import TagEditor
from vmssailor.studio.theme import (
C_CYAN,
C_FOAM,
@@ -125,15 +129,24 @@ class MainWindow(QMainWindow):
self._sidebar.setMinimumWidth(260)
self._sidebar.setMaximumWidth(380)
# Right pane: vertical splitter with canvas on top + equipment editor below
# Right pane: vertical splitter with canvas on top + editor tabs below
self._canvas = VesselCanvas()
self._equipment_editor = EquipmentEditor()
self._mimic_editor = MimicEditor()
self._tag_editor = TagEditor()
self._alarm_editor = AlarmEditor()
self._tabs = QTabWidget()
self._tabs.addTab(self._equipment_editor, "Equipos")
self._tabs.addTab(self._mimic_editor, "Mímicos")
self._tabs.addTab(self._tag_editor, "Tags")
self._tabs.addTab(self._alarm_editor, "Alarmas")
right_splitter = QSplitter(Qt.Vertical)
right_splitter.setChildrenCollapsible(False)
right_splitter.addWidget(self._canvas)
right_splitter.addWidget(self._equipment_editor)
right_splitter.setSizes([520, 380])
right_splitter.addWidget(self._tabs)
right_splitter.setSizes([460, 440])
self._right_splitter = right_splitter
self._splitter.addWidget(self._sidebar)
@@ -235,7 +248,12 @@ class MainWindow(QMainWindow):
self.projectChanged.connect(self._sidebar.set_project)
self.projectChanged.connect(self._canvas.set_project)
self.projectChanged.connect(self._equipment_editor.set_project)
self.projectChanged.connect(self._mimic_editor.set_project)
self.projectChanged.connect(self._tag_editor.set_project)
self.projectChanged.connect(self._alarm_editor.set_project)
self._equipment_editor.projectMutated.connect(self._on_project_mutated)
self._tag_editor.projectMutated.connect(self._on_project_mutated)
self._alarm_editor.projectMutated.connect(self._on_project_mutated)
# ----- Slots --------------------------------------------------------