Módulo 2 completo: HydrostaticsChartWidget + integración + tests (Tasks 14–16)
Task 14 — HydrostaticsChartWidget (QPainter): - 9 paneles cuadrícula 3x3: Δ, V, Awp, LCB/LCF, KB/BMT, KMT/KML, TPC, MCT, Cb/Cw/Cm - Cursor vertical compartido: clic/arrastre en cualquier panel mueve el cursor en todos y actualiza la barra de valores - _InfoBar: franja superior con valores interpolados al calado activo - _nice_ticks(): escala de ejes legible sin dependencias externas - Sin dependencias externas (sólo PySide6 + numpy) Task 15 — Integración en MainWindow: - MOD_CURVES cargado con HydrostaticsChartWidget (sustituye placeholder) - _on_compute_hydrostatics(): calcula HydrostaticCurves.compute(n=30) - _on_show_hydrostatics(): abre el módulo (calculando si no hay datos) - _on_export_hydrostatics_csv(): exporta CSV con QFileDialog - Ribbon tab Análisis: botones Calcular, Curvas, Exp. CSV activos - Menú Análisis → Hidrostática: 3 acciones funcionando - dark.qss: estilos para hydrostaticsChart, hydroInfoBar, hydroPlaceholder Task 16 — Tests V&V (58 tests): - Widget headless W-01..W-08: construcción, set_curves, señales, clampeo - CSV V037..V044: columnas, filas, monotonicidad, separadores, decimal coma - at_draft V045..V049: interpolación lineal, clampeo, tipo retorno - 5 familias V050..V055: Δ monótona, V>0, Cb∈(0,1), KMT>KB, KML>KMT, TPC>0 - IACS Rec.34 §4.3 V056..V062: Cb=4/9, Cw=2/3, KB, LCB=LCF=L/2, Cp=Cb/Cm, convergencia de malla <2% Total: 282 tests, 0 failed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -870,6 +870,11 @@ class MainWindow(QMainWindow):
|
||||
self._offsets_editor.hull_changed.connect(self._on_hull_changed_from_editor)
|
||||
self._module_area.set_module_widget(ModuleArea.MOD_OFFSETS, self._offsets_editor)
|
||||
|
||||
# Visor de curvas hidrostáticas (sustituye el placeholder MOD_CURVES)
|
||||
from arshipdesign.ui.widgets.hydrostatics_chart import HydrostaticsChartWidget
|
||||
self._hydro_chart = HydrostaticsChartWidget()
|
||||
self._module_area.set_module_widget(ModuleArea.MOD_CURVES, self._hydro_chart)
|
||||
|
||||
# Dock izquierdo — capas
|
||||
self._layers_panel = LayersPanel(self._strings)
|
||||
self._dock_layers = QDockWidget("Capas", self)
|
||||
@@ -964,9 +969,12 @@ class MainWindow(QMainWindow):
|
||||
|
||||
# ── Tab ANÁLISIS ──────────────────────────────────────────
|
||||
g = self._ribbon.new_group(RibbonBar.TAB_ANALYSIS, "Hidrostática")
|
||||
g.add_button(_spi(sp.SP_FileDialogDetailedView), "Calcular", "Calcular hidrostáticos", enabled=False)
|
||||
g.add_button(_spi(sp.SP_FileDialogDetailedView), "Calcular", "Calcular curvas hidrostáticas",
|
||||
self._on_compute_hydrostatics)
|
||||
g.add_button(_spi(sp.SP_FileDialogDetailedView), "Curvas", "Curvas hidrostáticas",
|
||||
lambda: self._module_area.activate(M.MOD_CURVES), False)
|
||||
self._on_show_hydrostatics)
|
||||
g.add_button(_spi(sp.SP_DialogSaveButton), "Exp. CSV", "Exportar curvas como CSV",
|
||||
self._on_export_hydrostatics_csv)
|
||||
|
||||
g = self._ribbon.new_group(RibbonBar.TAB_ANALYSIS, "Estabilidad")
|
||||
g.add_button(_spi(sp.SP_FileDialogDetailedView), "Curva GZ", "Curva GZ estática",
|
||||
@@ -1095,8 +1103,12 @@ class MainWindow(QMainWindow):
|
||||
m = mb.addMenu("Análisis")
|
||||
|
||||
sm = m.addMenu("Hidrostática")
|
||||
self._add_action(sm, "Calcular hidrostáticos", enabled=False)
|
||||
self._add_action(sm, "Curvas hidrostáticas", slot=lambda: self._module_area.activate(M.MOD_CURVES), enabled=False)
|
||||
self._add_action(sm, "Calcular hidrostáticos",
|
||||
slot=self._on_compute_hydrostatics)
|
||||
self._add_action(sm, "Curvas hidrostáticas",
|
||||
slot=self._on_show_hydrostatics)
|
||||
self._add_action(sm, "Exportar curvas CSV…",
|
||||
slot=self._on_export_hydrostatics_csv)
|
||||
|
||||
sm = m.addMenu("Estabilidad")
|
||||
self._add_action(sm, "Curva GZ — Estabilidad estática", slot=lambda: self._module_area.activate(M.MOD_STABILITY), enabled=False)
|
||||
@@ -1387,6 +1399,70 @@ class MainWindow(QMainWindow):
|
||||
except Exception as exc:
|
||||
logger.warning("Error al calcular hidrostáticos: %s", exc)
|
||||
|
||||
# ─────────────────────────────────────────────────────────
|
||||
# CURVAS HIDROSTÁTICAS
|
||||
# ─────────────────────────────────────────────────────────
|
||||
|
||||
def _on_compute_hydrostatics(self) -> None:
|
||||
"""Calcula las curvas hidrostáticas y muestra el módulo."""
|
||||
if self._current_hull is None:
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
QMessageBox.information(
|
||||
self, "Sin casco", "Crea o abre un proyecto con un casco definido."
|
||||
)
|
||||
return
|
||||
try:
|
||||
from arshipdesign.hydrostatics.curves_of_form import HydrostaticCurves
|
||||
self.statusBar().showMessage("Calculando curvas hidrostáticas…")
|
||||
QApplication.processEvents()
|
||||
curves = HydrostaticCurves.compute(
|
||||
self._current_hull, n_points=30, rho=1025.0
|
||||
)
|
||||
self._hydro_chart.set_curves(curves)
|
||||
self._module_area.activate(ModuleArea.MOD_CURVES)
|
||||
self.statusBar().showMessage(
|
||||
f"Curvas hidrostáticas calculadas — {curves.hull_name} "
|
||||
f"({len(curves.points)} puntos, T: "
|
||||
f"{curves.drafts[0]:.2f}–{curves.drafts[-1]:.2f} m)"
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error("Error al calcular curvas: %s", exc)
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
QMessageBox.critical(self, "Error al calcular", str(exc))
|
||||
|
||||
def _on_show_hydrostatics(self) -> None:
|
||||
"""Muestra el módulo de curvas (sin recalcular si ya hay datos)."""
|
||||
if self._hydro_chart.curves is None and self._current_hull is not None:
|
||||
self._on_compute_hydrostatics()
|
||||
else:
|
||||
self._module_area.activate(ModuleArea.MOD_CURVES)
|
||||
|
||||
def _on_export_hydrostatics_csv(self) -> None:
|
||||
"""Exporta las curvas hidrostáticas como CSV."""
|
||||
if self._hydro_chart.curves is None:
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
QMessageBox.information(
|
||||
self, "Sin datos", "Calcula las curvas hidrostáticas primero."
|
||||
)
|
||||
return
|
||||
curves = self._hydro_chart.curves
|
||||
default_name = f"{curves.hull_name}_hidrostatics.csv".replace(" ", "_")
|
||||
path, _ = QFileDialog.getSaveFileName(
|
||||
self, "Exportar curvas hidrostáticas",
|
||||
str(Path.home() / default_name),
|
||||
"CSV (*.csv);;Todos los archivos (*)",
|
||||
)
|
||||
if not path:
|
||||
return
|
||||
try:
|
||||
lines = curves.to_csv_lines(sep=",", decimal=".")
|
||||
Path(path).write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
self.statusBar().showMessage(f"CSV exportado: {path}")
|
||||
except Exception as exc:
|
||||
logger.error("Error al exportar CSV: %s", exc)
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
QMessageBox.critical(self, "Error al exportar", str(exc))
|
||||
|
||||
def _ask_save(self) -> bool:
|
||||
reply = QMessageBox.question(
|
||||
self, "Cambios sin guardar",
|
||||
|
||||
Reference in New Issue
Block a user