mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-27 10:51:13 +00:00
You can now edit nested arithmetic cost values by using spreadsheet-like formulas
This commit is contained in:
@@ -27,6 +27,7 @@ classes = (
|
|||||||
operator.EditCostItem,
|
operator.EditCostItem,
|
||||||
operator.EditCostItemQuantity,
|
operator.EditCostItemQuantity,
|
||||||
operator.EditCostValue,
|
operator.EditCostValue,
|
||||||
|
operator.EditCostValueFormula,
|
||||||
operator.EnableEditingCostSchedule,
|
operator.EnableEditingCostSchedule,
|
||||||
operator.EnableEditingCostItems,
|
operator.EnableEditingCostItems,
|
||||||
operator.EnableEditingCostItem,
|
operator.EnableEditingCostItem,
|
||||||
@@ -34,6 +35,7 @@ classes = (
|
|||||||
operator.EnableEditingCostItemQuantity,
|
operator.EnableEditingCostItemQuantity,
|
||||||
operator.EnableEditingCostItemValues,
|
operator.EnableEditingCostItemValues,
|
||||||
operator.EnableEditingCostItemValue,
|
operator.EnableEditingCostItemValue,
|
||||||
|
operator.EnableEditingCostItemValueFormula,
|
||||||
operator.DisableEditingCostItem,
|
operator.DisableEditingCostItem,
|
||||||
operator.DisableEditingCostSchedule,
|
operator.DisableEditingCostSchedule,
|
||||||
operator.DisableEditingCostItemQuantity,
|
operator.DisableEditingCostItemQuantity,
|
||||||
|
|||||||
@@ -637,6 +637,7 @@ class EnableEditingCostItemValue(bpy.types.Operator):
|
|||||||
self.props = context.scene.BIMCostProperties
|
self.props = context.scene.BIMCostProperties
|
||||||
self.props.cost_value_attributes.clear()
|
self.props.cost_value_attributes.clear()
|
||||||
self.props.active_cost_item_value_id = self.cost_value
|
self.props.active_cost_item_value_id = self.cost_value
|
||||||
|
self.props.cost_value_editing_type = "ATTRIBUTES"
|
||||||
data = Data.cost_values[self.cost_value]
|
data = Data.cost_values[self.cost_value]
|
||||||
|
|
||||||
blenderbim.bim.helper.import_attributes(
|
blenderbim.bim.helper.import_attributes(
|
||||||
@@ -706,12 +707,50 @@ class DisableEditingCostItemValue(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = context.scene.BIMCostProperties
|
props = context.scene.BIMCostProperties
|
||||||
props.active_cost_item_value_id = 0
|
props.active_cost_item_value_id = 0
|
||||||
|
props.cost_value_editing_type = ""
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class EnableEditingCostItemValueFormula(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.enable_editing_cost_item_value_formula"
|
||||||
|
bl_label = "Enable Editing Cost Item Value Formula"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
cost_value: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.props = context.scene.BIMCostProperties
|
||||||
|
self.props.cost_value_attributes.clear()
|
||||||
|
self.props.active_cost_item_value_id = self.cost_value
|
||||||
|
self.props.cost_value_editing_type = "FORMULA"
|
||||||
|
self.props.cost_value_formula = Data.cost_values[self.cost_value]["Formula"]
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class EditCostValueFormula(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.edit_cost_value_formula"
|
||||||
|
bl_label = "Edit Cost Value Formula"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
cost_value: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
props = context.scene.BIMCostProperties
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"cost.edit_cost_value_formula",
|
||||||
|
self.file,
|
||||||
|
**{"cost_value": self.file.by_id(self.cost_value), "formula": props.cost_value_formula},
|
||||||
|
)
|
||||||
|
Data.load(IfcStore.get_file())
|
||||||
|
bpy.ops.bim.disable_editing_cost_item_value()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class EditCostValue(bpy.types.Operator):
|
class EditCostValue(bpy.types.Operator):
|
||||||
bl_idname = "bim.edit_cost_value"
|
bl_idname = "bim.edit_cost_value"
|
||||||
bl_label = "Edit Cost Item Value"
|
bl_label = "Edit Cost Value"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
cost_value: bpy.props.IntProperty()
|
cost_value: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
|||||||
@@ -245,7 +245,9 @@ class BIMCostProperties(PropertyGroup):
|
|||||||
)
|
)
|
||||||
cost_category: StringProperty(name="Cost Category")
|
cost_category: StringProperty(name="Cost Category")
|
||||||
active_cost_item_value_id: IntProperty(name="Active Cost Item Value Id")
|
active_cost_item_value_id: IntProperty(name="Active Cost Item Value Id")
|
||||||
|
cost_value_editing_type: StringProperty(name="Cost Value Editing Type")
|
||||||
cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute)
|
cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute)
|
||||||
|
cost_value_formula: StringProperty(name="Cost Value Formula")
|
||||||
cost_column: StringProperty(name="Cost Column")
|
cost_column: StringProperty(name="Cost Column")
|
||||||
should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False)
|
should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False)
|
||||||
columns: CollectionProperty(name="Columns", type=StrProperty)
|
columns: CollectionProperty(name="Columns", type=StrProperty)
|
||||||
|
|||||||
@@ -190,65 +190,42 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
|
|
||||||
for cost_value_id in Data.cost_items[self.props.active_cost_item_id]["CostValues"]:
|
for cost_value_id in Data.cost_items[self.props.active_cost_item_id]["CostValues"]:
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.label(text=Data.cost_values[cost_value_id]["Formula"])
|
|
||||||
self.draw_readonly_cost_value_ui(row, cost_value_id)
|
self.draw_readonly_cost_value_ui(row, cost_value_id)
|
||||||
|
|
||||||
if self.props.active_cost_item_value_id:
|
if self.props.cost_value_editing_type == "ATTRIBUTES":
|
||||||
box = self.layout.box()
|
box = self.layout.box()
|
||||||
self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_item_value_id])
|
self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_item_value_id])
|
||||||
|
|
||||||
def draw_readonly_cost_value_ui(self, layout, cost_value_id):
|
def draw_readonly_cost_value_ui(self, layout, cost_value_id):
|
||||||
# This UI is really poor. Delete and start again.
|
|
||||||
cost_value = Data.cost_values[cost_value_id]
|
cost_value = Data.cost_values[cost_value_id]
|
||||||
cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"])
|
|
||||||
if cost_value["Category"]:
|
if self.props.active_cost_item_value_id == cost_value_id and self.props.cost_value_editing_type == "FORMULA":
|
||||||
cost_value_label += " ({})".format(cost_value["Category"])
|
layout.prop(self.props, "cost_value_formula", text="")
|
||||||
layout.label(text="", icon="DISC")
|
else:
|
||||||
|
cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"])
|
||||||
|
cost_value_label += " = " + cost_value["Formula"]
|
||||||
|
layout.label(text=cost_value_label, icon="DISC")
|
||||||
|
|
||||||
self.draw_cost_value_operator_ui(layout, cost_value_id, self.props.active_cost_item_id)
|
self.draw_cost_value_operator_ui(layout, cost_value_id, self.props.active_cost_item_id)
|
||||||
layout.label(text=cost_value_label)
|
|
||||||
|
|
||||||
for component_id in cost_value["Components"] or []:
|
|
||||||
self.draw_readonly_component_cost_value_ui(layout, component_id, cost_value["id"])
|
|
||||||
|
|
||||||
def draw_readonly_component_cost_value_ui(self, layout, cost_value_id, parent_id, level=1):
|
|
||||||
self.draw_cost_value_operator_ui(layout, cost_value_id, parent_id)
|
|
||||||
cost_value = Data.cost_values[cost_value_id]
|
|
||||||
cost_value_label = ">" * level
|
|
||||||
cost_value_label += "{0:.2f}".format(cost_value["AppliedValue"])
|
|
||||||
if cost_value["Category"]:
|
|
||||||
cost_value_label += " ({})".format(cost_value["Category"])
|
|
||||||
layout.label(text=cost_value_label)
|
|
||||||
|
|
||||||
for component_id in cost_value["Components"] or []:
|
|
||||||
self.draw_readonly_component_cost_value_ui(layout, component_id, cost_value["id"], level + 1)
|
|
||||||
|
|
||||||
def draw_cost_value_operator_ui(self, layout, cost_value_id, parent_id):
|
def draw_cost_value_operator_ui(self, layout, cost_value_id, parent_id):
|
||||||
if self.props.active_cost_item_value_id and self.props.active_cost_item_value_id == cost_value_id:
|
if self.props.active_cost_item_value_id and self.props.active_cost_item_value_id == cost_value_id:
|
||||||
op = layout.operator("bim.edit_cost_value", text="", icon="CHECKMARK")
|
if self.props.cost_value_editing_type == "ATTRIBUTES":
|
||||||
op.cost_value = cost_value_id
|
op = layout.operator("bim.edit_cost_value", text="", icon="CHECKMARK")
|
||||||
op = layout.operator("bim.add_cost_value", text="", icon="ADD")
|
op.cost_value = cost_value_id
|
||||||
op.parent = cost_value_id
|
elif self.props.cost_value_editing_type == "FORMULA":
|
||||||
op.cost_type = self.props.cost_types
|
op = layout.operator("bim.edit_cost_value_formula", text="", icon="CHECKMARK")
|
||||||
if self.props.cost_types == "CATEGORY":
|
op.cost_value = cost_value_id
|
||||||
op.cost_category = self.props.cost_category
|
|
||||||
layout.operator("bim.disable_editing_cost_item_value", text="", icon="CANCEL")
|
layout.operator("bim.disable_editing_cost_item_value", text="", icon="CANCEL")
|
||||||
elif self.props.active_cost_item_value_id:
|
elif self.props.active_cost_item_value_id:
|
||||||
op = layout.operator("bim.add_cost_value", text="", icon="ADD")
|
|
||||||
op.parent = cost_value_id
|
|
||||||
op.cost_type = self.props.cost_types
|
|
||||||
if self.props.cost_types == "CATEGORY":
|
|
||||||
op.cost_category = self.props.cost_category
|
|
||||||
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
|
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
|
||||||
op.parent = parent_id
|
op.parent = parent_id
|
||||||
op.cost_value = cost_value_id
|
op.cost_value = cost_value_id
|
||||||
else:
|
else:
|
||||||
|
op = layout.operator("bim.enable_editing_cost_item_value_formula", text="", icon="CON_TRANSLIKE")
|
||||||
|
op.cost_value = cost_value_id
|
||||||
op = layout.operator("bim.enable_editing_cost_item_value", text="", icon="GREASEPENCIL")
|
op = layout.operator("bim.enable_editing_cost_item_value", text="", icon="GREASEPENCIL")
|
||||||
op.cost_value = cost_value_id
|
op.cost_value = cost_value_id
|
||||||
op = layout.operator("bim.add_cost_value", text="", icon="ADD")
|
|
||||||
op.parent = cost_value_id
|
|
||||||
op.cost_type = self.props.cost_types
|
|
||||||
if self.props.cost_types == "CATEGORY":
|
|
||||||
op.cost_category = self.props.cost_category
|
|
||||||
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
|
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
|
||||||
op.parent = parent_id
|
op.parent = parent_id
|
||||||
op.cost_value = cost_value_id
|
op.cost_value = cost_value_id
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.cost
|
||||||
|
import ifcopenshell.util.unit
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, **settings):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {"cost_value": None, "formula": {}}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
try:
|
||||||
|
data = ifcopenshell.util.cost.unserialise_cost_value(self.settings["formula"], self.settings["cost_value"])
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
self.edit_cost_value(data)
|
||||||
|
|
||||||
|
def edit_cost_value(self, data, parent=None):
|
||||||
|
ifc = data.get("ifc", None)
|
||||||
|
if not ifc:
|
||||||
|
ifc = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=parent)
|
||||||
|
if "AppliedValue" in data:
|
||||||
|
if data["AppliedValue"]:
|
||||||
|
ifc.AppliedValue = self.file.createIfcMonetaryMeasure(data["AppliedValue"])
|
||||||
|
else:
|
||||||
|
ifc.AppliedValue = None
|
||||||
|
ifc.Category = data["Category"] if "Category" in data else None
|
||||||
|
ifc.ArithmeticOperator = data["ArithmeticOperator"] if "ArithmeticOperator" in data else None
|
||||||
|
if "Components" in data:
|
||||||
|
for component in data["Components"]:
|
||||||
|
self.edit_cost_value(component, ifc)
|
||||||
@@ -52,7 +52,7 @@ def unserialise_cost_value(formula, cost_value):
|
|||||||
result["ifc"] = element
|
result["ifc"] = element
|
||||||
for i, component in enumerate(result.get("Components", [])):
|
for i, component in enumerate(result.get("Components", [])):
|
||||||
if element.Components and i < len(element.Components):
|
if element.Components and i < len(element.Components):
|
||||||
map_element_to_formula(element.Components[i], result["Components"][i])
|
map_element_to_result(element.Components[i], result["Components"][i])
|
||||||
map_element_to_result(cost_value, result)
|
map_element_to_result(cost_value, result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -116,11 +116,14 @@ class CostValueUnserialiser:
|
|||||||
def get_operand(self, operand):
|
def get_operand(self, operand):
|
||||||
child = operand.children[0]
|
child = operand.children[0]
|
||||||
if child.data == "value":
|
if child.data == "value":
|
||||||
return {"AppliedValue": self.get_value(child)}
|
value = self.get_value(child)
|
||||||
|
return {"AppliedValue": float(value) if value else None}
|
||||||
elif child.data == "category":
|
elif child.data == "category":
|
||||||
data = {}
|
data = {}
|
||||||
category = self.get_category(child)
|
category = self.get_category(child)
|
||||||
if category:
|
if category:
|
||||||
|
if category.lower() == "sum":
|
||||||
|
category = "*"
|
||||||
data["Category"] = category
|
data["Category"] = category
|
||||||
formula = self.get_formula(operand.children[1])
|
formula = self.get_formula(operand.children[1])
|
||||||
if formula.get("Components"):
|
if formula.get("Components"):
|
||||||
|
|||||||
Reference in New Issue
Block a user