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)
This commit is contained in:
Gorgious56
2025-01-08 11:29:06 +01:00
parent 5c3f0f0df9
commit d4ff789688
3 changed files with 49 additions and 17 deletions
@@ -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"}
@@ -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)
+2 -2
View File
@@ -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: