mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Implement task-based parametric cost item quantities
This commit is contained in:
@@ -26,8 +26,8 @@ classes = (
|
||||
operator.ExpandCostItem,
|
||||
operator.ContractCostItem,
|
||||
operator.RemoveCostItem,
|
||||
operator.AssignCostItemProduct,
|
||||
operator.UnassignCostItemProduct,
|
||||
operator.AssignCostItemQuantity,
|
||||
operator.UnassignCostItemQuantity,
|
||||
operator.AddCostItemQuantity,
|
||||
operator.RemoveCostItemQuantity,
|
||||
operator.AddCostValue,
|
||||
@@ -38,13 +38,13 @@ classes = (
|
||||
operator.ImportCostScheduleCsv,
|
||||
operator.LoadCostItemQuantities,
|
||||
prop.CostItem,
|
||||
prop.CostItemProduct,
|
||||
prop.CostItemQuantity,
|
||||
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,
|
||||
ui.BIM_UL_cost_item_quantities,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -297,41 +297,48 @@ class EditCostItem(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignCostItemProduct(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_cost_item_product"
|
||||
bl_label = "Assign Control"
|
||||
class AssignCostItemQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_cost_item_quantity"
|
||||
bl_label = "Assign Cost Item Quantity"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
cost_item: bpy.props.IntProperty()
|
||||
related_object: bpy.props.StringProperty()
|
||||
related_object_type: bpy.props.StringProperty()
|
||||
prop_name: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
related_objects = (
|
||||
[bpy.data.objects.get(self.related_object)] if self.related_object else context.selected_objects
|
||||
)
|
||||
self.file = IfcStore.get_file()
|
||||
self.props = context.scene.BIMCostProperties
|
||||
if self.related_object_type == "PRODUCT":
|
||||
products = [
|
||||
self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
|
||||
for o in context.selected_objects
|
||||
if o.BIMObjectProperties.ifc_definition_id
|
||||
]
|
||||
elif self.related_object_type == "PROCESS":
|
||||
products = [
|
||||
self.file.by_id(
|
||||
context.scene.BIMTaskTreeProperties.tasks[
|
||||
context.scene.BIMWorkScheduleProperties.active_task_index
|
||||
].ifc_definition_id
|
||||
)
|
||||
]
|
||||
ifcopenshell.api.run(
|
||||
"cost.assign_cost_item_product",
|
||||
"cost.assign_cost_item_quantity",
|
||||
self.file,
|
||||
cost_item=self.file.by_id(self.cost_item),
|
||||
products=[
|
||||
self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
|
||||
for o in related_objects
|
||||
if o.BIMObjectProperties.ifc_definition_id
|
||||
],
|
||||
prop_name=self.props.quantity_names,
|
||||
products=products,
|
||||
prop_name=self.prop_name,
|
||||
)
|
||||
Data.load(self.file)
|
||||
bpy.ops.bim.load_cost_item_quantities()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class UnassignCostItemProduct(bpy.types.Operator):
|
||||
bl_idname = "bim.unassign_cost_item_product"
|
||||
bl_label = "Unassign Control"
|
||||
class UnassignCostItemQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.unassign_cost_item_quantity"
|
||||
bl_label = "Unassign Cost Item Quantity"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
cost_item: bpy.props.IntProperty()
|
||||
related_object: bpy.props.IntProperty()
|
||||
@@ -350,7 +357,7 @@ class UnassignCostItemProduct(bpy.types.Operator):
|
||||
if o.BIMObjectProperties.ifc_definition_id
|
||||
]
|
||||
ifcopenshell.api.run(
|
||||
"cost.unassign_cost_item_product",
|
||||
"cost.unassign_cost_item_quantity",
|
||||
self.file,
|
||||
cost_item=self.file.by_id(self.cost_item),
|
||||
products=products,
|
||||
@@ -708,19 +715,21 @@ class LoadCostItemQuantities(bpy.types.Operator):
|
||||
self.file = IfcStore.get_file()
|
||||
while len(self.props.cost_item_products) > 0:
|
||||
self.props.cost_item_products.remove(0)
|
||||
while len(self.props.cost_item_processes) > 0:
|
||||
self.props.cost_item_processes.remove(0)
|
||||
ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
|
||||
for control_id, quantity_ids in Data.cost_items[ifc_definition_id]["Controls"].items():
|
||||
related_object = self.file.by_id(control_id)
|
||||
if related_object.is_a("IfcProduct"):
|
||||
new = self.props.cost_item_products.add()
|
||||
new.ifc_definition_id = control_id
|
||||
new.name = related_object.Name or "Unnamed"
|
||||
total_quantity = 0
|
||||
for quantity_id in quantity_ids:
|
||||
total_quantity += self.file.by_id(quantity_id)[3]
|
||||
new.total_quantity = total_quantity
|
||||
elif related_object.is_a("IfcProduct"):
|
||||
pass
|
||||
elif related_object.is_a("IfcProcess"):
|
||||
new = self.props.cost_item_processes.add()
|
||||
elif related_object.is_a("IfcResource"):
|
||||
pass
|
||||
new.ifc_definition_id = control_id
|
||||
new.name = related_object.Name or "Unnamed"
|
||||
total_quantity = 0
|
||||
for quantity_id in quantity_ids:
|
||||
total_quantity += self.file.by_id(quantity_id)[3]
|
||||
new.total_quantity = total_quantity
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -18,17 +18,23 @@ from bpy.props import (
|
||||
|
||||
|
||||
quantitytypes_enum = []
|
||||
quantitynames_enum = []
|
||||
quantitynames_count = []
|
||||
productquantitynames_enum = []
|
||||
productquantitynames_count = []
|
||||
processquantitynames_enum = []
|
||||
processquantitynames_id = 0
|
||||
|
||||
|
||||
def purge():
|
||||
global quantitytypes_enum
|
||||
global quantitynames_enum
|
||||
global quantitynames_count
|
||||
global productquantitynames_enum
|
||||
global productquantitynames_count
|
||||
global processquantitynames_enum
|
||||
global processquantitynames_id
|
||||
quantitytypes_enum = []
|
||||
quantitynames_enum = []
|
||||
quantitynames_count = []
|
||||
productquantitynames_enum = []
|
||||
productquantitynames_count = []
|
||||
processquantitynames_enum = []
|
||||
processquantitynames_id = 0
|
||||
|
||||
|
||||
def getQuantityTypes(self, context):
|
||||
@@ -44,14 +50,14 @@ def getQuantityTypes(self, context):
|
||||
return quantitytypes_enum
|
||||
|
||||
|
||||
def getQuantityNames(self, context):
|
||||
global quantitynames_enum
|
||||
global quantitynames_count
|
||||
def getProductQuantityNames(self, context):
|
||||
global productquantitynames_enum
|
||||
global productquantitynames_count
|
||||
ifc_file = IfcStore.get_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
|
||||
if total_selected_objects != productquantitynames_count or total_selected_objects == 1:
|
||||
productquantitynames_enum = []
|
||||
productquantitynames_count = total_selected_objects
|
||||
names = set()
|
||||
for obj in context.selected_objects:
|
||||
element_id = obj.BIMObjectProperties.ifc_definition_id
|
||||
@@ -64,8 +70,32 @@ def getQuantityNames(self, context):
|
||||
qto = PsetData.qtos[qto_id]
|
||||
[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
|
||||
productquantitynames_enum.extend([(n, n, "") for n in names])
|
||||
return productquantitynames_enum
|
||||
|
||||
|
||||
def getProcessQuantityNames(self, context):
|
||||
global processquantitynames_enum
|
||||
global processquantitynames_id
|
||||
ifc_file = IfcStore.get_file()
|
||||
active_task_index = context.scene.BIMWorkScheduleProperties.active_task_index
|
||||
if not active_task_index:
|
||||
return []
|
||||
try:
|
||||
ifc_definition_id = context.scene.BIMTaskTreeProperties.tasks[active_task_index].ifc_definition_id
|
||||
except:
|
||||
return []
|
||||
if processquantitynames_id != ifc_definition_id:
|
||||
processquantitynames_enum = []
|
||||
processquantitynames_id = ifc_definition_id
|
||||
names = set()
|
||||
if ifc_definition_id not in PsetData.products:
|
||||
PsetData.load(ifc_file, ifc_definition_id)
|
||||
for qto_id in PsetData.products[ifc_definition_id]["qtos"]:
|
||||
qto = PsetData.qtos[qto_id]
|
||||
[names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]]
|
||||
processquantitynames_enum.extend([(n, n, "") for n in names])
|
||||
return processquantitynames_enum
|
||||
|
||||
|
||||
def update_cost_item_index(self, context):
|
||||
@@ -96,7 +126,7 @@ class CostItem(PropertyGroup):
|
||||
level_index: IntProperty(name="Level Index")
|
||||
|
||||
|
||||
class CostItemProduct(PropertyGroup):
|
||||
class CostItemQuantity(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
total_quantity: FloatProperty(name="Total Quantity")
|
||||
@@ -113,7 +143,8 @@ class BIMCostProperties(PropertyGroup):
|
||||
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")
|
||||
quantity_names: EnumProperty(items=getQuantityNames, name="Quantity Names")
|
||||
product_quantity_names: EnumProperty(items=getProductQuantityNames, name="Product Quantity Names")
|
||||
process_quantity_names: EnumProperty(items=getProcessQuantityNames, name="Process Quantity Names")
|
||||
active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id")
|
||||
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
|
||||
cost_types: EnumProperty(
|
||||
@@ -131,5 +162,7 @@ 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)
|
||||
cost_item_products: CollectionProperty(name="Cost Item Products", type=CostItemQuantity)
|
||||
active_cost_item_product_index: IntProperty(name="Active Cost Item Product Index")
|
||||
cost_item_processes: CollectionProperty(name="Cost Item Processes", type=CostItemQuantity)
|
||||
active_cost_item_process_index: IntProperty(name="Active Cost Item Process Index")
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import blenderbim.bim.module.cost.prop as CostProp
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.cost.data import Data
|
||||
@@ -108,8 +109,6 @@ class BIM_PT_cost_schedules(Panel):
|
||||
def draw_editable_cost_item_quantities_ui(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "quantity_types", text="")
|
||||
if self.props.quantity_types == "QTO":
|
||||
row.prop(self.props, "quantity_names", text="")
|
||||
op = row.operator("bim.add_cost_item_quantity", text="", icon="ADD")
|
||||
op.cost_item = self.props.active_cost_item_id
|
||||
op.ifc_class = self.props.quantity_types
|
||||
@@ -266,42 +265,19 @@ class BIM_PT_cost_item_quantities(Panel):
|
||||
|
||||
cost_item = self.props.cost_items[self.props.active_cost_item_index]
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
grid = self.layout.grid_flow(columns=3, even_columns=True)
|
||||
|
||||
row2 = row.row(align=True)
|
||||
# Column1
|
||||
col = grid.column()
|
||||
|
||||
row2 = col.row(align=True)
|
||||
row2.label(text="Elements")
|
||||
op = row.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="")
|
||||
op = row2.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",
|
||||
row2 = col.row()
|
||||
row2.template_list(
|
||||
"BIM_UL_cost_item_quantities",
|
||||
"",
|
||||
self.props,
|
||||
"cost_item_products",
|
||||
@@ -309,23 +285,64 @@ class BIM_PT_cost_item_quantities(Panel):
|
||||
"active_cost_item_product_index",
|
||||
)
|
||||
|
||||
row = self.layout.row()
|
||||
|
||||
row2 = row.row(align=True)
|
||||
row2.prop(self.props, "quantity_names", text="")
|
||||
op = row2.operator("bim.unassign_cost_item_product", text="", icon="REMOVE")
|
||||
row2 = col.row(align=True)
|
||||
row2.prop(self.props, "product_quantity_names", text="")
|
||||
op = row2.operator("bim.unassign_cost_item_quantity", text="", icon="REMOVE")
|
||||
op.cost_item = cost_item.ifc_definition_id
|
||||
op.related_object = 0
|
||||
op = row2.operator("bim.assign_cost_item_product", text="", icon="ADD")
|
||||
if CostProp.productquantitynames_enum:
|
||||
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD")
|
||||
op.related_object_type = "PRODUCT"
|
||||
op.cost_item = cost_item.ifc_definition_id
|
||||
op.prop_name = self.props.product_quantity_names
|
||||
|
||||
# Column2
|
||||
col = grid.column()
|
||||
|
||||
row2 = col.row(align=True)
|
||||
row2.label(text="Tasks")
|
||||
op = row2.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.prop(self.props, "quantity_names", text="")
|
||||
row2.operator("bim.unassign_cost_item_product", text="", icon="ADD")
|
||||
row2 = col.row()
|
||||
row2.template_list(
|
||||
"BIM_UL_cost_item_quantities",
|
||||
"",
|
||||
self.props,
|
||||
"cost_item_processes",
|
||||
self.props,
|
||||
"active_cost_item_process_index",
|
||||
)
|
||||
|
||||
row2 = row.row(align=True)
|
||||
row2.prop(self.props, "quantity_names", text="")
|
||||
row2.operator("bim.unassign_cost_item_product", text="", icon="ADD")
|
||||
row2 = col.row(align=True)
|
||||
row2.prop(self.props, "process_quantity_names", text="")
|
||||
if CostProp.processquantitynames_enum:
|
||||
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD")
|
||||
op.related_object_type = "PROCESS"
|
||||
op.cost_item = cost_item.ifc_definition_id
|
||||
op.prop_name = self.props.process_quantity_names
|
||||
|
||||
# Column3
|
||||
col = grid.column()
|
||||
|
||||
row2 = col.row(align=True)
|
||||
row2.label(text="Resources")
|
||||
op = row2.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="")
|
||||
op.cost_item = cost_item.ifc_definition_id
|
||||
|
||||
row2 = col.row()
|
||||
row2.template_list(
|
||||
"BIM_UL_cost_item_quantities",
|
||||
"",
|
||||
self.props,
|
||||
"cost_item_products",
|
||||
self.props,
|
||||
"active_cost_item_product_index",
|
||||
)
|
||||
|
||||
row2 = col.row(align=True)
|
||||
row2.prop(self.props, "process_quantity_names", text="")
|
||||
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD")
|
||||
|
||||
|
||||
class BIM_UL_cost_items(UIList):
|
||||
@@ -393,15 +410,16 @@ class BIM_UL_cost_columns(UIList):
|
||||
row.operator("bim.remove_cost_column", text="", icon="X").name = item.name
|
||||
|
||||
|
||||
class BIM_UL_cost_item_products(UIList):
|
||||
class BIM_UL_cost_item_quantities(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="")
|
||||
row.split(factor=0.8)
|
||||
row.label(text=item.name)
|
||||
row.label(text="{0:.2f}".format(item.total_quantity))
|
||||
op = row.operator("bim.unassign_cost_item_product", text="", icon="X")
|
||||
op = row.operator("bim.unassign_cost_item_quantity", text="", icon="X")
|
||||
op.cost_item = cost_item.ifc_definition_id
|
||||
op.related_object = item.ifc_definition_id
|
||||
|
||||
@@ -30,7 +30,7 @@ class IfcSchema:
|
||||
self.property_files = []
|
||||
property_paths = self.data_dir.joinpath("pset").glob("*.ifc")
|
||||
# TODO: add IFC2X3 PsetQto template support
|
||||
self.psetqto = ifcopenshell.util.pset.PsetQto("IFC4")
|
||||
self.psetqto = ifcopenshell.util.pset.get_template("IFC4")
|
||||
for path in property_paths:
|
||||
self.psetqto.templates.append(ifcopenshell.open(path))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user