From d4ff789688d82d03c1683d9c3891670c037d4d74 Mon Sep 17 00:00:00 2001 From: Gorgious56 Date: Wed, 8 Jan 2025 11:29:06 +0100 Subject: [PATCH] You can now mass enable edition, disable edition and validate edition of Ifc Entity Attributes. This highlights a potential bug that already existed due to the way the attribute system is designed. Where the user mass-edits attributes from different IFC types, resulting in discrepancies due to the same attributes having a different index. eg IfcWindow has the Tag attribute at index 3, IfcDoor at index 4. So using the ALT modifier feature from vanilla Blender to mass-modify similarly named attributes on the Tag attribute of the IfcWindow will modify the PredefinedType attribute on the IfcDoor (index 3). For this reason I added the ALT modifier to the buttons so mass-operations are a conscious effort for more knowledgeable users. Might reconsider if problems arise but this is IMO a good QOL feature. Also fix floating point precision errors on Pset float values stored as strings (re last commit) --- .../bonsai/bim/module/attribute/operator.py | 59 +++++++++++++++---- src/bonsai/bonsai/bim/module/attribute/ui.py | 3 - src/bonsai/bonsai/bim/module/pset/ui.py | 4 +- 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/attribute/operator.py b/src/bonsai/bonsai/bim/module/attribute/operator.py index 15520e57bf..85daf93fef 100644 --- a/src/bonsai/bonsai/bim/module/attribute/operator.py +++ b/src/bonsai/bonsai/bim/module/attribute/operator.py @@ -28,20 +28,33 @@ import bonsai.core.attribute as core from bonsai.bim.ifc import IfcStore +def get_objs_for_operation(operator_properties, context): + if operator_properties.obj: + return [bpy.data.objects[operator_properties.obj]] + if operator_properties.mass_operation: + return context.selected_objects[:] + return [context.active_object] + + class EnableEditingAttributes(bpy.types.Operator): bl_idname = "bim.enable_editing_attributes" bl_label = "Enable Editing Attributes" + bl_description = "ALT + Left Click to enable editing attributes on all selected objects" bl_options = {"REGISTER", "UNDO"} - obj: bpy.props.StringProperty() + obj: bpy.props.StringProperty(options={"SKIP_SAVE"}) + mass_operation: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"}) - def execute(self, context): - self.file = IfcStore.get_file() - obj = bpy.data.objects[self.obj] + def invoke(self, context, event): + self.mass_operation = event.alt + return self.execute(context) + + def enable_editing_attribute_on_obj(self, obj): props = obj.BIMAttributeProperties props.attributes.clear() element = tool.Ifc.get_entity(obj) - assert element + if not element: + return has_inherited_predefined_type = False if not element.is_a("IfcTypeObject") and (element_type := ifcopenshell.util.element.get_type(element)): # Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818 @@ -67,31 +80,48 @@ class EnableEditingAttributes(bpy.types.Operator): bonsai.bim.helper.import_attributes2(element, props.attributes, callback=callback) props.is_editing_attributes = True + + def execute(self, context): + for obj in get_objs_for_operation(self, context): + self.enable_editing_attribute_on_obj(obj) return {"FINISHED"} class DisableEditingAttributes(bpy.types.Operator): bl_idname = "bim.disable_editing_attributes" bl_label = "Disable Editing Attributes" + bl_description = "ALT + Left Click to disable editing attributes on all selected objects" bl_options = {"REGISTER", "UNDO"} - obj: bpy.props.StringProperty() + obj: bpy.props.StringProperty(options={"SKIP_SAVE"}) + mass_operation: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"}) - def execute(self, context): - obj = bpy.data.objects.get(self.obj) + def invoke(self, context, event): + self.mass_operation = event.alt + return self.execute(context) + + def disable_editing_attributes_on_obj(self, obj): props = obj.BIMAttributeProperties props.is_editing_attributes = False + + def execute(self, context): + for obj in get_objs_for_operation(self, context): + self.disable_editing_attributes_on_obj(obj) return {"FINISHED"} class EditAttributes(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_attributes" bl_label = "Edit Attributes" + bl_description = "ALT + Left Click to edit attributes on all selected objects" bl_options = {"REGISTER", "UNDO"} - obj: bpy.props.StringProperty() + obj: bpy.props.StringProperty(options={"SKIP_SAVE"}) + mass_operation: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"}) - def _execute(self, context): - self.file = IfcStore.get_file() - obj = bpy.data.objects.get(self.obj) + def invoke(self, context, event): + self.mass_operation = event.alt + return self.execute(context) + + def edit_attributes_on_obj(self, obj): props = obj.BIMAttributeProperties product = tool.Ifc.get_entity(obj) @@ -112,6 +142,11 @@ class EditAttributes(bpy.types.Operator, tool.Ifc.Operator): if (name := tool.Loader.get_name(product)) and obj.name != name: obj.name = name bpy.ops.bim.disable_editing_attributes(obj=obj.name) + + def _execute(self, context): + self.file = IfcStore.get_file() + for obj in get_objs_for_operation(self, context): + self.edit_attributes_on_obj(obj) return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/attribute/ui.py b/src/bonsai/bonsai/bim/module/attribute/ui.py index 85062852dc..65529d9d3b 100644 --- a/src/bonsai/bonsai/bim/module/attribute/ui.py +++ b/src/bonsai/bonsai/bim/module/attribute/ui.py @@ -31,15 +31,12 @@ def draw_ui(context, layout, attributes): if props.is_editing_attributes: row = layout.row(align=True) op = row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes") - op.obj = obj.name op = row.operator("bim.disable_editing_attributes", icon="CANCEL", text="") - op.obj = obj.name bonsai.bim.helper.draw_attributes(props.attributes, layout, copy_operator="bim.copy_attribute_to_selection") else: row = layout.row() op = row.operator("bim.enable_editing_attributes", icon="GREASEPENCIL", text="Edit") - op.obj = obj.name for attribute in attributes: row = layout.row(align=True) diff --git a/src/bonsai/bonsai/bim/module/pset/ui.py b/src/bonsai/bonsai/bim/module/pset/ui.py index ad656a1d4b..98839b66c3 100644 --- a/src/bonsai/bonsai/bim/module/pset/ui.py +++ b/src/bonsai/bonsai/bim/module/pset/ui.py @@ -20,7 +20,7 @@ import bpy import bonsai.tool as tool from bpy.types import Panel from bonsai.bim.ifc import IfcStore -from bonsai.bim.helper import prop_with_search +from bonsai.bim.helper import prop_with_search, get_display_value from bonsai.bim.module.pset.data import ( ObjectPsetsData, ObjectQtosData, @@ -197,7 +197,7 @@ def draw_psetqto_ui( row = box.row(align=True) row.scale_y = 0.8 row.label(text=prop["Name"]) - op = row.operator("bim.select_similar", text=nominal_value, icon="NONE", emboss=False) + op = row.operator("bim.select_similar", text=get_display_value(nominal_value), icon="NONE", emboss=False) op.key = '"' + pset["Name"].replace('"', '\\"') + '"."' + prop["Name"].replace('"', '\\"') + '"' # calculate sum of all selected objects if active_operator: