From 3fe5c889104160013312c22f678cf2a1ac818467 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sun, 17 Jan 2021 21:07:01 +1100 Subject: [PATCH] WIP implement material attributes panel. See #1222. --- .../blenderbim/bim/__init__.py | 2 + .../blenderbim/bim/import_ifc.py | 10 +- .../bim/module/attribute/__init__.py | 12 +- .../bim/module/attribute/operator.py | 39 +++-- .../blenderbim/bim/module/attribute/prop.py | 20 +++ .../blenderbim/bim/module/attribute/ui.py | 147 +++++++++--------- src/ifcblenderexport/blenderbim/bim/prop.py | 3 - src/ifcblenderexport/blenderbim/bim/ui.py | 58 ------- 8 files changed, 135 insertions(+), 156 deletions(-) create mode 100644 src/ifcblenderexport/blenderbim/bim/module/attribute/prop.py diff --git a/src/ifcblenderexport/blenderbim/bim/__init__.py b/src/ifcblenderexport/blenderbim/bim/__init__.py index 86060a65cd..593e0786df 100644 --- a/src/ifcblenderexport/blenderbim/bim/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/__init__.py @@ -297,6 +297,7 @@ if bpy is not None: bpy.types.Scene.MapConversion = bpy.props.PointerProperty(type=prop.MapConversion) bpy.types.Scene.TargetCRS = bpy.props.PointerProperty(type=prop.TargetCRS) bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) + bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) # Check if we need this bpy.types.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties) bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties) @@ -321,6 +322,7 @@ if bpy is not None: del bpy.types.Scene.MapConversion del bpy.types.Scene.TargetCRS del bpy.types.Object.BIMObjectProperties + del bpy.types.Material.BIMObjectProperties del bpy.types.Collection.BIMObjectProperties # Check if we need this del bpy.types.Material.BIMMaterialProperties del bpy.types.Mesh.BIMMeshProperties diff --git a/src/ifcblenderexport/blenderbim/bim/import_ifc.py b/src/ifcblenderexport/blenderbim/bim/import_ifc.py index fddb7bc985..71b608cc06 100644 --- a/src/ifcblenderexport/blenderbim/bim/import_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/import_ifc.py @@ -204,7 +204,7 @@ class MaterialCreator: def create_new_single(self, material): self.materials[material.Name] = obj = bpy.data.materials.new(material.Name) - obj.BIMMaterialProperties.ifc_definition_id = int(material.id()) + obj.BIMObjectProperties.ifc_definition_id = int(material.id()) if not material.HasRepresentation or not material.HasRepresentation[0].Representations: return for representation in material.HasRepresentation[0].Representations: @@ -233,7 +233,6 @@ class MaterialCreator: if not style.is_a("IfcSurfaceStyle"): continue material.BIMMaterialProperties.ifc_style_id = int(style.id()) - external_style = None for surface_style in style.Styles: if surface_style.is_a("IfcSurfaceStyleShading"): alpha = 1.0 @@ -246,13 +245,6 @@ class MaterialCreator: surface_style.SurfaceColour.Blue, alpha, ) - elif surface_style.is_a("IfcExternallyDefinedSurfaceStyle"): - external_style = surface_style - if external_style: - material.BIMMaterialProperties.is_external = True - material.BIMMaterialProperties.location = external_style.Location - material.BIMMaterialProperties.identification = external_style.Identification - material.BIMMaterialProperties.name = external_style.Name # IfcPresentationStyleAssignment is deprecated as of IFC4 # However it is still widely used thanks to Revit :( diff --git a/src/ifcblenderexport/blenderbim/bim/module/attribute/__init__.py b/src/ifcblenderexport/blenderbim/bim/module/attribute/__init__.py index 9d28fb060d..1801a76bfa 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/attribute/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/module/attribute/__init__.py @@ -1,17 +1,21 @@ import bpy -from . import ui, operator +from . import ui, prop, operator classes = ( operator.EnableEditingAttributes, operator.DisableEditingAttributes, operator.EditAttributes, - ui.BIM_PT_attributes, + prop.BIMAttributeProperties, + ui.BIM_PT_object_attributes, + ui.BIM_PT_material_attributes, ) def register(): - pass + bpy.types.Object.BIMAttributeProperties = bpy.props.PointerProperty(type=prop.BIMAttributeProperties) + bpy.types.Material.BIMAttributeProperties = bpy.props.PointerProperty(type=prop.BIMAttributeProperties) def unregister(): - pass + del bpy.types.Object.BIMAttributeProperties + del bpy.types.Material.BIMAttributeProperties diff --git a/src/ifcblenderexport/blenderbim/bim/module/attribute/operator.py b/src/ifcblenderexport/blenderbim/bim/module/attribute/operator.py index d696bb775b..93665b6565 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/attribute/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/module/attribute/operator.py @@ -8,14 +8,20 @@ from blenderbim.bim.module.attribute.data import Data class EnableEditingAttributes(bpy.types.Operator): bl_idname = "bim.enable_editing_attributes" bl_label = "Enable Editing Attributes" + obj: bpy.props.StringProperty() + obj_type: bpy.props.StringProperty() def execute(self, context): self.file = IfcStore.get_file() - obj = bpy.context.active_object - props = obj.BIMObjectProperties + if self.obj_type == "Object": + obj = bpy.data.objects.get(self.obj) + elif self.obj_type == "Material": + obj = bpy.data.materials.get(self.obj) + oprops = obj.BIMObjectProperties + props = obj.BIMAttributeProperties while len(props.attributes) > 0: props.attributes.remove(0) - for attribute in Data.products[props.ifc_definition_id]: + for attribute in Data.products[oprops.ifc_definition_id]: new = props.attributes.add() if attribute["type"] == "entity": continue @@ -38,10 +44,15 @@ class EnableEditingAttributes(bpy.types.Operator): class DisableEditingAttributes(bpy.types.Operator): bl_idname = "bim.disable_editing_attributes" bl_label = "Disable Editing Attributes" + obj: bpy.props.StringProperty() + obj_type: bpy.props.StringProperty() def execute(self, context): - obj = bpy.context.active_object - props = obj.BIMObjectProperties + if self.obj_type == "Object": + obj = bpy.data.objects.get(self.obj) + elif self.obj_type == "Material": + obj = bpy.data.materials.get(self.obj) + props = obj.BIMAttributeProperties props.is_editing_attributes = False return {"FINISHED"} @@ -49,13 +60,19 @@ class DisableEditingAttributes(bpy.types.Operator): class EditAttributes(bpy.types.Operator): bl_idname = "bim.edit_attributes" bl_label = "Edit Attributes" + obj: bpy.props.StringProperty() + obj_type: bpy.props.StringProperty() def execute(self, context): self.file = IfcStore.get_file() - obj = bpy.context.active_object - props = obj.BIMObjectProperties + if self.obj_type == "Object": + obj = bpy.data.objects.get(self.obj) + elif self.obj_type == "Material": + obj = bpy.data.materials.get(self.obj) + oprops = obj.BIMObjectProperties + props = obj.BIMAttributeProperties attributes = {} - for attribute in Data.products[props.ifc_definition_id]: + for attribute in Data.products[oprops.ifc_definition_id]: blender_attribute = props.attributes.get(attribute["name"]) if not blender_attribute: continue @@ -77,9 +94,9 @@ class EditAttributes(bpy.types.Operator): elif attribute["type"] == "enum": attributes[attribute["name"]] = blender_attribute.enum_value edit_attributes.Usecase(self.file, { - "product": self.file.by_id(props.ifc_definition_id), + "product": self.file.by_id(oprops.ifc_definition_id), "attributes": attributes }).execute() - Data.load(props.ifc_definition_id) - bpy.ops.bim.disable_editing_attributes() + Data.load(oprops.ifc_definition_id) + bpy.ops.bim.disable_editing_attributes(obj=self.obj, obj_type=self.obj_type) return {"FINISHED"} diff --git a/src/ifcblenderexport/blenderbim/bim/module/attribute/prop.py b/src/ifcblenderexport/blenderbim/bim/module/attribute/prop.py new file mode 100644 index 0000000000..8e57d7af72 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/attribute/prop.py @@ -0,0 +1,20 @@ +import bpy +import blenderbim.bim.schema # refactor +from blenderbim.bim.module.material.data import Data +from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.prop import StrProperty, Attribute +from bpy.types import PropertyGroup +from bpy.props import ( + PointerProperty, + StringProperty, + EnumProperty, + BoolProperty, + IntProperty, + FloatProperty, + FloatVectorProperty, + CollectionProperty, +) + +class BIMAttributeProperties(PropertyGroup): + attributes: CollectionProperty(name="Attributes", type=Attribute) + is_editing_attributes: BoolProperty(name="Is Editing Attributes") diff --git a/src/ifcblenderexport/blenderbim/bim/module/attribute/ui.py b/src/ifcblenderexport/blenderbim/bim/module/attribute/ui.py index 8b47150d7a..fa2826d073 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/attribute/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/module/attribute/ui.py @@ -2,9 +2,71 @@ from bpy.types import Panel from blenderbim.bim.module.attribute.data import Data -class BIM_PT_attributes(Panel): +def draw_ui(context, layout, obj_type): + obj = context.active_object if obj_type == "Object" else context.active_object.active_material + oprops = obj.BIMObjectProperties + props = obj.BIMAttributeProperties + if not oprops.ifc_definition_id: + return + if oprops.ifc_definition_id not in Data.products: + Data.load(oprops.ifc_definition_id) + + if props.is_editing_attributes: + row = layout.row(align=True) + op = row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes") + op.obj_type = obj_type + op.obj = obj.name + op = row.operator("bim.disable_editing_attributes", icon="X", text="") + op.obj_type = obj_type + op.obj = obj.name + + for attribute in Data.products[oprops.ifc_definition_id]: + if attribute["type"] == "entity": + continue + row = layout.row(align=True) + blender_attribute = props.attributes.get(attribute["name"]) + if attribute["type"] == "string" or attribute["type"] == "list": + row.prop(blender_attribute, "string_value", text=attribute["name"]) + elif attribute["type"] == "integer": + row.prop(blender_attribute, "int_value", text=attribute["name"]) + elif attribute["type"] == "float": + row.prop(blender_attribute, "float_value", text=attribute["name"]) + elif attribute["type"] == "enum": + row.prop(blender_attribute, "enum_value", text=attribute["name"]) + + if attribute["name"] == "GlobalId": + row.operator("bim.generate_global_id", icon="FILE_REFRESH", text="") + if attribute["is_optional"]: + row.prop( + blender_attribute, + "is_null", + icon="RADIOBUT_OFF" if blender_attribute.is_null else "RADIOBUT_ON", + text="", + ) + # TODO: reimplement, see #1222 + # op = row.operator("bim.copy_attribute_to_selection", icon="COPYDOWN", text="") + # op.attribute_name = attribute.name + # op.attribute_value = attribute.string_value + else: + row = layout.row() + op = row.operator("bim.enable_editing_attributes", icon="GREASEPENCIL", text="Edit") + op.obj_type = obj_type + op.obj = obj.name + for attribute in Data.products[oprops.ifc_definition_id]: + if attribute["value"] is None or attribute["type"] == "entity": + continue + row = layout.row(align=True) + row.label(text=attribute["name"]) + row.label(text=str(attribute["value"])) + + # TODO: reimplement, see #1222 + # if "IfcSite/" in context.active_object.name or "IfcBuilding/" in context.active_object.name: + # self.draw_addresses_ui() + + +class BIM_PT_object_attributes(Panel): bl_label = "IFC Attributes" - bl_idname = "BIM_PT_attributes" + bl_idname = "BIM_PT_object_attributes" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "object" @@ -14,76 +76,19 @@ class BIM_PT_attributes(Panel): return bool(context.active_object.BIMObjectProperties.ifc_definition_id) def draw(self, context): - props = context.active_object.BIMObjectProperties - if not props.ifc_definition_id: - return - if props.ifc_definition_id not in Data.products: - Data.load(props.ifc_definition_id) + draw_ui(context, self.layout, "Object") - if props.is_editing_attributes: - row = self.layout.row(align=True) - row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes") - row.operator("bim.disable_editing_attributes", icon="X", text="") - for attribute in Data.products[props.ifc_definition_id]: - if attribute["type"] == "entity": - continue - row = self.layout.row(align=True) - blender_attribute = props.attributes.get(attribute["name"]) - if attribute["type"] == "string" or attribute["type"] == "list": - row.prop(blender_attribute, "string_value", text=attribute["name"]) - elif attribute["type"] == "integer": - row.prop(blender_attribute, "int_value", text=attribute["name"]) - elif attribute["type"] == "float": - row.prop(blender_attribute, "float_value", text=attribute["name"]) - elif attribute["type"] == "enum": - row.prop(blender_attribute, "enum_value", text=attribute["name"]) +class BIM_PT_material_attributes(Panel): + bl_label = "IFC Attributes" + bl_idname = "BIM_PT_material_attributes" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "material" - if attribute["name"] == "GlobalId": - row.operator("bim.generate_global_id", icon="FILE_REFRESH", text="") - if attribute["is_optional"]: - row.prop(blender_attribute, "is_null", icon="RADIOBUT_OFF" if blender_attribute.is_null else "RADIOBUT_ON", text="") - # TODO: reimplement, see #1222 - #op = row.operator("bim.copy_attribute_to_selection", icon="COPYDOWN", text="") - #op.attribute_name = attribute.name - #op.attribute_value = attribute.string_value - else: - row = self.layout.row() - row.operator("bim.enable_editing_attributes", icon="GREASEPENCIL", text="Edit") - for attribute in Data.products[props.ifc_definition_id]: - if attribute["value"] is None or attribute["type"] == "entity": - continue - row = self.layout.row(align=True) - row.label(text=attribute["name"]) - row.label(text=str(attribute["value"])) - - # TODO: reimplement, see #1222 - #if "IfcSite/" in context.active_object.name or "IfcBuilding/" in context.active_object.name: - # self.draw_addresses_ui() - - def draw_addresses_ui(self): - self.layout.label(text="Address:") - address = bpy.context.active_object.BIMObjectProperties.address - row = self.layout.row() - row.prop(address, "purpose") - if address.purpose == "USERDEFINED": - row = self.layout.row() - row.prop(address, "user_defined_purpose") - row = self.layout.row() - row.prop(address, "description") - - row = self.layout.row() - row.prop(address, "internal_location") - row = self.layout.row() - row.prop(address, "address_lines") - row = self.layout.row() - row.prop(address, "postal_box") - row = self.layout.row() - row.prop(address, "town") - row = self.layout.row() - row.prop(address, "region") - row = self.layout.row() - row.prop(address, "postal_code") - row = self.layout.row() - row.prop(address, "country") + @classmethod + def poll(cls, context): + return bool(context.active_object.active_material.BIMObjectProperties.ifc_definition_id) + def draw(self, context): + draw_ui(context, self.layout, "Material") diff --git a/src/ifcblenderexport/blenderbim/bim/prop.py b/src/ifcblenderexport/blenderbim/bim/prop.py index 8ca62ef43e..42ad0d073f 100644 --- a/src/ifcblenderexport/blenderbim/bim/prop.py +++ b/src/ifcblenderexport/blenderbim/bim/prop.py @@ -1323,8 +1323,6 @@ class BIMObjectProperties(PropertyGroup): ifc_definition_id: IntProperty(name="IFC Definition ID") is_reassigning_class: BoolProperty(name="Is Reassigning Class") global_ids: CollectionProperty(name="GlobalIds", type=GlobalId) - attributes: CollectionProperty(name="Attributes", type=Attribute) - is_editing_attributes: BoolProperty(name="Is Editing Attributes") relating_object: PointerProperty(name="Aggregate", type=bpy.types.Object) is_editing_aggregate: BoolProperty(name="Is Editing Aggregate") is_editing_container: BoolProperty(name="Is Editing Container") @@ -1355,7 +1353,6 @@ class BIMMaterialProperties(PropertyGroup): psets: CollectionProperty(name="Psets", type=PsetQto) attributes: CollectionProperty(name="Attributes", type=Attribute) applicable_attributes: EnumProperty(items=getApplicableMaterialAttributes, name="Attribute Names") - ifc_definition_id: IntProperty(name="IFC Definition ID") # In Blender, a material object can map to an IFC material, IFC surface style, or both ifc_style_id: IntProperty(name="IFC Style ID") diff --git a/src/ifcblenderexport/blenderbim/bim/ui.py b/src/ifcblenderexport/blenderbim/bim/ui.py index 25823d5ef9..64c95d33b2 100644 --- a/src/ifcblenderexport/blenderbim/bim/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/ui.py @@ -437,64 +437,6 @@ class BIM_PT_presentation_layer_data(Panel): op.index = scene_props.active_presentation_layer_index -class BIM_PT_material(Panel): - bl_label = "IFC Materials" - bl_idname = "BIM_PT_material" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "material" - - @classmethod - def poll(cls, context): - return context.active_object is not None and context.active_object.active_material is not None - - def draw(self, context): - if not bpy.context.active_object.active_material: - return - props = context.active_object.active_material.BIMMaterialProperties - layout = self.layout - row = layout.row() - row.prop(props, "is_external") - row = layout.row(align=True) - row.prop(props, "location") - row.operator("bim.select_external_material_dir", icon="FILE_FOLDER", text="") - row = layout.row() - row.prop(props, "identification") - row = layout.row() - row.prop(props, "name") - - row = layout.row() - row.operator("bim.fetch_external_material") - - layout.label(text="Attributes:") - row = layout.row(align=True) - row.prop(props, "applicable_attributes", text="") - row.operator("bim.add_material_attribute") - - for index, attribute in enumerate(props.attributes): - row = layout.row(align=True) - row.prop(attribute, "name", text="") - row.prop(attribute, "string_value", text="") - row.operator("bim.remove_material_attribute", icon="X", text="").attribute_index = index - - row = layout.row() - row.prop(props, "attributes") - - layout.label(text="Property Sets:") - row = layout.row(align=True) - row.prop(props, "pset_name", text="") - row.operator("bim.add_material_pset") - - for index, pset in enumerate(props.psets): - row = layout.row(align=True) - row.prop(pset, "name", text="") - row.operator("bim.remove_material_pset", icon="X", text="").pset_index = index - for prop in pset.properties: - row = layout.row(align=True) - row.prop(prop, "name", text="") - row.prop(prop, "string_value", text="") - - class BIM_PT_gis(Panel): bl_label = "IFC Georeferencing" bl_idname = "BIM_PT_gis"