fix: ElasticSectionForm round-trips the SectionShape union
The form still passed flat b/h/d/tw/tf/diameter fields, which ElasticSection (extra='forbid') rejects, so reading any elastic section crashed. Rebuild the display-only hint as a PipeShape / AngleShape / RectShape member instead; an incomplete shape degrades to None rather than raising.
This commit is contained in:
parent
ba783718d4
commit
ba3cad4eff
1 changed files with 43 additions and 18 deletions
|
|
@ -19,8 +19,11 @@ from PySide6.QtWidgets import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from otko.core import (
|
from otko.core import (
|
||||||
|
AngleShape,
|
||||||
ElasticSection,
|
ElasticSection,
|
||||||
FiberSection,
|
FiberSection,
|
||||||
|
PipeShape,
|
||||||
|
RectShape,
|
||||||
SectionAggregator,
|
SectionAggregator,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -94,14 +97,13 @@ class SectionFormBase(QWidget):
|
||||||
class ElasticSectionForm(SectionFormBase):
|
class ElasticSectionForm(SectionFormBase):
|
||||||
type_label = "Elastic Section"
|
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, ...]]] = {
|
_SHAPE_DIMS: ClassVar[dict[str | None, tuple[str, ...]]] = {
|
||||||
None: (),
|
None: (),
|
||||||
"rect": ("b", "h"),
|
"rect": ("b", "d"),
|
||||||
"I": ("b", "d", "tw", "tf"),
|
"angle": ("b", "d", "t"),
|
||||||
"H": ("b", "d", "tw", "tf"),
|
"pipe": ("od", "t"),
|
||||||
"pipe": ("diameter",),
|
|
||||||
"circle": ("diameter",),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, parent: QWidget | None = None) -> None:
|
def __init__(self, parent: QWidget | None = None) -> None:
|
||||||
|
|
@ -127,14 +129,14 @@ class ElasticSectionForm(SectionFormBase):
|
||||||
self._layout.addRow(QLabel("<i>Extruded-view shape (display only).</i>"))
|
self._layout.addRow(QLabel("<i>Extruded-view shape (display only).</i>"))
|
||||||
self._shape = QComboBox()
|
self._shape = QComboBox()
|
||||||
self._shape.addItem("(none)", None)
|
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.addItem(key, key)
|
||||||
self._shape.currentIndexChanged.connect(self._update_dim_visibility)
|
self._shape.currentIndexChanged.connect(self._update_dim_visibility)
|
||||||
self._layout.addRow("Shape:", self._shape)
|
self._layout.addRow("Shape:", self._shape)
|
||||||
|
|
||||||
self._dim_spins: dict[str, QDoubleSpinBox] = {}
|
self._dim_spins: dict[str, QDoubleSpinBox] = {}
|
||||||
self._dim_labels: dict[str, QLabel] = {}
|
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()
|
spin = _opt_spin()
|
||||||
lbl = QLabel(f"{key}:")
|
lbl = QLabel(f"{key}:")
|
||||||
self._dim_spins[key] = spin
|
self._dim_spins[key] = spin
|
||||||
|
|
@ -160,20 +162,15 @@ class ElasticSectionForm(SectionFormBase):
|
||||||
self._g.setValue(s.G)
|
self._g.setValue(s.G)
|
||||||
if s.J is not None:
|
if s.J is not None:
|
||||||
self._j.setValue(s.J)
|
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)
|
self._shape.setCurrentIndex(idx if idx >= 0 else 0)
|
||||||
for key, spin in self._dim_spins.items():
|
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)
|
spin.setValue(v if v is not None else _UNSET_SENTINEL)
|
||||||
self._update_dim_visibility()
|
self._update_dim_visibility()
|
||||||
|
|
||||||
def _read_specific(self, sid: int) -> ElasticSection:
|
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(
|
return ElasticSection(
|
||||||
id=sid,
|
id=sid,
|
||||||
name=self._name_edit.text(),
|
name=self._name_edit.text(),
|
||||||
|
|
@ -183,10 +180,38 @@ class ElasticSectionForm(SectionFormBase):
|
||||||
Iy=self._iy.value(),
|
Iy=self._iy.value(),
|
||||||
G=self._g.value(),
|
G=self._g.value(),
|
||||||
J=self._j.value(),
|
J=self._j.value(),
|
||||||
shape=shape,
|
shape=self._read_shape(),
|
||||||
**dims, # type: ignore[arg-type]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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):
|
class FiberSectionSummaryForm(SectionFormBase):
|
||||||
"""Read-only overview of a :class:`FiberSection`.
|
"""Read-only overview of a :class:`FiberSection`.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue