This commit is contained in:
Andrej730
2025-04-11 10:57:06 +05:00
parent 858e73c9d9
commit 53b26a9b66
7 changed files with 54 additions and 33 deletions
@@ -26,30 +26,42 @@ import bonsai.bim.helper
import bonsai.tool as tool
import bonsai.core.attribute as core
import bonsai.core.spatial
from typing import TYPE_CHECKING
def get_objs_for_operation(operator_properties, context):
def get_objs_for_operation(
operator_properties: "AttributesOperator", context: bpy.types.Context
) -> list[bpy.types.Object]:
if operator_properties.obj:
return [bpy.data.objects[operator_properties.obj]]
if operator_properties.mass_operation:
return context.selected_objects[:]
return [context.active_object]
obj = context.active_object
assert obj
return [obj]
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"}
class AttributesOperator:
obj: bpy.props.StringProperty(options={"SKIP_SAVE"})
mass_operation: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"})
if TYPE_CHECKING:
obj: str
mass_operation: bool
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
class EnableEditingAttributes(bpy.types.Operator, AttributesOperator):
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"}
def enable_editing_attribute_on_obj(self, obj: bpy.types.Object) -> None:
props = tool.Blender.get_object_attribute_props(obj)
props.attributes.clear()
element = tool.Ifc.get_entity(obj)
@@ -87,20 +99,14 @@ class EnableEditingAttributes(bpy.types.Operator):
return {"FINISHED"}
class DisableEditingAttributes(bpy.types.Operator):
class DisableEditingAttributes(bpy.types.Operator, AttributesOperator):
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(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 disable_editing_attributes_on_obj(self, obj):
props = obj.BIMAttributeProperties
def disable_editing_attributes_on_obj(self, obj: bpy.types.Object) -> None:
props = tool.Blender.get_object_attribute_props(obj)
props.is_editing_attributes = False
def execute(self, context):
@@ -118,7 +124,7 @@ class EditAttributes(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
self.file = tool.Ifc.get()
obj = tool.Blender.get_active_object(is_selected=False)
if not (element := tool.Ifc.get_entity(obj)):
if not obj or not (element := tool.Ifc.get_entity(obj)):
return
def callback(attributes, prop):
@@ -130,7 +136,7 @@ class EditAttributes(bpy.types.Operator, tool.Ifc.Operator):
attributes[prop.name] = None
return True
props = obj.BIMAttributeProperties
props = tool.Blender.get_object_attribute_props(obj)
attributes = bonsai.bim.helper.export_attributes(props.attributes, callback=callback)
ifcopenshell.api.attribute.edit_attributes(self.file, product=element, attributes=attributes)
@@ -165,13 +171,12 @@ class GenerateGlobalId(bpy.types.Operator, tool.Ifc.Operator):
element.GlobalId = ifcopenshell.guid.new()
obj = context.active_object
if not obj or not obj.BIMAttributeProperties.is_editing_attributes:
if not obj or not (props := tool.Blender.get_object_attribute_props(obj)).is_editing_attributes:
return {"FINISHED"}
props = obj.BIMAttributeProperties
element = tool.Ifc.get_entity(obj)
if not element.is_a("IfcRoot"):
if not element or not element.is_a("IfcRoot"):
return {"FINISHED"}
if self.use_selected and obj in context.selected_objects:
@@ -191,7 +196,10 @@ class CopyAttributeToSelection(bpy.types.Operator, tool.Ifc.Operator):
name: bpy.props.StringProperty()
def _execute(self, context):
value = tool.Blender.get_active_object().BIMAttributeProperties.attributes.get(self.name).get_value()
obj = tool.Blender.get_active_object()
assert obj
props = tool.Blender.get_object_attribute_props(obj)
value = props.attributes[self.name].get_value()
total = core.copy_attribute_to_selection(
tool.Ifc, tool.Blender, tool.Root, tool.Spatial, name=self.name, value=value
)
@@ -29,8 +29,13 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
from typing import TYPE_CHECKING
class BIMAttributeProperties(PropertyGroup):
attributes: CollectionProperty(name="Attributes", type=Attribute)
is_editing_attributes: BoolProperty(name="Is Editing Attributes")
if TYPE_CHECKING:
attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
is_editing_attributes: bool
+4 -2
View File
@@ -17,14 +17,16 @@
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bonsai.bim.helper
import bpy.types
from bpy.types import Panel
from bonsai.bim.module.attribute.data import AttributesData
import bonsai.tool as tool
def draw_ui(context, layout, attributes):
def draw_ui(context: bpy.types.Context, layout: bpy.types.UILayout, attributes) -> None:
obj = context.active_object
props = obj.BIMAttributeProperties
assert obj
props = tool.Blender.get_object_attribute_props(obj)
if props.is_editing_attributes:
row = layout.row(align=True)
+5
View File
@@ -43,6 +43,7 @@ from typing_extensions import assert_never
if TYPE_CHECKING:
from bonsai.bim.prop import BIMProperties, BIMObjectProperties
from bonsai.bim.module.attribute.prop import BIMAttributeProperties
from bonsai.bim.module.csv.prop import CsvProperties
from bonsai.bim.module.diff.prop import DiffProperties
@@ -1635,6 +1636,10 @@ class Blender(bonsai.core.tool.Blender):
def get_object_bim_props(cls, obj: bpy.types.Object) -> BIMObjectProperties:
return obj.BIMObjectProperties
@classmethod
def get_object_attribute_props(cls, obj: bpy.types.Object) -> BIMAttributeProperties:
return obj.BIMAttributeProperties
@classmethod
def get_ifc_definition_id(cls, obj: IFC_CONNECTED_TYPE) -> int:
if isinstance(obj, bpy.types.Object):
+1 -1
View File
@@ -53,7 +53,7 @@ def execute(args: ArgumentsDict) -> Union[ifcopenshell.file, str]:
:param args: A dictionary of arguments, corresponding to the parameters
listed subsequent to this in this docstring.
:type args: dict
:type args: ArgumentsDict
:param file: An IFC model to apply the patch recipe to.
Required for most recipes except the ones that require `input`.
:type file: ifcopenshell.file, optional
@@ -18,6 +18,7 @@
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.project
import ifcopenshell.guid
import ifcopenshell.util.selector
from typing import Union
@@ -81,8 +82,8 @@ class Patcher:
pass
if element.is_a("IfcProject"):
return self.new.add(element)
return ifcopenshell.api.run(
"project.append_asset", self.new, library=self.file, element=element, reuse_identities=self.reuse_identities
return ifcopenshell.api.project.append_asset(
self.new, library=self.file, element=element, reuse_identities=self.reuse_identities
)
def add_spatial_structures(
@@ -18,8 +18,10 @@
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.type
import ifcopenshell.util.element
from logging import Logger
from typing import Any
class Patcher:
@@ -48,7 +50,6 @@ class Patcher:
:param attribute: The name of the attribute to merge element types based
on. Typically this will be "Tag" as it stores the unique ID from the
proprietary BIM software.
:type attribute: str
Example:
@@ -66,7 +67,7 @@ class Patcher:
def patch(self):
key = self.attribute
keys = {}
keys: dict[Any, ifcopenshell.entity_instance] = {}
for element_type in self.file.by_type("IfcTypeObject"):
original_type = keys.get(getattr(element_type, key), None)
if original_type:
@@ -86,8 +87,7 @@ class Patcher:
# since that would do other things like
# map type representations or recalculate material set usages which is
# risky when we're patching an existing dataset.
ifcopenshell.api.run(
"type.assign_type",
ifcopenshell.api.type.assign_type(
self.file,
relating_type=relating_type,
related_objects=related_objects,