diff --git a/src/otko/views/dialogs/section_forms.py b/src/otko/views/dialogs/section_forms.py index c824db7..ec386e4 100644 --- a/src/otko/views/dialogs/section_forms.py +++ b/src/otko/views/dialogs/section_forms.py @@ -19,8 +19,11 @@ from PySide6.QtWidgets import ( ) from otko.core import ( + AngleShape, ElasticSection, FiberSection, + PipeShape, + RectShape, SectionAggregator, ) @@ -94,14 +97,13 @@ class SectionFormBase(QWidget): class ElasticSectionForm(SectionFormBase): type_label = "Elastic Section" - #: Shape → visible display-only dimensions. ``None`` hides them all. + #: Shape kind → visible display-only dimensions (see the core + #: ``SectionShape`` union). ``None`` hides them all. _SHAPE_DIMS: ClassVar[dict[str | None, tuple[str, ...]]] = { None: (), - "rect": ("b", "h"), - "I": ("b", "d", "tw", "tf"), - "H": ("b", "d", "tw", "tf"), - "pipe": ("diameter",), - "circle": ("diameter",), + "rect": ("b", "d"), + "angle": ("b", "d", "t"), + "pipe": ("od", "t"), } def __init__(self, parent: QWidget | None = None) -> None: @@ -127,14 +129,14 @@ class ElasticSectionForm(SectionFormBase): self._layout.addRow(QLabel("Extruded-view shape (display only).")) self._shape = QComboBox() self._shape.addItem("(none)", None) - for key in ("rect", "I", "H", "pipe", "circle"): + for key in ("rect", "angle", "pipe"): self._shape.addItem(key, key) self._shape.currentIndexChanged.connect(self._update_dim_visibility) self._layout.addRow("Shape:", self._shape) self._dim_spins: dict[str, QDoubleSpinBox] = {} self._dim_labels: dict[str, QLabel] = {} - for key in ("b", "h", "d", "tw", "tf", "diameter"): + for key in ("b", "d", "t", "od"): spin = _opt_spin() lbl = QLabel(f"{key}:") self._dim_spins[key] = spin @@ -160,20 +162,15 @@ class ElasticSectionForm(SectionFormBase): self._g.setValue(s.G) if s.J is not None: self._j.setValue(s.J) - idx = self._shape.findData(s.shape) + kind = getattr(s.shape, "kind", None) if s.shape is not None else None + idx = self._shape.findData(kind) self._shape.setCurrentIndex(idx if idx >= 0 else 0) for key, spin in self._dim_spins.items(): - v = getattr(s, key, None) + v = getattr(s.shape, key, None) if s.shape is not None else None spin.setValue(v if v is not None else _UNSET_SENTINEL) self._update_dim_visibility() def _read_specific(self, sid: int) -> ElasticSection: - shape = self._shape.currentData() - visible = set(self._SHAPE_DIMS.get(shape, ())) - dims = { - key: (_opt_value(spin) if key in visible else None) - for key, spin in self._dim_spins.items() - } return ElasticSection( id=sid, name=self._name_edit.text(), @@ -183,10 +180,38 @@ class ElasticSectionForm(SectionFormBase): Iy=self._iy.value(), G=self._g.value(), J=self._j.value(), - shape=shape, - **dims, # type: ignore[arg-type] + shape=self._read_shape(), ) + def _read_shape(self) -> PipeShape | AngleShape | RectShape | None: + """Build the display-only shape hint from the form fields. + + Dimensions are optional in the UI, so an incomplete shape degrades + to ``None`` instead of raising — the hint is never emitted to + OpenSees and cannot change an analysis result. + """ + + def dim(key: str) -> float | None: + return _opt_value(self._dim_spins[key]) + + kind = self._shape.currentData() + if kind == "rect": + b, d = dim("b"), dim("d") + if b is None or d is None: + return None + return RectShape(b=b, d=d) + if kind == "angle": + b, d, t = dim("b"), dim("d"), dim("t") + if b is None or d is None or t is None: + return None + return AngleShape(b=b, d=d, t=t) + if kind == "pipe": + od, t = dim("od"), dim("t") + if od is None or t is None: + return None + return PipeShape(od=od, t=t) + return None + class FiberSectionSummaryForm(SectionFormBase): """Read-only overview of a :class:`FiberSection`.