mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Linked cost item products now auto update cost quantities upon assignment and unassignment, with user friendly quantity type selection dropdown
This commit is contained in:
@@ -18,7 +18,7 @@ def import_attributes(ifc_class, props, data, callback=None):
|
||||
new.is_null = data[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
new.data_type = data_type if isinstance(data_type, str) else ""
|
||||
is_handled_by_callback = callback(attribute.name(), new, data) if callback else False
|
||||
is_handled_by_callback = callback(attribute.name(), new, data) if callback else None
|
||||
if is_handled_by_callback:
|
||||
pass # Our job is done
|
||||
elif is_handled_by_callback is False:
|
||||
|
||||
@@ -24,8 +24,8 @@ classes = (
|
||||
operator.ExpandCostItem,
|
||||
operator.ContractCostItem,
|
||||
operator.RemoveCostItem,
|
||||
operator.AssignControl,
|
||||
operator.UnassignControl,
|
||||
operator.AssignCostItemProduct,
|
||||
operator.UnassignCostItemProduct,
|
||||
operator.AddCostItemQuantity,
|
||||
operator.RemoveCostItemQuantity,
|
||||
operator.AddCostValue,
|
||||
|
||||
@@ -3,6 +3,7 @@ import bpy
|
||||
import json
|
||||
import ifcopenshell.api
|
||||
import blenderbim.bim.helper
|
||||
from blenderbim.bim.module.cost.prop import purge
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.cost.data import Data
|
||||
|
||||
@@ -259,8 +260,8 @@ class EditCostItem(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignControl(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_control"
|
||||
class AssignCostItemProduct(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_cost_item_product"
|
||||
bl_label = "Assign Control"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
related_object: bpy.props.StringProperty()
|
||||
@@ -269,20 +270,23 @@ class AssignControl(bpy.types.Operator):
|
||||
related_objects = (
|
||||
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects
|
||||
)
|
||||
for related_object in related_objects:
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"control.assign_control",
|
||||
self.file,
|
||||
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
|
||||
relating_control=self.file.by_id(self.cost_item),
|
||||
)
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"cost.assign_cost_item_product",
|
||||
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
|
||||
],
|
||||
)
|
||||
Data.load(self.file)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class UnassignControl(bpy.types.Operator):
|
||||
bl_idname = "bim.unassign_control"
|
||||
class UnassignCostItemProduct(bpy.types.Operator):
|
||||
bl_idname = "bim.unassign_cost_item_product"
|
||||
bl_label = "Unassign Control"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
related_object: bpy.props.StringProperty()
|
||||
@@ -291,14 +295,17 @@ class UnassignControl(bpy.types.Operator):
|
||||
related_objects = (
|
||||
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects
|
||||
)
|
||||
for related_object in related_objects:
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"control.unassign_control",
|
||||
self.file,
|
||||
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
|
||||
relating_control=self.file.by_id(self.cost_item),
|
||||
)
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"cost.unassign_cost_item_product",
|
||||
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
|
||||
],
|
||||
)
|
||||
Data.load(self.file)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -312,6 +319,7 @@ class EnableEditingCostItemQuantities(bpy.types.Operator):
|
||||
props = context.scene.BIMCostProperties
|
||||
props.active_cost_item_id = self.cost_item
|
||||
props.cost_item_editing_type = "QUANTITIES"
|
||||
purge()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -348,8 +356,7 @@ class AddCostItemQuantity(bpy.types.Operator):
|
||||
"cost.assign_cost_item_product_quantities",
|
||||
self.file,
|
||||
cost_item=self.file.by_id(self.cost_item),
|
||||
qto_name=self.props.qto_name,
|
||||
prop_name=self.props.prop_name
|
||||
prop_name=self.props.quantity_names,
|
||||
)
|
||||
|
||||
def add_manual_quantity(self):
|
||||
@@ -439,9 +446,7 @@ class AddCostValue(bpy.types.Operator):
|
||||
elif self.cost_type == "CATEGORY":
|
||||
category = self.cost_category
|
||||
value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.file.by_id(self.parent))
|
||||
ifcopenshell.api.run(
|
||||
"cost.edit_cost_value", self.file, cost_value=value, attributes={"Category": category}
|
||||
)
|
||||
ifcopenshell.api.run("cost.edit_cost_value", self.file, cost_value=value, attributes={"Category": category})
|
||||
Data.load(self.file)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import bpy
|
||||
import ifcopenshell.api
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.cost.data import Data
|
||||
from ifcopenshell.api.pset.data import Data as PsetData
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -17,17 +18,19 @@ from bpy.props import (
|
||||
|
||||
|
||||
quantitytypes_enum = []
|
||||
quantitynames_enum = []
|
||||
|
||||
|
||||
def purge():
|
||||
global quantitytypes_enum
|
||||
global quantitynames_enum
|
||||
quantitytypes_enum = []
|
||||
quantitynames_enum = []
|
||||
|
||||
|
||||
def getQuantityTypes(self, context):
|
||||
global quantitytypes_enum
|
||||
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
|
||||
quantitytypes_enum.clear()
|
||||
quantitytypes_enum = [("QTO", "Qto", "Derive quantities from IFC quantity sets")]
|
||||
quantitytypes_enum.extend(
|
||||
[
|
||||
@@ -38,6 +41,21 @@ def getQuantityTypes(self, context):
|
||||
return quantitytypes_enum
|
||||
|
||||
|
||||
def getQuantityNames(self, context):
|
||||
global quantitynames_enum
|
||||
ifc_file = IfcStore.get_file()
|
||||
if len(quantitynames_enum) == 0 and ifc_file:
|
||||
names = set()
|
||||
for element_id in Data.cost_items[self.active_cost_item_id]["Controls"]:
|
||||
if element_id not in PsetData.products:
|
||||
PsetData.load(IfcStore.get_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"]]
|
||||
quantitynames_enum.extend([(n, n, "") for n in names])
|
||||
return quantitynames_enum
|
||||
|
||||
|
||||
def updateCostItemName(self, context):
|
||||
if self.name == "Unnamed":
|
||||
return
|
||||
@@ -73,8 +91,7 @@ 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")
|
||||
qto_name: StringProperty(name="Qto Name")
|
||||
prop_name: StringProperty(name="Prop Name")
|
||||
quantity_names: EnumProperty(items=getQuantityNames, name="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(
|
||||
|
||||
@@ -97,8 +97,7 @@ class BIM_PT_cost_schedules(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "quantity_types", text="")
|
||||
if self.props.quantity_types == "QTO":
|
||||
row.prop(self.props, "qto_name", text="")
|
||||
row.prop(self.props, "prop_name", text="")
|
||||
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
|
||||
@@ -254,10 +253,10 @@ class BIM_UL_cost_items(UIList):
|
||||
oprops = context.active_object.BIMObjectProperties
|
||||
row = layout.row(align=True)
|
||||
if oprops.ifc_definition_id in cost_item["Controls"]:
|
||||
op = row.operator("bim.unassign_control", text="", icon="KEYFRAME_HLT", emboss=False)
|
||||
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_control", text="", icon="KEYFRAME", emboss=False)
|
||||
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:
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import ifcopenshell.api
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"cost_item": None, "products": []}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
quantity_names = set()
|
||||
for quantity in self.settings["cost_item"].CostQuantities or []:
|
||||
if quantity.Name:
|
||||
quantity_names.add(quantity.Name)
|
||||
|
||||
for product in self.settings["products"]:
|
||||
ifcopenshell.api.run(
|
||||
"control.assign_control",
|
||||
self.file,
|
||||
related_object=product,
|
||||
relating_control=self.settings["cost_item"],
|
||||
)
|
||||
|
||||
for name in quantity_names:
|
||||
ifcopenshell.api.run(
|
||||
"cost.assign_cost_item_product_quantities",
|
||||
self.file,
|
||||
cost_item=self.settings["cost_item"],
|
||||
prop_name=name
|
||||
)
|
||||
+2
-2
@@ -4,7 +4,7 @@ import ifcopenshell.api
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"cost_item": None, "qto_name": "", "prop_name": ""}
|
||||
self.settings = {"cost_item": None, "prop_name": ""}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
@@ -25,7 +25,7 @@ class Usecase:
|
||||
self.add_quantity_from_qto(relationship.RelatingPropertyDefinition)
|
||||
|
||||
def add_quantity_from_qto(self, qto):
|
||||
if not qto.is_a("IfcElementQuantity") or qto.Name.lower() != self.settings["qto_name"].lower():
|
||||
if not qto.is_a("IfcElementQuantity"):
|
||||
return
|
||||
for prop in qto.Quantities:
|
||||
if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == self.settings["prop_name"].lower():
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import ifcopenshell.api
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"cost_item": None, "products": []}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
self.quantities = set(self.settings["cost_item"].CostQuantities or [])
|
||||
for quantity in self.settings["cost_item"].CostQuantities or []:
|
||||
for inverse in self.file.get_inverse(quantity):
|
||||
if not inverse.is_a("IfcElementQuantity"):
|
||||
continue
|
||||
for rel in inverse.DefinesOccurrence or []:
|
||||
for related_object in rel.RelatedObjects:
|
||||
if related_object in self.settings["products"]:
|
||||
self.quantities.remove(quantity)
|
||||
self.settings["cost_item"].CostQuantities = list(self.quantities)
|
||||
|
||||
for product in self.settings["products"]:
|
||||
ifcopenshell.api.run(
|
||||
"control.unassign_control",
|
||||
self.file,
|
||||
related_object=product,
|
||||
relating_control=self.settings["cost_item"],
|
||||
)
|
||||
Reference in New Issue
Block a user