mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 06:58:56 +00:00
Bonsai: derive prefs PropertyGroups from EDIT_TYPES
Collapse two parallel hand-maintained lists in the addon-preferences PropertyGroups into derivations from `tool.Parametric.EDIT_TYPES`: - GizmoPreferences: the 10 `<name>: BoolProperty` annotations now generated from the full EDIT_TYPES list. - DefaultParameters: add `has_default_parameters` flag to ParametricObject (set True on door/window/stair/railing/roof); derive the 5 `<name>: PointerProperty(type=BIM<X>Properties)` annotations and collapse the 5 hand-written `draw_expandable_panel` blocks in `draw_default_parameters` into loops driven by the flag. Existing `test_gizmo_preferences_field_per_registry_entry` pinned the GizmoPreferences contract; new `test_default_parameters_field_per_registry_entry_with_defaults` pins the DefaultParameters contract (one-directional: flag=True implies field present, flag=False allows absence). Generated with the assistance of an AI coding tool.
This commit is contained in:
+43
-68
@@ -41,20 +41,8 @@ import bonsai.bim.helper
|
|||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
from bonsai.bim.ifc import is_cache_locked_by_other_process
|
from bonsai.bim.ifc import is_cache_locked_by_other_process
|
||||||
from bonsai.bim.module.bsdd.prop import BIMBSDDProperties, BSDDProperty
|
from bonsai.bim.module.bsdd.prop import BIMBSDDProperties, BSDDProperty
|
||||||
from bonsai.bim.module.model.prop import (
|
from bonsai.bim.module.model import prop as _model_prop
|
||||||
BIMDoorProperties,
|
from bonsai.bim.module.model import ui as _model_ui
|
||||||
BIMRailingProperties,
|
|
||||||
BIMRoofProperties,
|
|
||||||
BIMStairProperties,
|
|
||||||
BIMWindowProperties,
|
|
||||||
)
|
|
||||||
from bonsai.bim.module.model.ui import (
|
|
||||||
draw_door_properties,
|
|
||||||
draw_railing_properties,
|
|
||||||
draw_roof_properties,
|
|
||||||
draw_stair_properties,
|
|
||||||
draw_window_properties,
|
|
||||||
)
|
|
||||||
from bonsai.bim.module.pset.prop import IfcProperty
|
from bonsai.bim.module.pset.prop import IfcProperty
|
||||||
from bonsai.bim.prop import Attribute
|
from bonsai.bim.prop import Attribute
|
||||||
|
|
||||||
@@ -279,34 +267,29 @@ class BIM_UL_panel_visibilities(bpy.types.UIList):
|
|||||||
class GizmoPreferences(bpy.types.PropertyGroup):
|
class GizmoPreferences(bpy.types.PropertyGroup):
|
||||||
"""Aggregator for parametric gizmo visibility settings. One flat bool per
|
"""Aggregator for parametric gizmo visibility settings. One flat bool per
|
||||||
parametric feature; controls whether that feature's gizmo group polls
|
parametric feature; controls whether that feature's gizmo group polls
|
||||||
visible in the viewport."""
|
visible in the viewport.
|
||||||
|
|
||||||
|
The per-feature ``<name>: BoolProperty`` fields are derived from
|
||||||
|
``tool.Parametric.EDIT_TYPES`` at module load — adding a new parametric
|
||||||
|
type to the registry automatically surfaces its toggle here, with no
|
||||||
|
parallel hand-maintained list to keep in sync."""
|
||||||
|
|
||||||
draw_gizmos_in_3d_viewport: BoolProperty(
|
draw_gizmos_in_3d_viewport: BoolProperty(
|
||||||
name="Draw Gizmos In 3D Viewport",
|
name="Draw Gizmos In 3D Viewport",
|
||||||
default=True,
|
default=True,
|
||||||
description="Show interactive gizmos in the 3D viewport for parametric elements",
|
description="Show interactive gizmos in the 3D viewport for parametric elements",
|
||||||
)
|
)
|
||||||
door: BoolProperty(name="Door", default=True)
|
|
||||||
window: BoolProperty(name="Window", default=True)
|
|
||||||
stair: BoolProperty(name="Stair", default=True)
|
|
||||||
railing: BoolProperty(name="Railing", default=True)
|
|
||||||
roof: BoolProperty(name="Roof", default=True)
|
|
||||||
array: BoolProperty(name="Array", default=True)
|
|
||||||
pipe_segment: BoolProperty(name="Pipe Segment", default=True)
|
|
||||||
duct_segment: BoolProperty(name="Duct Segment", default=True)
|
|
||||||
wall: BoolProperty(name="Wall", default=True)
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
draw_gizmos_in_3d_viewport: bool
|
draw_gizmos_in_3d_viewport: bool
|
||||||
door: bool
|
|
||||||
window: bool
|
|
||||||
stair: bool
|
for _gizmo_pref_entry in tool.Parametric.EDIT_TYPES:
|
||||||
railing: bool
|
GizmoPreferences.__annotations__[_gizmo_pref_entry.name] = BoolProperty(
|
||||||
roof: bool
|
name=_gizmo_pref_entry.name.replace("_", " ").title(),
|
||||||
array: bool
|
default=True,
|
||||||
pipe_segment: bool
|
)
|
||||||
duct_segment: bool
|
del _gizmo_pref_entry
|
||||||
wall: bool
|
|
||||||
|
|
||||||
|
|
||||||
class DocPreferences(bpy.types.PropertyGroup):
|
class DocPreferences(bpy.types.PropertyGroup):
|
||||||
@@ -402,11 +385,22 @@ class DocPreferences(bpy.types.PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class DefaultParameters(bpy.types.PropertyGroup):
|
class DefaultParameters(bpy.types.PropertyGroup):
|
||||||
door: bpy.props.PointerProperty(type=BIMDoorProperties)
|
"""Per-type preset values used to seed new parametric instances.
|
||||||
window: bpy.props.PointerProperty(type=BIMWindowProperties)
|
|
||||||
railing: bpy.props.PointerProperty(type=BIMRailingProperties)
|
The ``<name>: PointerProperty`` fields are derived from the subset of
|
||||||
roof: bpy.props.PointerProperty(type=BIMRoofProperties)
|
``tool.Parametric.EDIT_TYPES`` flagged ``has_default_parameters=True``,
|
||||||
stair: bpy.props.PointerProperty(type=BIMStairProperties)
|
each pointing at the matching ``BIM<Name>Properties`` class. Adding a
|
||||||
|
new entry with that flag automatically surfaces a preferences section
|
||||||
|
and gives the create operator a preset to copy from."""
|
||||||
|
|
||||||
|
|
||||||
|
for _default_params_entry in tool.Parametric.EDIT_TYPES:
|
||||||
|
if not _default_params_entry.has_default_parameters:
|
||||||
|
continue
|
||||||
|
DefaultParameters.__annotations__[_default_params_entry.name] = bpy.props.PointerProperty(
|
||||||
|
type=getattr(_model_prop, _default_params_entry.props_attr),
|
||||||
|
)
|
||||||
|
del _default_params_entry
|
||||||
|
|
||||||
|
|
||||||
class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||||
@@ -828,36 +822,17 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
|||||||
|
|
||||||
def draw_default_parameters(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
|
def draw_default_parameters(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
|
||||||
box = layout.box()
|
box = layout.box()
|
||||||
bonsai.bim.helper.draw_expandable_panel(
|
for entry in tool.Parametric.EDIT_TYPES:
|
||||||
box,
|
if not entry.has_default_parameters:
|
||||||
context,
|
continue
|
||||||
"Door",
|
props = getattr(self.default_parameters, entry.name)
|
||||||
lambda _layout, _context: draw_door_properties(_layout, self.default_parameters.door),
|
draw_props = getattr(_model_ui, f"draw_{entry.name}_properties")
|
||||||
)
|
bonsai.bim.helper.draw_expandable_panel(
|
||||||
bonsai.bim.helper.draw_expandable_panel(
|
box,
|
||||||
box,
|
context,
|
||||||
context,
|
entry.name.replace("_", " ").title(),
|
||||||
"Window",
|
lambda _layout, _context, _draw=draw_props, _props=props: _draw(_layout, _props),
|
||||||
lambda _layout, _context: draw_window_properties(_layout, self.default_parameters.window),
|
)
|
||||||
)
|
|
||||||
bonsai.bim.helper.draw_expandable_panel(
|
|
||||||
box,
|
|
||||||
context,
|
|
||||||
"Railing",
|
|
||||||
lambda _layout, _context: draw_railing_properties(_layout, self.default_parameters.railing),
|
|
||||||
)
|
|
||||||
bonsai.bim.helper.draw_expandable_panel(
|
|
||||||
box,
|
|
||||||
context,
|
|
||||||
"Roof",
|
|
||||||
lambda _layout, _context: draw_roof_properties(_layout, self.default_parameters.roof),
|
|
||||||
)
|
|
||||||
bonsai.bim.helper.draw_expandable_panel(
|
|
||||||
box,
|
|
||||||
context,
|
|
||||||
"Stair",
|
|
||||||
lambda _layout, _context: draw_stair_properties(_layout, self.default_parameters.stair),
|
|
||||||
)
|
|
||||||
|
|
||||||
def draw_other_settings(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
|
def draw_other_settings(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
|
||||||
layout.prop(self, "opening_focus_opacity")
|
layout.prop(self, "opening_focus_opacity")
|
||||||
|
|||||||
@@ -81,11 +81,19 @@ class ParametricObject:
|
|||||||
``_cancel_targets``) and that therefore wire their operators through
|
``_cancel_targets``) and that therefore wire their operators through
|
||||||
``build_edit_lifecycle``. Entries with bespoke edit lifecycles (per-attribute
|
``build_edit_lifecycle``. Entries with bespoke edit lifecycles (per-attribute
|
||||||
diff dispatch, layer-stack editing, mid-spline gizmo drag) leave this
|
diff dispatch, layer-stack editing, mid-spline gizmo drag) leave this
|
||||||
False and declare their operator classes directly."""
|
False and declare their operator classes directly.
|
||||||
|
|
||||||
|
``has_default_parameters`` marks entries whose ``BIM<Name>Properties``
|
||||||
|
class exposes ``get_general_kwargs`` / ``copy_to`` and a matching
|
||||||
|
``draw_<name>_properties`` UI helper, so the addon-preferences panel can
|
||||||
|
surface a per-type defaults section and the create operator can seed new
|
||||||
|
instances from the preset. Entries without that machinery leave this False
|
||||||
|
and don't appear in the preferences ``Default Parameters`` panel."""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
has_non_editable_path: bool = False
|
has_non_editable_path: bool = False
|
||||||
supports_build_edit_lifecycle: bool = False
|
supports_build_edit_lifecycle: bool = False
|
||||||
|
has_default_parameters: bool = False
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
if not _VALID_NAME_RE.match(self.name):
|
if not _VALID_NAME_RE.match(self.name):
|
||||||
@@ -148,11 +156,17 @@ class Parametric(bonsai.core.tool.Parametric):
|
|||||||
self._gen = None
|
self._gen = None
|
||||||
|
|
||||||
EDIT_TYPES: list[ParametricObject] = [
|
EDIT_TYPES: list[ParametricObject] = [
|
||||||
ParametricObject("door", has_non_editable_path=True, supports_build_edit_lifecycle=True),
|
ParametricObject(
|
||||||
ParametricObject("window", has_non_editable_path=True, supports_build_edit_lifecycle=True),
|
"door", has_non_editable_path=True, supports_build_edit_lifecycle=True, has_default_parameters=True
|
||||||
ParametricObject("stair", has_non_editable_path=True, supports_build_edit_lifecycle=True),
|
),
|
||||||
ParametricObject("railing", supports_build_edit_lifecycle=True),
|
ParametricObject(
|
||||||
ParametricObject("roof", supports_build_edit_lifecycle=True),
|
"window", has_non_editable_path=True, supports_build_edit_lifecycle=True, has_default_parameters=True
|
||||||
|
),
|
||||||
|
ParametricObject(
|
||||||
|
"stair", has_non_editable_path=True, supports_build_edit_lifecycle=True, has_default_parameters=True
|
||||||
|
),
|
||||||
|
ParametricObject("railing", supports_build_edit_lifecycle=True, has_default_parameters=True),
|
||||||
|
ParametricObject("roof", supports_build_edit_lifecycle=True, has_default_parameters=True),
|
||||||
ParametricObject("array", supports_build_edit_lifecycle=True),
|
ParametricObject("array", supports_build_edit_lifecycle=True),
|
||||||
ParametricObject("pipe_segment", supports_build_edit_lifecycle=True),
|
ParametricObject("pipe_segment", supports_build_edit_lifecycle=True),
|
||||||
ParametricObject("duct_segment", supports_build_edit_lifecycle=True),
|
ParametricObject("duct_segment", supports_build_edit_lifecycle=True),
|
||||||
|
|||||||
@@ -123,6 +123,31 @@ def test_every_predicate_does_not_raise_on_non_matching_element(registry):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_parameters_field_per_registry_entry_with_defaults(registry):
|
||||||
|
"""Every entry flagged ``has_default_parameters=True`` must have a matching
|
||||||
|
``<name>: PointerProperty`` field on ``ui.DefaultParameters`` pointing at
|
||||||
|
its ``BIM<Name>Properties`` class.
|
||||||
|
|
||||||
|
The addon-preferences ``Default Parameters`` panel iterates flagged entries
|
||||||
|
to render per-type defaults sections, and the matching create operator
|
||||||
|
(``bim.add_door``, ``bim.add_window``, …) reads the field to seed new
|
||||||
|
instances from the user's preset values. A missing field means the preset
|
||||||
|
silently never reaches the operator.
|
||||||
|
|
||||||
|
Entries WITHOUT the flag are not required to appear — the contract is
|
||||||
|
one-directional: ``has_default_parameters=True`` implies a field, but
|
||||||
|
``False`` allows absence."""
|
||||||
|
from bonsai.bim import ui
|
||||||
|
|
||||||
|
annotations = getattr(ui.DefaultParameters, "__annotations__", {})
|
||||||
|
missing = [e.name for e in registry if e.has_default_parameters and e.name not in annotations]
|
||||||
|
assert not missing, (
|
||||||
|
f"ui.DefaultParameters missing PointerProperty field(s) for: {missing} — "
|
||||||
|
f"each EDIT_TYPES entry with has_default_parameters=True must have a matching "
|
||||||
|
f"<name>: PointerProperty(type=BIM<Name>Properties) field"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_gizmo_preferences_field_per_registry_entry(registry):
|
def test_gizmo_preferences_field_per_registry_entry(registry):
|
||||||
"""Every registry entry must have a matching ``<name>: BoolProperty`` field
|
"""Every registry entry must have a matching ``<name>: BoolProperty`` field
|
||||||
on ``ui.GizmoPreferences`` so the addon-preferences UI auto-renders a
|
on ``ui.GizmoPreferences`` so the addon-preferences UI auto-renders a
|
||||||
|
|||||||
Reference in New Issue
Block a user