From 06a2cecb5103cca87e989d50c01bda56e503a1c9 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 6 May 2025 12:44:48 +0500 Subject: [PATCH] typing --- src/bonsai/bonsai/bim/module/type/data.py | 3 +- src/bonsai/bonsai/bim/module/type/operator.py | 22 +++++++---- src/bonsai/bonsai/bim/module/type/prop.py | 19 ++++++--- src/bonsai/bonsai/bim/module/type/ui.py | 11 ++++-- src/bonsai/bonsai/core/geometry.py | 39 +++++++++++-------- src/bonsai/bonsai/tool/type.py | 14 +++++-- src/bonsai/test/tool/test_type.py | 5 ++- 7 files changed, 73 insertions(+), 40 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/type/data.py b/src/bonsai/bonsai/bim/module/type/data.py index 37586df924..d93b86261d 100644 --- a/src/bonsai/bonsai/bim/module/type/data.py +++ b/src/bonsai/bonsai/bim/module/type/data.py @@ -67,7 +67,8 @@ class TypeData: if not relating_type_classes: return [] results = [] - relating_type_class = bpy.context.active_object.BIMTypeProperties.relating_type_class + assert (obj := bpy.context.active_object) + relating_type_class = tool.Type.get_object_type_props(obj).relating_type_class if not relating_type_class and relating_type_classes: relating_type_class = relating_type_classes[0][0] elements = tool.Ifc.get().by_type(relating_type_class) diff --git a/src/bonsai/bonsai/bim/module/type/operator.py b/src/bonsai/bonsai/bim/module/type/operator.py index fac2f93546..f8c5c1ae6b 100644 --- a/src/bonsai/bonsai/bim/module/type/operator.py +++ b/src/bonsai/bonsai/bim/module/type/operator.py @@ -46,9 +46,13 @@ class AssignType(bpy.types.Operator, tool.Ifc.Operator): related_object: str def _execute(self, context): - relating_type = tool.Ifc.get().by_id( - self.relating_type or int(context.active_object.BIMTypeProperties.relating_type) - ) + if self.relating_type: + relating_type = self.relating_type + else: + assert (obj := context.active_object) + props = tool.Type.get_object_type_props(obj) + relating_type = int(props.relating_type) + relating_type = tool.Ifc.get().by_id(relating_type) if self.related_object: related_objects = [bpy.data.objects[self.related_object]] else: @@ -130,8 +134,10 @@ class EnableEditingType(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} def execute(self, context): - context.active_object.BIMTypeProperties.is_editing_type = True - context.active_object.BIMTypeProperties.relating_type_object = None + assert (obj := context.active_object) + props = tool.Type.get_object_type_props(obj) + props.is_editing_type = True + props.relating_type_object = None return {"FINISHED"} @@ -142,8 +148,10 @@ class DisableEditingType(bpy.types.Operator): obj: bpy.props.StringProperty() def execute(self, context): - obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object - obj.BIMTypeProperties.is_editing_type = False + obj = bpy.data.objects[self.obj] if self.obj else context.active_object + assert obj + props = tool.Type.get_object_type_props(obj) + props.is_editing_type = False return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/type/prop.py b/src/bonsai/bonsai/bim/module/type/prop.py index 974e4b5d53..814d678390 100644 --- a/src/bonsai/bonsai/bim/module/type/prop.py +++ b/src/bonsai/bonsai/bim/module/type/prop.py @@ -21,6 +21,7 @@ import ifcopenshell.util.element import ifcopenshell.util.type from bonsai.bim.module.type.data import TypeData import bonsai.tool as tool +from typing import TYPE_CHECKING, Union from bpy.types import PropertyGroup from bpy.props import ( PointerProperty, @@ -34,23 +35,23 @@ from bpy.props import ( ) -def get_relating_type_class(self, context): +def get_relating_type_class(self: "BIMTypeProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]: if not TypeData.is_loaded: TypeData.load() return TypeData.data["relating_type_classes"] -def get_relating_type(self, context): +def get_relating_type(self: "BIMTypeProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]: if not TypeData.is_loaded: TypeData.load() return TypeData.data["relating_types"] -def update_relating_type_class(self, context): +def update_relating_type_class(self: "BIMTypeProperties", context: bpy.types.Context) -> None: TypeData.is_loaded = False -def update_relating_type_from_object(self, context): +def update_relating_type_from_object(self: "BIMTypeProperties", context: bpy.types.Context) -> None: if self.relating_type_object is None: return element = tool.Ifc.get_entity(self.relating_type_object) @@ -63,12 +64,12 @@ def update_relating_type_from_object(self, context): bpy.ops.bim.assign_type() -def is_object_class_applicable(self, obj): +def is_object_class_applicable(self: "BIMTypeProperties", obj: bpy.types.Object) -> bool: if not TypeData.is_loaded: TypeData.load() element = tool.Ifc.get_entity(obj) if not element: - return + return False element_type = ifcopenshell.util.element.get_type(element) if element_type is None: return False @@ -89,3 +90,9 @@ class BIMTypeProperties(PropertyGroup): update=update_relating_type_from_object, poll=is_object_class_applicable, ) + + if TYPE_CHECKING: + is_editing_type: bool + relating_type_class: str + relating_type: str + relating_type_object: Union[bpy.types.Object, None] diff --git a/src/bonsai/bonsai/bim/module/type/ui.py b/src/bonsai/bonsai/bim/module/type/ui.py index 291c37f1ba..701dff465d 100644 --- a/src/bonsai/bonsai/bim/module/type/ui.py +++ b/src/bonsai/bonsai/bim/module/type/ui.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with Bonsai. If not, see . +import bpy import bonsai.tool as tool import bonsai.bim.module.type.prop as type_prop from bpy.types import Panel @@ -55,8 +56,9 @@ class BIM_PT_type(Panel): else: self.draw_type_ui(context) - def draw_type_ui(self, context): - oprops = tool.Blender.get_object_bim_props(context.active_object) + def draw_type_ui(self, context: bpy.types.Context) -> None: + assert (obj := context.active_object) + oprops = tool.Blender.get_object_bim_props(obj) row = self.layout.row(align=True) row.label(text=f"{TypeData.data['total_instances']} Typed Objects") select_type_objects_row = row.row(align=True) @@ -66,9 +68,10 @@ class BIM_PT_type(Panel): op.element = oprops.ifc_definition_id row.operator("bim.auto_rename_occurrences", icon="ITALIC", text="") - def draw_product_ui(self, context): + def draw_product_ui(self, context: bpy.types.Context) -> None: layout = self.layout - props = context.active_object.BIMTypeProperties + assert (obj := context.active_object) + props = tool.Type.get_object_type_props(obj) if props.is_editing_type: row = layout.row(align=True) diff --git a/src/bonsai/bonsai/core/geometry.py b/src/bonsai/bonsai/core/geometry.py index f4eefcbcc0..b5fcd61285 100644 --- a/src/bonsai/bonsai/core/geometry.py +++ b/src/bonsai/bonsai/core/geometry.py @@ -26,9 +26,9 @@ if TYPE_CHECKING: def edit_object_placement( - ifc: tool.Ifc, - geometry: tool.Geometry, - surveyor: tool.Surveyor, + ifc: type[tool.Ifc], + geometry: type[tool.Geometry], + surveyor: type[tool.Surveyor], obj: Optional[bpy.types.Object] = None, apply_scale: bool = True, ) -> None: @@ -50,10 +50,10 @@ def edit_object_placement( def add_representation( - ifc: tool.Ifc, - geometry: tool.Geometry, - style: tool.Style, - surveyor: tool.Surveyor, + ifc: type[tool.Ifc], + geometry: type[tool.Geometry], + style: type[tool.Style], + surveyor: type[tool.Surveyor], obj: bpy.types.Object, context: ifcopenshell.entity_instance, ifc_representation_class: Optional[str] = None, @@ -110,8 +110,8 @@ def add_representation( def switch_representation( - ifc: tool.Ifc, - geometry: tool.Geometry, + ifc: type[tool.Ifc], + geometry: type[tool.Geometry], obj: bpy.types.Object, representation: ifcopenshell.entity_instance, should_reload: bool = True, @@ -139,13 +139,16 @@ def switch_representation( def get_representation_ifc_parameters( - geometry: tool.Geometry, obj: bpy.types.Object, should_sync_changes_first: bool = False + geometry: type[tool.Geometry], obj: bpy.types.Object, should_sync_changes_first: bool = False ) -> None: geometry.import_representation_parameters(geometry.get_object_data(obj)) def remove_representation( - ifc: tool.Ifc, geometry: tool.Geometry, obj: bpy.types.Object, representation: ifcopenshell.entity_instance + ifc: type[tool.Ifc], + geometry: type[tool.Geometry], + obj: bpy.types.Object, + representation: ifcopenshell.entity_instance, ) -> None: """Remove IFC representation from an object. @@ -183,7 +186,7 @@ def remove_representation( geometry.delete_data(data) -def purge_unused_representations(ifc: tool.Ifc, geometry: tool.Geometry) -> int: +def purge_unused_representations(ifc: type[tool.Ifc], geometry: type[tool.Geometry]) -> int: """Purge representations without inverses. :return: A number of purged representations. @@ -196,15 +199,17 @@ def purge_unused_representations(ifc: tool.Ifc, geometry: tool.Geometry) -> int: return purged_representations -def select_connection(geometry: tool.Geometry, connection: ifcopenshell.entity_instance) -> None: +def select_connection(geometry: type[tool.Geometry], connection: ifcopenshell.entity_instance) -> None: geometry.select_connection(connection) -def remove_connection(geometry: tool.Geometry, connection: ifcopenshell.entity_instance) -> None: +def remove_connection(geometry: type[tool.Geometry], connection: ifcopenshell.entity_instance) -> None: geometry.remove_connection(connection) -def get_similar_openings(ifc: tool.Ifc, opening: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: +def get_similar_openings( + ifc: type[tool.Ifc], opening: ifcopenshell.entity_instance +) -> list[ifcopenshell.entity_instance]: model = ifc.get() all_openings = model.by_type("IfcOpeningElement") similar_openings = [o for o in all_openings if o.ObjectPlacement == opening.ObjectPlacement and o != opening] @@ -212,7 +217,7 @@ def get_similar_openings(ifc: tool.Ifc, opening: ifcopenshell.entity_instance) - def get_similar_openings_building_objs( - ifc: tool.Ifc, similar_openings: list[ifcopenshell.entity_instance] + ifc: type[tool.Ifc], similar_openings: list[ifcopenshell.entity_instance] ) -> list[bpy.types.Object]: building_objs = [] for similar_opening in similar_openings: @@ -221,7 +226,7 @@ def get_similar_openings_building_objs( def edit_similar_opening_placement( - geometry: tool.Geometry, + geometry: type[tool.Geometry], opening: Optional[ifcopenshell.entity_instance] = None, similar_openings: Sequence[ifcopenshell.entity_instance] = (), ) -> None: diff --git a/src/bonsai/bonsai/tool/type.py b/src/bonsai/bonsai/tool/type.py index d05e3c6dc7..bda2ed4502 100644 --- a/src/bonsai/bonsai/tool/type.py +++ b/src/bonsai/bonsai/tool/type.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 bpy import ifcopenshell import ifcopenshell.util.element @@ -23,18 +24,25 @@ import ifcopenshell.util.representation import bonsai.core.tool import bonsai.core.geometry import bonsai.tool as tool -import bonsai.bim.helper -from typing import Union +from typing import Union, TYPE_CHECKING + +if TYPE_CHECKING: + from bonsai.bim.module.type.prop import BIMTypeProperties class Type(bonsai.core.tool.Type): + @classmethod + def get_object_type_props(cls, obj: bpy.types.Object) -> BIMTypeProperties: + return obj.BIMTypeProperties + @classmethod def change_object_data(cls, obj: bpy.types.Object, data: bpy.types.ID, is_global: bool = False) -> None: tool.Geometry.change_object_data(obj, data, is_global) @classmethod def disable_editing(cls, obj: bpy.types.Object) -> None: - obj.BIMTypeProperties.is_editing_type = False + props = cls.get_object_type_props(obj) + props.is_editing_type = False @classmethod def get_body_context(cls) -> ifcopenshell.entity_instance: diff --git a/src/bonsai/test/tool/test_type.py b/src/bonsai/test/tool/test_type.py index 4bb9c4b694..ed434e9cc5 100644 --- a/src/bonsai/test/tool/test_type.py +++ b/src/bonsai/test/tool/test_type.py @@ -52,9 +52,10 @@ class TestChangeObjectData(NewFile): class TestDisableEditing(NewFile): def test_run(self): obj = bpy.data.objects.new("Object", None) - obj.BIMTypeProperties.is_editing_type = True + props = tool.Type.get_object_type_props(obj) + props.is_editing_type = True subject.disable_editing(obj) - assert obj.BIMTypeProperties.is_editing_type is False + assert props.is_editing_type is False class TestGetBodyContext(NewFile):