diff --git a/src/bonsai/bonsai/bim/ifc.py b/src/bonsai/bonsai/bim/ifc.py index b5aa7df820..5578b4cc84 100644 --- a/src/bonsai/bonsai/bim/ifc.py +++ b/src/bonsai/bonsai/bim/ifc.py @@ -105,7 +105,7 @@ class IfcStore: IfcStore.session_files = {} @staticmethod - def get_file(): + def get_file() -> ifcopenshell.file | None: if IfcStore.file is None: props = tool.Blender.get_bim_props() IfcStore.set_path(props.ifc_file) @@ -117,14 +117,14 @@ class IfcStore: return IfcStore.file @staticmethod - def set_path(value): + def set_path(value: str) -> None: IfcStore.path = value # Interpret relative paths as relative to .blend file. if IfcStore.path and not os.path.isabs(IfcStore.path): IfcStore.path = os.path.abspath(os.path.join(bpy.path.abspath("//"), IfcStore.path)) @staticmethod - def get_cache(): + def get_cache() -> ifcopenshell.geom.serializers.hdf5 | None: if IfcStore.cache is None and IfcStore.path: props = tool.Blender.get_bim_props() ifc_key = IfcStore.path + IfcStore.file.wrapped_data.header.file_name.time_stamp @@ -163,7 +163,7 @@ class IfcStore: return IfcStore.cache @staticmethod - def update_cache(): + def update_cache() -> None: if not IfcStore.cache: return assert IfcStore.cache_path @@ -183,7 +183,7 @@ class IfcStore: IfcStore.get_cache() @staticmethod - def load_file(path) -> None: + def load_file(path: str) -> None: if not os.path.isfile(path): return extension = path.split(".")[-1] diff --git a/src/bonsai/bonsai/bim/module/classification/data.py b/src/bonsai/bonsai/bim/module/classification/data.py index 86c7d27443..c8d40a32a1 100644 --- a/src/bonsai/bonsai/bim/module/classification/data.py +++ b/src/bonsai/bonsai/bim/module/classification/data.py @@ -33,7 +33,7 @@ def refresh(): class ClassificationsData: - data = {} + data: dict[str, Any] = {} is_loaded = False @classmethod @@ -94,7 +94,7 @@ class ReferencesData: class ClassificationReferencesData(ReferencesData): - data = {} + data: dict[str, Any] = {} is_loaded = False @classmethod @@ -118,7 +118,7 @@ class ClassificationReferencesData(ReferencesData): class MaterialClassificationsData(ReferencesData): - data = {} + data: dict[str, Any] = {} is_loaded = False @classmethod @@ -134,8 +134,7 @@ class MaterialClassificationsData(ReferencesData): results = [] props = tool.Material.get_material_props() - if props.materials and props.active_material_index < len(props.materials): - material = props.materials[props.active_material_index] + if material := props.active_material: if material.ifc_definition_id: element = tool.Ifc.get().by_id(material.ifc_definition_id) for reference in ifcopenshell.util.classification.get_references(element): @@ -146,7 +145,7 @@ class MaterialClassificationsData(ReferencesData): class CostClassificationsData(ReferencesData): - data = {} + data: dict[str, Any] = {} is_loaded = False @classmethod diff --git a/src/bonsai/bonsai/bim/module/classification/ui.py b/src/bonsai/bonsai/bim/module/classification/ui.py index e2a27edbfe..41f638c93b 100644 --- a/src/bonsai/bonsai/bim/module/classification/ui.py +++ b/src/bonsai/bonsai/bim/module/classification/ui.py @@ -28,7 +28,7 @@ from bonsai.bim.module.classification.data import ( MaterialClassificationsData, CostClassificationsData, ) -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any, Union if TYPE_CHECKING: from bonsai.bim.module.classification.prop import BIMClassificationProperties, ClassificationReference @@ -113,9 +113,15 @@ class BIM_PT_classifications(Panel): class ReferenceUI: layout: bpy.types.UILayout + data: type[ + Union[ + ClassificationReferencesData, + MaterialClassificationsData, + CostClassificationsData, + ] + ] - def draw_ui(self, context): - obj = context.active_object + def draw_ui(self, context) -> None: self.sprops = tool.Classification.get_classification_props() self.bprops = tool.Bsdd.get_bsdd_props() self.props = tool.Classification.get_classification_reference_props() @@ -133,7 +139,7 @@ class ReferenceUI: else: self.draw_reference_ui(reference) - def draw_add_ui(self, context): + def draw_add_ui(self, context) -> None: row = self.layout.row(align=True) row.label(text="Source", icon="OUTLINER") row.prop(self.sprops, "classification_source", text="") @@ -145,7 +151,7 @@ class ReferenceUI: else: self.draw_add_bsdd_ui(context) - def draw_add_manual_ui(self, context): + def draw_add_manual_ui(self, context) -> None: row = self.layout.row() row.prop(self.props, "classifications", text="") if self.props.is_adding: @@ -158,7 +164,7 @@ class ReferenceUI: row = self.layout.row() row.operator("bim.enable_adding_manual_classification_reference", text="Add Reference", icon="ADD") - def draw_add_bsdd_ui(self, context): + def draw_add_bsdd_ui(self, context) -> None: row = self.layout.row(align=True) row.prop(self.bprops, "keyword", text="") row.prop(self.bprops, "should_filter_ifc_class", text="", icon="FILTER") @@ -185,7 +191,7 @@ class ReferenceUI: op.obj = self.obj op.obj_type = self.obj_type - def draw_add_file_ui(self, context): + def draw_add_file_ui(self, context) -> None: if not self.data.data["active_classification_library"]: row = self.layout.row(align=True) row.label(text="No Active Classification Library", icon="ERROR") @@ -219,13 +225,13 @@ class ReferenceUI: "active_library_reference_index", ) - def draw_editable_ui(self): + def draw_editable_ui(self) -> None: row = self.layout.row(align=True) row.operator("bim.edit_classification_reference", text="Save changes", icon="CHECKMARK") row.operator("bim.disable_editing_classification_reference", text="", icon="CANCEL") bonsai.bim.helper.draw_attributes(self.props.reference_attributes, self.layout) - def draw_reference_ui(self, reference): + def draw_reference_ui(self, reference: dict[str, Any]) -> None: row = self.layout.row(align=True) if self.file.schema == "IFC2X3": name = reference["ItemReference"] or "No Identification" diff --git a/src/bonsai/bonsai/bim/module/group/operator.py b/src/bonsai/bonsai/bim/module/group/operator.py index c55c1f6a1f..3172c2c38a 100644 --- a/src/bonsai/bonsai/bim/module/group/operator.py +++ b/src/bonsai/bonsai/bim/module/group/operator.py @@ -31,6 +31,8 @@ class LoadGroups(bpy.types.Operator, tool.Ifc.Operator): bl_label = "Load Groups" bl_options = {"REGISTER", "UNDO"} + expanded_groups: list[int] + def _execute(self, context): self.props = tool.Blender.get_group_props() self.expanded_groups = json.loads(self.props.expanded_groups_json) @@ -82,6 +84,7 @@ class ToggleGroup(bpy.types.Operator, tool.Ifc.Operator): def _execute(self, context): props = tool.Blender.get_group_props() + expanded_groups: set[int] expanded_groups = set(json.loads(props.expanded_groups_json)) if self.option == "Expand": expanded_groups.add(self.ifc_definition_id) diff --git a/src/bonsai/bonsai/bim/module/group/prop.py b/src/bonsai/bonsai/bim/module/group/prop.py index f87e021cde..e96f6f8a2a 100644 --- a/src/bonsai/bonsai/bim/module/group/prop.py +++ b/src/bonsai/bonsai/bim/module/group/prop.py @@ -70,6 +70,7 @@ class BIMGroupProperties(PropertyGroup): active_group_index: IntProperty(name="Active Group Index", update=update_active_group_index) active_group_id: IntProperty(name="Active Group Id") expanded_groups_json: StringProperty(name="JSON String", default="[]") + """JSON serialized list[group_id].""" if TYPE_CHECKING: group_attributes: bpy.types.bpy_prop_collection_idprop[Attribute] diff --git a/src/bonsai/bonsai/bim/module/material/prop.py b/src/bonsai/bonsai/bim/module/material/prop.py index 04a596e73f..c65dae65fe 100644 --- a/src/bonsai/bonsai/bim/module/material/prop.py +++ b/src/bonsai/bonsai/bim/module/material/prop.py @@ -169,9 +169,8 @@ class BIMMaterialProperties(PropertyGroup): contexts: EnumProperty(items=get_contexts, name="Contexts") @property - def active_material(self): - if 0 <= self.active_material_index < len(self.materials): - return self.materials[self.active_material_index] + def active_material(self) -> Material | None: + return tool.Blender.get_active_uilist_element(self.materials, self.active_material_index) if TYPE_CHECKING: is_editing: bool diff --git a/src/bonsai/bonsai/bim/module/pset/data.py b/src/bonsai/bonsai/bim/module/pset/data.py index 1dbf533ae7..802a7f8319 100644 --- a/src/bonsai/bonsai/bim/module/pset/data.py +++ b/src/bonsai/bonsai/bim/module/pset/data.py @@ -176,8 +176,8 @@ class MaterialPsetsData(Data): def load(cls): ifc_definition_id = None props = tool.Material.get_material_props() - if props.materials and props.active_material_index < len(props.materials): - ifc_definition_id = props.materials[props.active_material_index].ifc_definition_id + if material := props.active_material: + ifc_definition_id = material.ifc_definition_id cls.data = { "ifc_definition_id": ifc_definition_id, @@ -189,8 +189,7 @@ class MaterialPsetsData(Data): @classmethod def pset_name(cls): props = tool.Material.get_material_props() - if props.materials and props.active_material_index < len(props.materials): - material = props.materials[props.active_material_index] + if material := props.active_material: if material.ifc_definition_id: material = tool.Ifc.get().by_id(material.ifc_definition_id) category = getattr(material, "Category", None) or None diff --git a/src/bonsai/bonsai/bim/module/pset/ui.py b/src/bonsai/bonsai/bim/module/pset/ui.py index d09bfe072d..bfe19909eb 100644 --- a/src/bonsai/bonsai/bim/module/pset/ui.py +++ b/src/bonsai/bonsai/bim/module/pset/ui.py @@ -474,9 +474,10 @@ class BIM_PT_material_psets(Panel): return False def draw(self, context): + assert self.layout props = tool.Material.get_material_props() - if props.materials and props.active_material_index < len(props.materials): - ifc_definition_id = props.materials[props.active_material_index].ifc_definition_id + if material := props.active_material: + ifc_definition_id = material.ifc_definition_id if not MaterialPsetsData.is_loaded: MaterialPsetsData.load() diff --git a/src/bonsai/bonsai/bim/ui.py b/src/bonsai/bonsai/bim/ui.py index fe291fb107..0afb579837 100644 --- a/src/bonsai/bonsai/bim/ui.py +++ b/src/bonsai/bonsai/bim/ui.py @@ -40,6 +40,8 @@ from natsort import natsorted class IFCFileSelector: + layout: bpy.types.UILayout + # Avoid overriding blender prop annotations at runtime. if TYPE_CHECKING: filepath: str @@ -76,6 +78,7 @@ class IFCFileSelector: assert isinstance(context.space_data, bpy.types.SpaceFileBrowser) # Access filepath & Directory https://blender.stackexchange.com/a/207665 params = context.space_data.params + assert params # Decode byte string https://stackoverflow.com/a/47737082/ directory = Path(params.directory.decode("utf-8")) filepath = os.path.join(directory, params.filename) @@ -122,6 +125,7 @@ class BIM_PT_section_plane(Panel): bl_parent_id = "BIM_PT_tab_sandbox" def draw(self, context): + assert self.layout layout = self.layout layout.use_property_split = True props = tool.Blender.get_bim_props() @@ -145,6 +149,7 @@ class BIM_PT_section_with_cappings(Panel): bl_parent_id = "BIM_PT_tab_sandbox" def draw(self, context): + assert self.layout layout = self.layout wm = context.window_manager row = layout.row(align=True) @@ -189,7 +194,16 @@ class BIM_UL_clipping_plane(bpy.types.UIList): class BIM_UL_generic(bpy.types.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: bpy.types.PropertyGroup, + icon, + active_data, + active_propname, + ) -> None: if item: layout.prop(item, "name", text="", emboss=False) else: