From ee0b9004e3ee98a9dcefa3494adf7d2c6ab7be38 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sun, 17 Jan 2021 21:53:31 +1100 Subject: [PATCH] WIP reimplement material psets with new features. See #1222. * Data type sensitive UI * New standardised UI * Accomodate custom pset templates for materials * Explicitly disable IFC2X3 material psets which are horrible and I don't want to support it --- .../blenderbim/bim/module/attribute/ui.py | 7 +- .../blenderbim/bim/module/pset/__init__.py | 3 + .../blenderbim/bim/module/pset/add_pset.py | 3 +- .../blenderbim/bim/module/pset/data.py | 9 +++ .../blenderbim/bim/module/pset/operator.py | 39 +++++++-- .../blenderbim/bim/module/pset/prop.py | 12 +++ .../blenderbim/bim/module/pset/ui.py | 79 ++++++++++++++++--- 7 files changed, 130 insertions(+), 22 deletions(-) diff --git a/src/ifcblenderexport/blenderbim/bim/module/attribute/ui.py b/src/ifcblenderexport/blenderbim/bim/module/attribute/ui.py index fa2826d073..8ae5613b01 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/attribute/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/module/attribute/ui.py @@ -6,8 +6,6 @@ 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) @@ -88,7 +86,10 @@ class BIM_PT_material_attributes(Panel): @classmethod def poll(cls, context): - return bool(context.active_object.active_material.BIMObjectProperties.ifc_definition_id) + try: + return bool(context.active_object.active_material.BIMObjectProperties.ifc_definition_id) + except: + return False def draw(self, context): draw_ui(context, self.layout, "Material") diff --git a/src/ifcblenderexport/blenderbim/bim/module/pset/__init__.py b/src/ifcblenderexport/blenderbim/bim/module/pset/__init__.py index d4a42735be..7c5964f3ca 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/pset/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/module/pset/__init__.py @@ -13,12 +13,15 @@ classes = ( prop.PsetProperties, ui.BIM_PT_object_psets, ui.BIM_PT_object_qtos, + ui.BIM_PT_material_psets, ) def register(): bpy.types.Object.PsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties) + bpy.types.Material.PsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties) def unregister(): del bpy.types.Object.PsetProperties + del bpy.types.Material.PsetProperties diff --git a/src/ifcblenderexport/blenderbim/bim/module/pset/add_pset.py b/src/ifcblenderexport/blenderbim/bim/module/pset/add_pset.py index 14301e3d1a..17d2612ddf 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/pset/add_pset.py +++ b/src/ifcblenderexport/blenderbim/bim/module/pset/add_pset.py @@ -32,9 +32,8 @@ class Usecase: self.settings["product"].HasPropertySets = has_property_sets elif self.settings["product"].is_a("IfcMaterialDefinition"): pset = self.file.create_entity( - "IfcPropertySet", + "IfcMaterialProperties", **{ - "GlobalId": ifcopenshell.guid.new(), "Name": self.settings["Name"], "Material": self.settings["product"], } diff --git a/src/ifcblenderexport/blenderbim/bim/module/pset/data.py b/src/ifcblenderexport/blenderbim/bim/module/pset/data.py index 5c350f56f6..aeea11415d 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/pset/data.py +++ b/src/ifcblenderexport/blenderbim/bim/module/pset/data.py @@ -17,6 +17,8 @@ class Data: cls.products[product_id] = {"psets": set(), "qtos": set()} if product.is_a("IfcElementType"): cls.add_type_product_psets(product, product_id) + elif product.is_a("IfcMaterialDefinition"): + cls.add_material_psets(product, product_id) else: cls.add_product_psets(product, product_id) @@ -28,6 +30,13 @@ class Data: if definition.is_a("IfcPropertySet"): cls.add_pset(definition, product_id) + @classmethod + def add_material_psets(cls, product, product_id): + if not product.HasProperties: + return + for pset in product.HasProperties: + cls.add_pset(pset, product_id) + @classmethod def add_product_psets(cls, product, product_id): if not hasattr(product, "IsDefinedBy") or not product.IsDefinedBy: diff --git a/src/ifcblenderexport/blenderbim/bim/module/pset/operator.py b/src/ifcblenderexport/blenderbim/bim/module/pset/operator.py index d1389c2004..28a93a66c1 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/pset/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/module/pset/operator.py @@ -27,9 +27,14 @@ class EnablePsetEditing(bpy.types.Operator): bl_idname = "bim.enable_pset_editing" bl_label = "Enable Pset Editing" pset_id: bpy.props.IntProperty() + obj: bpy.props.StringProperty() + obj_type: bpy.props.StringProperty() def execute(self, context): - obj = bpy.context.active_object + 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.PsetProperties while len(props.properties) > 0: @@ -61,9 +66,14 @@ class EnablePsetEditing(bpy.types.Operator): class DisablePsetEditing(bpy.types.Operator): bl_idname = "bim.disable_pset_editing" bl_label = "Disable Pset Editing" + obj: bpy.props.StringProperty() + obj_type: bpy.props.StringProperty() def execute(self, context): - obj = bpy.context.active_object + 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.PsetProperties props.active_pset_id = 0 return {"FINISHED"} @@ -72,10 +82,15 @@ class DisablePsetEditing(bpy.types.Operator): class EditPset(bpy.types.Operator): bl_idname = "bim.edit_pset" bl_label = "Edit Pset" + obj: bpy.props.StringProperty() + obj_type: bpy.props.StringProperty() def execute(self, context): self.file = IfcStore.get_file() - obj = bpy.context.active_object + 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.PsetProperties properties = {} @@ -109,7 +124,7 @@ class EditPset(bpy.types.Operator): "Properties": properties }).execute() Data.load(oprops.ifc_definition_id) - bpy.ops.bim.disable_pset_editing() + bpy.ops.bim.disable_pset_editing(obj=self.obj, obj_type=self.obj_type) return {"FINISHED"} @@ -117,10 +132,15 @@ class RemovePset(bpy.types.Operator): bl_idname = "bim.remove_pset" bl_label = "Remove Pset" pset_id: bpy.props.IntProperty() + obj: bpy.props.StringProperty() + obj_type: bpy.props.StringProperty() def execute(self, context): self.file = IfcStore.get_file() - obj = bpy.context.active_object + 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.BIMObjectProperties remove_pset.Usecase(self.file, { "product": self.file.by_id(props.ifc_definition_id), @@ -133,15 +153,20 @@ class RemovePset(bpy.types.Operator): class AddPset(bpy.types.Operator): bl_idname = "bim.add_pset" bl_label = "Add Pset" + obj: bpy.props.StringProperty() + obj_type: bpy.props.StringProperty() def execute(self, context): self.file = IfcStore.get_file() - obj = bpy.context.active_object + 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.PsetProperties add_pset.Usecase(self.file, { "product": self.file.by_id(oprops.ifc_definition_id), - "Name": props.pset_name, + "Name": props.pset_name if self.obj_type == "Object" else props.material_pset_name, }).execute() Data.load(oprops.ifc_definition_id) return {"FINISHED"} diff --git a/src/ifcblenderexport/blenderbim/bim/module/pset/prop.py b/src/ifcblenderexport/blenderbim/bim/module/pset/prop.py index 8762af706c..2b408eede9 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/pset/prop.py +++ b/src/ifcblenderexport/blenderbim/bim/module/pset/prop.py @@ -29,6 +29,17 @@ def getPsetNames(self, context): return [] +def getMaterialPsetNames(self, context): + global psetnames + if "/" in context.active_object.name: + ifc_class = "IfcMaterial" + if ifc_class not in psetnames: + psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True) + psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets] + return psetnames[ifc_class] + return [] + + def getQtoNames(self, context): global qtonames if "/" in context.active_object.name: @@ -46,3 +57,4 @@ class PsetProperties(PropertyGroup): properties: CollectionProperty(name="Properties", type=Attribute) pset_name: EnumProperty(items=getPsetNames, name="Pset Name") qto_name: EnumProperty(items=getQtoNames, name="Qto Name") + material_pset_name: EnumProperty(items=getMaterialPsetNames, name="Pset Name") diff --git a/src/ifcblenderexport/blenderbim/bim/module/pset/ui.py b/src/ifcblenderexport/blenderbim/bim/module/pset/ui.py index 37b8567583..fcfef2965b 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/pset/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/module/pset/ui.py @@ -1,22 +1,37 @@ from bpy.types import Panel from blenderbim.bim.module.pset.data import Data +from blenderbim.bim.ifc import IfcStore -def draw_psetqto_ui(pset_id, pset, props, layout): + +def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type): box = layout.box() row = box.row(align=True) icon = "TRIA_DOWN" if pset["is_expanded"] else "TRIA_RIGHT" row.operator("bim.toggle_pset_expansion", icon=icon, text="", emboss=False).pset_id = pset_id if not props.active_pset_id: row.label(text=pset["Name"], icon="COPY_ID") - row.operator("bim.enable_pset_editing", icon="GREASEPENCIL", text="").pset_id = pset_id - row.operator("bim.remove_pset", icon="X", text="").pset_id = pset_id + op = row.operator("bim.enable_pset_editing", icon="GREASEPENCIL", text="") + op.pset_id = pset_id + op.obj = context.active_object.name if obj_type == "Object" else context.active_object.active_material.name + op.obj_type = obj_type + op = row.operator("bim.remove_pset", icon="X", text="") + op.pset_id = pset_id + op.obj = context.active_object.name if obj_type == "Object" else context.active_object.active_material.name + op.obj_type = obj_type elif props.active_pset_id != pset_id: row.label(text=pset["Name"], icon="COPY_ID") - row.operator("bim.remove_pset", icon="X", text="").pset_id = pset_id + op = row.operator("bim.remove_pset", icon="X", text="") + op.pset_id = pset_id + op.obj = context.active_object.name if obj_type == "Object" else context.active_object.active_material.name + op.obj_type = obj_type elif props.active_pset_id == pset_id: row.prop(props, "active_pset_name", icon="COPY_ID", text="") - row.operator("bim.edit_pset", icon="CHECKMARK", text="") - row.operator("bim.disable_pset_editing", icon="X", text="") + op = row.operator("bim.edit_pset", icon="CHECKMARK", text="") + op.obj = context.active_object.name if obj_type == "Object" else context.active_object.active_material.name + op.obj_type = obj_type + op = row.operator("bim.disable_pset_editing", icon="X", text="") + op.obj = context.active_object.name if obj_type == "Object" else context.active_object.active_material.name + op.obj_type = obj_type if pset["is_expanded"]: if props.active_pset_id == pset_id: for prop in pset["Properties"]: @@ -30,6 +45,7 @@ def draw_psetqto_ui(pset_id, pset, props, layout): row.label(text=prop["Name"]) row.label(text=str(prop["value"])) + def draw_psetqto_editable_ui(box, props, prop): row = box.row(align=True) blender_prop = props.properties.get(prop["Name"]) @@ -60,6 +76,7 @@ def draw_psetqto_editable_ui(box, props, prop): op = row.operator("bim.guess_quantity", icon="SPHERE", text="") op.prop = prop["Name"] + class BIM_PT_object_psets(Panel): bl_label = "IFC Object Property Sets" bl_idname = "BIM_PT_object_psets" @@ -89,14 +106,16 @@ class BIM_PT_object_psets(Panel): Data.load(oprops.ifc_definition_id) row = self.layout.row(align=True) row.prop(props, "pset_name", text="") - row.operator("bim.add_pset", icon="ADD", text="") + op = row.operator("bim.add_pset", icon="ADD", text="") + op.obj = context.active_object.name + op.obj_type = "Object" for pset_id in Data.products[oprops.ifc_definition_id]["psets"]: pset = Data.psets[pset_id] - draw_psetqto_ui(pset_id, pset, props, self.layout) + draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Object") # TODO reimplement. See #1222. - #if props.relating_type and props.relating_type.PsetProperties.psets: + # if props.relating_type and props.relating_type.PsetProperties.psets: # self.layout.label(text="Inherited Psets:") # self.draw_psets_ui(props.relating_type.PsetProperties, enabled=False) @@ -134,4 +153,44 @@ class BIM_PT_object_qtos(Panel): for qto_id in Data.products[oprops.ifc_definition_id]["qtos"]: qto = Data.qtos[qto_id] - draw_psetqto_ui(qto_id, qto, props, self.layout) + draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Object") + + +class BIM_PT_material_psets(Panel): + bl_label = "IFC Material Property Sets" + bl_idname = "BIM_PT_material_psets" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "material" + + @classmethod + def poll(cls, context): + if not context.active_object: + return False + props = context.active_object.active_material.BIMObjectProperties + if not props.ifc_definition_id: + return False + if IfcStore.get_file().schema == "IFC2X3": + return False # We don't support material psets in IFC2X3 because they suck + if props.ifc_definition_id not in Data.products: + Data.load(props.ifc_definition_id) + if not Data.products[props.ifc_definition_id]: + return False + return True + + def draw(self, context): + oprops = context.active_object.active_material.BIMObjectProperties + props = context.active_object.active_material.PsetProperties + if not oprops.ifc_definition_id: + return + if oprops.ifc_definition_id not in Data.products: + Data.load(oprops.ifc_definition_id) + row = self.layout.row(align=True) + row.prop(props, "material_pset_name", text="") + op = row.operator("bim.add_pset", icon="ADD", text="") + op.obj = context.active_object.active_material.name + op.obj_type = "Material" + + for pset_id in Data.products[oprops.ifc_definition_id]["psets"]: + pset = Data.psets[pset_id] + draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Material")