diff --git a/src/bonsai/bonsai/bim/module/profile/data.py b/src/bonsai/bonsai/bim/module/profile/data.py index 38a32d34f7..cc28d8b008 100644 --- a/src/bonsai/bonsai/bim/module/profile/data.py +++ b/src/bonsai/bonsai/bim/module/profile/data.py @@ -21,6 +21,7 @@ import bpy.utils import bpy.utils.previews import ifcopenshell.util.doc import bonsai.tool as tool +from typing import Any def refresh(): @@ -28,7 +29,7 @@ def refresh(): class ProfileData: - data = {} + data: dict[str, Any] = {} failed_previews: set[int] = set() preview_collection = bpy.utils.previews.new() is_loaded = False @@ -37,6 +38,7 @@ class ProfileData: def load(cls): cls.data = { "total_profiles": cls.total_profiles(), + "does_active_profile_exist": cls.does_active_profile_exist(), "active_profile_users": cls.active_profile_users(), "profile_classes": cls.profile_classes(), "is_arbitrary_profile": cls.is_arbitrary_profile(), @@ -50,12 +52,30 @@ class ProfileData: return len([p for p in tool.Ifc.get().by_type("IfcProfileDef") if p.ProfileName]) @classmethod - def active_profile_users(cls): - profiles_props = tool.Profile.get_profile_props() - if profiles_props.active_profile_index >= len(profiles_props.profiles): + def update_active_profile_data(cls) -> None: + cls.data["does_active_profile_exist"] = cls.does_active_profile_exist() + cls.data["active_profile_users"] = cls.active_profile_users() + + @classmethod + def does_active_profile_exist(cls) -> bool: + """ + Currently not sure if our UI is always preserving existing named profiles, + so this check is added to avoid breaking UI in case of a missing profile. + """ + active_profile = tool.Profile.get_active_profile_ui() + if active_profile is None: + return False + profile_ifc = tool.Ifc.get_entity_by_id(active_profile.ifc_definition_id) + return profile_ifc is not None + + @classmethod + def active_profile_users(cls) -> int: + active_profile = tool.Profile.get_active_profile_ui() + if active_profile is None: + return 0 + profile_ifc = tool.Ifc.get_entity_by_id(active_profile.ifc_definition_id) + if profile_ifc is None: return 0 - profile_prop = profiles_props.profiles[profiles_props.active_profile_index] - profile_ifc = tool.Ifc.get().by_id(profile_prop.ifc_definition_id) return tool.Ifc.get().get_total_inverses(profile_ifc) @classmethod diff --git a/src/bonsai/bonsai/bim/module/profile/prop.py b/src/bonsai/bonsai/bim/module/profile/prop.py index d63c555689..e2da08091f 100644 --- a/src/bonsai/bonsai/bim/module/profile/prop.py +++ b/src/bonsai/bonsai/bim/module/profile/prop.py @@ -46,7 +46,9 @@ def get_profile_classes(self: "BIMProfileProperties", context: bpy.types.Context def update_profile_name(self: "Profile", context: bpy.types.Context) -> None: from bonsai.bim.handler import refresh_ui_data - profile = tool.Ifc.get().by_id(self.ifc_definition_id) + profile = tool.Ifc.get_entity_by_id(self.ifc_definition_id) + if not profile: + return profile.ProfileName = self.name refresh_ui_data() @@ -63,15 +65,17 @@ class Profile(PropertyGroup): def update_active_profile_index(self: "BIMProfileProperties", context: bpy.types.Context) -> None: - ProfileData.data["active_profile_users"] = ProfileData.active_profile_users() + ProfileData.update_active_profile_data() class BIMProfileProperties(PropertyGroup): is_editing: BoolProperty(name="Is Editing") profiles: CollectionProperty(name="Profiles", type=Profile) active_profile_index: IntProperty(name="Active Profile Index", update=update_active_profile_index) - active_profile_id: IntProperty(name="Active Profile Id") - active_arbitrary_profile_id: IntProperty(name="Active Arbitrary Profile Id") + active_profile_id: IntProperty(name="Active Profile Id", description="Currently edited profile ID (attributes).") + active_arbitrary_profile_id: IntProperty( + name="Active Arbitrary Profile Id", description="Currently edited arbitrary profile ID." + ) profile_attributes: CollectionProperty(name="Profile Attributes", type=Attribute) profile_classes: EnumProperty(items=get_profile_classes, name="Profile Classes") is_filtering_material_profiles: bpy.props.BoolProperty( diff --git a/src/bonsai/bonsai/bim/module/profile/ui.py b/src/bonsai/bonsai/bim/module/profile/ui.py index ee2a93f107..1de4a077c6 100644 --- a/src/bonsai/bonsai/bim/module/profile/ui.py +++ b/src/bonsai/bonsai/bim/module/profile/ui.py @@ -75,6 +75,8 @@ class BIM_PT_profiles(Panel): if not self.props.is_editing: return + does_active_profile_exist: bool = ProfileData.data["does_active_profile_exist"] + row = self.layout.row(align=True) if self.props.profile_classes == "IfcArbitraryClosedProfileDef": split = row.split(factor=0.5, align=True) @@ -86,7 +88,14 @@ class BIM_PT_profiles(Panel): row.prop(self.props, "profile_classes", text="") row.operator("bim.add_profile_def", text="", icon="ADD") - if active_profile: + if active_profile and not does_active_profile_exist: + box = self.layout.box() + box.label(icon="ERROR", text=f"Active profile is missing from IFC project.") + row = box.row(align=True) + row.label(text="Reload Profiles UI.") + row.operator("bim.load_profiles", text="", icon="FILE_REFRESH") + + elif active_profile and does_active_profile_exist: row = self.layout.row(align=True) row.alignment = "RIGHT" @@ -122,7 +131,7 @@ class BIM_PT_profiles(Panel): row = self.layout.row() row.prop(self.props, "is_filtering_material_profiles", text="Filter Material Profiles") - if active_profile: + if active_profile and does_active_profile_exist: users_of_profile = ProfileData.data["active_profile_users"] self.layout.label(icon="INFO", text=f"Profile has {users_of_profile} inverse relationship(s) in project")