mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-16 13:46:54 +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_null = data[attribute.name()] is None
|
||||||
new.is_optional = attribute.optional()
|
new.is_optional = attribute.optional()
|
||||||
new.data_type = data_type if isinstance(data_type, str) else ""
|
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:
|
if is_handled_by_callback:
|
||||||
pass # Our job is done
|
pass # Our job is done
|
||||||
elif is_handled_by_callback is False:
|
elif is_handled_by_callback is False:
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ classes = (
|
|||||||
operator.ExpandCostItem,
|
operator.ExpandCostItem,
|
||||||
operator.ContractCostItem,
|
operator.ContractCostItem,
|
||||||
operator.RemoveCostItem,
|
operator.RemoveCostItem,
|
||||||
operator.AssignControl,
|
operator.AssignCostItemProduct,
|
||||||
operator.UnassignControl,
|
operator.UnassignCostItemProduct,
|
||||||
operator.AddCostItemQuantity,
|
operator.AddCostItemQuantity,
|
||||||
operator.RemoveCostItemQuantity,
|
operator.RemoveCostItemQuantity,
|
||||||
operator.AddCostValue,
|
operator.AddCostValue,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import bpy
|
|||||||
import json
|
import json
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import blenderbim.bim.helper
|
import blenderbim.bim.helper
|
||||||
|
from blenderbim.bim.module.cost.prop import purge
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from ifcopenshell.api.cost.data import Data
|
from ifcopenshell.api.cost.data import Data
|
||||||
|
|
||||||
@@ -259,8 +260,8 @@ class EditCostItem(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class AssignControl(bpy.types.Operator):
|
class AssignCostItemProduct(bpy.types.Operator):
|
||||||
bl_idname = "bim.assign_control"
|
bl_idname = "bim.assign_cost_item_product"
|
||||||
bl_label = "Assign Control"
|
bl_label = "Assign Control"
|
||||||
cost_item: bpy.props.IntProperty()
|
cost_item: bpy.props.IntProperty()
|
||||||
related_object: bpy.props.StringProperty()
|
related_object: bpy.props.StringProperty()
|
||||||
@@ -269,20 +270,23 @@ class AssignControl(bpy.types.Operator):
|
|||||||
related_objects = (
|
related_objects = (
|
||||||
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_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()
|
||||||
self.file = IfcStore.get_file()
|
ifcopenshell.api.run(
|
||||||
ifcopenshell.api.run(
|
"cost.assign_cost_item_product",
|
||||||
"control.assign_control",
|
self.file,
|
||||||
self.file,
|
cost_item=self.file.by_id(self.cost_item),
|
||||||
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
|
products=[
|
||||||
relating_control=self.file.by_id(self.cost_item),
|
self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
|
||||||
)
|
for o in related_objects
|
||||||
|
if o.BIMObjectProperties.ifc_definition_id
|
||||||
|
],
|
||||||
|
)
|
||||||
Data.load(self.file)
|
Data.load(self.file)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class UnassignControl(bpy.types.Operator):
|
class UnassignCostItemProduct(bpy.types.Operator):
|
||||||
bl_idname = "bim.unassign_control"
|
bl_idname = "bim.unassign_cost_item_product"
|
||||||
bl_label = "Unassign Control"
|
bl_label = "Unassign Control"
|
||||||
cost_item: bpy.props.IntProperty()
|
cost_item: bpy.props.IntProperty()
|
||||||
related_object: bpy.props.StringProperty()
|
related_object: bpy.props.StringProperty()
|
||||||
@@ -291,14 +295,17 @@ class UnassignControl(bpy.types.Operator):
|
|||||||
related_objects = (
|
related_objects = (
|
||||||
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_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()
|
||||||
self.file = IfcStore.get_file()
|
ifcopenshell.api.run(
|
||||||
ifcopenshell.api.run(
|
"cost.unassign_cost_item_product",
|
||||||
"control.unassign_control",
|
self.file,
|
||||||
self.file,
|
cost_item=self.file.by_id(self.cost_item),
|
||||||
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
|
products=[
|
||||||
relating_control=self.file.by_id(self.cost_item),
|
self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
|
||||||
)
|
for o in related_objects
|
||||||
|
if o.BIMObjectProperties.ifc_definition_id
|
||||||
|
],
|
||||||
|
)
|
||||||
Data.load(self.file)
|
Data.load(self.file)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -312,6 +319,7 @@ class EnableEditingCostItemQuantities(bpy.types.Operator):
|
|||||||
props = context.scene.BIMCostProperties
|
props = context.scene.BIMCostProperties
|
||||||
props.active_cost_item_id = self.cost_item
|
props.active_cost_item_id = self.cost_item
|
||||||
props.cost_item_editing_type = "QUANTITIES"
|
props.cost_item_editing_type = "QUANTITIES"
|
||||||
|
purge()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -348,8 +356,7 @@ class AddCostItemQuantity(bpy.types.Operator):
|
|||||||
"cost.assign_cost_item_product_quantities",
|
"cost.assign_cost_item_product_quantities",
|
||||||
self.file,
|
self.file,
|
||||||
cost_item=self.file.by_id(self.cost_item),
|
cost_item=self.file.by_id(self.cost_item),
|
||||||
qto_name=self.props.qto_name,
|
prop_name=self.props.quantity_names,
|
||||||
prop_name=self.props.prop_name
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_manual_quantity(self):
|
def add_manual_quantity(self):
|
||||||
@@ -439,9 +446,7 @@ class AddCostValue(bpy.types.Operator):
|
|||||||
elif self.cost_type == "CATEGORY":
|
elif self.cost_type == "CATEGORY":
|
||||||
category = self.cost_category
|
category = self.cost_category
|
||||||
value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.file.by_id(self.parent))
|
value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.file.by_id(self.parent))
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run("cost.edit_cost_value", self.file, cost_value=value, attributes={"Category": category})
|
||||||
"cost.edit_cost_value", self.file, cost_value=value, attributes={"Category": category}
|
|
||||||
)
|
|
||||||
Data.load(self.file)
|
Data.load(self.file)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import bpy
|
|||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from ifcopenshell.api.cost.data import Data
|
from ifcopenshell.api.cost.data import Data
|
||||||
|
from ifcopenshell.api.pset.data import Data as PsetData
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
@@ -17,17 +18,19 @@ from bpy.props import (
|
|||||||
|
|
||||||
|
|
||||||
quantitytypes_enum = []
|
quantitytypes_enum = []
|
||||||
|
quantitynames_enum = []
|
||||||
|
|
||||||
|
|
||||||
def purge():
|
def purge():
|
||||||
global quantitytypes_enum
|
global quantitytypes_enum
|
||||||
|
global quantitynames_enum
|
||||||
quantitytypes_enum = []
|
quantitytypes_enum = []
|
||||||
|
quantitynames_enum = []
|
||||||
|
|
||||||
|
|
||||||
def getQuantityTypes(self, context):
|
def getQuantityTypes(self, context):
|
||||||
global quantitytypes_enum
|
global quantitytypes_enum
|
||||||
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
|
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
|
||||||
quantitytypes_enum.clear()
|
|
||||||
quantitytypes_enum = [("QTO", "Qto", "Derive quantities from IFC quantity sets")]
|
quantitytypes_enum = [("QTO", "Qto", "Derive quantities from IFC quantity sets")]
|
||||||
quantitytypes_enum.extend(
|
quantitytypes_enum.extend(
|
||||||
[
|
[
|
||||||
@@ -38,6 +41,21 @@ def getQuantityTypes(self, context):
|
|||||||
return quantitytypes_enum
|
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):
|
def updateCostItemName(self, context):
|
||||||
if self.name == "Unnamed":
|
if self.name == "Unnamed":
|
||||||
return
|
return
|
||||||
@@ -73,8 +91,7 @@ class BIMCostProperties(PropertyGroup):
|
|||||||
cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
|
cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
|
||||||
contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]")
|
contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]")
|
||||||
quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types")
|
quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types")
|
||||||
qto_name: StringProperty(name="Qto Name")
|
quantity_names: EnumProperty(items=getQuantityNames, name="Quantity Names")
|
||||||
prop_name: StringProperty(name="Prop Name")
|
|
||||||
active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id")
|
active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id")
|
||||||
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
|
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
|
||||||
cost_types: EnumProperty(
|
cost_types: EnumProperty(
|
||||||
|
|||||||
@@ -97,8 +97,7 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.prop(self.props, "quantity_types", text="")
|
row.prop(self.props, "quantity_types", text="")
|
||||||
if self.props.quantity_types == "QTO":
|
if self.props.quantity_types == "QTO":
|
||||||
row.prop(self.props, "qto_name", text="")
|
row.prop(self.props, "quantity_names", text="")
|
||||||
row.prop(self.props, "prop_name", text="")
|
|
||||||
op = row.operator("bim.add_cost_item_quantity", text="", icon="ADD")
|
op = row.operator("bim.add_cost_item_quantity", text="", icon="ADD")
|
||||||
op.cost_item = self.props.active_cost_item_id
|
op.cost_item = self.props.active_cost_item_id
|
||||||
op.ifc_class = self.props.quantity_types
|
op.ifc_class = self.props.quantity_types
|
||||||
@@ -254,10 +253,10 @@ class BIM_UL_cost_items(UIList):
|
|||||||
oprops = context.active_object.BIMObjectProperties
|
oprops = context.active_object.BIMObjectProperties
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
if oprops.ifc_definition_id in cost_item["Controls"]:
|
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
|
op.cost_item = item.ifc_definition_id
|
||||||
else:
|
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
|
op.cost_item = item.ifc_definition_id
|
||||||
|
|
||||||
if props.active_cost_item_id == 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:
|
class Usecase:
|
||||||
def __init__(self, file, **settings):
|
def __init__(self, file, **settings):
|
||||||
self.file = file
|
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():
|
for key, value in settings.items():
|
||||||
self.settings[key] = value
|
self.settings[key] = value
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ class Usecase:
|
|||||||
self.add_quantity_from_qto(relationship.RelatingPropertyDefinition)
|
self.add_quantity_from_qto(relationship.RelatingPropertyDefinition)
|
||||||
|
|
||||||
def add_quantity_from_qto(self, qto):
|
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
|
return
|
||||||
for prop in qto.Quantities:
|
for prop in qto.Quantities:
|
||||||
if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == self.settings["prop_name"].lower():
|
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