Fix #6060. Updating long name in spatial manager now also edits attribute for convenience.

Editing attributes now also refreshes the spatial manager (i.e. vice
versa), and so does bulk attribute copying.
This commit is contained in:
Dion Moult
2025-01-30 17:47:29 +11:00
parent 8f78fe56de
commit 838e22d27c
4 changed files with 48 additions and 44 deletions
@@ -19,12 +19,13 @@
import bpy
import json
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.attribute
import ifcopenshell.guid
import ifcopenshell.util.element
import bonsai.bim.helper
import bonsai.tool as tool
import bonsai.core.attribute as core
import bonsai.core.spatial
from bonsai.bim.ifc import IfcStore
@@ -112,20 +113,14 @@ class DisableEditingAttributes(bpy.types.Operator):
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_description = "Edit the attributes of the active object"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty(options={"SKIP_SAVE"})
mass_operation: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"})
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)
assert product
object_name = tool.Loader.get_name(product)
def _execute(self, context):
self.file = IfcStore.get_file()
obj = tool.Blender.get_active_object(is_selected=False)
if not (element := tool.Ifc.get_entity(obj)):
return
def callback(attributes, prop):
if prop.name in ("RefLatitude", "RefLongitude"):
@@ -136,17 +131,15 @@ class EditAttributes(bpy.types.Operator, tool.Ifc.Operator):
attributes[prop.name] = None
return True
props = obj.BIMAttributeProperties
attributes = bonsai.bim.helper.export_attributes(props.attributes, callback=callback)
ifcopenshell.api.run("attribute.edit_attributes", self.file, product=product, attributes=attributes)
ifcopenshell.api.attribute.edit_attributes(self.file, product=element, attributes=attributes)
tool.Root.set_object_name(obj, product)
tool.Root.set_object_name(obj, element)
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"}
if tool.Root.is_spatial_element(element):
bonsai.core.spatial.import_spatial_decomposition(tool.Spatial)
class GenerateGlobalId(bpy.types.Operator, tool.Ifc.Operator):
@@ -195,14 +188,12 @@ class GenerateGlobalId(bpy.types.Operator, tool.Ifc.Operator):
class CopyAttributeToSelection(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.copy_attribute_to_selection"
bl_label = "Copy Attribute To Selection"
name: bpy.props.StringProperty()
bl_options = {"REGISTER", "UNDO"}
name: bpy.props.StringProperty()
def _execute(self, context):
value = context.active_object.BIMAttributeProperties.attributes.get(self.name).get_value()
i = 0
for obj in tool.Blender.get_selected_objects():
success = core.copy_attribute_to_selection(tool.Ifc, tool.Root, name=self.name, value=value, obj=obj)
if success:
i += 1
self.report({"INFO"}, f"Attribute was successfully copied to {i} elements.")
value = tool.Blender.get_active_object().BIMAttributeProperties.attributes.get(self.name).get_value()
total = core.copy_attribute_to_selection(
tool.Ifc, tool.Blender, tool.Root, tool.Spatial, name=self.name, value=value
)
self.report({"INFO"}, f"Attribute was successfully copied to {total} elements.")
+2 -3
View File
@@ -25,13 +25,12 @@ import bonsai.tool as tool
def draw_ui(context, layout, attributes):
obj = context.active_object
oprops = obj.BIMObjectProperties
props = obj.BIMAttributeProperties
if props.is_editing_attributes:
row = layout.row(align=True)
op = row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes")
op = row.operator("bim.disable_editing_attributes", icon="CANCEL", text="")
row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes")
row.operator("bim.disable_editing_attributes", icon="CANCEL", text="")
bonsai.bim.helper.draw_attributes(props.attributes, layout, copy_operator="bim.copy_attribute_to_selection")
else:
+9 -2
View File
@@ -65,7 +65,14 @@ def update_name(self: "BIMContainer", context: bpy.types.Context) -> None:
element = tool.Ifc.get().by_id(ifc_definition_id)
tool.Spatial.edit_container_name(element, self.name)
if obj := tool.Ifc.get_object(element):
obj.name = tool.Loader.get_name(element)
tool.Root.set_object_name(obj, element)
bonsai.bim.handler.refresh_ui_data()
def update_long_name(self: "BIMContainer", context: bpy.types.Context) -> None:
if ifc_definition_id := self.ifc_definition_id:
element = tool.Ifc.get().by_id(ifc_definition_id)
tool.Ifc.run("attribute.edit_attributes", product=element, attributes={"LongName": self.long_name})
bonsai.bim.handler.refresh_ui_data()
@@ -148,7 +155,7 @@ class BIMContainer(PropertyGroup):
name: StringProperty(name="Name", update=update_name)
ifc_class: StringProperty(name="IFC Class")
description: StringProperty(name="Description")
long_name: StringProperty(name="Long Name")
long_name: StringProperty(name="Long Name", update=update_long_name)
elevation: StringProperty(name="Elevation", update=update_elevation)
level_index: IntProperty(name="Level Index")
has_children: BoolProperty(name="Has Children")
+18 -11
View File
@@ -26,14 +26,21 @@ if TYPE_CHECKING:
def copy_attribute_to_selection(
ifc: tool.Ifc, root: tool.Root, name: str, value: Union[str, None], obj: bpy.types.Object
) -> bool:
if element := ifc.get_entity(obj):
try:
ifc.run("attribute.edit_attributes", product=element, attributes={name: value})
if name in ("Name", "AxisTag"):
root.set_object_name(obj, element)
return True
except:
pass
return False
ifc: tool.Ifc, blender: tool.Blender, root: tool.Root, spatial: tool.Spatial, name: str, value: Union[str, None]
) -> int:
total_changed = 0
has_edited_spatial_name = False
for obj in blender.get_selected_objects(include_active=False):
if element := ifc.get_entity(obj):
try:
ifc.run("attribute.edit_attributes", product=element, attributes={name: value})
if name in ("Name", "AxisTag"):
root.set_object_name(obj, element)
if name in ("Name", "LongName") and root.is_spatial_element(element):
has_edited_spatial_name = True
total_changed += 1
except:
pass
if has_edited_spatial_name:
spatial.import_spatial_decomposition()
return total_changed