From a2a4da596f171ac45a7b4b4e4df36a592c825034 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 25 Jan 2025 14:01:56 +1100 Subject: [PATCH] Fix #6030. Consolidate group management into single UI and allow for bulk assign / unassign. --- .../bonsai/bim/module/group/__init__.py | 1 - src/bonsai/bonsai/bim/module/group/data.py | 6 +- .../bonsai/bim/module/group/operator.py | 56 ++++++------ src/bonsai/bonsai/bim/module/group/prop.py | 5 ++ src/bonsai/bonsai/bim/module/group/ui.py | 90 ++++--------------- 5 files changed, 48 insertions(+), 110 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/group/__init__.py b/src/bonsai/bonsai/bim/module/group/__init__.py index e2c06b46bd..970c6cfbb8 100644 --- a/src/bonsai/bonsai/bim/module/group/__init__.py +++ b/src/bonsai/bonsai/bim/module/group/__init__.py @@ -38,7 +38,6 @@ classes = ( ui.BIM_PT_groups, ui.BIM_PT_object_groups, ui.BIM_UL_groups, - ui.BIM_UL_object_groups, ) diff --git a/src/bonsai/bonsai/bim/module/group/data.py b/src/bonsai/bonsai/bim/module/group/data.py index e6d3b416db..60b97e77f0 100644 --- a/src/bonsai/bonsai/bim/module/group/data.py +++ b/src/bonsai/bonsai/bim/module/group/data.py @@ -48,13 +48,9 @@ class ObjectGroupsData: @classmethod def load(cls): - cls.data = {"total_groups": cls.total_groups(), "groups": cls.groups()} + cls.data = {"groups": cls.groups()} cls.is_loaded = True - @classmethod - def total_groups(cls): - return len(tool.Ifc.get().by_type("IfcGroup", include_subtypes=False)) - @classmethod def groups(cls): element = tool.Ifc.get_entity(bpy.context.active_object) diff --git a/src/bonsai/bonsai/bim/module/group/operator.py b/src/bonsai/bonsai/bim/module/group/operator.py index df539e76bc..cf1d761bf5 100644 --- a/src/bonsai/bonsai/bim/module/group/operator.py +++ b/src/bonsai/bonsai/bim/module/group/operator.py @@ -164,52 +164,48 @@ class AssignGroup(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.assign_group" bl_label = "Assign Group" bl_options = {"REGISTER", "UNDO"} - product: bpy.props.StringProperty() - group: bpy.props.IntProperty() + bl_description = "Assign the selected objects to the selected group\nALT + CLICK to unassign." + group: bpy.props.IntProperty(options={"SKIP_SAVE"}) + is_assigning: bpy.props.BoolProperty(default=True, options={"SKIP_SAVE"}) + + def invoke(self, context, event): + self.is_assigning = not event.alt + return self.execute(context) def _execute(self, context): - self.file = IfcStore.get_file() - products = [bpy.data.objects.get(self.product)] if self.product else context.selected_objects - for product in products: - if not product.BIMObjectProperties.ifc_definition_id: - continue - ifcopenshell.api.run( - "group.assign_group", - self.file, - products=[self.file.by_id(product.BIMObjectProperties.ifc_definition_id)], - group=self.file.by_id(self.group), - ) - return {"FINISHED"} + if not self.is_assigning: + return bpy.ops.bim.unassign_group(group=self.group) + products = [ + tool.Ifc.get_entity(o) + for o in tool.Blender.get_selected_objects(include_active=False) + if tool.Ifc.get_entity(o) + ] + ifcopenshell.api.group.assign_group(tool.Ifc.get(), products=products, group=tool.Ifc.get().by_id(self.group)) class UnassignGroup(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.unassign_group" bl_label = "Unassign Group" bl_options = {"REGISTER", "UNDO"} - product: bpy.props.StringProperty() - group: bpy.props.IntProperty() + bl_description = "Unassign the selected objects from the selected group" + group: bpy.props.IntProperty(options={"SKIP_SAVE"}) def _execute(self, context): - self.file = IfcStore.get_file() - products = [bpy.data.objects.get(self.product)] if self.product else context.selected_objects - for product in products: - if not product.BIMObjectProperties.ifc_definition_id: - continue - ifcopenshell.api.run( - "group.unassign_group", - self.file, - **{ - "products": [self.file.by_id(product.BIMObjectProperties.ifc_definition_id)], - "group": self.file.by_id(self.group), - } - ) - return {"FINISHED"} + products = [ + tool.Ifc.get_entity(o) + for o in tool.Blender.get_selected_objects(include_active=False) + if tool.Ifc.get_entity(o) + ] + if not products: + return + ifcopenshell.api.group.unassign_group(tool.Ifc.get(), products=products, group=tool.Ifc.get().by_id(self.group)) class SelectGroupProducts(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.select_group_products" bl_label = "Select Group Products" bl_options = {"REGISTER", "UNDO"} + bl_description = "Select objects assigned to the selected group" group: bpy.props.IntProperty() def _execute(self, context): diff --git a/src/bonsai/bonsai/bim/module/group/prop.py b/src/bonsai/bonsai/bim/module/group/prop.py index d16bf4e801..2a091e21ae 100644 --- a/src/bonsai/bonsai/bim/module/group/prop.py +++ b/src/bonsai/bonsai/bim/module/group/prop.py @@ -55,3 +55,8 @@ class BIMGroupProperties(PropertyGroup): groups: CollectionProperty(name="Groups", type=Group) active_group_index: IntProperty(name="Active Group Index", update=update_active_group_index) active_group_id: IntProperty(name="Active Group Id") + + @property + def active_group(self): + if self.active_group_index < len(self.groups): + return self.groups[self.active_group_index] diff --git a/src/bonsai/bonsai/bim/module/group/ui.py b/src/bonsai/bonsai/bim/module/group/ui.py index 5b721040cd..f85b235258 100644 --- a/src/bonsai/bonsai/bim/module/group/ui.py +++ b/src/bonsai/bonsai/bim/module/group/ui.py @@ -49,21 +49,25 @@ class BIM_PT_groups(Panel): else: row.operator("bim.load_groups", text="", icon="GREASEPENCIL") - if self.props.is_editing: - self.layout.template_list( - "BIM_UL_groups", - "", - self.props, - "groups", - self.props, - "active_group_index", - ) + if not self.props.is_editing: + return + + if (group := self.props.active_group) and (group_id := group.ifc_definition_id): + row = self.layout.row(align=True) + row.alignment = "RIGHT" + row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF").group = group_id + row.operator("bim.assign_group", text="", icon="FOLDER_REDIRECT").group = group_id + row.operator("bim.enable_editing_group", text="", icon="GREASEPENCIL").group = group_id + row.operator("bim.add_group", text="", icon="ADD").group = group_id + row.operator("bim.remove_group", text="", icon="X").group = group_id if self.props.active_group_id: - self.draw_editable_ui(context) + draw_attributes(self.props.group_attributes, self.layout) + row = self.layout.row(align=True) + row.operator("bim.edit_group", text="Save Group", icon="CHECKMARK") + row.operator("bim.disable_editing_group", text="", icon="CANCEL") - def draw_editable_ui(self, context: bpy.types.Context) -> None: - draw_attributes(self.props.group_attributes, self.layout) + self.layout.template_list("BIM_UL_groups", "", self.props, "groups", self.props, "active_group_index") class BIM_PT_object_groups(Panel): @@ -86,21 +90,6 @@ class BIM_PT_object_groups(Panel): if not ObjectGroupsData.is_loaded: ObjectGroupsData.load() self.props = context.scene.BIMGroupProperties - row = self.layout.row(align=True) - if self.props.is_editing: - row.label(text="Adding Groups", icon="OUTLINER") - row.operator("bim.disable_group_editing_ui", text="", icon="CANCEL") - self.layout.template_list( - "BIM_UL_object_groups", - "", - self.props, - "groups", - self.props, - "active_group_index", - ) - else: - row.label(text=f"{ObjectGroupsData.data['total_groups']} Groups in IFC Project", icon="OUTLINER") - row.operator("bim.load_groups", text="", icon="GREASEPENCIL") for group in ObjectGroupsData.data["groups"]: row = self.layout.row(align=True) @@ -126,51 +115,4 @@ class BIM_UL_groups(UIList): op.ifc_definition_id = item.ifc_definition_id op.index = index op.option = "Collapse" if item.is_expanded else "Expand" - else: - row.label(text="", icon="BLANK1") - row.label(text=item.name) - group_id = item.ifc_definition_id - if context.scene.BIMGroupProperties.active_group_id == group_id: - op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF") - op.group = group_id - row.operator("bim.edit_group", text="", icon="CHECKMARK") - row.operator("bim.disable_editing_group", text="", icon="CANCEL") - elif context.scene.BIMGroupProperties.active_group_id: - op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF") - op.group = group_id - op = row.operator("bim.add_group", text="", icon="ADD") - op.group = group_id - op = row.operator("bim.remove_group", text="", icon="X") - op.group = group_id - else: - op = row.operator("bim.select_group_products", text="", icon="RESTRICT_SELECT_OFF") - op.group = group_id - op = row.operator("bim.enable_editing_group", text="", icon="GREASEPENCIL") - op.group = group_id - op = row.operator("bim.add_group", text="", icon="ADD") - op.group = group_id - op = row.operator("bim.remove_group", text="", icon="X") - op.group = group_id - - -class BIM_UL_object_groups(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - if item: - row = layout.row(align=True) - for i in range(0, item.tree_depth): - row.label(text="", icon="BLANK1") - if item.has_children: - op = row.operator( - "bim.toggle_group", icon="TRIA_DOWN" if item.is_expanded else "TRIA_RIGHT", text="", emboss=False - ) - op.ifc_definition_id = item.ifc_definition_id - op.index = index - op.option = "Collapse" if item.is_expanded else "Expand" - else: - row.label(text="", icon="BLANK1") - row.label(text=item.name) - op = row.operator("bim.remove_group", text="", icon="X") - op.group = item.ifc_definition_id - op = row.operator("bim.assign_group", text="", icon="ADD") - op.group = item.ifc_definition_id