From f18c93c9ee5eb498edac89abcdc2c28425067dea Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 15 Jul 2025 11:19:32 +0500 Subject: [PATCH] typing --- src/bonsai/bonsai/bim/module/bcf/operator.py | 3 +++ src/bonsai/bonsai/bim/module/material/ui.py | 2 +- .../bonsai/bim/module/model/polyline.py | 8 +++--- src/bonsai/bonsai/bim/module/pset/prop.py | 16 +++++++----- src/bonsai/bonsai/bim/module/pset/ui.py | 26 ++++++++++++------- src/bonsai/bonsai/bim/module/search/prop.py | 2 +- src/bonsai/bonsai/core/tool.py | 1 - src/bonsai/bonsai/tool/debug.py | 3 +++ src/bonsai/bonsai/tool/material.py | 10 +------ src/bonsai/bonsai/tool/pset.py | 4 +-- src/bonsai/bonsai/tool/resource.py | 8 ++++-- src/bonsai/bonsai/tool/spatial.py | 4 +-- ...ayout_horizontal_alignment_by_pi_method.py | 1 + .../test/fixtures/rules/generate_10.py | 2 ++ .../recipes/FixArchiCADToRevitDoorSwings.py | 19 ++++++++------ 15 files changed, 65 insertions(+), 44 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/bcf/operator.py b/src/bonsai/bonsai/bim/module/bcf/operator.py index ed2859ac8b..b208a7e5fb 100644 --- a/src/bonsai/bonsai/bim/module/bcf/operator.py +++ b/src/bonsai/bonsai/bim/module/bcf/operator.py @@ -1208,9 +1208,11 @@ class ActivateBcfViewpoint(bpy.types.Operator): self.file = tool.Ifc.get() bcfxml = bcfstore.BcfStore.get_bcfxml() assert bcfxml + assert context.scene props = tool.Bcf.get_bcf_props() blender_topic = props.active_topic + assert blender_topic topic = bcfxml.topics[blender_topic.name] if self.viewpoint_guid: viewpoint_guid = self.viewpoint_guid @@ -1234,6 +1236,7 @@ class ActivateBcfViewpoint(bpy.types.Operator): cam_height = context.scene.render.resolution_y cam_aspect = cam_width / cam_height + assert isinstance(obj.data, bpy.types.Camera) obj.data.background_images.clear() if viewpoint.snapshot: obj.data.show_background_images = True diff --git a/src/bonsai/bonsai/bim/module/material/ui.py b/src/bonsai/bonsai/bim/module/material/ui.py index 99efecce65..019ee57a94 100644 --- a/src/bonsai/bonsai/bim/module/material/ui.py +++ b/src/bonsai/bonsai/bim/module/material/ui.py @@ -48,7 +48,7 @@ class BIM_PT_materials(Panel): MaterialsData.load() self.props = tool.Material.get_material_props() - material = tool.Material.get_active_material_item() + material = self.props.active_material material_id = material.ifc_definition_id if material else None row = self.layout.row(align=True) diff --git a/src/bonsai/bonsai/bim/module/model/polyline.py b/src/bonsai/bonsai/bim/module/model/polyline.py index cacb288a89..4acd27dd07 100644 --- a/src/bonsai/bonsai/bim/module/model/polyline.py +++ b/src/bonsai/bonsai/bim/module/model/polyline.py @@ -371,7 +371,9 @@ def get_vertical_profile_preview_data( return data -def get_horizontal_profile_preview_data(context, relating_type): +def get_horizontal_profile_preview_data( + context: bpy.types.Context, relating_type: ifcopenshell.entity_instance +) -> dict[str, Any]: material = ifcopenshell.util.element.get_material(relating_type) try: profile_curve = material.MaterialProfiles[0].Profile @@ -385,7 +387,7 @@ def get_horizontal_profile_preview_data(context, relating_type): polyline_data = context.scene.BIMPolylineProperties.insertion_polyline polyline_points = polyline_data[0].polyline_points if polyline_data else [] if len(polyline_points) < 2: - return + return {} for point in polyline_points: polyline_verts.append(Vector((point.x, point.y, point.z))) polyline_edges = [(i, i + 1) for i in range(len(polyline_verts) - 1)] @@ -433,7 +435,7 @@ def get_horizontal_profile_preview_data(context, relating_type): case "9": grouped_verts = [(v[0] + x_offset, v[1] - y_offset, v[2]) for v in grouped_verts] - data = {} + data: dict[str, Any] = {} data["verts"] = [] data["edges"] = [] data["tris"] = [] diff --git a/src/bonsai/bonsai/bim/module/pset/prop.py b/src/bonsai/bonsai/bim/module/pset/prop.py index 1ecdd8c378..d5cf85791d 100644 --- a/src/bonsai/bonsai/bim/module/pset/prop.py +++ b/src/bonsai/bonsai/bim/module/pset/prop.py @@ -115,7 +115,7 @@ def get_material_pset_names(self: "PsetProperties", context: object) -> tool.Ble return MaterialPsetsData.data["pset_name"] -def get_material_set_pset_names(self: "PsetProperties", context: object): +def get_material_set_pset_names(self: "PsetProperties", context: object) -> tool.Blender.BLENDER_ENUM_ITEMS: global psetnames if not ObjectMaterialData.is_loaded: ObjectMaterialData.load() @@ -128,12 +128,14 @@ def get_material_set_pset_names(self: "PsetProperties", context: object): return psetnames[ifc_class] -def get_material_set_item_pset_names(self: "PsetProperties", context: object) -> tool.Blender.BLENDER_ENUM_ITEMS: +def get_material_set_item_pset_names( + self: "PsetProperties", context: bpy.types.Context +) -> tool.Blender.BLENDER_ENUM_ITEMS: global psetnames obj = context.active_object assert obj omprops = tool.Material.get_object_material_props(obj) - if not omprops.active_material_set_item_id: + if not (ifc_definition_id := omprops.active_material_set_item_id): return [] ifc_class = tool.Ifc.get().by_id(ifc_definition_id).is_a() if ifc_class not in psetnames: @@ -151,7 +153,7 @@ def get_task_qto_names(self: "PsetProperties", context: object) -> tool.Blender. return qtonames[ifc_class] -def get_resource_pset_names(self: "PsetProperties", context: object) -> tool.Blender.BLENDER_ENUM_ITEMS: +def get_resource_pset_names(self: "PsetProperties", context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS: global psetnames rprops = context.scene.BIMResourceProperties rtprops = context.scene.BIMResourceTreeProperties @@ -162,7 +164,7 @@ def get_resource_pset_names(self: "PsetProperties", context: object) -> tool.Ble return psetnames[ifc_class] -def get_resource_qto_names(self: "PsetProperties", context: object) -> tool.Blender.BLENDER_ENUM_ITEMS: +def get_resource_qto_names(self: "PsetProperties", context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS: global qtonames rprops = context.scene.BIMResourceProperties rtprops = context.scene.BIMResourceTreeProperties @@ -223,7 +225,9 @@ def get_qto_name(self: "PsetProperties", context: bpy.types.Context) -> tool.Ble results = get_resource_qto_names(self, context) elif prop_type == "GroupPsetProperties": results = get_group_qto_names(self, context) - return [("BBIM_CUSTOM", "Custom Qto", "Create a quantity set without using a template."), None] + results + else: + assert False + return [("BBIM_CUSTOM", "Custom Qto", "Create a quantity set without using a template."), None] + list(results) def get_object_qto_name(self: "PsetProperties", context: object) -> tool.Blender.BLENDER_ENUM_ITEMS: diff --git a/src/bonsai/bonsai/bim/module/pset/ui.py b/src/bonsai/bonsai/bim/module/pset/ui.py index bfe19909eb..bb571a842e 100644 --- a/src/bonsai/bonsai/bim/module/pset/ui.py +++ b/src/bonsai/bonsai/bim/module/pset/ui.py @@ -484,7 +484,7 @@ class BIM_PT_material_psets(Panel): elif ifc_definition_id != MaterialPsetsData.data["ifc_definition_id"]: MaterialPsetsData.load() - props = context.scene.MaterialPsetProperties + props = tool.Pset.get_pset_props("", "Material") row = self.layout.row(align=True) prop_with_search(row, props, "pset_name", text="") op = row.operator("bim.add_pset", icon="ADD", text="") @@ -524,6 +524,7 @@ class BIM_PT_material_set_item_psets(Panel): if not MaterialSetItemPsetsData.is_loaded: MaterialSetItemPsetsData.load() + assert self.layout obj = context.active_object assert obj omprops = tool.Material.get_object_material_props(obj) @@ -531,7 +532,7 @@ class BIM_PT_material_set_item_psets(Panel): self.layout.label(text="No Material Set Item Edited.") return - props = obj.MaterialSetItemPsetProperties + props = tool.Pset.get_pset_props(obj.name, "MaterialSetItem") row = self.layout.row(align=True) prop_with_search(row, props, "pset_name", text="") op = row.operator("bim.add_pset", icon="ADD", text="") @@ -570,7 +571,8 @@ class BIM_PT_task_qtos(Panel): if not TaskQtosData.is_loaded: TaskQtosData.load() - props = context.scene.TaskPsetProperties + assert self.layout + props = tool.Pset.get_pset_props("", "Task") row = self.layout.row(align=True) row.prop(props, "qto_name", text="") op = row.operator("bim.add_qto", icon="ADD", text="") @@ -604,7 +606,8 @@ class BIM_PT_resource_qtos(Panel): if not ResourceQtosData.is_loaded: ResourceQtosData.load() - props = context.scene.ResourcePsetProperties + assert self.layout + props = tool.Pset.get_pset_props("", "Resource") row = self.layout.row(align=True) row.prop(props, "qto_name", text="") op = row.operator("bim.add_qto", icon="ADD", text="") @@ -638,7 +641,8 @@ class BIM_PT_resource_psets(Panel): if not ResourcePsetsData.is_loaded: ResourcePsetsData.load() - props = context.scene.ResourcePsetProperties + assert self.layout + props = tool.Pset.get_pset_props("", "Resource") row = self.layout.row(align=True) prop_with_search(row, props, "pset_name", text="") op = row.operator("bim.add_pset", icon="ADD", text="") @@ -672,7 +676,8 @@ class BIM_PT_group_qtos(Panel): if not GroupQtosData.is_loaded: GroupQtosData.load() - props = context.scene.GroupPsetProperties + assert self.layout + props = tool.Pset.get_pset_props("", "Group") row = self.layout.row(align=True) row.prop(props, "qto_name", text="") op = row.operator("bim.add_qto", icon="ADD", text="") @@ -706,7 +711,8 @@ class BIM_PT_group_psets(Panel): if not GroupPsetData.is_loaded: GroupPsetData.load() - props = context.scene.GroupPsetProperties + assert self.layout + props = tool.Pset.get_pset_props("", "Group") row = self.layout.row(align=True) prop_with_search(row, props, "pset_name", text="") op = row.operator("bim.add_pset", icon="ADD", text="") @@ -750,7 +756,8 @@ class BIM_PT_profile_psets(Panel): ): ProfilePsetsData.load() - props = context.scene.ProfilePsetProperties + assert self.layout + props = tool.Pset.get_pset_props("", "Profile") row = self.layout.row(align=True) prop_with_search(row, props, "pset_name", text="") op = row.operator("bim.add_pset", icon="ADD", text="") @@ -782,7 +789,8 @@ class BIM_PT_work_schedule_psets(Panel): if not WorkSchedulePsetsData.is_loaded: WorkSchedulePsetsData.load() - props = context.scene.WorkSchedulePsetProperties + assert self.layout + props = tool.Pset.get_pset_props("", "WorkSchedule") row = self.layout.row(align=True) prop_with_search(row, props, "pset_name", text="") op = row.operator("bim.add_pset", icon="ADD", text="") diff --git a/src/bonsai/bonsai/bim/module/search/prop.py b/src/bonsai/bonsai/bim/module/search/prop.py index 66fda03bc3..110d015b01 100644 --- a/src/bonsai/bonsai/bim/module/search/prop.py +++ b/src/bonsai/bonsai/bim/module/search/prop.py @@ -33,7 +33,7 @@ from bpy.props import ( FloatVectorProperty, CollectionProperty, ) -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Literal def get_element_key(self: "BIMSearchProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]: diff --git a/src/bonsai/bonsai/core/tool.py b/src/bonsai/bonsai/core/tool.py index d4eaf402a8..4ab7e9d9ab 100644 --- a/src/bonsai/bonsai/core/tool.py +++ b/src/bonsai/bonsai/core/tool.py @@ -543,7 +543,6 @@ class Material: def ensure_material_assigned(cls, elements, material_type, material): pass def ensure_material_unassigned(cls, elements): pass def ensure_new_material_set_is_valid(cls, material): pass - def get_active_material_item(cls): pass def get_active_material_type(cls): pass def get_default_material(cls): pass def get_elements_by_material(cls, material): pass diff --git a/src/bonsai/bonsai/tool/debug.py b/src/bonsai/bonsai/tool/debug.py index 1999f84829..7b25c33401 100644 --- a/src/bonsai/bonsai/tool/debug.py +++ b/src/bonsai/bonsai/tool/debug.py @@ -19,6 +19,7 @@ from __future__ import annotations import os import json +import bmesh import bpy import ifcopenshell.ifcopenshell_wrapper as W import ifcopenshell.api.material @@ -70,6 +71,7 @@ class Debug(bonsai.core.tool.Debug): mesh = bpy.data.meshes.new("Debug") bm.to_mesh(mesh) obj = bpy.data.objects.new(name, mesh) + assert bpy.context.scene bpy.context.scene.collection.objects.link(obj) return obj @@ -80,6 +82,7 @@ class Debug(bonsai.core.tool.Debug): mesh = bpy.data.meshes.new("Debug") mesh.from_pydata(verts, edges, []) obj = bpy.data.objects.new(name, mesh) + assert bpy.context.scene bpy.context.scene.collection.objects.link(obj) return obj diff --git a/src/bonsai/bonsai/tool/material.py b/src/bonsai/bonsai/tool/material.py index 2f2c916299..5e8b531c5c 100644 --- a/src/bonsai/bonsai/tool/material.py +++ b/src/bonsai/bonsai/tool/material.py @@ -76,14 +76,6 @@ class Material(bonsai.core.tool.Material): def get_elements_by_material(cls, material: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: return ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material) - @classmethod - def get_active_material_item(cls) -> Union[MaterialItem, None]: - """Get active material props item if index is valid, otherwise, return None.""" - props = tool.Material.get_material_props() - if 0 <= props.active_material_index < len(props.materials): - return props.materials[props.active_material_index] - return None - @classmethod def get_material_category(cls, material: ifcopenshell.entity_instance) -> str: # IfcMaterial has Category since IFC4. @@ -95,7 +87,7 @@ class Material(bonsai.core.tool.Material): # Store active category name to reselect it later. # Occurs when we expand/contract all categories. - active_item = cls.get_active_material_item() + active_item = props.active_material previously_selected_category = None if active_item and active_item.is_category: previously_selected_category = active_item.name diff --git a/src/bonsai/bonsai/tool/pset.py b/src/bonsai/bonsai/tool/pset.py index 3390c02c4c..f8e9857aa1 100644 --- a/src/bonsai/bonsai/tool/pset.py +++ b/src/bonsai/bonsai/tool/pset.py @@ -75,11 +75,11 @@ class Pset(bonsai.core.tool.Pset): @classmethod def get_pset_props(cls, obj: str, obj_type: tool.Ifc.OBJECT_TYPE) -> PsetProperties: if obj_type == "Object": - return bpy.data.objects.get(obj).PsetProperties + return bpy.data.objects[obj].PsetProperties elif obj_type == "Material": return bpy.context.scene.MaterialPsetProperties elif obj_type == "MaterialSetItem": - return bpy.data.objects.get(obj).MaterialSetItemPsetProperties + return bpy.data.objects[obj].MaterialSetItemPsetProperties elif obj_type == "Task": return bpy.context.scene.TaskPsetProperties elif obj_type == "Resource": diff --git a/src/bonsai/bonsai/tool/resource.py b/src/bonsai/bonsai/tool/resource.py index 80a3cbe6b2..65448440f6 100644 --- a/src/bonsai/bonsai/tool/resource.py +++ b/src/bonsai/bonsai/tool/resource.py @@ -33,9 +33,10 @@ import ifcopenshell.util.date as ifcdateutils import ifcopenshell.util.cost import ifcopenshell.util.resource import ifcopenshell.util.constraint -from typing import Any, Union, TYPE_CHECKING +from typing import Any, Union, TYPE_CHECKING, Literal if TYPE_CHECKING: + from bonsai.bim.prop import Attribute from bonsai.bim.module.resource.prop import BIMResourceProperties @@ -142,7 +143,9 @@ class Resource(bonsai.core.tool.Resource): @classmethod def get_resource_time_attributes(cls) -> dict[str, Any]: - def callback(attributes, prop): + import bonsai.bim.module.sequence.helper as helper + + def callback(attributes: dict[str, Any], prop: Attribute) -> bool: if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime": if prop.is_null: attributes[prop.name] = None @@ -155,6 +158,7 @@ class Resource(bonsai.core.tool.Resource): return True attributes[prop.name] = helper.parse_duration(prop.string_value) return True + return False props = bpy.context.scene.BIMResourceProperties return bonsai.bim.helper.export_attributes(props.resource_time_attributes, callback) diff --git a/src/bonsai/bonsai/tool/spatial.py b/src/bonsai/bonsai/tool/spatial.py index acb3b3ebcf..d29ec909ff 100644 --- a/src/bonsai/bonsai/tool/spatial.py +++ b/src/bonsai/bonsai/tool/spatial.py @@ -561,8 +561,8 @@ class Spatial(bonsai.core.tool.Spatial): @classmethod def get_active_container(cls) -> Union[ifcopenshell.entity_instance, None]: props = cls.get_spatial_props() - if props.active_container_index < len(props.containers): - container = tool.Ifc.get().by_id(props.containers[props.active_container_index].ifc_definition_id) + if active_container := props.active_container: + container = tool.Ifc.get().by_id(active_container.ifc_definition_id) return container @classmethod diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/layout_horizontal_alignment_by_pi_method.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/layout_horizontal_alignment_by_pi_method.py index 0ed3c0dfd1..0be11c95d5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/alignment/layout_horizontal_alignment_by_pi_method.py +++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/layout_horizontal_alignment_by_pi_method.py @@ -17,6 +17,7 @@ # along with IfcOpenShell. If not, see . import ifcopenshell +import ifcopenshell.api.alignment from ifcopenshell import entity_instance import math diff --git a/src/ifcopenshell-python/test/fixtures/rules/generate_10.py b/src/ifcopenshell-python/test/fixtures/rules/generate_10.py index 674271c812..b717411dfd 100644 --- a/src/ifcopenshell-python/test/fixtures/rules/generate_10.py +++ b/src/ifcopenshell-python/test/fixtures/rules/generate_10.py @@ -5,6 +5,8 @@ defaults = {"Girth": 1.0, "WallThickness": 0.11} depths = [2.0, 3.0] widths = [0.2, 0.3] +Girth, Depth, WallThickness, Width = 0.0, 0.0, 0.0, 0.0 + for d, w in itertools.product(depths, widths): D = dict(defaults, Depth=d, Width=w) diff --git a/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py b/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py index e6a34716eb..06f3639d02 100644 --- a/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py +++ b/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py @@ -17,17 +17,21 @@ # along with IfcPatch. If not, see . -import os +import logging import math +import os +from typing import Union + +import ifcpatch import ifcopenshell import ifcopenshell.geom -import ifcopenshell.util.unit -import ifcopenshell.util.schema import ifcopenshell.util.element +import ifcopenshell.util.schema +import ifcopenshell.util.unit -class Patcher: - def __init__(self, file, logger): +class Patcher(ifcpatch.BasePatcher): + def __init__(self, file: ifcopenshell.file, logger: Union[logging.Logger, None] = None): """Fix missing door swings in Revit when viewing ArchiCAD IFCs ArchiCAD has the ability to store 2D data with objects like doors for @@ -56,10 +60,9 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "FixArchiCADToRevitDoorSwings", "arguments": []}) """ - self.file = file - self.logger = logger + super().__init__(file, logger) - def patch(self): + def patch(self) -> None: # Revit has the ability to switch between 3D representations and 2D # representations (e.g. in plan view). It does this by detecting IFC # representations that belong to either the Model Body representation