diff --git a/src/bonsai/bonsai/bim/helper.py b/src/bonsai/bonsai/bim/helper.py
index 147eb424b2..12c5ef259b 100644
--- a/src/bonsai/bonsai/bim/helper.py
+++ b/src/bonsai/bonsai/bim/helper.py
@@ -76,8 +76,8 @@ def draw_attribute(
elif value_name == "filepath_value":
attribute.filepath_value.layout_file_select(layout, filter_glob=attribute.filter_glob, text=attribute.name)
elif attribute.name in ("ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"):
- propis = bpy.context.scene.BIMWorkScheduleProperties
- for item in propis.durations_attributes:
+ props = tool.Sequence.get_work_schedule_props()
+ for item in props.durations_attributes:
if item.name == attribute.name:
duration_props = item
layout.label(text=attribute.name)
diff --git a/src/bonsai/bonsai/bim/module/classification/data.py b/src/bonsai/bonsai/bim/module/classification/data.py
index e4c5e30ca8..35bf6900ec 100644
--- a/src/bonsai/bonsai/bim/module/classification/data.py
+++ b/src/bonsai/bonsai/bim/module/classification/data.py
@@ -145,11 +145,8 @@ class CostClassificationsData(ReferencesData):
@classmethod
def references(cls):
results = []
- element = tool.Ifc.get().by_id(
- bpy.context.scene.BIMCostProperties.cost_items[
- bpy.context.scene.BIMCostProperties.active_cost_item_index
- ].ifc_definition_id
- )
+ props = tool.Cost.get_cost_props()
+ element = tool.Ifc.get().by_id(props.cost_items[props.active_cost_item_index].ifc_definition_id)
if element:
for reference in ifcopenshell.util.classification.get_references(element):
data = reference.get_info()
diff --git a/src/bonsai/bonsai/bim/module/classification/ui.py b/src/bonsai/bonsai/bim/module/classification/ui.py
index c171ff3cd6..4b9081c3a8 100644
--- a/src/bonsai/bonsai/bim/module/classification/ui.py
+++ b/src/bonsai/bonsai/bim/module/classification/ui.py
@@ -338,7 +338,8 @@ class BIM_PT_cost_classifications(Panel, ReferenceUI):
def poll(cls, context):
if not tool.Ifc.get():
return False
- return bool(context.scene.BIMCostProperties.cost_items)
+ props = tool.Cost.get_cost_props()
+ return bool(props.cost_items)
def draw(self, context):
if not CostClassificationsData.is_loaded:
diff --git a/src/bonsai/bonsai/bim/module/cost/data.py b/src/bonsai/bonsai/bim/module/cost/data.py
index cb4dc26a71..f1cb4d93c0 100644
--- a/src/bonsai/bonsai/bim/module/cost/data.py
+++ b/src/bonsai/bonsai/bim/module/cost/data.py
@@ -66,8 +66,9 @@ class CostSchedulesData:
@classmethod
def schedules(cls):
results = []
- if bpy.context.scene.BIMCostProperties.active_cost_schedule_id:
- schedule = tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id)
+ props = tool.Cost.get_cost_props()
+ if props.active_cost_schedule_id:
+ schedule = tool.Ifc.get().by_id(props.active_cost_schedule_id)
results.append(
{
"id": schedule.id(),
@@ -88,7 +89,8 @@ class CostSchedulesData:
@classmethod
def is_editing_rates(cls):
- ifc_id = bpy.context.scene.BIMCostProperties.active_cost_schedule_id
+ props = tool.Cost.get_cost_props()
+ ifc_id = props.active_cost_schedule_id
if not ifc_id:
return
return tool.Ifc.get().by_id(ifc_id).PredefinedType == "SCHEDULEOFRATES"
@@ -256,7 +258,8 @@ class CostSchedulesData:
@classmethod
def cost_quantities(cls):
results = []
- ifc_id = bpy.context.scene.BIMCostProperties.active_cost_item_id
+ props = tool.Cost.get_cost_props()
+ ifc_id = props.active_cost_item_id
if not ifc_id:
return results
for quantity in tool.Ifc.get().by_id(ifc_id).CostQuantities or []:
@@ -265,7 +268,8 @@ class CostSchedulesData:
@classmethod
def cost_values(cls):
- ifc_id = bpy.context.scene.BIMCostProperties.active_cost_item_id
+ props = tool.Cost.get_cost_props()
+ ifc_id = props.active_cost_item_id
if not ifc_id:
return []
return ifcopenshell.util.cost.get_cost_values(tool.Ifc.get().by_id(ifc_id))
@@ -324,7 +328,8 @@ class CostItemQuantitiesData:
@classmethod
def process_quantity_names(cls):
- active_task_index = bpy.context.scene.BIMWorkScheduleProperties.active_task_index
+ props = tool.Sequence.get_work_schedule_props()
+ active_task_index = props.active_task_index
tprops = tool.Sequence.get_task_tree_props()
total_tasks = len(tprops.tasks)
if not total_tasks or active_task_index >= total_tasks:
diff --git a/src/bonsai/bonsai/bim/module/cost/operator.py b/src/bonsai/bonsai/bim/module/cost/operator.py
index 3cbe9b4d68..79338d5ede 100644
--- a/src/bonsai/bonsai/bim/module/cost/operator.py
+++ b/src/bonsai/bonsai/bim/module/cost/operator.py
@@ -36,16 +36,18 @@ class AddCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
object_type: bpy.props.StringProperty()
def _execute(self, context):
- predefined_type = context.scene.BIMCostProperties.cost_schedule_predefined_types
+ props = tool.Cost.get_cost_props()
+ predefined_type = props.cost_schedule_predefined_types
if predefined_type == "USERDEFINED":
predefined_type = self.object_type
core.add_cost_schedule(tool.Ifc, name=self.name, predefined_type=predefined_type)
def draw(self, context):
layout = self.layout
+ props = tool.Cost.get_cost_props()
layout.prop(self, "name", text="Name")
- layout.prop(context.scene.BIMCostProperties, "cost_schedule_predefined_types", text="Type")
- if context.scene.BIMCostProperties.cost_schedule_predefined_types == "USERDEFINED":
+ layout.prop(props, "cost_schedule_predefined_types", text="Type")
+ if props.cost_schedule_predefined_types == "USERDEFINED":
layout.prop(self, "object_type", text="Object type")
def invoke(self, context, event):
@@ -58,10 +60,11 @@ class EditCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
+ props = tool.Cost.get_cost_props()
core.edit_cost_schedule(
tool.Ifc,
tool.Cost,
- cost_schedule=tool.Ifc.get().by_id(context.scene.BIMCostProperties.active_cost_schedule_id),
+ cost_schedule=tool.Ifc.get().by_id(props.active_cost_schedule_id),
)
@@ -559,7 +562,8 @@ class AddCostColumn(bpy.types.Operator):
@classmethod
def poll(cls, context):
- if not context.scene.BIMCostProperties.cost_column:
+ props = tool.Cost.get_cost_props()
+ if not props.cost_column:
cls.poll_message_set("Cost column name is empty")
return False
return True
diff --git a/src/bonsai/bonsai/bim/module/cost/prop.py b/src/bonsai/bonsai/bim/module/cost/prop.py
index abe035fdb6..1ce39c6f1a 100644
--- a/src/bonsai/bonsai/bim/module/cost/prop.py
+++ b/src/bonsai/bonsai/bim/module/cost/prop.py
@@ -33,6 +33,7 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
+from typing import TYPE_CHECKING, Literal
def get_schedule_of_rates(self, context):
@@ -78,8 +79,8 @@ def update_active_cost_item_index(self, context):
CostClassificationsData.load()
-def update_cost_item_identification(self, context):
- props = context.scene.BIMCostProperties
+def update_cost_item_identification(self: "CostItem", context: bpy.types.Context):
+ props = tool.Cost.get_cost_props()
if not props.is_cost_update_enabled or self.identification == "XXX":
return
self.file = tool.Ifc.get()
@@ -93,8 +94,8 @@ def update_cost_item_identification(self, context):
attribute.string_value = self.identification
-def update_cost_item_name(self, context):
- props = context.scene.BIMCostProperties
+def update_cost_item_name(self: "CostItem", context: bpy.types.Context) -> None:
+ props = tool.Cost.get_cost_props()
if not props.is_cost_update_enabled or self.name == "Unnamed":
return
self.file = tool.Ifc.get()
@@ -142,6 +143,14 @@ class CostItem(PropertyGroup):
is_expanded: BoolProperty(name="Is Expanded")
level_index: IntProperty(name="Level Index")
+ if TYPE_CHECKING:
+ name: str
+ identification: str
+ ifc_definition_id: int
+ has_children: bool
+ is_expanded: bool
+ level_index: int
+
class CostItemQuantity(PropertyGroup):
name: StringProperty(name="Name")
@@ -150,11 +159,22 @@ class CostItemQuantity(PropertyGroup):
unit_symbol: StringProperty(name="Unit Symbol")
total_cost_quantity: FloatProperty(name="Total Quantity")
+ if TYPE_CHECKING:
+ name: str
+ ifc_definition_id: int
+ total_quantity: float
+ unit_symbol: str
+ total_cost_quantity: float
+
class CostItemType(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
+ if TYPE_CHECKING:
+ name: str
+ ifc_definition_id: int
+
def update_cost_item_parent(self, context):
cost_item = tool.Cost.get_highlighted_cost_item()
@@ -176,6 +196,9 @@ def update_active_cost_item_resources(self, context):
class ScheduleColumn(PropertyGroup):
schedule_id: IntProperty()
+ if TYPE_CHECKING:
+ schedule_id: int
+
class BIMCostProperties(PropertyGroup):
cost_schedule_predefined_types: EnumProperty(
@@ -249,3 +272,57 @@ class BIMCostProperties(PropertyGroup):
custom_currency: StringProperty(
name="Custom Currency", default="USD", description="Custom Currency in ISO 4217 format"
)
+
+ if TYPE_CHECKING:
+ cost_schedule_predefined_types: str
+ is_cost_update_enabled: bool
+ cost_schedule_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ is_editing: str
+ active_cost_schedule_id: int
+ cost_items: bpy.types.bpy_prop_collection_idprop[CostItem]
+ active_cost_item_id: int
+ cost_item_editing_type: str
+ active_cost_item_index: int
+ cost_item_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ contracted_cost_items: str
+ quantity_types: str
+ product_quantity_names: str
+ process_quantity_names: str
+ resource_quantity_names: str
+ active_cost_item_quantity_id: int
+ quantity_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ cost_types: Literal["FIXED", "SUM", "CATEGORY"]
+ cost_category: str
+ fixed_cost_value: float
+ active_cost_value_id: int
+ cost_value_editing_type: str
+ cost_value_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ cost_value_formula: str
+ cost_column: str
+ should_show_column_ui: bool
+ should_show_currency_ui: bool
+ columns: bpy.types.bpy_prop_collection_idprop[StrProperty]
+ columns_storage: bpy.types.bpy_prop_collection_idprop[ScheduleColumn]
+ active_column_index: int
+ cost_item_products: bpy.types.bpy_prop_collection_idprop[CostItemQuantity]
+ active_cost_item_product_index: int
+ cost_item_processes: bpy.types.bpy_prop_collection_idprop[CostItemQuantity]
+ active_cost_item_process_index: int
+ cost_item_resources: bpy.types.bpy_prop_collection_idprop[CostItemQuantity]
+ active_cost_item_resource_index: int
+ cost_item_type_products: bpy.types.bpy_prop_collection_idprop[CostItemType]
+ active_cost_item_type_product_index: int
+ schedule_of_rates: str
+ cost_item_rates: bpy.types.bpy_prop_collection_idprop[CostItem]
+ active_cost_item_rate_index: int
+ contracted_cost_item_rates: str
+ product_cost_items: bpy.types.bpy_prop_collection_idprop[CostItemQuantity]
+ active_product_cost_item_index: int
+ enable_reorder: bool
+ show_nested_elements: bool
+ show_nested_tasks: bool
+ show_nested_resources: bool
+ change_cost_item_parent: bool
+ show_cost_item_operators: bool
+ currency: str
+ custom_currency: str
diff --git a/src/bonsai/bonsai/bim/module/cost/ui.py b/src/bonsai/bonsai/bim/module/cost/ui.py
index e8df647071..6897e636cc 100644
--- a/src/bonsai/bonsai/bim/module/cost/ui.py
+++ b/src/bonsai/bonsai/bim/module/cost/ui.py
@@ -16,13 +16,17 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see .
+from __future__ import annotations
import bpy
import bonsai.bim.helper
import bonsai.bim.module.cost.prop as CostProp
import bonsai.tool as tool
from bpy.types import Panel, UIList
from bonsai.bim.module.cost.data import CostSchedulesData
-from typing import Any
+from typing import Any, TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from bonsai.bim.module.cost.prop import BIMCostProperties, CostItemQuantity
class BIM_PT_cost_schedules(Panel):
@@ -43,7 +47,7 @@ class BIM_PT_cost_schedules(Panel):
if not CostSchedulesData.is_loaded:
CostSchedulesData.load()
- self.props = context.scene.BIMCostProperties
+ self.props = tool.Cost.get_cost_props()
if not self.props.active_cost_schedule_id:
row = self.layout.row(align=True)
if CostSchedulesData.data["total_cost_schedules"]:
@@ -299,7 +303,7 @@ class BIM_PT_cost_item_types(Panel):
@classmethod
def poll(cls, context):
- props = context.scene.BIMCostProperties
+ props = tool.Cost.get_cost_props()
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
@@ -312,7 +316,7 @@ class BIM_PT_cost_item_types(Panel):
return False
def draw(self, context):
- self.props = context.scene.BIMCostProperties
+ self.props = tool.Cost.get_cost_props()
cost_item = self.props.cost_items[self.props.active_cost_item_index]
grid = self.layout.grid_flow(columns=3, even_columns=True)
@@ -385,7 +389,7 @@ class BIM_PT_cost_item_quantities(Panel):
@classmethod
def poll(cls, context):
- props = context.scene.BIMCostProperties
+ props = tool.Cost.get_cost_props()
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
@@ -398,7 +402,7 @@ class BIM_PT_cost_item_quantities(Panel):
return False
def draw(self, context):
- self.props = context.scene.BIMCostProperties
+ self.props = tool.Cost.get_cost_props()
cost_item = self.props.cost_items[self.props.active_cost_item_index]
@@ -462,7 +466,7 @@ class BIM_PT_cost_item_quantities(Panel):
row2.label(text="Tasks ({})".format(total_cost_item_processes))
tprops = tool.Sequence.get_task_tree_props()
- wprops = context.scene.BIMWorkScheduleProperties
+ wprops = tool.Sequence.get_work_schedule_props()
if tprops.tasks and wprops.active_task_index < len(tprops.tasks):
if has_quantity_names:
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES")
@@ -557,7 +561,7 @@ class BIM_PT_cost_item_rates(Panel):
@classmethod
def poll(cls, context):
- props = context.scene.BIMCostProperties
+ props = tool.Cost.get_cost_props()
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
@@ -570,7 +574,7 @@ class BIM_PT_cost_item_rates(Panel):
return False
def draw(self, context):
- self.props = context.scene.BIMCostProperties
+ self.props = tool.Cost.get_cost_props()
row = self.layout.row(align=True)
row.prop(self.props, "schedule_of_rates", text="")
if self.props.active_cost_item_rate_index < len(self.props.cost_item_rates):
@@ -606,13 +610,14 @@ class BIM_UL_cost_items_trait:
split2.label(text="Quantity")
split2.label(text="Value")
- for column in bpy.context.scene.BIMCostProperties.columns:
+ props = tool.Cost.get_cost_props()
+ for column in props.columns:
split2.label(text=column.name)
split2.label(text="Total Cost")
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
- self.props = context.scene.BIMCostProperties
+ self.props = tool.Cost.get_cost_props()
cost_item = CostSchedulesData.data["cost_items"][item.ifc_definition_id]
row = layout.row(align=True)
@@ -728,7 +733,6 @@ class BIM_UL_cost_item_rates(BIM_UL_cost_items_trait, UIList):
class BIM_UL_cost_columns(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
- props = context.scene.BIMCostProperties
if item:
row = layout.row(align=True)
row.prop(item, "name", emboss=False, text="")
@@ -737,7 +741,7 @@ class BIM_UL_cost_columns(UIList):
class BIM_UL_cost_item_types(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
- props = context.scene.BIMCostProperties
+ props = tool.Cost.get_cost_props()
cost_item = props.cost_items[props.active_cost_item_index]
if item:
@@ -749,9 +753,17 @@ class BIM_UL_cost_item_types(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]
+ def draw_item(
+ self,
+ context: bpy.types.Context,
+ layout: bpy.types.UILayout,
+ data: BIMCostProperties,
+ item: CostItemQuantity,
+ icon,
+ active_data,
+ active_propname,
+ ):
+ cost_item = data.cost_items[data.active_cost_item_index]
if item:
row = layout.row(align=True)
op = row.operator("bim.select_product", text="", icon="RESTRICT_SELECT_OFF")
@@ -788,15 +800,15 @@ class BIM_PT_Costing_Tools(Panel):
bl_parent_id = "BIM_PT_tab_cost"
def draw(self, context):
- self.props = context.scene.BIMCostProperties
+ props = tool.Cost.get_cost_props()
row = self.layout.row()
row.operator("bim.load_product_cost_items", icon="FILE_REFRESH")
row = self.layout.row()
row.template_list(
"BIM_UL_product_cost_items",
"",
- self.props,
+ props,
"product_cost_items",
- self.props,
+ props,
"active_product_cost_item_index",
)
diff --git a/src/bonsai/bonsai/bim/module/csv/prop.py b/src/bonsai/bonsai/bim/module/csv/prop.py
index f66ee75c51..371776bef2 100644
--- a/src/bonsai/bonsai/bim/module/csv/prop.py
+++ b/src/bonsai/bonsai/bim/module/csv/prop.py
@@ -61,6 +61,7 @@ class CsvAttribute(PropertyGroup):
formatting: StringProperty(default="{{value}}", name="Formatting")
if TYPE_CHECKING:
+ name: str
header: str
sort: Literal["NONE", "ASC", "DESC"]
group: Literal["NONE", "GROUP", "CONCAT", "VARIES", "SUM", "AVERAGE", "MIN", "MAX"]
diff --git a/src/bonsai/bonsai/bim/module/material/operator.py b/src/bonsai/bonsai/bim/module/material/operator.py
index 5562f4a3e3..12f71adbff 100644
--- a/src/bonsai/bonsai/bim/module/material/operator.py
+++ b/src/bonsai/bonsai/bim/module/material/operator.py
@@ -281,9 +281,12 @@ class UnassignMaterial(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
+ if TYPE_CHECKING:
+ obj: str
+
def _execute(self, context):
- objects = [bpy.data.objects.get(self.obj)] if self.obj else tool.Blender.get_selected_objects()
- core.unassign_material(tool.Ifc, tool.Material, objects=objects)
+ objects = [bpy.data.objects[self.obj]] if self.obj else tool.Blender.get_selected_objects()
+ core.unassign_material(tool.Ifc, tool.Material, objects=list(objects))
class AddConstituent(bpy.types.Operator, tool.Ifc.Operator):
diff --git a/src/bonsai/bonsai/bim/module/pset/data.py b/src/bonsai/bonsai/bim/module/pset/data.py
index 0c015f784a..09d63b41a5 100644
--- a/src/bonsai/bonsai/bim/module/pset/data.py
+++ b/src/bonsai/bonsai/bim/module/pset/data.py
@@ -227,7 +227,7 @@ class TaskQtosData(Data):
@classmethod
def load(cls):
- wprops = bpy.context.scene.BIMWorkScheduleProperties
+ wprops = tool.Sequence.get_work_schedule_props()
tprops = tool.Sequence.get_task_tree_props()
ifc_definition_id = tprops.tasks[wprops.active_task_index].ifc_definition_id
cls.data = {"qtos": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), qtos_only=True)}
@@ -311,7 +311,8 @@ class WorkSchedulePsetsData(Data):
@classmethod
def load(cls):
- ifc_definition_id = bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id
+ props = tool.Sequence.get_work_schedule_props()
+ ifc_definition_id = props.active_work_schedule_id
cls.data = {"psets": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), psets_only=True)}
cls.is_loaded = True
diff --git a/src/bonsai/bonsai/bim/module/pset/ui.py b/src/bonsai/bonsai/bim/module/pset/ui.py
index 62fd6366c9..19d275430f 100644
--- a/src/bonsai/bonsai/bim/module/pset/ui.py
+++ b/src/bonsai/bonsai/bim/module/pset/ui.py
@@ -494,7 +494,7 @@ class BIM_PT_task_qtos(Panel):
@classmethod
def poll(cls, context):
- props = context.scene.BIMWorkScheduleProperties
+ props = tool.Sequence.get_work_schedule_props()
if not props.active_work_schedule_id:
return False
tprops = tool.Sequence.get_task_tree_props()
@@ -712,9 +712,8 @@ class BIM_PT_work_schedule_psets(Panel):
@classmethod
def poll(cls, context):
- if not context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
- return False
- return True
+ props = tool.Sequence.get_work_schedule_props()
+ return bool(props.active_work_schedule_id)
def draw(self, context):
if not WorkSchedulePsetsData.is_loaded:
diff --git a/src/bonsai/bonsai/bim/module/sequence/data.py b/src/bonsai/bonsai/bim/module/sequence/data.py
index be7f19f28b..512056f1b3 100644
--- a/src/bonsai/bonsai/bim/module/sequence/data.py
+++ b/src/bonsai/bonsai/bim/module/sequence/data.py
@@ -307,20 +307,19 @@ class WorkScheduleData:
cls.is_loaded = True
@classmethod
- def can_have_baselines(cls):
- if not bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
+ def can_have_baselines(cls) -> bool:
+ props = tool.Sequence.get_work_schedule_props()
+ if not props.active_work_schedule_id:
return False
- return (
- tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id).PredefinedType
- == "PLANNED"
- )
+ return tool.Ifc.get().by_id(props.active_work_schedule_id).PredefinedType == "PLANNED"
@classmethod
- def active_work_schedule_baselines(cls):
+ def active_work_schedule_baselines(cls) -> list[dict[str, Any]]:
results = []
- if not bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
+ props = tool.Sequence.get_work_schedule_props()
+ if not props.active_work_schedule_id:
return []
- for rel in tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id).Declares:
+ for rel in tool.Ifc.get().by_id(props.active_work_schedule_id).Declares:
for work_schedule in rel.RelatedObjects:
if work_schedule.PredefinedType == "BASELINE":
results.append(
diff --git a/src/bonsai/bonsai/bim/module/sequence/operator.py b/src/bonsai/bonsai/bim/module/sequence/operator.py
index 98e7ae6bce..dcd80c78e7 100644
--- a/src/bonsai/bonsai/bim/module/sequence/operator.py
+++ b/src/bonsai/bonsai/bim/module/sequence/operator.py
@@ -232,7 +232,7 @@ class AddWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
def draw(self, context):
layout = self.layout
layout.prop(self, "name", text="Name")
- self.props = context.scene.BIMWorkScheduleProperties
+ self.props = tool.Sequence.get_work_schedule_props()
layout.prop(self.props, "work_schedule_predefined_types", text="Type")
if self.props.work_schedule_predefined_types == "USERDEFINED":
layout.prop(self.props, "object_type", text="Object type")
@@ -247,10 +247,11 @@ class EditWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
+ props = tool.Sequence.get_work_schedule_props()
core.edit_work_schedule(
tool.Ifc,
tool.Sequence,
- work_schedule=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_work_schedule_id),
+ work_schedule=tool.Ifc.get().by_id(props.active_work_schedule_id),
)
@@ -376,11 +377,12 @@ class EditTaskTime(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
+ props = tool.Sequence.get_work_schedule_props()
core.edit_task_time(
tool.Ifc,
tool.Sequence,
tool.Resource,
- task_time=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_task_time_id),
+ task_time=tool.Ifc.get().by_id(props.active_task_time_id),
)
@@ -411,9 +413,8 @@ class EditTask(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
- core.edit_task(
- tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_task_id)
- )
+ props = tool.Sequence.get_work_schedule_props()
+ core.edit_task(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(props.active_task_id))
class CopyTaskAttribute(bpy.types.Operator, tool.Ifc.Operator):
@@ -1083,10 +1084,11 @@ class EditSequenceAttributes(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
+ props = tool.Sequence.get_work_schedule_props()
core.edit_sequence_attributes(
tool.Ifc,
tool.Sequence,
- rel_sequence=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_sequence_id),
+ rel_sequence=tool.Ifc.get().by_id(props.active_sequence_id),
)
@@ -1138,7 +1140,8 @@ class VisualiseWorkScheduleDate(bpy.types.Operator):
@classmethod
def poll(cls, context):
- return bool(bpy.context.scene.BIMWorkScheduleProperties.visualisation_start)
+ props = tool.Sequence.get_work_schedule_props()
+ return bool(props.visualisation_start)
def execute(self, context):
core.visualise_work_schedule_date(tool.Sequence, work_schedule=tool.Ifc.get().by_id(self.work_schedule))
@@ -1163,10 +1166,8 @@ class VisualiseWorkScheduleDateRange(bpy.types.Operator):
@classmethod
def poll(cls, context):
- has_start, has_finish = (
- bpy.context.scene.BIMWorkScheduleProperties.visualisation_start,
- bpy.context.scene.BIMWorkScheduleProperties.visualisation_finish,
- )
+ props = tool.Sequence.get_work_schedule_props()
+ has_start, has_finish = props.visualisation_start, props.visualisation_finish
return bool(has_start and has_finish) and not "-" in (has_start, has_finish)
def execute(self, context):
@@ -1421,11 +1422,12 @@ class LoadAnimationColorScheme(bpy.types.Operator, tool.Ifc.Operator):
bl_description = "Loads the animation color scheme"
def _execute(self, context):
- group = tool.Ifc.get().by_id(int(context.scene.BIMAnimationProperties.saved_color_schemes))
+ props = tool.Sequence.get_animation_props()
+ group = tool.Ifc.get().by_id(int(props.saved_color_schemes))
core.load_animation_color_scheme(tool.Sequence, scheme=group)
def draw(self, context):
- props = context.scene.BIMAnimationProperties
+ props = tool.Sequence.get_animation_props()
row = self.layout.row()
row.prop(props, "saved_color_schemes", text="")
diff --git a/src/bonsai/bonsai/bim/module/sequence/prop.py b/src/bonsai/bonsai/bim/module/sequence/prop.py
index a63a4928c0..7f305ab49e 100644
--- a/src/bonsai/bonsai/bim/module/sequence/prop.py
+++ b/src/bonsai/bonsai/bim/module/sequence/prop.py
@@ -19,6 +19,7 @@
import bpy
import isodate
import ifcopenshell.api
+import ifcopenshell.api.sequence
import ifcopenshell.util.attribute
import ifcopenshell.util.date
import bonsai.tool as tool
@@ -26,6 +27,7 @@ import bonsai.core.sequence as core
from bonsai.bim.module.sequence.data import SequenceData, AnimationColorSchemeData, refresh as refresh_sequence_data
import bonsai.bim.module.resource.data
import bonsai.bim.module.pset.data
+from mathutils import Color
from bonsai.bim.prop import StrProperty, Attribute
from dateutil import parser
from bpy.types import PropertyGroup
@@ -39,7 +41,7 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Literal
def getTaskColumns(self, context):
@@ -93,48 +95,48 @@ def update_active_task_inputs(self, context):
tool.Sequence.load_task_inputs(inputs)
-def updateTaskName(self, context):
- props = context.scene.BIMWorkScheduleProperties
+def updateTaskName(self: "Task", context: bpy.types.Context) -> None:
+ props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled or self.name == "Unnamed":
return
- self.file = tool.Ifc.get()
- ifcopenshell.api.run(
- "sequence.edit_task",
- self.file,
- **{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}},
+ ifc_file = tool.Ifc.get()
+ ifcopenshell.api.sequence.edit_task(
+ ifc_file,
+ task=ifc_file.by_id(self.ifc_definition_id),
+ attributes={"Name": self.name},
)
SequenceData.load()
if props.active_task_id == self.ifc_definition_id:
- attribute = props.task_attributes.get("Name")
+ attribute = props.task_attributes["Name"]
attribute.string_value = self.name
-def updateTaskIdentification(self, context):
- props = context.scene.BIMWorkScheduleProperties
+def updateTaskIdentification(self: "Task", context: bpy.types.Context) -> None:
+ props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled or self.identification == "XXX":
return
- self.file = tool.Ifc.get()
- ifcopenshell.api.run(
- "sequence.edit_task",
- self.file,
- **{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Identification": self.identification}},
+ ifc_file = tool.Ifc.get()
+ ifcopenshell.api.sequence.edit_task(
+ ifc_file,
+ task=ifc_file.by_id(self.ifc_definition_id),
+ attributes={"Identification": self.identification},
)
SequenceData.load()
if props.active_task_id == self.ifc_definition_id:
- attribute = props.task_attributes.get("Identification")
+ attribute = props.task_attributes["Identification"]
attribute.string_value = self.identification
-def updateTaskTimeStart(self, context):
+def updateTaskTimeStart(self: "Task", context: bpy.types.Context) -> None:
updateTaskTimeDateTime(self, context, "start")
-def updateTaskTimeFinish(self, context):
+def updateTaskTimeFinish(self: "Task", context: bpy.types.Context) -> None:
updateTaskTimeDateTime(self, context, "finish")
-def updateTaskTimeDateTime(self, context, startfinish):
- props = context.scene.BIMWorkScheduleProperties
+def updateTaskTimeDateTime(self: "Task", context: bpy.types.Context, startfinish: Literal["start", "finish"]) -> None:
+ props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled:
return
@@ -149,7 +151,7 @@ def updateTaskTimeDateTime(self, context, startfinish):
if startfinish_value == "-":
return
- self.file = tool.Ifc.get()
+ ifc_file = tool.Ifc.get()
try:
startfinish_datetime = parser.isoparse(startfinish_value)
@@ -160,11 +162,11 @@ def updateTaskTimeDateTime(self, context, startfinish):
setattr(self, startfinish, "-")
return
- task = self.file.by_id(self.ifc_definition_id)
+ task = ifc_file.by_id(self.ifc_definition_id)
if task.TaskTime:
task_time = task.TaskTime
else:
- task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=task)
+ task_time = ifcopenshell.api.sequence.add_task_time(ifc_file, task=task)
SequenceData.load()
startfinish_key = "Schedule" + startfinish.capitalize()
@@ -174,17 +176,17 @@ def updateTaskTimeDateTime(self, context, startfinish):
setattr(self, startfinish, canonical_startfinish_value)
return
- ifcopenshell.api.run(
- "sequence.edit_task_time",
- self.file,
- **{"task_time": task_time, "attributes": {startfinish_key: startfinish_datetime}},
+ ifcopenshell.api.sequence.edit_task_time(
+ ifc_file,
+ task_time=task_time,
+ attributes={startfinish_key: startfinish_datetime},
)
SequenceData.load()
bpy.ops.bim.load_task_properties()
-def updateTaskDuration(self, context):
- props = context.scene.BIMWorkScheduleProperties
+def updateTaskDuration(self: "Task", context: bpy.types.Context) -> None:
+ props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled:
return
@@ -196,12 +198,17 @@ def updateTaskDuration(self, context):
self.duration = "-"
return
- task = tool.Ifc.get().by_id(self.ifc_definition_id)
+ ifc_file = tool.Ifc.get()
+ task = ifc_file.by_id(self.ifc_definition_id)
if task.TaskTime:
task_time = task.TaskTime
else:
- task_time = tool.Ifc.run("sequence.add_task_time", task=task)
- tool.Ifc.run("sequence.edit_task_time", task_time=task_time, attributes={"ScheduleDuration": duration})
+ task_time = ifcopenshell.api.sequence.add_task_time(ifc_file, task=task)
+ ifcopenshell.api.sequence.edit_task_time(
+ ifc_file,
+ task_time=task_time,
+ attributes={"ScheduleDuration": duration},
+ )
core.load_task_properties(tool.Sequence)
tool.Sequence.refresh_task_resources()
@@ -212,15 +219,19 @@ def get_schedule_predefined_types(self, context):
return SequenceData.data["schedule_predefined_types_enum"]
-def update_visualisation_start(self, context):
+def update_visualisation_start(self: "BIMWorkScheduleProperties", context: bpy.types.Context) -> None:
update_visualisation_start_finish(self, context, "visualisation_start")
-def update_visualisation_finish(self, context):
+def update_visualisation_finish(self: "BIMWorkScheduleProperties", context: bpy.types.Context) -> None:
update_visualisation_start_finish(self, context, "visualisation_finish")
-def update_visualisation_start_finish(self, context, startfinish):
+def update_visualisation_start_finish(
+ self: "BIMWorkScheduleProperties",
+ context: bpy.types.Context,
+ startfinish: Literal["visualisation_start", "visualisation_finish"],
+) -> None:
def canonicalise_time(time):
if not time:
return "-"
@@ -243,34 +254,34 @@ def update_visualisation_start_finish(self, context, startfinish):
def update_color_full(self, context):
material = bpy.data.materials.get("color_full")
if material:
- color_full = bpy.context.scene.BIMAnimationProperties.color_full
+ props = tool.Sequence.get_animation_props()
inputs = tool.Blender.get_material_node(material, "BSDF_PRINCIPLED").inputs
color = inputs["Base Color"].default_value
- color[0] = color_full.r
- color[1] = color_full.g
- color[2] = color_full.b
+ color[0] = props.color_full.r
+ color[1] = props.color_full.g
+ color[2] = props.color_full.b
def update_color_progress(self, context):
material = bpy.data.materials.get("color_progress")
if material:
- color_progress = bpy.context.scene.BIMAnimationProperties.color_progress
+ props = tool.Sequence.get_animation_props()
inputs = tool.Blender.get_material_node(material, "BSDF_PRINCIPLED").inputs
color = inputs["Base Color"].default_value
- color[0] = color_progress.r
- color[1] = color_progress.g
- color[2] = color_progress.b
+ color[0] = props.color_progress.r
+ color[1] = props.color_progress.g
+ color[2] = props.color_progress.b
-def update_sort_reversed(self, context):
- if context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
+def update_sort_reversed(self: "BIMWorkScheduleProperties", context: bpy.types.Context) -> None:
+ if self.active_work_schedule_id:
core.load_task_tree(
tool.Sequence,
- work_schedule=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_work_schedule_id),
+ work_schedule=tool.Ifc.get().by_id(self.active_work_schedule_id),
)
-def update_filter_by_active_schedule(self, context):
+def update_filter_by_active_schedule(self: "BIMWorkScheduleProperties", context: bpy.types.Context) -> None:
if obj := context.active_object:
product = tool.Ifc.get_entity(obj)
assert product
@@ -314,8 +325,9 @@ def updateAssignedResourceUsage(self, context):
bonsai.bim.module.pset.data.refresh()
-def update_task_bar_list(self, context):
- if not context.scene.BIMWorkScheduleProperties.is_task_update_enabled:
+def update_task_bar_list(self: "Task", context: bpy.types.Context) -> None:
+ props = tool.Sequence.get_work_schedule_props()
+ if not props.is_task_update_enabled:
return
if self.has_bar_visual:
tool.Sequence.add_task_bar(self.ifc_definition_id)
@@ -368,17 +380,30 @@ class WorkPlan(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
+ if TYPE_CHECKING:
+ name: str
+ ifc_definition_id: int
+
class TaskResource(PropertyGroup):
name: StringProperty(name="Name", update=updateAssignedResourceName)
ifc_definition_id: IntProperty(name="IFC Definition ID")
schedule_usage: FloatProperty(name="Schedule Usage", update=updateAssignedResourceUsage)
+ if TYPE_CHECKING:
+ name: str
+ ifc_definition_id: int
+ schedule_usage: float
+
class TaskProduct(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
+ if TYPE_CHECKING:
+ name: str
+ ifc_definition_id: int
+
class BIMWorkPlanProperties(PropertyGroup):
work_plan_attributes: CollectionProperty(name="Work Plan Attributes", type=Attribute)
@@ -388,6 +413,14 @@ class BIMWorkPlanProperties(PropertyGroup):
active_work_plan_id: IntProperty(name="Active Work Plan Id")
work_schedules: EnumProperty(items=getWorkSchedules, name="Work Schedules")
+ if TYPE_CHECKING:
+ work_plan_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ editing_type: str
+ work_plans: bpy.types.bpy_prop_collection_idprop[WorkPlan]
+ active_work_plan_index: int
+ active_work_plan_id: int
+ work_schedules: str
+
class ISODuration(PropertyGroup):
name: StringProperty(name="Name")
@@ -398,16 +431,33 @@ class ISODuration(PropertyGroup):
minutes: IntProperty(name="Minutes", default=0)
seconds: IntProperty(name="Seconds", default=0)
+ if TYPE_CHECKING:
+ name: str
+ years: int
+ months: int
+ days: int
+ hours: int
+ minutes: int
+ seconds: int
+
class IFCStatus(PropertyGroup):
name: StringProperty(name="Name")
is_visible: BoolProperty(name="Is Visible", default=True, update=lambda x, y: bpy.ops.bim.activate_status_filters())
+ if TYPE_CHECKING:
+ name: str
+ is_visible: bool
+
class BIMStatusProperties(PropertyGroup):
is_enabled: BoolProperty(name="Is Enabled")
statuses: CollectionProperty(name="Statuses", type=IFCStatus)
+ if TYPE_CHECKING:
+ is_enabled: bool
+ statuses: bpy.types.bpy_prop_collection_idprop[IFCStatus]
+
class BIMWorkScheduleProperties(PropertyGroup):
work_schedule_predefined_types: EnumProperty(
@@ -493,6 +543,66 @@ class BIMWorkScheduleProperties(PropertyGroup):
name="Filter By Active Schedule", default=False, update=update_filter_by_active_schedule
)
+ if TYPE_CHECKING:
+ work_schedule_predefined_types: str
+ object_type: str
+ durations_attributes: bpy.types.bpy_prop_collection_idprop[ISODuration]
+ work_calendars: str
+ work_schedule_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ editing_type: str
+ editing_task_type: str
+ active_work_schedule_index: int
+ active_work_schedule_id: int
+ active_task_index: int
+ active_task_id: int
+ highlighted_task_id: int
+ task_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ should_show_visualisation_ui: bool
+ should_show_task_bar_selection: bool
+ should_show_snapshot_ui: bool
+ should_show_column_ui: bool
+ columns: bpy.types.bpy_prop_collection_idprop[Attribute]
+ active_column_index: int
+ sort_column: str
+ is_sort_reversed: bool
+ column_types: str
+ task_columns: str
+ task_time_columns: str
+ other_columns: str
+ active_task_time_id: int
+ task_time_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ contracted_tasks: str
+ task_bars: str
+ is_task_update_enabled: bool
+ editing_sequence_type: str
+ active_sequence_id: int
+ sequence_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ lag_time_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ visualisation_start: str
+ visualisation_finish: str
+ speed_multiplier: float
+ speed_animation_duration: str
+ speed_animation_frames: int
+ speed_real_duration: str
+ speed_types: str
+ task_resources: bpy.types.bpy_prop_collection_idprop[TaskResource]
+ active_task_resource_index: int
+ task_inputs: bpy.types.bpy_prop_collection_idprop[TaskProduct]
+ active_task_input_index: int
+ task_outputs: bpy.types.bpy_prop_collection_idprop[TaskProduct]
+ active_task_output_index: int
+ show_nested_outputs: bool
+ show_nested_resources: bool
+ show_nested_inputs: bool
+ product_input_tasks: bpy.types.bpy_prop_collection_idprop[TaskProduct]
+ product_output_tasks: bpy.types.bpy_prop_collection_idprop[TaskProduct]
+ active_product_output_task_index: int
+ active_product_input_task_index: int
+ enable_reorder: bool
+ show_task_operators: bool
+ should_show_schedule_baseline_ui: bool
+ filter_by_active_schedule: bool
+
class BIMTaskTreeProperties(PropertyGroup):
# This belongs by itself for performance reasons. https://developer.blender.org/T87737
@@ -507,11 +617,19 @@ class WorkCalendar(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
+ if TYPE_CHECKING:
+ name: str
+ ifc_definition_id: int
+
class RecurrenceComponent(PropertyGroup):
name: StringProperty(name="Name")
is_specified: BoolProperty(name="Is Specified")
+ if TYPE_CHECKING:
+ name: str
+ is_specified: bool
+
class BIMWorkCalendarProperties(PropertyGroup):
work_calendar_attributes: CollectionProperty(name="Work Calendar Attributes", type=Attribute)
@@ -542,6 +660,22 @@ class BIMWorkCalendarProperties(PropertyGroup):
start_time: StringProperty(name="Start Time")
end_time: StringProperty(name="End Time")
+ if TYPE_CHECKING:
+ work_calendar_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ work_time_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ editing_type: str
+ active_work_calendar_id: int
+ active_work_time_id: int
+ day_components: bpy.types.bpy_prop_collection_idprop[RecurrenceComponent]
+ weekday_components: bpy.types.bpy_prop_collection_idprop[RecurrenceComponent]
+ month_components: bpy.types.bpy_prop_collection_idprop[RecurrenceComponent]
+ position: int
+ interval: int
+ occurrences: int
+ recurrence_types: str
+ start_time: str
+ end_time: str
+
def update_selected_date(self: "DatePickerProperties", context: bpy.types.Context) -> None:
# `include_time` is `True`, otherwise time props are not displayed in UI.
@@ -561,6 +695,13 @@ class DatePickerProperties(PropertyGroup):
selected_min: IntProperty(min=0, max=59, update=update_selected_date)
selected_sec: IntProperty(min=0, max=59, update=update_selected_date)
+ if TYPE_CHECKING:
+ display_date: str
+ selected_date: str
+ selected_hour: int
+ selected_min: int
+ selected_sec: int
+
class BIMDateTextProperties(PropertyGroup):
start_frame: IntProperty(name="Start Frame")
@@ -568,6 +709,12 @@ class BIMDateTextProperties(PropertyGroup):
start: StringProperty(name="Start")
finish: StringProperty(name="Finish")
+ if TYPE_CHECKING:
+ start_frame: int
+ total_frames: int
+ start: str
+ finish: str
+
class BIMTaskTypeColor(PropertyGroup):
name: StringProperty(name="Name")
@@ -580,6 +727,11 @@ class BIMTaskTypeColor(PropertyGroup):
max=1.0,
)
+ if TYPE_CHECKING:
+ name: str
+ animation_type: str
+ color: tuple[float, float, float]
+
class BIMAnimationProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Loaded", default=False)
@@ -607,3 +759,14 @@ class BIMAnimationProperties(PropertyGroup):
update=update_color_progress,
)
should_show_task_bar_options: BoolProperty(name="Show Task Bar Options", default=False)
+
+ if TYPE_CHECKING:
+ is_editing: bool
+ saved_color_schemes: str
+ active_color_component_outputs_index: int
+ active_color_component_inputs_index: int
+ task_input_colors: bpy.types.bpy_prop_collection_idprop[BIMTaskTypeColor]
+ task_output_colors: bpy.types.bpy_prop_collection_idprop[BIMTaskTypeColor]
+ color_full: Color
+ color_progress: Color
+ should_show_task_bar_options: bool
diff --git a/src/bonsai/bonsai/bim/module/sequence/ui.py b/src/bonsai/bonsai/bim/module/sequence/ui.py
index 21ed962852..3edffe170e 100644
--- a/src/bonsai/bonsai/bim/module/sequence/ui.py
+++ b/src/bonsai/bonsai/bim/module/sequence/ui.py
@@ -30,7 +30,11 @@ from bonsai.bim.module.sequence.data import (
TaskICOMData,
AnimationColorSchemeData,
)
-from typing import Any, Optional
+from typing import Any, Optional, TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from bonsai.bim.prop import Attribute
+ from bonsai.bim.module.sequence.prop import BIMWorkScheduleProperties, BIMTaskTreeProperties, Task
class BIM_PT_status(Panel):
@@ -154,7 +158,7 @@ class BIM_PT_work_schedules(Panel):
SequenceData.load()
if not WorkScheduleData.is_loaded:
WorkScheduleData.load()
- self.props = context.scene.BIMWorkScheduleProperties
+ self.props = tool.Sequence.get_work_schedule_props()
self.tprops = tool.Sequence.get_task_tree_props()
if not self.props.active_work_schedule_id:
@@ -479,14 +483,14 @@ class BIM_PT_animation_tools(Panel):
@classmethod
def poll(cls, context):
- props = context.scene.BIMWorkScheduleProperties
+ props = tool.Sequence.get_work_schedule_props()
if props.active_work_schedule_id:
return True
return False
def draw(self, context):
- self.props = context.scene.BIMWorkScheduleProperties
- self.animation_props = context.scene.BIMAnimationProperties
+ self.props = tool.Sequence.get_work_schedule_props()
+ self.animation_props = tool.Sequence.get_animation_props()
row = self.layout.row(align=True)
row.alignment = "RIGHT"
row.prop(self.props, "should_show_visualisation_ui", text="Animation Settings", icon="SETTINGS")
@@ -598,7 +602,7 @@ class BIM_PT_animation_Color_Scheme(Panel):
if not AnimationColorSchemeData.is_loaded:
AnimationColorSchemeData.load()
- self.animation_props = context.scene.BIMAnimationProperties
+ self.animation_props = tool.Sequence.get_animation_props()
row = self.layout.row(align=True)
row.alignment = "RIGHT"
row.operator("bim.load_default_animation_color_scheme", text="Load default", icon="SEQUENCE_COLOR_04")
@@ -645,7 +649,7 @@ class BIM_PT_task_icom(Panel):
@classmethod
def poll(cls, context):
- props = context.scene.BIMWorkScheduleProperties
+ props = tool.Sequence.get_work_schedule_props()
if not props.active_work_schedule_id:
return False
tprops = tool.Sequence.get_task_tree_props()
@@ -658,7 +662,7 @@ class BIM_PT_task_icom(Panel):
if not TaskICOMData.is_loaded:
TaskICOMData.load()
- self.props = context.scene.BIMWorkScheduleProperties
+ self.props = tool.Sequence.get_work_schedule_props()
self.tprops = tool.Sequence.get_task_tree_props()
task = self.tprops.tasks[self.props.active_task_index]
@@ -751,8 +755,17 @@ class BIM_PT_task_icom(Panel):
class BIM_UL_task_columns(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
- props = context.scene.BIMWorkScheduleProperties
+ def draw_item(
+ self,
+ context,
+ layout: bpy.types.UILayout,
+ data: "BIMWorkScheduleProperties",
+ item: "Attribute",
+ icon,
+ active_data,
+ active_propname,
+ ):
+ props = tool.Sequence.get_work_schedule_props()
if item:
row = layout.row(align=True)
row.prop(item, "name", emboss=False, text="")
@@ -820,7 +833,7 @@ class BIM_UL_product_output_tasks(UIList):
class BIM_UL_tasks(UIList):
@classmethod
def draw_header(cls, layout: bpy.types.UILayout):
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = tool.Sequence.get_work_schedule_props()
row = layout.row(align=True)
split1 = row.split(factor=0.1)
@@ -829,9 +842,18 @@ class BIM_UL_tasks(UIList):
split2.label(text="Name")
cls.draw_custom_columns(props, split2, header=True)
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ def draw_item(
+ self,
+ context,
+ layout: bpy.types.UILayout,
+ data: "BIMTaskTreeProperties",
+ item: "Task",
+ icon,
+ active_data,
+ active_propname,
+ ):
if item:
- self.props = context.scene.BIMWorkScheduleProperties
+ self.props = tool.Sequence.get_work_schedule_props()
task = SequenceData.data["tasks"][item.ifc_definition_id]
row = layout.row(align=True)
@@ -1140,7 +1162,7 @@ class BIM_PT_4D_Tools(Panel):
bl_parent_id = "BIM_PT_tab_sequence"
def draw(self, context):
- self.props = context.scene.BIMWorkScheduleProperties
+ self.props = tool.Sequence.get_work_schedule_props()
row = self.layout.row()
row.operator("bim.load_product_related_tasks", text="Load Tasks", icon="FILE_REFRESH")
row.prop(self.props, "filter_by_active_schedule", text="Filter by Active Schedule")
diff --git a/src/bonsai/bonsai/tool/blender.py b/src/bonsai/bonsai/tool/blender.py
index d5269a405f..6136c0e6da 100644
--- a/src/bonsai/bonsai/tool/blender.py
+++ b/src/bonsai/bonsai/tool/blender.py
@@ -214,11 +214,11 @@ class Blender(bonsai.core.tool.Blender):
return omprops.active_material_set_item_id
elif obj_type == "Task":
tprops = tool.Sequence.get_task_tree_props()
- return tprops.tasks[context.scene.BIMWorkScheduleProperties.active_task_index].ifc_definition_id
+ wsprops = tool.Sequence.get_work_schedule_props()
+ return tprops.tasks[wsprops.active_task_index].ifc_definition_id
elif obj_type == "Cost":
- return context.scene.BIMCostProperties.cost_items[
- context.scene.BIMCostProperties.active_cost_item_index
- ].ifc_definition_id
+ cost_props = tool.Cost.get_cost_props()
+ return cost_props.cost_items[cost_props.active_cost_item_index].ifc_definition_id
elif obj_type == "Resource":
return context.scene.BIMResourceTreeProperties.resources[
context.scene.BIMResourceProperties.active_resource_index
@@ -227,7 +227,8 @@ class Blender(bonsai.core.tool.Blender):
props = tool.Profile.get_profile_props()
return props.profiles[props.active_profile_index].ifc_definition_id
elif obj_type == "WorkSchedule":
- return context.scene.BIMWorkScheduleProperties.active_work_schedule_id
+ wsprops = tool.Sequence.get_work_schedule_props()
+ return wsprops.active_work_schedule_id
elif obj_type == "Group":
prop = context.scene.BIMGroupProperties
return prop.groups[prop.active_group_index].ifc_definition_id
diff --git a/src/bonsai/bonsai/tool/cost.py b/src/bonsai/bonsai/tool/cost.py
index 251c03c61b..3775da554c 100644
--- a/src/bonsai/bonsai/tool/cost.py
+++ b/src/bonsai/bonsai/tool/cost.py
@@ -1,4 +1,22 @@
-import os
+# Bonsai - OpenBIM Blender Add-on
+# Copyright (C) 2021 Dion Moult , 2022 Yassine Oualid
+#
+# This file is part of Bonsai.
+#
+# Bonsai is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Bonsai is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Bonsai. If not, see .
+
+from __future__ import annotations
import bpy
import bonsai.core.tool
import bonsai.tool as tool
@@ -10,27 +28,35 @@ import ifcopenshell.util.unit
import bonsai.bim.helper
import json
from pathlib import Path
-from typing import Optional, Any, Generator, Union, Literal
+from typing import Optional, Any, Generator, Union, Literal, TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from bonsai.bim.module.cost.prop import BIMCostProperties, CostItemQuantity
class Cost(bonsai.core.tool.Cost):
RELATED_OBJECT_TYPE = Literal["PRODUCT", "PROCESS", "RESOURCE"]
+ @classmethod
+ def get_cost_props(cls) -> "BIMCostProperties":
+ return bpy.context.scene.BIMCostProperties
+
@classmethod
def get_cost_schedule_attributes(cls) -> dict[str, Any]:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
return bonsai.bim.helper.export_attributes(props.cost_schedule_attributes)
@classmethod
def disable_editing_cost_schedule(cls) -> None:
cls.store_active_schedule_columns()
- bpy.context.scene.BIMCostProperties.active_cost_schedule_id = 0
+ props = cls.get_cost_props()
+ props.active_cost_schedule_id = 0
cls.disable_editing_cost_item()
@classmethod
def load_active_schedule_columns(cls) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
active_columns = props.columns
storage = props.columns_storage
active_cost_schedule_id = cls.get_active_cost_schedule().id()
@@ -53,7 +79,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def store_active_schedule_columns(cls) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
active_columns = props.columns
storage = props.columns_storage
active_cost_schedule_id = cls.get_active_cost_schedule().id()
@@ -67,7 +93,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def remove_stored_schedule_columns(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
storage = props.columns_storage
active_cost_schedule_id = cost_schedule.id()
@@ -79,8 +105,9 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def enable_editing_cost_schedule_attributes(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
- bpy.context.scene.BIMCostProperties.active_cost_schedule_id = cost_schedule.id()
- bpy.context.scene.BIMCostProperties.is_editing = "COST_SCHEDULE_ATTRIBUTES"
+ props = cls.get_cost_props()
+ props.active_cost_schedule_id = cost_schedule.id()
+ props.is_editing = "COST_SCHEDULE_ATTRIBUTES"
@classmethod
def load_cost_schedule_attributes(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
@@ -89,13 +116,13 @@ class Cost(bonsai.core.tool.Cost):
prop.string_value = "" if prop.is_null else ifcopenshell.util.date.ifc2datetime(data[name]).isoformat()
return True
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.cost_schedule_attributes.clear()
bonsai.bim.helper.import_attributes2(cost_schedule, props.cost_schedule_attributes, callback=special_import)
@classmethod
def enable_editing_cost_items(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.active_cost_schedule_id = cost_schedule.id()
props.is_editing = "COST_ITEMS"
@@ -123,7 +150,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def load_cost_schedule_tree(cls) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.is_cost_update_enabled = False
cost_schedule = tool.Ifc.get().by_id(props.active_cost_schedule_id)
props.cost_items.clear()
@@ -137,7 +164,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def expand_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
if not hasattr(cls, "contracted_cost_items"):
cls.contracted_cost_items = json.loads(props.contracted_cost_items)
if cost_item.id() in cls.contracted_cost_items:
@@ -146,7 +173,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def expand_cost_items(cls) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
cls.contracted_cost_items = json.loads(props.contracted_cost_items)
for cost_item in props.cost_items:
if cost_item.ifc_definition_id in cls.contracted_cost_items:
@@ -155,7 +182,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def contract_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
if not hasattr(cls, "contracted_cost_items"):
cls.contracted_cost_items = json.loads(props.contracted_cost_items)
cls.contracted_cost_items.append(cost_item.id())
@@ -163,7 +190,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def contract_cost_items(cls) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
if not hasattr(cls, "contracted_cost_items"):
cls.contracted_cost_items = json.loads(props.contracted_cost_items)
for cost_item in props.cost_items:
@@ -173,7 +200,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def clean_up_cost_item_tree(cls, cost_item_id: int) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
if not hasattr(cls, "contracted_cost_items"):
cls.contracted_cost_items = json.loads(props.contracted_cost_items)
if props.active_cost_item_id == cost_item_id:
@@ -185,35 +212,37 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def enable_editing_cost_item_attributes(cls, cost_item: ifcopenshell.entity_instance):
- bpy.context.scene.BIMCostProperties.active_cost_item_id = cost_item.id()
- bpy.context.scene.BIMCostProperties.cost_item_editing_type = "ATTRIBUTES"
+ props = cls.get_cost_props()
+ props.active_cost_item_id = cost_item.id()
+ props.cost_item_editing_type = "ATTRIBUTES"
@classmethod
def load_cost_item_attributes(cls, cost_item: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.cost_item_attributes.clear()
bonsai.bim.helper.import_attributes2(cost_item, props.cost_item_attributes)
@classmethod
def disable_editing_cost_item(cls) -> None:
- bpy.context.scene.BIMCostProperties.active_cost_item_id = 0
- bpy.context.scene.BIMCostProperties.change_cost_item_parent = False
+ props = cls.get_cost_props()
+ props.active_cost_item_id = 0
+ props.change_cost_item_parent = False
@classmethod
def get_cost_item_attributes(cls) -> dict[str, Any]:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
return bonsai.bim.helper.export_attributes(props.cost_item_attributes)
@classmethod
def get_active_cost_item(cls) -> Union[ifcopenshell.entity_instance, None]:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
if not props.active_cost_item_id:
return None
- return tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_item_id)
+ return tool.Ifc.get().by_id(props.active_cost_item_id)
@classmethod
def get_highlighted_cost_item(cls) -> Union[ifcopenshell.entity_instance, None]:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
if not props.active_cost_schedule_id:
return
if props.active_cost_item_index < len(props.cost_items):
@@ -226,7 +255,7 @@ class Cost(bonsai.core.tool.Cost):
cost_item = cls.get_highlighted_cost_item()
if not cost_item:
return
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.cost_item_type_products.clear()
# TODO implement process and resource types
# props.cost_item_processes.clear()
@@ -248,7 +277,9 @@ class Cost(bonsai.core.tool.Cost):
cls, cost_item: ifcopenshell.entity_instance, related_object_type: RELATED_OBJECT_TYPE
) -> None:
def create_list_items(
- collection: bpy.types.CollectionProperty, cost_item: ifcopenshell.entity_instance, is_deep: bool
+ collection: bpy.types.bpy_prop_collection_idprop[CostItemQuantity],
+ cost_item: ifcopenshell.entity_instance,
+ is_deep: bool,
) -> None:
products = cls.get_cost_item_assignments(cost_item, filter_by_type=related_object_type, is_deep=False)
for product in products:
@@ -262,18 +293,18 @@ class Cost(bonsai.core.tool.Cost):
for cost_item in ifcopenshell.util.cost.get_nested_cost_items(cost_item, is_deep):
create_list_items(collection, cost_item, is_deep=False)
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
if related_object_type == "PRODUCT":
props.cost_item_products.clear()
- is_deep = bpy.context.scene.BIMCostProperties.show_nested_elements
+ is_deep = props.show_nested_elements
create_list_items(props.cost_item_products, cost_item, is_deep)
elif related_object_type == "PROCESS":
props.cost_item_processes.clear()
- is_deep = bpy.context.scene.BIMCostProperties.show_nested_tasks
+ is_deep = props.show_nested_tasks
create_list_items(props.cost_item_processes, cost_item, is_deep)
elif related_object_type == "RESOURCE":
props.cost_item_resources.clear()
- is_deep = bpy.context.scene.BIMCostProperties.show_nested_resources
+ is_deep = props.show_nested_resources
create_list_items(props.cost_item_resources, cost_item, is_deep)
@classmethod
@@ -321,33 +352,35 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def enable_editing_cost_item_quantities(cls, cost_item: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.active_cost_item_id = cost_item.id()
props.cost_item_editing_type = "QUANTITIES"
@classmethod
def enable_editing_cost_item_quantity(cls, physical_quantity: ifcopenshell.entity_instance) -> None:
- bpy.context.scene.BIMCostProperties.active_cost_item_quantity_id = physical_quantity.id()
+ props = cls.get_cost_props()
+ props.active_cost_item_quantity_id = physical_quantity.id()
@classmethod
def load_cost_item_quantity_attributes(cls, physical_quantity: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.quantity_attributes.clear()
bonsai.bim.helper.import_attributes2(physical_quantity, props.quantity_attributes)
@classmethod
def enable_editing_cost_item_values(cls, cost_item: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.active_cost_item_id = cost_item.id()
props.cost_item_editing_type = "VALUES"
@classmethod
def disable_editing_cost_item_quantity(cls) -> None:
- bpy.context.scene.BIMCostProperties.active_cost_item_quantity_id = 0
+ props = cls.get_cost_props()
+ props.active_cost_item_quantity_id = 0
@classmethod
def get_cost_item_quantity_attributes(cls) -> dict[str, Any]:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
return bonsai.bim.helper.export_attributes(props.quantity_attributes)
@classmethod
@@ -356,7 +389,8 @@ class Cost(bonsai.core.tool.Cost):
) -> dict[str, Any]:
if cost_type == "FIXED":
category = None
- attributes = {"AppliedValue": bpy.context.scene.BIMCostProperties.fixed_cost_value}
+ props = cls.get_cost_props()
+ attributes = {"AppliedValue": props.fixed_cost_value}
elif cost_type == "SUM":
category = "*"
attributes = {"Category": category}
@@ -404,7 +438,7 @@ class Cost(bonsai.core.tool.Cost):
break
return True
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.cost_value_attributes.clear()
is_rates = cls.is_active_schedule_of_rates()
callback = lambda name, prop, data: import_attributes(
@@ -420,38 +454,37 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def is_active_schedule_of_rates(cls) -> bool:
- return (
- tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id).PredefinedType
- == "SCHEDULEOFRATES"
- )
+ props = cls.get_cost_props()
+ return tool.Ifc.get().by_id(props.active_cost_schedule_id).PredefinedType == "SCHEDULEOFRATES"
@classmethod
def enable_editing_cost_item_value(cls, cost_value: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.active_cost_value_id = cost_value.id()
props.cost_value_editing_type = "ATTRIBUTES"
@classmethod
def disable_editing_cost_item_value(cls) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.active_cost_value_id = 0
props.cost_value_editing_type = ""
@classmethod
def load_cost_item_value_formula_attributes(cls, cost_value: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.cost_value_attributes.clear()
- bpy.context.scene.BIMCostProperties.cost_value_formula = ifcopenshell.util.cost.serialise_cost_value(cost_value)
+ props.cost_value_formula = ifcopenshell.util.cost.serialise_cost_value(cost_value)
@classmethod
def enable_editing_cost_item_value_formula(cls, cost_value: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.active_cost_value_id = cost_value.id()
props.cost_value_editing_type = "FORMULA"
@classmethod
def get_cost_item_value_formula(cls) -> str:
- return bpy.context.scene.BIMCostProperties.cost_value_formula
+ props = cls.get_cost_props()
+ return props.cost_value_formula
@classmethod
def get_cost_value_attributes(cls) -> dict[str, Any]:
@@ -468,15 +501,14 @@ class Cost(bonsai.core.tool.Cost):
if prop.name == "UnitBasisUnit":
return True
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
callback = lambda attributes, prop: export_attributes(attributes, prop)
return bonsai.bim.helper.export_attributes(props.cost_value_attributes, callback)
@classmethod
def get_cost_value_unit_component(cls) -> ifcopenshell.entity_instance:
- return tool.Ifc.get().by_id(
- int(bpy.context.scene.BIMCostProperties.cost_value_attributes.get("UnitBasisUnit").enum_value)
- )
+ props = cls.get_cost_props()
+ return tool.Ifc.get().by_id(int(props.cost_value_attributes["UnitBasisUnit"].enum_value))
@classmethod
def get_cost_item_assignments(
@@ -491,7 +523,8 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def show_nested_cost_item_elements(cls) -> bool:
- return bpy.context.scene.BIMCostProperties.show_nested_elements
+ props = cls.get_cost_props()
+ return props.show_nested_elements
@classmethod
def get_cost_item_products(
@@ -543,18 +576,18 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def add_cost_column(cls, name: str) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
new = props.columns.add()
new.name = name
@classmethod
def remove_cost_column(cls, name: str) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.columns.remove(props.columns.find(name))
@classmethod
def get_active_schedule_of_rates(cls) -> Union[ifcopenshell.entity_instance, None]:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
schedule_id = tool.Blender.get_enum_safe(props, "schedule_of_rates")
if schedule_id is None:
return
@@ -562,7 +595,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def expand_cost_item_rate(cls, cost_item: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates)
contracted_cost_item_rates.remove(cost_item)
props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates)
@@ -570,7 +603,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def contract_cost_item_rate(cls, cost_item: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates)
contracted_cost_item_rates.append(cost_item)
props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates)
@@ -605,7 +638,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def load_schedule_of_rates_tree(cls, schedule_of_rates: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.is_cost_update_enabled = False
props.cost_item_rates.clear()
props.columns.clear()
@@ -689,13 +722,15 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def is_cost_schedule_active(cls, cost_schedule: ifcopenshell.entity_instance) -> bool:
- return True if cost_schedule.id() == bpy.context.scene.BIMCostProperties.active_cost_schedule_id else False
+ props = cls.get_cost_props()
+ return True if cost_schedule.id() == props.active_cost_schedule_id else False
@classmethod
def get_active_cost_schedule(cls) -> Union[ifcopenshell.entity_instance, None]:
- if not bpy.context.scene.BIMCostProperties.active_cost_schedule_id:
+ props = cls.get_cost_props()
+ if not props.active_cost_schedule_id:
return None
- return tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id)
+ return tool.Ifc.get().by_id(props.active_cost_schedule_id)
@classmethod
def highlight_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None:
@@ -707,13 +742,11 @@ class Cost(bonsai.core.tool.Cost):
expand_ancestors(parent_cost)
cls.load_cost_schedule_tree()
- cost_props = bpy.context.scene.BIMCostProperties
+ cost_props = cls.get_cost_props()
if not cost_item.id() in [item.ifc_definition_id for item in cost_props.cost_items]:
expand_ancestors(cost_item)
- cost_item_index = [item.ifc_definition_id for item in bpy.context.scene.BIMCostProperties.cost_items].index(
- cost_item.id()
- ) or 0
- bpy.context.scene.BIMCostProperties.active_cost_item_index = cost_item_index
+ cost_item_index = [item.ifc_definition_id for item in cost_props.cost_items].index(cost_item.id()) or 0
+ cost_props.active_cost_item_index = cost_item_index
@classmethod
def get_cost_items_for_product(cls, product: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
@@ -732,7 +765,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def load_product_cost_items(cls, product: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
props.is_cost_update_enabled = False
props.product_cost_items.clear()
cost_items = ifcopenshell.util.cost.get_cost_items_for_product(product)
@@ -766,7 +799,7 @@ class Cost(bonsai.core.tool.Cost):
def toggle_cost_item_parent_change(cls, cost_item: Optional[ifcopenshell.entity_instance] = None) -> None:
if not cost_item:
return
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
if props.change_cost_item_parent:
props.active_cost_item_id = cost_item.id()
props.cost_item_editing_type = "PARENT"
@@ -781,8 +814,9 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def disable_editing_cost_item_parent(cls) -> None:
- bpy.context.scene.BIMCostProperties.active_cost_item_id = 0
- bpy.context.scene.BIMCostProperties.change_cost_item_parent = False
+ props = cls.get_cost_props()
+ props.active_cost_item_id = 0
+ props.change_cost_item_parent = False
@classmethod
def load_cost_item_quantities(cls, cost_item: Optional[ifcopenshell.entity_instance] = None) -> None:
@@ -822,7 +856,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def get_currency_attributes(cls) -> dict[str, str]:
- props = bpy.context.scene.BIMCostProperties
+ props = cls.get_cost_props()
currency = props.currency
if currency == "CUSTOM":
currency = props.custom_currency
diff --git a/src/bonsai/bonsai/tool/sequence.py b/src/bonsai/bonsai/tool/sequence.py
index 046906387e..4b3e1064ee 100644
--- a/src/bonsai/bonsai/tool/sequence.py
+++ b/src/bonsai/bonsai/tool/sequence.py
@@ -42,17 +42,25 @@ from typing import Optional, Any, Union, Literal, TYPE_CHECKING, Iterable
if TYPE_CHECKING:
import bonsai.bim.prop
- from bonsai.bim.module.sequence.prop import BIMTaskTreeProperties
+ from bonsai.bim.module.sequence.prop import BIMTaskTreeProperties, BIMWorkScheduleProperties, BIMAnimationProperties
class Sequence(bonsai.core.tool.Sequence):
RELATED_OBJECT_TYPE = Literal["RESOURCE", "PRODUCT", "CONTROL"]
+ @classmethod
+ def get_animation_props(cls) -> BIMAnimationProperties:
+ return bpy.context.scene.BIMAnimationProperties
+
@classmethod
def get_task_tree_props(cls) -> BIMTaskTreeProperties:
return bpy.context.scene.BIMTaskTreeProperties
+ @classmethod
+ def get_work_schedule_props(cls) -> BIMWorkScheduleProperties:
+ return bpy.context.scene.BIMWorkScheduleProperties
+
@classmethod
def get_work_plan_attributes(cls) -> dict[str, Any]:
import bonsai.bim.module.sequence.helper as helper
@@ -119,7 +127,7 @@ class Sequence(bonsai.core.tool.Sequence):
attributes[prop.name] = helper.parse_duration(prop.string_value)
return True
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
return bonsai.bim.helper.export_attributes(props.work_schedule_attributes, callback)
@classmethod
@@ -129,23 +137,25 @@ class Sequence(bonsai.core.tool.Sequence):
prop.string_value = "" if prop.is_null else data[name]
return True
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.work_schedule_attributes.clear()
bonsai.bim.helper.import_attributes2(work_schedule, props.work_schedule_attributes, callback)
@classmethod
def enable_editing_work_schedule(cls, work_schedule: ifcopenshell.entity_instance) -> None:
- bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = work_schedule.id()
- bpy.context.scene.BIMWorkScheduleProperties.editing_type = "WORK_SCHEDULE"
+ props = cls.get_work_schedule_props()
+ props.active_work_schedule_id = work_schedule.id()
+ props.editing_type = "WORK_SCHEDULE"
@classmethod
def disable_editing_work_schedule(cls) -> None:
- bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = 0
+ props = cls.get_work_schedule_props()
+ props.active_work_schedule_id = 0
@classmethod
def enable_editing_work_schedule_tasks(cls, work_schedule: Union[ifcopenshell.entity_instance, None]) -> None:
if work_schedule:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.active_work_schedule_id = work_schedule.id()
props.editing_type = "TASKS"
@@ -153,7 +163,7 @@ class Sequence(bonsai.core.tool.Sequence):
def load_task_tree(cls, work_schedule: ifcopenshell.entity_instance) -> None:
props = cls.get_task_tree_props()
props.tasks.clear()
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
cls.contracted_tasks = json.loads(props.contracted_tasks)
related_objects_ids = cls.get_sorted_tasks_ids(ifcopenshell.util.sequence.get_root_tasks(work_schedule))
@@ -162,13 +172,15 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def get_sorted_tasks_ids(cls, tasks: list[ifcopenshell.entity_instance]) -> list[int]:
+ props = cls.get_work_schedule_props()
+
def get_sort_key(task):
# Sorting only applies to actual tasks, not the WBS
# for rel in task.IsNestedBy:
# for object in rel.RelatedObjects:
# if object.is_a("IfcTask"):
# return "0000000000" + (task.Identification or "")
- column_type, name = bpy.context.scene.BIMWorkScheduleProperties.sort_column.split(".")
+ column_type, name = props.sort_column.split(".")
if column_type == "IfcTask":
return task.get_info(task)[name] or ""
elif column_type == "IfcTaskTime" and task.TaskTime:
@@ -179,12 +191,12 @@ class Sequence(bonsai.core.tool.Sequence):
s = sort_keys[i]
return [int(text) if text.isdigit() else text.lower() for text in _nsre.split(s)]
- if bpy.context.scene.BIMWorkScheduleProperties.sort_column:
+ if props.sort_column:
sort_keys = {task.id(): get_sort_key(task) for task in tasks}
related_object_ids = sorted(sort_keys, key=natural_sort_key)
else:
related_object_ids = [task.id() for task in tasks]
- if bpy.context.scene.BIMWorkScheduleProperties.is_sort_reversed:
+ if props.is_sort_reversed:
related_object_ids.reverse()
return related_object_ids
@@ -205,7 +217,7 @@ class Sequence(bonsai.core.tool.Sequence):
# TODO: task argument is never used?
@classmethod
def load_task_properties(cls, task: Optional[ifcopenshell.entity_instance] = None) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
task_props = cls.get_task_tree_props()
tasks_with_visual_bar = cls.get_task_bar_list()
props.is_task_update_enabled = False
@@ -270,24 +282,26 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def get_active_work_schedule(cls) -> Union[ifcopenshell.entity_instance, None]:
- if not bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
+ props = cls.get_work_schedule_props()
+ if not props.active_work_schedule_id:
return None
- return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id)
+ return tool.Ifc.get().by_id(props.active_work_schedule_id)
@classmethod
def expand_task(cls, task: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
contracted_tasks = json.loads(props.contracted_tasks)
contracted_tasks.remove(task.id())
props.contracted_tasks = json.dumps(contracted_tasks)
@classmethod
def expand_all_tasks(cls) -> None:
- bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks = json.dumps([])
+ props = cls.get_work_schedule_props()
+ props.contracted_tasks = json.dumps([])
@classmethod
def contract_all_tasks(cls) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
tprops = cls.get_task_tree_props()
contracted_tasks = json.loads(props.contracted_tasks)
for task_item in tprops.tasks:
@@ -297,23 +311,24 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def contract_task(cls, task: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
contracted_tasks = json.loads(props.contracted_tasks)
contracted_tasks.append(task.id())
props.contracted_tasks = json.dumps(contracted_tasks)
@classmethod
def disable_work_schedule(cls) -> None:
- bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = 0
+ props = cls.get_work_schedule_props()
+ props.active_work_schedule_id = 0
@classmethod
def disable_selecting_deleted_task(cls) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
if props.active_task_id not in [
task.ifc_definition_id for task in cls.get_task_tree_props().tasks
]: # Task was deleted
- bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0
- bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
+ props.active_task_id = 0
+ props.active_task_time_id = 0
@classmethod
def get_checked_tasks(cls) -> list[ifcopenshell.entity_instance]:
@@ -323,11 +338,13 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def get_task_attribute_value(cls, attribute_name: str) -> Any:
- return bpy.context.scene.BIMWorkScheduleProperties.task_attributes.get(attribute_name).get_value()
+ props = cls.get_work_schedule_props()
+ return props.task_attributes.get(attribute_name).get_value()
@classmethod
def get_active_task(cls) -> ifcopenshell.entity_instance:
- return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_task_id)
+ props = cls.get_work_schedule_props()
+ return tool.Ifc.get().by_id(props.active_task_id)
@classmethod
def get_active_work_time(cls) -> ifcopenshell.entity_instance:
@@ -339,31 +356,34 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def load_task_attributes(cls, task: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.task_attributes.clear()
bonsai.bim.helper.import_attributes2(task, props.task_attributes)
@classmethod
def enable_editing_task_attributes(cls, task: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.active_task_id = task.id()
props.editing_task_type = "ATTRIBUTES"
@classmethod
def get_task_attributes(cls) -> dict[str, Any]:
- return bonsai.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.task_attributes)
+ props = cls.get_work_schedule_props()
+ return bonsai.bim.helper.export_attributes(props.task_attributes)
@classmethod
def load_task_time_attributes(cls, task_time: ifcopenshell.entity_instance) -> None:
import bonsai.bim.module.sequence.helper as helper
+ props = cls.get_work_schedule_props()
+
def callback(
name: str, prop: Union[bonsai.bim.prop.Attribute, None], data: dict[str, Any]
) -> Union[bool, None]:
if prop and prop.data_type == "string":
# TODO: Check actual attribute type instead of providing attribute names.
if name in ("ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"):
- duration_props = bpy.context.scene.BIMWorkScheduleProperties.durations_attributes.add()
+ duration_props = props.durations_attributes.add()
duration_props.name = name
if prop.is_null:
for key in duration_props.keys():
@@ -378,28 +398,30 @@ class Sequence(bonsai.core.tool.Sequence):
prop.string_value = "" if prop.is_null else data[name].isoformat()
return True
- props = bpy.context.scene.BIMWorkScheduleProperties
props.task_time_attributes.clear()
props.durations_attributes.clear()
bonsai.bim.helper.import_attributes2(task_time, props.task_time_attributes, callback)
@classmethod
def enable_editing_task_time(cls, task: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.active_task_id = task.id()
props.active_task_time_id = task.TaskTime.id()
props.editing_task_type = "TASKTIME"
@classmethod
def disable_editing_task(cls) -> None:
- bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0
- bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
- bpy.context.scene.BIMWorkScheduleProperties.editing_task_type = ""
+ props = cls.get_work_schedule_props()
+ props.active_task_id = 0
+ props.active_task_time_id = 0
+ props.editing_task_type = ""
@classmethod
def get_task_time_attributes(cls) -> dict[str, Any]:
import bonsai.bim.module.sequence.helper as helper
+ props = cls.get_work_schedule_props()
+
def callback(attributes, prop):
if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
if prop.is_null:
@@ -408,7 +430,6 @@ class Sequence(bonsai.core.tool.Sequence):
attributes[prop.name] = helper.parse_datetime(prop.string_value)
return True
elif prop.name in ["ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"]:
- props = bpy.context.scene.BIMWorkScheduleProperties
if prop.is_null:
attributes[prop.name] = None
for value in props.durations_attributes.values():
@@ -424,13 +445,12 @@ class Sequence(bonsai.core.tool.Sequence):
value = 0
return True
- props = bpy.context.scene.BIMWorkScheduleProperties
return bonsai.bim.helper.export_attributes(props.task_time_attributes, callback)
@classmethod
def load_task_resources(cls, task: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
- rprops = bpy.context.scene.BIMResourceProperties
+ props = cls.get_work_schedule_props()
+ rprops = cls.get_resource_props()
props.task_resources.clear()
rprops.is_resource_update_enabled = False
for resource in cls.get_task_resources(task) or []:
@@ -442,12 +462,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def get_task_inputs(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
- is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_inputs
+ props = cls.get_work_schedule_props()
+ is_deep = props.show_nested_inputs
return ifcopenshell.util.sequence.get_task_inputs(task, is_deep)
@classmethod
def get_task_outputs(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
- is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_outputs
+ props = cls.get_work_schedule_props()
+ is_deep = props.show_nested_outputs
return ifcopenshell.util.sequence.get_task_outputs(task, is_deep)
@classmethod
@@ -468,12 +490,13 @@ class Sequence(bonsai.core.tool.Sequence):
) -> Union[list[ifcopenshell.entity_instance], None]:
if not task:
return
- is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_resources
+ props = cls.get_work_schedule_props()
+ is_deep = props.show_nested_resources
return ifcopenshell.util.sequence.get_task_resources(task, is_deep)
@classmethod
def load_task_inputs(cls, inputs: list[ifcopenshell.entity_instance]) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.task_inputs.clear()
for input in inputs:
new = props.task_inputs.add()
@@ -482,7 +505,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def load_task_outputs(cls, outputs: list[ifcopenshell.entity_instance]) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.task_outputs.clear()
if outputs:
for output in outputs:
@@ -493,10 +516,9 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def get_highlighted_task(cls) -> Union[ifcopenshell.entity_instance, None]:
tasks = cls.get_task_tree_props().tasks
- if len(tasks) and len(tasks) > bpy.context.scene.BIMWorkScheduleProperties.active_task_index:
- return tool.Ifc.get().by_id(
- tasks[bpy.context.scene.BIMWorkScheduleProperties.active_task_index].ifc_definition_id
- )
+ props = cls.get_work_schedule_props()
+ if len(tasks) and len(tasks) > props.active_task_index:
+ return tool.Ifc.get().by_id(tasks[props.active_task_index].ifc_definition_id)
@classmethod
def get_direct_nested_tasks(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
@@ -648,37 +670,40 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def enable_editing_task_calendar(cls, task: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.active_task_id = task.id()
props.editing_task_type = "CALENDAR"
@classmethod
def enable_editing_task_sequence(cls) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.editing_task_type = "SEQUENCE"
@classmethod
def disable_editing_task_time(cls) -> None:
- bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0
- bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
+ props = cls.get_work_schedule_props()
+ props.active_task_id = 0
+ props.active_task_time_id = 0
@classmethod
def load_rel_sequence_attributes(cls, rel_sequence: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.sequence_attributes.clear()
bonsai.bim.helper.import_attributes2(rel_sequence, props.sequence_attributes)
@classmethod
def enable_editing_rel_sequence_attributes(cls, rel_sequence: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.active_sequence_id = rel_sequence.id()
props.editing_sequence_type = "ATTRIBUTES"
@classmethod
def load_lag_time_attributes(cls, lag_time: ifcopenshell.entity_instance) -> None:
+ props = cls.get_work_schedule_props()
+
def callback(name, prop, data):
if name == "LagValue":
- prop = bpy.context.scene.BIMWorkScheduleProperties.lag_time_attributes.add()
+ prop = props.lag_time_attributes.add()
prop.name = name
prop.is_null = data[name] is None
prop.is_optional = False
@@ -688,27 +713,29 @@ class Sequence(bonsai.core.tool.Sequence):
)
return True
- props = bpy.context.scene.BIMWorkScheduleProperties
props.lag_time_attributes.clear()
bonsai.bim.helper.import_attributes2(lag_time, props.lag_time_attributes, callback)
@classmethod
def enable_editing_sequence_lag_time(cls, rel_sequence: ifcopenshell.entity_instance) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.active_sequence_id = rel_sequence.id()
props.editing_sequence_type = "LAG_TIME"
@classmethod
def get_rel_sequence_attributes(cls) -> dict[str, Any]:
- return bonsai.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.sequence_attributes)
+ props = cls.get_work_schedule_props()
+ return bonsai.bim.helper.export_attributes(props.sequence_attributes)
@classmethod
def disable_editing_rel_sequence(cls) -> None:
- bpy.context.scene.BIMWorkScheduleProperties.active_sequence_id = 0
+ props = cls.get_work_schedule_props()
+ props.active_sequence_id = 0
@classmethod
def get_lag_time_attributes(cls) -> dict[str, Any]:
- return bonsai.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.lag_time_attributes)
+ props = cls.get_work_schedule_props()
+ return bonsai.bim.helper.export_attributes(props.lag_time_attributes)
@classmethod
def select_products(cls, products: Iterable[ifcopenshell.entity_instance]) -> None:
@@ -719,14 +746,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def add_task_column(cls, column_type: str, name: str, data_type: str) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
new = props.columns.add()
new.name = f"{column_type}.{name}"
new.data_type = data_type
@classmethod
def setup_default_task_columns(cls) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.columns.clear()
default_columns = ["ScheduleStart", "ScheduleFinish", "ScheduleDuration"]
for item in default_columns:
@@ -736,14 +763,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def remove_task_column(cls, name: str) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.columns.remove(props.columns.find(name))
if props.sort_column == name:
props.sort_column = ""
@classmethod
def set_task_sort_column(cls, column: str) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.sort_column = column
@classmethod
@@ -772,12 +799,13 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def is_work_schedule_active(cls, work_schedule):
- return (
- True if work_schedule.id() == bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id else False
- )
+ props = cls.get_work_schedule_props()
+ return True if work_schedule.id() == props.active_work_schedule_id else False
@classmethod
def go_to_task(cls, task):
+ props = cls.get_work_schedule_props()
+
def get_ancestor_ids(task):
ids = []
for rel in task.Nests or []:
@@ -785,11 +813,11 @@ class Sequence(bonsai.core.tool.Sequence):
ids.extend(get_ancestor_ids(rel.RelatingObject))
return ids
- contracted_tasks = json.loads(bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks)
+ contracted_tasks = json.loads(props.contracted_tasks)
for ancestor_id in get_ancestor_ids(task):
if ancestor_id in contracted_tasks:
contracted_tasks.remove(ancestor_id)
- bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks = json.dumps(contracted_tasks)
+ props.contracted_tasks = json.dumps(contracted_tasks)
work_schedule = cls.get_active_work_schedule()
cls.load_task_tree(work_schedule)
@@ -797,7 +825,7 @@ class Sequence(bonsai.core.tool.Sequence):
task_props = cls.get_task_tree_props()
expanded_tasks = [item.ifc_definition_id for item in task_props.tasks]
- bpy.context.scene.BIMWorkScheduleProperties.active_task_index = expanded_tasks.index(task.id()) or 0
+ props.active_task_index = expanded_tasks.index(task.id()) or 0
# TODO: proper typing
@classmethod
@@ -808,7 +836,7 @@ class Sequence(bonsai.core.tool.Sequence):
def update_visualisation_date(cls, start_date, finish_date):
if not (start_date and finish_date):
return
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.visualisation_start = ifcopenshell.util.date.canonicalise_time(start_date)
props.visualisation_finish = ifcopenshell.util.date.canonicalise_time(finish_date)
@@ -849,7 +877,7 @@ class Sequence(bonsai.core.tool.Sequence):
}
def create_task_bar_data(tasks, vertical_increment, collection):
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
settings = {
"viz_start": (
parser.parse(props.visualisation_start, dayfirst=True, fuzzy=True)
@@ -874,7 +902,8 @@ class Sequence(bonsai.core.tool.Sequence):
position_shift = task_data["start_frame"] * size_to_duration_ratio
bar_size = (task_data["finish_frame"] - task_data["start_frame"]) * size_to_duration_ratio
- color_progress = bpy.context.scene.BIMAnimationProperties.color_progress
+ anim_props = cls.get_animation_props()
+ color_progress = anim_props.color_progress
bar = add_bar(
material=material_progress,
vertical_increment=vertical_increment,
@@ -887,7 +916,7 @@ class Sequence(bonsai.core.tool.Sequence):
name=task_data["name"] + "/Progress Bar",
)
- color_full = bpy.context.scene.BIMAnimationProperties.color_full
+ color_full = anim_props.color_full
bar2 = add_bar(
material=material_full,
vertical_increment=vertical_increment,
@@ -1082,7 +1111,7 @@ class Sequence(bonsai.core.tool.Sequence):
"Color": (0.2, 0.2, 0.2),
},
}
- props = bpy.context.scene.BIMAnimationProperties
+ props = cls.get_animation_props()
props.task_output_colors.clear()
props.task_input_colors.clear()
for group, data in groups.items():
@@ -1102,14 +1131,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def get_start_date(cls) -> Union[datetime, None]:
- start = parser.parse(bpy.context.scene.BIMWorkScheduleProperties.visualisation_start, dayfirst=True, fuzzy=True)
+ props = cls.get_work_schedule_props()
+ start = parser.parse(props.visualisation_start, dayfirst=True, fuzzy=True)
return start or None
@classmethod
def get_finish_date(cls) -> Union[datetime, None]:
- finish = parser.parse(
- bpy.context.scene.BIMWorkScheduleProperties.visualisation_finish, dayfirst=True, fuzzy=True
- )
+ props = cls.get_work_schedule_props()
+ finish = parser.parse(props.visualisation_finish, dayfirst=True, fuzzy=True)
return finish or None
@classmethod
@@ -1241,7 +1270,7 @@ class Sequence(bonsai.core.tool.Sequence):
def calculate_using_frames(start, finish, animation_frames, real_duration):
return ((finish - start) / real_duration) * animation_frames
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
if not (props.visualisation_start and props.visualisation_finish):
return
@@ -1346,7 +1375,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def animate_input(cls, obj, start_frame, product_frame, animation_type):
- props = bpy.context.scene.BIMAnimationProperties
+ props = cls.get_animation_props()
color = props.task_input_colors[product_frame["type"]].color
if product_frame["type"] in ["LOGISTIC", "MOVE", "DISPOSAL"]:
cls.animate_destruction(obj, start_frame, product_frame, color, animation_type)
@@ -1355,7 +1384,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def animate_output(cls, obj, start_frame, product_frame, animation_type):
- props = bpy.context.scene.BIMAnimationProperties
+ props = cls.get_animation_props()
color = props.task_output_colors[product_frame["type"]].color
if product_frame["type"] in ["CONSTRUCTION", "INSTALLATION", "NOTDEFINED"]:
cls.animate_creation(obj, start_frame, product_frame, color)
@@ -1466,8 +1495,9 @@ class Sequence(bonsai.core.tool.Sequence):
obj.data.BIMDateTextProperties.start_frame = settings["start_frame"]
obj.data.BIMDateTextProperties.total_frames = int(settings["total_frames"])
- obj.data.BIMDateTextProperties.start = bpy.context.scene.BIMWorkScheduleProperties.visualisation_start
- obj.data.BIMDateTextProperties.finish = bpy.context.scene.BIMWorkScheduleProperties.visualisation_finish
+ props = cls.get_work_schedule_props()
+ obj.data.BIMDateTextProperties.start = props.visualisation_start
+ obj.data.BIMDateTextProperties.finish = props.visualisation_finish
append_handler(animate_text_handler)
@classmethod
@@ -1573,7 +1603,8 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def is_filter_by_active_schedule(cls) -> bool:
- return bpy.context.scene.BIMWorkScheduleProperties.filter_by_active_schedule
+ props = cls.get_work_schedule_props()
+ return props.filter_by_active_schedule
@classmethod
def get_tasks_for_product(
@@ -1585,7 +1616,7 @@ class Sequence(bonsai.core.tool.Sequence):
def load_product_related_tasks(
cls, task_inputs: list[ifcopenshell.entity_instance], task_ouputs: list[ifcopenshell.entity_instance]
) -> None:
- props = bpy.context.scene.BIMWorkScheduleProperties
+ props = cls.get_work_schedule_props()
props.product_input_tasks.clear()
props.product_output_tasks.clear()
for task in task_inputs or []:
@@ -1617,18 +1648,21 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def is_sorting_enabled(cls):
- return bpy.context.scene.BIMWorkScheduleProperties.sort_column
+ props = cls.get_work_schedule_props()
+ return props.sort_column
@classmethod
def is_sort_reversed(cls):
- return bpy.context.scene.BIMWorkScheduleProperties.is_sort_reversed
+ props = cls.get_work_schedule_props()
+ return props.is_sort_reversed
@classmethod
def get_user_predefined_type(cls):
- predefined_type = bpy.context.scene.BIMWorkScheduleProperties.work_schedule_predefined_types
+ props = cls.get_work_schedule_props()
+ predefined_type = props.work_schedule_predefined_types
object_type = None
if predefined_type == "USERDEFINED":
- object_type = bpy.context.scene.BIMWorkScheduleProperties.object_type
+ object_type = props.object_type
return predefined_type, object_type
@classmethod
@@ -1649,7 +1683,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def save_animation_color_scheme(cls, name: str) -> ifcopenshell.entity_instance:
- props = bpy.context.scene.BIMAnimationProperties
+ props = cls.get_animation_props()
colour_scheme = {
"Inputs": {cs.name: cs.color[0:3] for cs in props.task_input_colors},
"Outputs": {cs.name: cs.color[0:3] for cs in props.task_output_colors},
@@ -1674,7 +1708,7 @@ class Sequence(bonsai.core.tool.Sequence):
if data.get("type") == "BBIM_AnimationColorScheme":
inputs_color_scheme = data.get("colourscheme").get("Inputs")
outputs_color_scheme = data.get("colourscheme").get("Outputs")
- props = bpy.context.scene.BIMAnimationProperties
+ props = cls.get_animation_props()
props.task_input_colors.clear()
props.task_output_colors.clear()
for value, colour in inputs_color_scheme.items():
@@ -1711,26 +1745,30 @@ class Sequence(bonsai.core.tool.Sequence):
return False
@classmethod
- def get_task_bar_list(cls):
- return json.loads(bpy.context.scene.BIMWorkScheduleProperties.task_bars)
+ def get_task_bar_list(cls) -> list[int]:
+ props = cls.get_work_schedule_props()
+ return json.loads(props.task_bars)
@classmethod
- def add_task_bar(cls, task_id):
+ def add_task_bar(cls, task_id: int) -> None:
task_bars = cls.get_task_bar_list()
task_bars.append(task_id)
- bpy.context.scene.BIMWorkScheduleProperties.task_bars = json.dumps(task_bars)
+ props = cls.get_work_schedule_props()
+ props.task_bars = json.dumps(task_bars)
@classmethod
- def remove_task_bar(cls, task_id):
+ def remove_task_bar(cls, task_id: int) -> None:
task_bars = cls.get_task_bar_list()
if task_id in task_bars:
task_bars.remove(task_id)
- bpy.context.scene.BIMWorkScheduleProperties.task_bars = json.dumps(task_bars)
+ props = cls.get_work_schedule_props()
+ props.task_bars = json.dumps(task_bars)
@classmethod
def get_animation_color_scheme(cls):
- if len(bpy.context.scene.BIMAnimationProperties.saved_color_schemes) > 0:
- return tool.Ifc.get().by_id(int(bpy.context.scene.BIMAnimationProperties.saved_color_schemes))
+ props = cls.get_animation_props()
+ if len(props.saved_color_schemes) > 0:
+ return tool.Ifc.get().by_id(int(props.saved_color_schemes))
@classmethod
def parse_isodate_datetime(cls, datetime_str: str, include_time: bool) -> datetime:
diff --git a/src/ifccsv/ifccsv.py b/src/ifccsv/ifccsv.py
index 34e4b37b32..df4b54a0c0 100755
--- a/src/ifccsv/ifccsv.py
+++ b/src/ifccsv/ifccsv.py
@@ -456,7 +456,15 @@ class IfcCsv:
self.import_pd(ifc_file, df, attributes, null, empty, bool_true, bool_false)
def import_pd(
- self, ifc_file, df, attributes=None, null="-", empty="", bool_true="YES", bool_false="NO", concat=", "
+ self,
+ ifc_file: ifcopenshell.file,
+ df: "pd.DataFrame",
+ attributes: Optional[list[Union[str, None]]] = None,
+ null: str = "-",
+ empty: str = "",
+ bool_true: str = "YES",
+ bool_false: str = "NO",
+ concat: str = ", ",
) -> None:
headers = df.columns.tolist()
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py
index fe10220a87..682dd4558d 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py
@@ -65,28 +65,22 @@ def assign_material(
:param products: The list of IfcProducts to assign the material or material set
to.
- :type products: list[ifcopenshell.entity_instance]
:param type: Choose from "IfcMaterial", "IfcMaterialConstituentSet",
"IfcMaterialLayerSet", "IfcMaterialLayerSetUsage",
"IfcMaterialProfileSet", "IfcMaterialProfileSetUsage", or
"IfcMaterialList". Note that "Set Usages" may only be assigned to
occurrences, not types. Defaults to "IfcMaterial".
- :type type: str
:param material: The IfcMaterial or material set you are assigning here.
If type is Usage then no need to provide `material`, it will be deduced
from the element type automatically.
If IfcMaterial is provided as material and type is not IfcMaterial,
provided material will be ignored except for IfcMaterialList
where it will be used as part of the list.
- :type material: ifcopenshell.entity_instance, optional
:return: IfcRelAssociatesMaterial entity
or a list of IfcRelAssociatesMaterial entities
(possible if `type` is Usage
and `products` require different Usages)
or `None` if `products` was empty list.
- :rtype: Union[
- ifcopenshell.entity_instance,
- list[ifcopenshell.entity_instance], None]
Example:
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py
index 51decaaa98..18477c6038 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py
@@ -62,6 +62,8 @@ def remove_material_set(file: ifcopenshell.file, material: ifcopenshell.entity_i
set_items = material.MaterialConstituents or []
elif material.is_a("IfcMaterialList"):
set_items = []
+ else:
+ raise ValueError(f"Unknown material set type: {material.is_a()}")
for set_item in set_items:
file.remove(set_item)
file.remove(material)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py
index f8386490c7..2ed3fd9e14 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py
@@ -32,9 +32,7 @@ def unassign_material(file: ifcopenshell.file, products: list[ifcopenshell.entit
If the product does not have a material, nothing happens.
:param products: The list IfcProducts that may or may not have a material
- :type product: list[ifcopenshell.entity_instance]
:return: None
- :rtype: None
Example:
@@ -54,18 +52,16 @@ def unassign_material(file: ifcopenshell.file, products: list[ifcopenshell.entit
"""
usecase = Usecase()
usecase.file = file
- usecase.settings = {"products": products}
- return usecase.execute()
+ return usecase.execute(products)
class Usecase:
file: ifcopenshell.file
- settings: dict[str, Any]
- def execute(self):
- self.products = set(self.settings["products"])
- if not self.products:
+ def execute(self, products: list[ifcopenshell.entity_instance]) -> None:
+ if not products:
return
+ self.products = set(products)
self.remove_material_usages_from_types()
self.unassign_materials()