Systems UI - display hierarchy of systems #7062

This commit is contained in:
Andrej730
2025-09-15 10:02:32 +05:00
parent 646f2f2e32
commit 4c1af9e2f5
6 changed files with 100 additions and 38 deletions
+17 -14
View File
@@ -22,7 +22,7 @@ import ifcopenshell.api.group
import ifcopenshell.util.element
import bonsai.bim.helper
import bonsai.tool as tool
import json
from typing import TYPE_CHECKING, Literal, get_args
class LoadGroups(bpy.types.Operator, tool.Ifc.Operator):
@@ -31,7 +31,7 @@ class LoadGroups(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
tool.Group.import_groups()
tool.Group.import_groups("IfcGroup")
tool.Group.enable_group_editing_ui()
tool.Group.disable_editing_group()
return {"FINISHED"}
@@ -41,20 +41,23 @@ class ToggleGroup(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.toggle_group"
bl_label = "Toggle Group"
bl_options = {"REGISTER", "UNDO"}
ifc_definition_id: bpy.props.IntProperty()
index: bpy.props.IntProperty()
option: bpy.props.StringProperty(name="Expand or Collapse")
ifc_definition_id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
group_type: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
items=[(i, i, "") for i in get_args(tool.Group.GroupType)],
)
option: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
items=[(i, i, "") for i in get_args(tool.Group.ToggleOption)],
)
if TYPE_CHECKING:
ifc_definition_id: int
group_type: tool.Group.GroupType
option: tool.Group.ToggleOption
def _execute(self, context):
props = tool.Group.get_group_props()
expanded_groups: set[int]
expanded_groups = set(json.loads(props.expanded_groups_json))
if self.option == "Expand":
expanded_groups.add(self.ifc_definition_id)
elif self.ifc_definition_id in expanded_groups:
expanded_groups.remove(self.ifc_definition_id)
props.expanded_groups_json = json.dumps(list(expanded_groups))
bpy.ops.bim.load_groups()
group = tool.Ifc.get().by_id(self.ifc_definition_id)
tool.Group.toggle_group(group, self.group_type, self.option)
return {"FINISHED"}
+2 -2
View File
@@ -139,7 +139,7 @@ class BIM_UL_groups(UIList):
op = row.operator(
"bim.toggle_group", icon="TRIA_DOWN" if item.is_expanded else "TRIA_RIGHT", text="", emboss=False
)
op.group_type = "IfcGroup"
op.ifc_definition_id = item.ifc_definition_id
op.index = index
op.option = "Collapse" if item.is_expanded else "Expand"
op.option = "COLLAPSE" if item.is_expanded else "EXPAND"
row.prop(item, "name", text="", emboss=False)
@@ -54,11 +54,17 @@ class System(PropertyGroup):
name: StringProperty(name="Name", update=update_system_name)
ifc_class: StringProperty(name="IFC Class")
ifc_definition_id: IntProperty(name="IFC Definition ID")
has_children: BoolProperty(name="Has Children")
tree_depth: IntProperty(name="Tree Depth")
is_expanded: BoolProperty(name="Is Expanded")
if TYPE_CHECKING:
name: str
ifc_class: str
ifc_definition_id: int
has_children: bool
tree_depth: int
is_expanded: bool
def update_zone_name(self: "Zone", context: bpy.types.Context) -> None:
@@ -91,6 +97,8 @@ class BIMSystemProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing", default=False)
is_adding: BoolProperty(name="Is Adding", default=False)
systems: CollectionProperty(name="Systems", type=System)
expanded_groups_json: StringProperty(name="Expanded Systems JSON", default="[]")
"""See `expanded_groups_json`. We name it "groups" for compatibility with Group UI code."""
active_system_index: IntProperty(name="Active System Index")
active_system_id: IntProperty(name="Active System Id")
edited_system_id: IntProperty(name="Edited System Id")
@@ -104,6 +112,7 @@ class BIMSystemProperties(PropertyGroup):
is_editing: bool
is_adding: bool
systems: bpy.types.bpy_prop_collection_idprop[System]
expanded_groups_json: str
active_system_index: int
active_system_id: int
edited_system_id: int
+13 -1
View File
@@ -428,10 +428,22 @@ class BIM_UL_systems(UIList):
icon,
active_data,
active_propname,
):
) -> None:
if item:
row = layout.row(align=True)
for i in range(0, item.tree_depth):
row.label(text="", icon="BLANK1")
system_id = item.ifc_definition_id
if item.has_children:
op = row.operator(
"bim.toggle_group",
text="",
icon="TRIA_DOWN" if item.is_expanded else "TRIA_RIGHT",
emboss=False,
)
op.group_type = "IfcSystem"
op.ifc_definition_id = item.ifc_definition_id
op.option = "COLLAPSE" if item.is_expanded else "EXPAND"
if data.edited_system_id == system_id:
row.label(text="", icon="GREASEPENCIL")
row.prop(item, "name", text="", icon=SYSTEM_ICONS[item.ifc_class], emboss=False)
+57 -12
View File
@@ -23,11 +23,12 @@ import ifcopenshell
import bonsai.bim.helper
import bonsai.core.tool
import bonsai.tool as tool
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Literal, assert_never, Union
from natsort import natsorted
if TYPE_CHECKING:
from bonsai.bim.module.group.prop import BIMGroupProperties
from bonsai.bim.module.group.prop import BIMGroupProperties, Group as GroupProp
from bonsai.bim.module.system.prop import BIMSystemProperties, System
class Group(bonsai.core.tool.System):
@@ -36,31 +37,60 @@ class Group(bonsai.core.tool.System):
assert (scene := bpy.context.scene)
return scene.BIMGroupProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def import_groups(cls) -> None:
props = tool.Group.get_group_props()
expanded_groups: list[int] = json.loads(props.expanded_groups_json)
props.groups.clear()
GroupType = Literal["IfcGroup", "IfcSystem"]
groups = [
group for group in tool.Ifc.get().by_type("IfcGroup", include_subtypes=False) if not group.HasAssignments
]
@classmethod
def get_groups_data(cls, group_type: GroupType) -> Union[
tuple[BIMGroupProperties, bpy.types.bpy_prop_collection_idprop[GroupProp]],
tuple[BIMSystemProperties, bpy.types.bpy_prop_collection_idprop[System]],
]:
if group_type == "IfcGroup":
props = tool.Group.get_group_props()
blender_groups = props.groups
return props, blender_groups
elif group_type == "IfcSystem":
props = tool.System.get_system_props()
blender_groups = props.systems
return props, blender_groups
else:
assert_never(group_type)
@classmethod
def import_groups(cls, group_type: GroupType) -> None:
from bonsai.bim.module.system.prop import System
ifc_file = tool.Ifc.get()
props, blender_groups = cls.get_groups_data(group_type)
if group_type == "IfcGroup":
base_groups = ifc_file.by_type("IfcGroup", include_subtypes=False)
elif group_type == "IfcSystem":
base_groups = [g for g in tool.System.get_systems() if not g.is_a("IfcStructuralAnalysisModel")]
else:
assert_never(group_type)
expanded_groups_json = props.expanded_groups_json
expanded_groups: list[int] = json.loads(expanded_groups_json)
blender_groups.clear()
groups = [g for g in base_groups if not g.HasAssignments]
sorted_groups = natsorted(groups, key=lambda group: group.Name or "Unnamed")
def load_group(group: ifcopenshell.entity_instance, tree_depth: int = 0) -> None:
new = props.groups.add()
new = blender_groups.add()
new.ifc_definition_id = group.id()
new["name"] = group.Name or "Unnamed"
new.tree_depth = tree_depth
new.has_children = False
new.is_expanded = group.id() in expanded_groups
if isinstance(new, System):
new.ifc_class = group.is_a()
related_groups: list[ifcopenshell.entity_instance]
related_groups = [
related_object
for rel in group.IsGroupedBy or []
for related_object in rel.RelatedObjects
if related_object.is_a("IfcGroup")
if related_object.is_a(group_type)
]
sorted_related_groups = natsorted(related_groups, key=lambda group: group.Name or "Unnamed")
@@ -92,3 +122,18 @@ class Group(bonsai.core.tool.System):
def set_active_group_to_edit(cls, group: ifcopenshell.entity_instance) -> None:
props = cls.get_group_props()
props.active_group_id = group.id()
ToggleOption = Literal["EXPAND", "COLLAPSE"]
@classmethod
def toggle_group(cls, group: ifcopenshell.entity_instance, group_type: GroupType, option: ToggleOption) -> None:
props, _ = cls.get_groups_data(group_type)
expanded_groups: set[int]
expanded_groups = set(json.loads(props.expanded_groups_json))
ifc_definition_id = group.id()
if option == "EXPAND":
expanded_groups.add(ifc_definition_id)
elif ifc_definition_id in expanded_groups:
expanded_groups.remove(ifc_definition_id)
props.expanded_groups_json = json.dumps(list(expanded_groups))
cls.import_groups(group_type)
+2 -9
View File
@@ -33,6 +33,7 @@ from mathutils import Matrix, Vector
from bonsai.bim.module.system.data import ObjectSystemData, SystemDecorationData
from enum import Enum
from typing import TYPE_CHECKING, Optional, Any, Union
from natsort import natsorted
if TYPE_CHECKING:
from bonsai.bim.module.system.prop import BIMSystemProperties, BIMZoneProperties, BIMZoneProperties
@@ -160,15 +161,7 @@ class System(bonsai.core.tool.System):
@classmethod
def import_systems(cls) -> None:
props = cls.get_system_props()
props.systems.clear()
for system in cls.get_systems():
if system.is_a() in ["IfcStructuralAnalysisModel"]:
continue
new = props.systems.add()
new.ifc_definition_id = system.id()
new["name"] = system.Name or "Unnamed"
new.ifc_class = system.is_a()
tool.Group.import_groups("IfcSystem")
@classmethod
def load_ports(cls, element: ifcopenshell.entity_instance, ports: list[ifcopenshell.entity_instance]) -> None: