From 0c27caf52264be5f5859b9458fc37deb67e56fdc Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 11 Aug 2021 16:50:38 +1000 Subject: [PATCH] Implement task quantity sets for parametric quantification --- .../blenderbim/bim/module/pset/__init__.py | 7 +- .../blenderbim/bim/module/pset/operator.py | 90 +++++++++---------- .../blenderbim/bim/module/pset/prop.py | 24 ++++- .../blenderbim/bim/module/pset/ui.py | 58 ++++++++++-- 4 files changed, 124 insertions(+), 55 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/pset/__init__.py b/src/blenderbim/blenderbim/bim/module/pset/__init__.py index 7c5964f3ca..aa2800ad73 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/pset/__init__.py @@ -11,17 +11,22 @@ classes = ( operator.AddQto, operator.GuessQuantity, prop.PsetProperties, + prop.MaterialPsetProperties, + prop.TaskPsetProperties, ui.BIM_PT_object_psets, ui.BIM_PT_object_qtos, ui.BIM_PT_material_psets, + ui.BIM_PT_task_qtos, ) def register(): bpy.types.Object.PsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties) - bpy.types.Material.PsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties) + bpy.types.Material.PsetProperties = bpy.props.PointerProperty(type=prop.MaterialPsetProperties) + bpy.types.Scene.TaskPsetProperties = bpy.props.PointerProperty(type=prop.TaskPsetProperties) def unregister(): del bpy.types.Object.PsetProperties del bpy.types.Material.PsetProperties + del bpy.types.Scene.TaskPsetProperties diff --git a/src/blenderbim/blenderbim/bim/module/pset/operator.py b/src/blenderbim/blenderbim/bim/module/pset/operator.py index 869af7db35..c25e717a49 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/operator.py +++ b/src/blenderbim/blenderbim/bim/module/pset/operator.py @@ -11,6 +11,30 @@ from ifcopenshell.api.cost.data import Data as CostData from blenderbim.bim.module.pset.qto_calculator import QtoCalculator +def get_pset_props(context, obj, obj_type): + if obj_type == "Object": + obj = bpy.data.objects.get(obj) + return obj.PsetProperties + elif obj_type == "Material": + obj = bpy.data.materials.get(obj) + return obj.PsetProperties + elif obj_type == "Task": + return context.scene.TaskPsetProperties + + +def get_pset_obj_ifc_definition_id(context, obj, obj_type): + if obj_type == "Object": + obj = bpy.data.objects.get(obj) + return obj.BIMObjectProperties.ifc_definition_id + elif obj_type == "Material": + obj = bpy.data.materials.get(obj) + return obj.BIMObjectProperties.ifc_definition_id + elif obj_type == "Task": + return context.scene.BIMTaskTreeProperties.tasks[ + context.scene.BIMWorkScheduleProperties.active_task_index + ].ifc_definition_id + + class TogglePsetExpansion(bpy.types.Operator): bl_idname = "bim.toggle_pset_expansion" bl_label = "Toggle Pset Expansion" @@ -32,11 +56,7 @@ class EnablePsetEditing(bpy.types.Operator): obj_type: bpy.props.StringProperty() def execute(self, context): - if self.obj_type == "Object": - obj = bpy.data.objects.get(self.obj) - elif self.obj_type == "Material": - obj = bpy.data.materials.get(self.obj) - self.props = obj.PsetProperties + self.props = get_pset_props(context, self.obj, self.obj_type) while len(self.props.properties) > 0: self.props.properties.remove(0) @@ -138,11 +158,7 @@ class DisablePsetEditing(bpy.types.Operator): obj_type: bpy.props.StringProperty() def execute(self, context): - 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 = get_pset_props(context, self.obj, self.obj_type) props.active_pset_id = 0 return {"FINISHED"} @@ -161,12 +177,8 @@ class EditPset(bpy.types.Operator): def _execute(self, context): self.file = IfcStore.get_file() - 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 + props = get_pset_props(context, self.obj, self.obj_type) + ifc_definition_id = get_pset_obj_ifc_definition_id(context, self.obj, self.obj_type) properties = {} pset_id = self.pset_id or props.active_pset_id @@ -213,7 +225,7 @@ class EditPset(bpy.types.Operator): }, ) CostData.purge() - Data.load(IfcStore.get_file(), oprops.ifc_definition_id) + Data.load(IfcStore.get_file(), ifc_definition_id) bpy.ops.bim.disable_pset_editing(obj=self.obj, obj_type=self.obj_type) return {"FINISHED"} @@ -231,20 +243,17 @@ class RemovePset(bpy.types.Operator): def _execute(self, context): self.file = IfcStore.get_file() - 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 + props = get_pset_props(context, self.obj, self.obj_type) + ifc_definition_id = get_pset_obj_ifc_definition_id(context, self.obj, self.obj_type) ifcopenshell.api.run( "pset.remove_pset", self.file, **{ - "product": self.file.by_id(props.ifc_definition_id), + "product": self.file.by_id(ifc_definition_id), "pset": self.file.by_id(self.pset_id), }, ) - Data.load(IfcStore.get_file(), props.ifc_definition_id) + Data.load(IfcStore.get_file(), ifc_definition_id) return {"FINISHED"} @@ -254,36 +263,24 @@ class AddPset(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} obj: bpy.props.StringProperty() obj_type: bpy.props.StringProperty() - pset_name: bpy.props.StringProperty() def execute(self, context): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): self.file = IfcStore.get_file() - 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 - - if self.pset_name: - pset_name = self.pset_name - elif self.obj_type == "Object": - pset_name = props.pset_name - elif self.obj_type == "Material": - pset_name = props.material_pset_name + props = get_pset_props(context, self.obj, self.obj_type) + ifc_definition_id = get_pset_obj_ifc_definition_id(context, self.obj, self.obj_type) ifcopenshell.api.run( "pset.add_pset", self.file, **{ - "product": self.file.by_id(oprops.ifc_definition_id), - "name": pset_name, + "product": self.file.by_id(ifc_definition_id), + "name": props.pset_name, }, ) - Data.load(IfcStore.get_file(), oprops.ifc_definition_id) + Data.load(IfcStore.get_file(), ifc_definition_id) return {"FINISHED"} @@ -291,24 +288,25 @@ class AddQto(bpy.types.Operator): bl_idname = "bim.add_qto" bl_label = "Add Qto" bl_options = {"REGISTER", "UNDO"} + obj: bpy.props.StringProperty() + obj_type: bpy.props.StringProperty() def execute(self, context): return IfcStore.execute_ifc_operator(self, context) def _execute(self, context): self.file = IfcStore.get_file() - obj = context.active_object - oprops = obj.BIMObjectProperties - props = obj.PsetProperties + props = get_pset_props(context, self.obj, self.obj_type) + ifc_definition_id = get_pset_obj_ifc_definition_id(context, self.obj, self.obj_type) ifcopenshell.api.run( "pset.add_qto", self.file, **{ - "product": self.file.by_id(oprops.ifc_definition_id), + "product": self.file.by_id(ifc_definition_id), "name": props.qto_name, }, ) - Data.load(IfcStore.get_file(), oprops.ifc_definition_id) + Data.load(IfcStore.get_file(), ifc_definition_id) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/pset/prop.py b/src/blenderbim/blenderbim/bim/module/pset/prop.py index 659ec986e9..bce2e8143d 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/prop.py +++ b/src/blenderbim/blenderbim/bim/module/pset/prop.py @@ -52,6 +52,15 @@ def getMaterialPsetNames(self, context): return psetnames[ifc_class] +def getTaskQtoNames(self, context): + global qtonames + ifc_class = "IfcTask" + if ifc_class not in qtonames: + psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True) + qtonames[ifc_class] = [(p.Name, p.Name, "") for p in psets] + return qtonames[ifc_class] + + def getQtoNames(self, context): global qtonames if "/" in context.active_object.name: @@ -69,4 +78,17 @@ 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") + + +class MaterialPsetProperties(PropertyGroup): + active_pset_id: IntProperty(name="Active Pset ID") + active_pset_name: StringProperty(name="Pset Name") + properties: CollectionProperty(name="Properties", type=Attribute) + pset_name: EnumProperty(items=getMaterialPsetNames, name="Pset Name") + + +class TaskPsetProperties(PropertyGroup): + active_pset_id: IntProperty(name="Active Pset ID") + active_pset_name: StringProperty(name="Pset Name") + properties: CollectionProperty(name="Properties", type=Attribute) + qto_name: EnumProperty(items=getTaskQtoNames, name="Qto Name") diff --git a/src/blenderbim/blenderbim/bim/module/pset/ui.py b/src/blenderbim/blenderbim/bim/module/pset/ui.py index e46aabc048..2d94c1bcb2 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/ui.py +++ b/src/blenderbim/blenderbim/bim/module/pset/ui.py @@ -3,6 +3,14 @@ from ifcopenshell.api.pset.data import Data from blenderbim.bim.ifc import IfcStore +def get_active_pset_obj_name(context, obj_type): + if obj_type == "Object": + return context.active_object.name + elif obj_type == "Material": + return context.active_object.active_material.name + return "" + + def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type): box = layout.box() row = box.row(align=True) @@ -10,29 +18,30 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type): pset["is_expanded"] = 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 + obj_name = get_active_pset_obj_name(context, obj_type) if not props.active_pset_id: row.label(text=pset["Name"], icon="COPY_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 = obj_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 = obj_name op.obj_type = obj_type elif props.active_pset_id != pset_id: row.label(text=pset["Name"], icon="COPY_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 = obj_name op.obj_type = obj_type elif props.active_pset_id == pset_id: row.prop(props, "active_pset_name", icon="COPY_ID", 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 = obj_name op.obj_type = obj_type op = row.operator("bim.disable_pset_editing", icon="CANCEL", text="") - op.obj = context.active_object.name if obj_type == "Object" else context.active_object.active_material.name + op.obj = obj_name op.obj_type = obj_type if pset["is_expanded"]: if props.active_pset_id == pset_id: @@ -163,7 +172,9 @@ class BIM_PT_object_qtos(Panel): Data.load(IfcStore.get_file(), oprops.ifc_definition_id) row = self.layout.row(align=True) row.prop(props, "qto_name", text="") - row.operator("bim.add_qto", icon="ADD", text="") + op = row.operator("bim.add_qto", icon="ADD", text="") + op.obj = context.active_object.name + op.obj_type = "Object" qtos = [(qto_id, Data.qtos[qto_id]) for qto_id in Data.products[oprops.ifc_definition_id]["qtos"]] for qto_id, qto in sorted(qtos, key = lambda v: v[1]["Name"]): @@ -202,7 +213,7 @@ class BIM_PT_material_psets(Panel): if oprops.ifc_definition_id not in Data.products: Data.load(IfcStore.get_file(), oprops.ifc_definition_id) row = self.layout.row(align=True) - row.prop(props, "material_pset_name", text="") + row.prop(props, "pset_name", text="") op = row.operator("bim.add_pset", icon="ADD", text="") op.obj = context.active_object.active_material.name op.obj_type = "Material" @@ -210,3 +221,36 @@ class BIM_PT_material_psets(Panel): psets = [(pset_id, Data.psets[pset_id]) for pset_id in Data.products[oprops.ifc_definition_id]["psets"]] for pset_id, pset in sorted(psets, key = lambda v: v[1]["Name"]): draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Material") + + +class BIM_PT_task_qtos(Panel): + bl_label = "IFC Task Quantity Sets" + bl_idname = "BIM_PT_task_qtos" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "scene" + bl_parent_id = "BIM_PT_work_schedules" + + @classmethod + def poll(cls, context): + props = context.scene.BIMWorkScheduleProperties + total_tasks = len(context.scene.BIMTaskTreeProperties.tasks) + if total_tasks > 0 and props.active_task_index < total_tasks: + return True + return False + + def draw(self, context): + props = context.scene.TaskPsetProperties + wprops = context.scene.BIMWorkScheduleProperties + tprops = context.scene.BIMTaskTreeProperties + ifc_definition_id = tprops.tasks[wprops.active_task_index].ifc_definition_id + if ifc_definition_id not in Data.products: + Data.load(IfcStore.get_file(), ifc_definition_id) + row = self.layout.row(align=True) + row.prop(props, "qto_name", text="") + op = row.operator("bim.add_qto", icon="ADD", text="") + op.obj_type = "Task" + + qtos = [(qto_id, Data.qtos[qto_id]) for qto_id in Data.products[ifc_definition_id]["qtos"]] + for qto_id, qto in sorted(qtos, key = lambda v: v[1]["Name"]): + draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Task")