mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 10:23:41 +00:00
Features to re-order cost items in a tree (changing parent & changing nesting order)
This commit is contained in:
@@ -70,10 +70,12 @@ classes = (
|
|||||||
operator.ClearCostItemAssignments,
|
operator.ClearCostItemAssignments,
|
||||||
operator.HighlightProductCostItem,
|
operator.HighlightProductCostItem,
|
||||||
operator.LoadProductCostItems,
|
operator.LoadProductCostItems,
|
||||||
|
operator.ReorderCostItem,
|
||||||
operator.SelectUnassignedProducts,
|
operator.SelectUnassignedProducts,
|
||||||
operator.LoadCostItemElementQuantities,
|
operator.LoadCostItemElementQuantities,
|
||||||
operator.LoadCostItemTaskQuantities,
|
operator.LoadCostItemTaskQuantities,
|
||||||
operator.LoadCostItemResourceQuantities,
|
operator.LoadCostItemResourceQuantities,
|
||||||
|
operator.ChangeParentCostItem,
|
||||||
prop.CostItem,
|
prop.CostItem,
|
||||||
prop.CostItemQuantity,
|
prop.CostItemQuantity,
|
||||||
prop.CostItemType,
|
prop.CostItemType,
|
||||||
|
|||||||
@@ -89,9 +89,16 @@ class CostSchedulesData:
|
|||||||
data = {}
|
data = {}
|
||||||
cls._load_cost_item_quantities(cost_item, data)
|
cls._load_cost_item_quantities(cost_item, data)
|
||||||
cls._load_cost_values(cost_item, data)
|
cls._load_cost_values(cost_item, data)
|
||||||
|
cls._load_nesting_index(cost_item, data)
|
||||||
results[cost_item.id()] = data
|
results[cost_item.id()] = data
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _load_nesting_index(cls, cost_item, data):
|
||||||
|
data["NestingIndex"] = None
|
||||||
|
for rel in cost_item.Nests or []:
|
||||||
|
data["NestingIndex"] = rel.RelatedObjects.index(cost_item)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _load_cost_values(cls, root_element, data):
|
def _load_cost_values(cls, root_element, data):
|
||||||
# data["CostValues"] = []
|
# data["CostValues"] = []
|
||||||
@@ -119,34 +126,51 @@ class CostSchedulesData:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _load_cost_item_quantities(cls, cost_item, data):
|
def _load_cost_item_quantities(cls, cost_item, data):
|
||||||
parametric_quantities = []
|
# parametric_quantities = []
|
||||||
for rel in cost_item.Controls:
|
# for rel in cost_item.Controls:
|
||||||
for related_object in rel.RelatedObjects or []:
|
# for related_object in rel.RelatedObjects or []:
|
||||||
quantities = cls._get_object_quantities(cost_item, related_object)
|
# quantities = cls._get_object_quantities(cost_item, related_object)
|
||||||
# data["Controls"][related_object.id()] = quantities
|
# parametric_quantities.extend(quantities)
|
||||||
parametric_quantities.extend(quantities)
|
|
||||||
|
|
||||||
# data["CostQuantities"] = []
|
|
||||||
data["TotalCostQuantity"] = ifcopenshell.util.cost.get_total_quantity(cost_item)
|
data["TotalCostQuantity"] = ifcopenshell.util.cost.get_total_quantity(cost_item)
|
||||||
for quantity in cost_item.CostQuantities or []:
|
data["UnitSymbol"] = "-"
|
||||||
if quantity.id() in parametric_quantities:
|
|
||||||
continue
|
|
||||||
# quantity_data = quantity.get_info()
|
|
||||||
# del quantity_data["Unit"]
|
|
||||||
# cls.physical_quantities[quantity.id()] = quantity_data
|
|
||||||
# data["CostQuantities"].append(quantity.id())
|
|
||||||
# data["Unit"] = None
|
|
||||||
data["UnitSymbol"] = "?"
|
|
||||||
if cost_item.CostQuantities:
|
if cost_item.CostQuantities:
|
||||||
quantity = cost_item.CostQuantities[0]
|
quantity = cost_item.CostQuantities[0]
|
||||||
unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get())
|
unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get())
|
||||||
if unit:
|
if unit:
|
||||||
# data["Unit"] = unit.id()
|
|
||||||
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
|
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
|
||||||
else:
|
else:
|
||||||
# data["Unit"] = None
|
|
||||||
data["UnitSymbol"] = None
|
data["UnitSymbol"] = None
|
||||||
|
|
||||||
|
# same_unit_nested_cost_item = set()
|
||||||
|
# data["DerivedTotalCostQuantity"] = None
|
||||||
|
# if cost_item.IsNestedBy:
|
||||||
|
# data["TotalCostQuantity"] = 0.0
|
||||||
|
# derived_quantity = 0
|
||||||
|
# derived_unit = None
|
||||||
|
# should_sum = False
|
||||||
|
# for rel in cost_item.IsNestedBy:
|
||||||
|
# for related_object in rel.RelatedObjects or []:
|
||||||
|
# for quantity in related_object.CostQuantities or []:
|
||||||
|
# nested_unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get())
|
||||||
|
# if derived_unit is None:
|
||||||
|
# derived_unit = nested_unit
|
||||||
|
# elif nested_unit and nested_unit.id() == derived_unit.id():
|
||||||
|
# same_unit_nested_cost_item.add(related_object)
|
||||||
|
# else:
|
||||||
|
# derived_unit = None
|
||||||
|
# break
|
||||||
|
# for cost in same_unit_nested_cost_item:
|
||||||
|
# qto = ifcopenshell.util.cost.get_total_quantity(cost)
|
||||||
|
# derived_quantity += qto
|
||||||
|
# print(derived_quantity)
|
||||||
|
# if derived_quantity not in [0, None]:
|
||||||
|
# data["DerivedTotalCostQuantity"] = derived_quantity
|
||||||
|
# if derived_unit:
|
||||||
|
# data["DerivedUnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(derived_unit)
|
||||||
|
# else:
|
||||||
|
# data["DerivedUnitSymbol"] = "?"
|
||||||
|
# print("Total Cost", data["DerivedTotalCostQuantity"], cost_item.Name)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_object_quantities(cls, cost_item, element):
|
def _get_object_quantities(cls, cost_item, element):
|
||||||
if not element.is_a("IfcObject"):
|
if not element.is_a("IfcObject"):
|
||||||
@@ -236,6 +260,7 @@ class CostSchedulesData:
|
|||||||
break
|
break
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
class CostItemRatesData:
|
class CostItemRatesData:
|
||||||
data = {}
|
data = {}
|
||||||
is_loaded = False
|
is_loaded = False
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import time
|
|||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import blenderbim.bim.helper
|
import blenderbim.bim.helper
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.ifc import IfcStore
|
|
||||||
from bpy_extras.io_utils import ImportHelper
|
from bpy_extras.io_utils import ImportHelper
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import blenderbim.core.cost as core
|
import blenderbim.core.cost as core
|
||||||
@@ -36,7 +35,7 @@ class AddCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_description = "Add a new cost schedule"
|
bl_description = "Add a new cost schedule"
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
core.add_cost_schedule(tool.Ifc, predefined_type = context.scene.BIMCostProperties.cost_schedule_predefined_types)
|
core.add_cost_schedule(tool.Ifc, predefined_type=context.scene.BIMCostProperties.cost_schedule_predefined_types)
|
||||||
|
|
||||||
|
|
||||||
class EditCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
class EditCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
@@ -685,6 +684,25 @@ class HighlightProductCostItem(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class ReorderCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.reorder_cost_item_nesting"
|
||||||
|
bl_label = "Reorder Nesting"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
new_index: bpy.props.IntProperty()
|
||||||
|
cost_item: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"nest.reorder_nesting",
|
||||||
|
tool.Ifc.get(),
|
||||||
|
**{
|
||||||
|
"item": tool.Ifc.get().by_id(self.cost_item),
|
||||||
|
"new_index": self.new_index,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
tool.Cost.load_cost_schedule_tree()
|
||||||
|
|
||||||
|
|
||||||
class SelectUnassignedProducts(bpy.types.Operator):
|
class SelectUnassignedProducts(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_unassigned_products"
|
bl_idname = "bim.select_unassigned_products"
|
||||||
bl_label = "Select Unassigned Products"
|
bl_label = "Select Unassigned Products"
|
||||||
@@ -693,3 +711,16 @@ class SelectUnassignedProducts(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
core.select_unassigned_products(tool.Ifc, tool.Cost, tool.Spatial)
|
core.select_unassigned_products(tool.Ifc, tool.Cost, tool.Spatial)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeParentCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.change_parent_cost_item"
|
||||||
|
bl_label = "Change Parent Cost Item"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
new_parent: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
r = core.change_parent_cost_item(tool.Ifc, tool.Cost, new_parent=tool.Ifc.get().by_id(self.new_parent))
|
||||||
|
if isinstance(r, str):
|
||||||
|
self.report({"WARNING"}, r)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -134,6 +134,12 @@ class CostItemType(PropertyGroup):
|
|||||||
name: StringProperty(name="Name")
|
name: StringProperty(name="Name")
|
||||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||||
|
|
||||||
|
|
||||||
|
def update_cost_item_parent(self, context):
|
||||||
|
cost_item = tool.Cost.get_highlighted_cost_item()
|
||||||
|
tool.Cost.toggle_cost_item_parent(cost_item=cost_item)
|
||||||
|
|
||||||
|
|
||||||
def update_active_cost_item_elements(self, context):
|
def update_active_cost_item_elements(self, context):
|
||||||
bpy.ops.bim.load_cost_item_element_quantities()
|
bpy.ops.bim.load_cost_item_element_quantities()
|
||||||
|
|
||||||
@@ -200,8 +206,10 @@ class BIMCostProperties(PropertyGroup):
|
|||||||
contracted_cost_item_rates: StringProperty(name="Contracted Cost Item Rates", default="[]")
|
contracted_cost_item_rates: StringProperty(name="Contracted Cost Item Rates", default="[]")
|
||||||
product_cost_items: CollectionProperty(name="Product Cost Items", type=CostItem)
|
product_cost_items: CollectionProperty(name="Product Cost Items", type=CostItem)
|
||||||
active_product_cost_item_index: IntProperty(name="Active Product Cost Item Index")
|
active_product_cost_item_index: IntProperty(name="Active Product Cost Item Index")
|
||||||
|
enable_reorder: BoolProperty(name="Enable Reorder", default=False)
|
||||||
show_nested_elements: BoolProperty(name="Show Nested Tasks", default=False, update=update_active_cost_item_elements)
|
show_nested_elements: BoolProperty(name="Show Nested Tasks", default=False, update=update_active_cost_item_elements)
|
||||||
show_nested_tasks: BoolProperty(name="Show Nested Tasks", default=False, update=update_active_cost_item_tasks)
|
show_nested_tasks: BoolProperty(name="Show Nested Tasks", default=False, update=update_active_cost_item_tasks)
|
||||||
show_nested_resources: BoolProperty(
|
show_nested_resources: BoolProperty(
|
||||||
name="Show Nested Tasks", default=False, update=update_active_cost_item_resources
|
name="Show Nested Tasks", default=False, update=update_active_cost_item_resources
|
||||||
)
|
)
|
||||||
|
change_cost_item_parent: BoolProperty(name="Change Cost Item Parent", default=False, update=update_cost_item_parent)
|
||||||
|
|||||||
@@ -105,7 +105,8 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
if self.props.cost_items and self.props.active_cost_item_index < len(self.props.cost_items):
|
if self.props.cost_items and self.props.active_cost_item_index < len(self.props.cost_items):
|
||||||
ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
|
ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
|
||||||
if ifc_definition_id:
|
if ifc_definition_id:
|
||||||
|
row.prop(self.props, "change_cost_item_parent", text="", icon="LINKED")
|
||||||
|
row.prop(self.props, "enable_reorder", text="", icon="SORTALPHA")
|
||||||
if not CostSchedulesData.data["is_editing_rates"]:
|
if not CostSchedulesData.data["is_editing_rates"]:
|
||||||
op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES")
|
op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES")
|
||||||
op.cost_item = ifc_definition_id
|
op.cost_item = ifc_definition_id
|
||||||
@@ -549,8 +550,23 @@ class BIM_UL_cost_items_trait:
|
|||||||
split2.label(text=str(cost_item["CategoryValues"].get(column.name, "-")))
|
split2.label(text=str(cost_item["CategoryValues"].get(column.name, "-")))
|
||||||
|
|
||||||
self.draw_total_cost_column(split2, cost_item)
|
self.draw_total_cost_column(split2, cost_item)
|
||||||
|
if self.props.enable_reorder:
|
||||||
|
self.draw_order_operator(row, item.ifc_definition_id, cost_item)
|
||||||
|
|
||||||
|
if self.props.change_cost_item_parent:
|
||||||
|
self.draw_parent_operator(row, item.ifc_definition_id)
|
||||||
|
|
||||||
# TODO: reimplement "bim.copy_cost_item_values" somewhere with better UX
|
# TODO: reimplement "bim.copy_cost_item_values" somewhere with better UX
|
||||||
|
|
||||||
|
def draw_parent_operator(self, row, cost_item_id):
|
||||||
|
if self.props.active_cost_item_id:
|
||||||
|
if self.props.active_cost_item_id != cost_item_id:
|
||||||
|
op = row.operator(
|
||||||
|
"bim.change_parent_cost_item", text="", icon="LINKED", emboss=False
|
||||||
|
).new_parent = cost_item_id
|
||||||
|
else:
|
||||||
|
row.label(text="", icon="BLANK1")
|
||||||
|
|
||||||
def draw_hierarchy(self, row, item):
|
def draw_hierarchy(self, row, item):
|
||||||
for i in range(0, item.level_index):
|
for i in range(0, item.level_index):
|
||||||
row.label(text="", icon="BLANK1")
|
row.label(text="", icon="BLANK1")
|
||||||
@@ -581,6 +597,17 @@ class BIM_UL_cost_items_trait:
|
|||||||
def draw_uom_column(self, layout, cost_item):
|
def draw_uom_column(self, layout, cost_item):
|
||||||
layout.label(text=cost_item["UnitBasisUnitSymbol"] or "?" if cost_item["UnitBasisValueComponent"] else "-")
|
layout.label(text=cost_item["UnitBasisUnitSymbol"] or "?" if cost_item["UnitBasisValueComponent"] else "-")
|
||||||
|
|
||||||
|
def draw_order_operator(self, row, ifc_definition_id, cost_item):
|
||||||
|
if cost_item["NestingIndex"] is not None:
|
||||||
|
if cost_item["NestingIndex"] == 0:
|
||||||
|
op = row.operator("bim.reorder_cost_item_nesting", icon="TRIA_DOWN", text="")
|
||||||
|
op.cost_item = ifc_definition_id
|
||||||
|
op.new_index = cost_item["NestingIndex"] + 1
|
||||||
|
elif cost_item["NestingIndex"] > 0:
|
||||||
|
op = row.operator("bim.reorder_cost_item_nesting", icon="TRIA_UP", text="")
|
||||||
|
op.cost_item = ifc_definition_id
|
||||||
|
op.new_index = cost_item["NestingIndex"] - 1
|
||||||
|
|
||||||
def draw_total_quantity_column(self, layout, cost_item):
|
def draw_total_quantity_column(self, layout, cost_item):
|
||||||
layout.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" {cost_item['UnitSymbol'] or '?'}")
|
layout.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" {cost_item['UnitSymbol'] or '?'}")
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ def remove_cost_schedule(ifc, cost_schedule):
|
|||||||
def enable_editing_cost_schedule_attributes(cost, cost_schedule):
|
def enable_editing_cost_schedule_attributes(cost, cost_schedule):
|
||||||
cost.load_cost_schedule_attributes(cost_schedule)
|
cost.load_cost_schedule_attributes(cost_schedule)
|
||||||
cost.enable_editing_cost_schedule_attributes(cost_schedule)
|
cost.enable_editing_cost_schedule_attributes(cost_schedule)
|
||||||
cost.play_sound()
|
|
||||||
|
|
||||||
def enable_editing_cost_items(cost, cost_schedule):
|
def enable_editing_cost_items(cost, cost_schedule):
|
||||||
cost.enable_editing_cost_items(cost_schedule)
|
cost.enable_editing_cost_items(cost_schedule)
|
||||||
@@ -258,3 +257,15 @@ def highlight_product_cost_item(spatial, cost, cost_item):
|
|||||||
cost.highlight_cost_item(cost_item)
|
cost.highlight_cost_item(cost_item)
|
||||||
else:
|
else:
|
||||||
return "Cost schedule is not active"
|
return "Cost schedule is not active"
|
||||||
|
|
||||||
|
def toggle_cost_item_parent(cost, cost_item):
|
||||||
|
cost.toggle_cost_item_parent(cost_item)
|
||||||
|
|
||||||
|
def change_parent_cost_item(ifc, cost, new_parent):
|
||||||
|
cost_item = cost.get_active_cost_item()
|
||||||
|
if cost_item and cost.is_root_cost_item(cost_item):
|
||||||
|
return "Cannot change root cost item"
|
||||||
|
if cost_item :
|
||||||
|
ifc.run("nest.change_nest", item=cost_item, new_parent=new_parent)
|
||||||
|
cost.disable_editing_cost_item_parent()
|
||||||
|
cost.load_cost_schedule_tree()
|
||||||
@@ -139,6 +139,7 @@ class Cost(blenderbim.core.tool.Cost):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def disable_editing_cost_item(cls):
|
def disable_editing_cost_item(cls):
|
||||||
bpy.context.scene.BIMCostProperties.active_cost_item_id = 0
|
bpy.context.scene.BIMCostProperties.active_cost_item_id = 0
|
||||||
|
bpy.context.scene.BIMCostProperties.change_cost_item_parent = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_cost_item_attributes(cls):
|
def get_cost_item_attributes(cls):
|
||||||
@@ -612,3 +613,20 @@ class Cost(blenderbim.core.tool.Cost):
|
|||||||
if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcCostSchedule"):
|
if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcCostSchedule"):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def toggle_cost_item_parent(cls, cost_item=None):
|
||||||
|
props = bpy.context.scene.BIMCostProperties
|
||||||
|
if props.change_cost_item_parent:
|
||||||
|
props.active_cost_item_id = cost_item.id()
|
||||||
|
props.cost_item_editing_type = "PARENT"
|
||||||
|
else:
|
||||||
|
cls.disable_editing_cost_item_parent()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def change_parent_cost_item(cls, cost_item, new_parent):
|
||||||
|
ifcopenshell.api.run("nest.change_nest", tool.Ifc.get(), item=cost_item, new_parent=new_parent)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def disable_editing_cost_item_parent(cls):
|
||||||
|
bpy.context.scene.BIMCostProperties.active_cost_item_id = 0
|
||||||
|
bpy.context.scene.BIMCostProperties.change_cost_item_parent = False
|
||||||
|
|||||||
Reference in New Issue
Block a user