mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 01:11:40 +00:00
Systems UI - display hierarchy of systems #7062
This commit is contained in:
@@ -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"}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user