From 858f48c49ddb5d99084cdf7f3664c312623f574c Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 11 Aug 2021 20:56:07 +1000 Subject: [PATCH] New interface to view physical products assigned to a cost item --- .../blenderbim/bim/module/cost/__init__.py | 4 + .../blenderbim/bim/module/cost/operator.py | 24 ++++ .../blenderbim/bim/module/cost/prop.py | 33 +++++- .../blenderbim/bim/module/cost/ui.py | 105 ++++++++++++++++-- .../blenderbim/bim/module/pset/ui.py | 10 +- 5 files changed, 154 insertions(+), 22 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/cost/__init__.py b/src/blenderbim/blenderbim/bim/module/cost/__init__.py index 1c9bd13b03..4c6b5f1a30 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/cost/__init__.py @@ -36,11 +36,15 @@ classes = ( operator.SelectCostItemProducts, operator.SelectCostScheduleProducts, operator.ImportCostScheduleCsv, + operator.LoadCostItemProducts, prop.CostItem, + prop.CostItemProduct, prop.BIMCostProperties, ui.BIM_PT_cost_schedules, + ui.BIM_PT_cost_item_quantities, ui.BIM_UL_cost_items, ui.BIM_UL_cost_columns, + ui.BIM_UL_cost_item_products, ) diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index f6da24010d..13f7ac06da 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -702,3 +702,27 @@ class RemoveCostColumn(bpy.types.Operator): Data.set_categories([c.name for c in self.props.columns]) Data.load(IfcStore.get_file()) return {"FINISHED"} + + +class LoadCostItemProducts(bpy.types.Operator): + bl_idname = "bim.load_cost_item_products" + bl_label = "Load Cost Item Products" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + self.props = context.scene.BIMCostProperties + self.file = IfcStore.get_file() + while len(self.props.cost_item_products) > 0: + self.props.cost_item_products.remove(0) + ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id + for control in Data.cost_items[ifc_definition_id]["Controls"]: + related_object = self.file.by_id(control) + if related_object.is_a("IfcProduct"): + new = self.props.cost_item_products.add() + new.ifc_definition_id = control + new.name = related_object.Name or "Unnamed" + elif related_object.is_a("IfcProduct"): + pass + elif related_object.is_a("IfcResource"): + pass + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/cost/prop.py b/src/blenderbim/blenderbim/bim/module/cost/prop.py index 98936463f9..f187989346 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/prop.py +++ b/src/blenderbim/blenderbim/bim/module/cost/prop.py @@ -19,13 +19,16 @@ from bpy.props import ( quantitytypes_enum = [] quantitynames_enum = [] +quantitynames_count = [] def purge(): global quantitytypes_enum global quantitynames_enum + global quantitynames_count quantitytypes_enum = [] quantitynames_enum = [] + quantitynames_count = [] def getQuantityTypes(self, context): @@ -43,19 +46,32 @@ def getQuantityTypes(self, context): def getQuantityNames(self, context): global quantitynames_enum + global quantitynames_count ifc_file = IfcStore.get_file() - if len(quantitynames_enum) == 0 and ifc_file: + total_selected_objects = len(context.selected_objects) + if total_selected_objects != quantitynames_count or total_selected_objects == 1: + quantitynames_enum = [] + quantitynames_count = total_selected_objects names = set() - for element_id in Data.cost_items[self.active_cost_item_id]["Controls"]: + for obj in context.selected_objects: + element_id = obj.BIMObjectProperties.ifc_definition_id + if not element_id: + continue + potential_names = set() if element_id not in PsetData.products: - PsetData.load(IfcStore.get_file(), element_id) + PsetData.load(ifc_file, element_id) for qto_id in PsetData.products[element_id]["qtos"]: qto = PsetData.qtos[qto_id] - [names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]] + [potential_names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]] + names = names.intersection(potential_names) if names else potential_names quantitynames_enum.extend([(n, n, "") for n in names]) return quantitynames_enum +def update_cost_item_index(self, context): + bpy.ops.bim.load_cost_item_products() + + def updateCostItemName(self, context): if self.name == "Unnamed": return @@ -80,6 +96,11 @@ class CostItem(PropertyGroup): level_index: IntProperty(name="Level Index") +class CostItemProduct(PropertyGroup): + name: StringProperty(name="Name") + ifc_definition_id: IntProperty(name="IFC Definition ID") + + class BIMCostProperties(PropertyGroup): cost_schedule_attributes: CollectionProperty(name="Cost Schedule Attributes", type=Attribute) is_editing: StringProperty(name="Is Editing") @@ -87,7 +108,7 @@ class BIMCostProperties(PropertyGroup): cost_items: CollectionProperty(name="Cost Items", type=CostItem) active_cost_item_id: IntProperty(name="Active Cost Id") cost_item_editing_type: StringProperty(name="Cost Item Editing Type") - active_cost_item_index: IntProperty(name="Active Cost Item Index") + active_cost_item_index: IntProperty(name="Active Cost Item Index", update=update_cost_item_index) cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute) contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]") quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types") @@ -109,3 +130,5 @@ class BIMCostProperties(PropertyGroup): should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False) columns: CollectionProperty(name="Columns", type=StrProperty) active_column_index: IntProperty(name="Active Column Index") + cost_item_products: CollectionProperty(name="Cost Item Products", type=CostItemProduct) + active_cost_item_product_index: IntProperty(name="Active Cost Item Product Index") diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index 658a387b47..4c14373eea 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -244,6 +244,87 @@ class BIM_PT_cost_schedules(Panel): row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") +class BIM_PT_cost_item_quantities(Panel): + bl_label = "IFC Cost Item Quantities" + bl_idname = "BIM_PT_cost_item_quantities" + bl_options = {"DEFAULT_CLOSED"} + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "scene" + bl_parent_id = "BIM_PT_cost_schedules" + + @classmethod + def poll(cls, context): + props = context.scene.BIMCostProperties + total_cost_items = len(props.cost_items) + if total_cost_items > 0 and props.active_cost_item_index < total_cost_items: + return True + return False + + def draw(self, context): + self.props = context.scene.BIMCostProperties + + cost_item = self.props.cost_items[self.props.active_cost_item_index] + + row = self.layout.row(align=True) + + row2 = row.row(align=True) + row2.label(text="Elements") + op = row.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="") + op.cost_item = cost_item.ifc_definition_id + + row2 = row.row(align=True) + row2.label(text="Tasks") + op = row.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="") + op.cost_item = cost_item.ifc_definition_id + + row2 = row.row(align=True) + row2.label(text="Resources") + op = row.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="") + op.cost_item = cost_item.ifc_definition_id + + row = self.layout.row(align=True) + row.template_list( + "BIM_UL_cost_item_products", + "", + self.props, + "cost_item_products", + self.props, + "active_cost_item_product_index", + ) + row.template_list( + "BIM_UL_cost_item_products", + "", + self.props, + "cost_item_products", + self.props, + "active_cost_item_product_index", + ) + row.template_list( + "BIM_UL_cost_item_products", + "", + self.props, + "cost_item_products", + self.props, + "active_cost_item_product_index", + ) + + row = self.layout.row(align=True) + + row2 = row.row(align=True) + row2.prop(self.props, "quantity_names", text="") + op = row.operator("bim.assign_cost_item_product", text="", icon="ADD") + op.cost_item = cost_item.ifc_definition_id + + row2 = row.row(align=True) + row2.prop(self.props, "quantity_names", text="") + row2.operator("bim.unassign_cost_item_product", text="", icon="ADD") + + row2 = row.row(align=True) + row2.prop(self.props, "quantity_names", text="") + row2.operator("bim.unassign_cost_item_product", text="", icon="ADD") + + class BIM_UL_cost_items(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: @@ -281,16 +362,6 @@ class BIM_UL_cost_items(UIList): row.label(text="{0:.2f}".format(cost_item["TotalCostValue"]), icon="CON_TRANSLIKE") - if context.active_object: - oprops = context.active_object.BIMObjectProperties - row = layout.row(align=True) - if oprops.ifc_definition_id in cost_item["Controls"]: - op = row.operator("bim.unassign_cost_item_product", text="", icon="KEYFRAME_HLT", emboss=False) - op.cost_item = item.ifc_definition_id - else: - op = row.operator("bim.assign_cost_item_product", text="", icon="KEYFRAME", emboss=False) - op.cost_item = item.ifc_definition_id - if props.active_cost_item_id == item.ifc_definition_id: if props.cost_item_editing_type == "ATTRIBUTES": row.operator("bim.edit_cost_item", text="", icon="CHECKMARK") @@ -303,8 +374,6 @@ class BIM_UL_cost_items(UIList): row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id row.operator("bim.remove_cost_item", text="", icon="X").cost_item = item.ifc_definition_id else: - op = row.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="") - op.cost_item = item.ifc_definition_id row.operator( "bim.enable_editing_cost_item", text="", icon="GREASEPENCIL" ).cost_item = item.ifc_definition_id @@ -319,3 +388,15 @@ class BIM_UL_cost_columns(UIList): row = layout.row(align=True) row.prop(item, "name", emboss=False, text="") row.operator("bim.remove_cost_column", text="", icon="X").name = item.name + + +class BIM_UL_cost_item_products(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + props = context.scene.BIMCostProperties + cost_item = props.cost_items[props.active_cost_item_index] + + if item: + row = layout.row(align=True) + row.prop(item, "name", emboss=False, text="") + op = row.operator("bim.unassign_cost_item_product", text="", icon="X") + op.cost_item = cost_item.ifc_definition_id diff --git a/src/blenderbim/blenderbim/bim/module/pset/ui.py b/src/blenderbim/blenderbim/bim/module/pset/ui.py index 6d613f0f95..c2c175e1ec 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/ui.py +++ b/src/blenderbim/blenderbim/bim/module/pset/ui.py @@ -132,7 +132,7 @@ class BIM_PT_object_psets(Panel): op.obj_type = "Object" 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"]): + for pset_id, pset in sorted(psets, key=lambda v: v[1]["Name"]): draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Object") # TODO reimplement. See #1222. @@ -177,7 +177,7 @@ class BIM_PT_object_qtos(Panel): 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"]): + for qto_id, qto in sorted(qtos, key=lambda v: v[1]["Name"]): draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Object") @@ -219,7 +219,7 @@ class BIM_PT_material_psets(Panel): op.obj_type = "Material" 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"]): + for pset_id, pset in sorted(psets, key=lambda v: v[1]["Name"]): draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Material") @@ -252,7 +252,7 @@ class BIM_PT_task_qtos(Panel): 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"]): + for qto_id, qto in sorted(qtos, key=lambda v: v[1]["Name"]): draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Task") @@ -285,5 +285,5 @@ class BIM_PT_resource_qtos(Panel): op.obj_type = "Resource" 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"]): + for qto_id, qto in sorted(qtos, key=lambda v: v[1]["Name"]): draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Resource")