chore/adopt-local-tree #4
1 changed files with 43 additions and 18 deletions
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.
commit
ba3cad4eff
|
|
@ -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("<i>Extruded-view shape (display only).</i>"))
|
||||
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`.
|
||||
|
|
|
|||
Loading…
Reference in a new issue