diff --git a/src/bonsai/bonsai/bim/import_ifc.py b/src/bonsai/bonsai/bim/import_ifc.py
index 57d8a1b6b8..a24ace134d 100644
--- a/src/bonsai/bonsai/bim/import_ifc.py
+++ b/src/bonsai/bonsai/bim/import_ifc.py
@@ -1143,7 +1143,7 @@ class IfcImportSettings:
@staticmethod
def factory(context=None, input_file=None, logger=None):
scene_diff = bpy.context.scene.DiffProperties
- props = bpy.context.scene.BIMProjectProperties
+ props = tool.Project.get_project_props()
settings = IfcImportSettings()
settings.input_file = input_file
if logger is None:
diff --git a/src/bonsai/bonsai/bim/module/material/prop.py b/src/bonsai/bonsai/bim/module/material/prop.py
index b623083d5f..d95dec9ccd 100644
--- a/src/bonsai/bonsai/bim/module/material/prop.py
+++ b/src/bonsai/bonsai/bim/module/material/prop.py
@@ -111,6 +111,14 @@ class Material(PropertyGroup):
has_style: BoolProperty(name="Has Style", default=True)
total_elements: IntProperty(name="Total Elements")
+ if TYPE_CHECKING:
+ name: str
+ ifc_definition_id: int
+ is_category: bool
+ is_expanded: bool
+ has_style: bool
+ total_elements: int
+
class BIMMaterialProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing", default=False)
@@ -126,9 +134,21 @@ class BIMMaterialProperties(PropertyGroup):
@property
def active_material(self):
- if self.active_material_index < len(self.materials):
+ if 0 <= self.active_material_index < len(self.materials):
return self.materials[self.active_material_index]
+ if TYPE_CHECKING:
+ is_editing: bool
+ material_type: str
+ materials: bpy.types.bpy_prop_collection_idprop[Material]
+ active_material_index: int
+ profiles: str
+ active_material_id: int
+ material_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ editing_material_type: str
+ styles: str
+ contexts: str
+
class BIMObjectMaterialProperties(PropertyGroup):
material_type: EnumProperty(items=get_object_material_type, name="Material Type")
@@ -146,3 +166,16 @@ class BIMObjectMaterialProperties(PropertyGroup):
parameterized_profile_classes: EnumProperty(
items=get_parameterized_profile_classes, name="Parameterized Profile Classes"
)
+
+ if TYPE_CHECKING:
+ material_type: str
+ material: str
+ is_editing: bool
+ material_set_usage_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ material_set_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ active_material_set_item_id: int
+ material_set_item_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ material_set_item_profile_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
+ material_set_item_material: str
+ profile_classes: str
+ parameterized_profile_classes: str
diff --git a/src/bonsai/bonsai/bim/module/material/ui.py b/src/bonsai/bonsai/bim/module/material/ui.py
index e787e199d4..1ac0936dea 100644
--- a/src/bonsai/bonsai/bim/module/material/ui.py
+++ b/src/bonsai/bonsai/bim/module/material/ui.py
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see .
+from __future__ import annotations
import bonsai.bim.helper
import bonsai.tool as tool
import bpy
@@ -25,6 +26,10 @@ from bonsai.bim.helper import draw_attributes
from bonsai.bim.helper import prop_with_search
from bonsai.bim.module.material.data import MaterialsData, ObjectMaterialData
from bonsai.bim.module.drawing.helper import format_distance
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from bonsai.bim.module.material.prop import Material
class BIM_PT_materials(Panel):
@@ -395,7 +400,9 @@ class BIM_PT_object_material(Panel):
class BIM_UL_materials(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ def draw_item(
+ self, context, layout: bpy.types.UILayout, data, item: Material, icon, active_data, active_propname
+ ) -> None:
mprops = context.scene.BIMMaterialProperties
material_type = mprops.material_type
diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py
index 1ef8fd022d..48128f1643 100644
--- a/src/bonsai/bonsai/bim/module/project/operator.py
+++ b/src/bonsai/bonsai/bim/module/project/operator.py
@@ -207,7 +207,7 @@ class RefreshLibrary(bpy.types.Operator):
bl_label = "Refresh Library"
def execute(self, context):
- self.props = context.scene.BIMProjectProperties
+ self.props = tool.Project.get_project_props()
self.props.library_elements.clear()
self.props.library_breadcrumb.clear()
@@ -227,11 +227,16 @@ class ChangeLibraryElement(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
element_name: bpy.props.StringProperty()
+ if TYPE_CHECKING:
+ element_name: str
+
def execute(self, context):
- self.props = context.scene.BIMProjectProperties
- self.file = IfcStore.get_file()
- self.library_file = IfcStore.library_file
- ifc_classes = set()
+ self.props = tool.Project.get_project_props()
+ self.file = tool.Ifc.get()
+ library_file = IfcStore.library_file
+ assert library_file
+ self.library_file = library_file
+ ifc_classes: set[str] = set()
self.props.active_library_element = self.element_name
crumb = self.props.library_breadcrumb.add()
diff --git a/src/bonsai/bonsai/bim/module/project/prop.py b/src/bonsai/bonsai/bim/module/project/prop.py
index 349497cb7a..7cdf062e18 100644
--- a/src/bonsai/bonsai/bim/module/project/prop.py
+++ b/src/bonsai/bonsai/bim/module/project/prop.py
@@ -34,6 +34,7 @@ from bpy.props import (
IntProperty,
StringProperty,
)
+from typing import TYPE_CHECKING, Literal, Union
def get_export_schema(self: "BIMProjectProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
@@ -97,11 +98,18 @@ def update_filter_mode(self: "BIMProjectProperties", context: bpy.types.Context)
class LibraryElement(PropertyGroup):
+ # For IFC class groups only name is filled, for assets - all fields.
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
is_declared: BoolProperty(name="Is Declared", default=False)
is_appended: BoolProperty(name="Is Appended", default=False)
+ if TYPE_CHECKING:
+ name: str
+ ifc_definition_id: int
+ is_declared: bool
+ is_appended: bool
+
class FilterCategory(PropertyGroup):
name: StringProperty(name="Name")
@@ -109,6 +117,12 @@ class FilterCategory(PropertyGroup):
is_selected: BoolProperty(name="Is Selected", default=False)
total_elements: IntProperty(name="Total Elements")
+ if TYPE_CHECKING:
+ name: str
+ ifc_definition_id: int
+ is_selected: bool
+ total_elements: int
+
class Link(PropertyGroup):
name: StringProperty(
@@ -125,10 +139,21 @@ class Link(PropertyGroup):
type=bpy.types.Object,
)
+ if TYPE_CHECKING:
+ name: str
+ is_loaded: bool
+ is_selectable: bool
+ is_wireframe: bool
+ is_hidden: bool
+ empty_handle: Union[bpy.types.Object, None]
+
class EditedObj(PropertyGroup):
obj: PointerProperty(type=bpy.types.Object)
+ if TYPE_CHECKING:
+ obj: Union[bpy.types.Object, None]
+
class BIMProjectProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing", default=False)
@@ -238,12 +263,62 @@ class BIMProjectProperties(PropertyGroup):
edited_objs: bpy.props.CollectionProperty(type=EditedObj)
@property
- def clipping_planes_objs(self):
+ def clipping_planes_objs(self) -> list[bpy.types.Object]:
return list({cp.obj for cp in self.clipping_planes if cp.obj})
- def get_library_element_index(self, lib_element):
+ def get_library_element_index(self, lib_element: LibraryElement) -> int:
return next((i for i in range(len(self.library_elements)) if self.library_elements[i] == lib_element))
+ if TYPE_CHECKING:
+ is_editing: bool
+ is_loading: bool
+ mvd: str
+ author_name: str
+ author_email: str
+ organisation_name: str
+ organisation_email: str
+ authorisation: str
+ active_library_element: str
+ library_breadcrumb: bpy.types.bpy_prop_collection_idprop[StrProperty]
+ library_elements: bpy.types.bpy_prop_collection_idprop[LibraryElement]
+ active_library_element_index: int
+ filter_mode: Literal["NONE", "DECOMPOSITION", "IFC_CLASS", "IFC_TYPE", "WHITELIST", "BLACKLIST"]
+ total_elements: int
+ filter_categories: bpy.types.bpy_prop_collection_idprop[FilterCategory]
+ active_filter_category_index: int
+ filter_query: str
+ should_filter_spatial_elements: bool
+ geometry_library: Literal["opencascade", "cgal", "cgal-simple", "hybrid-cgal-simple-opencascade"]
+ should_use_cpu_multiprocessing: bool
+ should_merge_materials_by_colour: bool
+ should_stream: bool
+ should_load_geometry: bool
+ should_clean_mesh: bool
+ should_cache: bool
+ deflection_tolerance: float
+ angular_tolerance: float
+ void_limit: int
+ distance_limit: float
+ false_origin_mode: Literal["AUTOMATIC", "MANUAL", "DISABLED"]
+ false_origin: str
+ project_north: str
+ element_limit_mode: Literal["UNLIMITED", "RANGE"]
+ element_offset: int
+ element_limit: int
+ load_indexed_maps: bool
+ should_disable_undo_on_save: bool
+ links: bpy.types.bpy_prop_collection_idprop[Link]
+ active_link_index: int
+ export_schema: str
+ template_file: str
+ library_file: str
+ use_relative_project_path: bool
+ queried_obj: Union[bpy.types.Object, None]
+ queried_obj_root: Union[bpy.types.Object, None]
+ clipping_planes: bpy.types.bpy_prop_collection_idprop[ObjProperty]
+ clipping_planes_active: int
+ edited_objs: bpy.types.bpy_prop_collection_idprop[EditedObj]
+
class MeasureToolSettings(PropertyGroup):
measurement_type_items = [
@@ -253,3 +328,6 @@ class MeasureToolSettings(PropertyGroup):
]
measurement_type: bpy.props.EnumProperty(items=measurement_type_items, default="POLYLINE")
+
+ if TYPE_CHECKING:
+ measurement_type: Literal["SINGLE", "POLYLINE", "AREA"]
diff --git a/src/bonsai/bonsai/bim/module/project/ui.py b/src/bonsai/bonsai/bim/module/project/ui.py
index bcc50b1690..2613ed1245 100644
--- a/src/bonsai/bonsai/bim/module/project/ui.py
+++ b/src/bonsai/bonsai/bim/module/project/ui.py
@@ -16,6 +16,8 @@
# 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 os
import bonsai.bim
import bonsai.tool as tool
@@ -23,6 +25,10 @@ from bonsai.bim.helper import prop_with_search
from bpy.types import Panel, Menu, UIList
from bonsai.bim.ifc import IfcStore
from bonsai.bim.module.project.data import ProjectData, LinksData
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from bonsai.bim.module.project.prop import LibraryElement, BIMProjectProperties
def file_import_menu(self, context):
@@ -441,7 +447,16 @@ class BIM_PT_links(Panel):
class BIM_UL_library(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ def draw_item(
+ self,
+ context,
+ layout: bpy.types.UILayout,
+ data: BIMProjectProperties,
+ item: LibraryElement,
+ icon,
+ active_data,
+ active_propname,
+ ):
if item:
row = layout.row(align=True)
if not item.ifc_definition_id:
diff --git a/src/bonsai/bonsai/tool/project.py b/src/bonsai/bonsai/tool/project.py
index ca4350def0..ea137067a3 100644
--- a/src/bonsai/bonsai/tool/project.py
+++ b/src/bonsai/bonsai/tool/project.py
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see .
+from __future__ import annotations
import os
import bpy
import ifcopenshell
@@ -33,10 +34,17 @@ import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore
from ifcopenshell.api.project.append_asset import APPENDABLE_ASSET_TYPES
from pathlib import Path
-from typing import Optional, Union
+from typing import Optional, Union, TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from bonsai.bim.module.project.prop import BIMProjectProperties
class Project(bonsai.core.tool.Project):
+ @classmethod
+ def get_project_props(cls) -> BIMProjectProperties:
+ return bpy.context.scene.BIMProjectProperties
+
@classmethod
def append_all_types_from_template(cls, template: str) -> None:
# TODO refactor