From 8469d10c8521fce0bfcbebf00a76659300f4b519 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 31 Jan 2025 12:17:43 +0500 Subject: [PATCH] Project library UI - make filter optional and allow assigning to different libraries --- .../bonsai/bim/module/project/operator.py | 63 ++++++++++++------- src/bonsai/bonsai/bim/module/project/prop.py | 20 +++++- src/bonsai/bonsai/bim/module/project/ui.py | 8 ++- src/bonsai/bonsai/tool/project.py | 15 ++++- 4 files changed, 79 insertions(+), 27 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 9a2c562818..60b263b6a7 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -288,11 +288,17 @@ class ChangeLibraryElement(bpy.types.Operator): is_declarable = project_libraries_exist and element.is_a("IfcObjectDefinition") new.is_declarable = is_declarable + selected_library = self.props.selected_project_library # is_declared. if not is_declarable: new.is_declared = False - elif (has_context := element.HasContext) and has_context[0].RelatingContext.is_a("IfcProjectLibrary"): - new.is_declared = True + elif has_context := element.HasContext: + relating_context: ifcopenshell.entity_instance + relating_context = has_context[0].RelatingContext + if selected_library in ("-", "*"): + new.is_declared = relating_context.is_a("IfcProjectLibrary") + else: + new.is_declared = relating_context == self.library_file.by_id(int(selected_library)) # is_appended. try: @@ -328,9 +334,13 @@ class RewindLibrary(bpy.types.Operator): class AssignLibraryDeclaration(bpy.types.Operator): bl_idname = "bim.assign_library_declaration" bl_label = "Assign Library Declaration" + bl_description = "Assign element to the active library. If no specific library selected, will assign it to the first library in the file." bl_options = {"REGISTER", "UNDO"} definition: bpy.props.IntProperty() + if TYPE_CHECKING: + definition: int + def execute(self, context): IfcStore.begin_transaction(self) IfcStore.library_file.begin_transaction() @@ -341,17 +351,22 @@ class AssignLibraryDeclaration(bpy.types.Operator): return result def _execute(self, context): - self.props = context.scene.BIMProjectProperties - self.file = IfcStore.library_file - ifcopenshell.api.run( - "project.assign_declaration", - self.file, - definitions=[self.file.by_id(self.definition)], - relating_context=self.file.by_type("IfcProjectLibrary")[0], + props = tool.Project.get_project_props() + library_file = IfcStore.library_file + assert library_file + + if props.selected_project_library in ("*", "-"): + project_library = library_file.by_type("IfcProjectLibrary")[0] + else: + project_library = library_file.by_id(int(props.selected_project_library)) + + ifcopenshell.api.project.assign_declaration( + library_file, + definitions=[library_file.by_id(self.definition)], + relating_context=project_library, ) - element_name = self.props.active_library_element - bpy.ops.bim.rewind_library() - bpy.ops.bim.change_library_element(element_name=element_name) + + tool.Project.update_current_library_page() return {"FINISHED"} def rollback(self, data): @@ -367,6 +382,9 @@ class UnassignLibraryDeclaration(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} definition: bpy.props.IntProperty() + if TYPE_CHECKING: + definition: int + def execute(self, context): IfcStore.begin_transaction(self) IfcStore.library_file.begin_transaction() @@ -377,17 +395,18 @@ class UnassignLibraryDeclaration(bpy.types.Operator): return result def _execute(self, context): - self.props = context.scene.BIMProjectProperties - self.file = IfcStore.library_file - ifcopenshell.api.run( - "project.unassign_declaration", - self.file, - definitions=[self.file.by_id(self.definition)], - relating_context=self.file.by_type("IfcProjectLibrary")[0], + props = tool.Project.get_project_props() + library_file = IfcStore.library_file + assert library_file + + element = library_file.by_id(self.definition) + ifcopenshell.api.project.unassign_declaration( + library_file, + definitions=[library_file.by_id(self.definition)], + relating_context=element.HasContext[0].RelatingContext, ) - element_name = self.props.active_library_element - bpy.ops.bim.rewind_library() - bpy.ops.bim.change_library_element(element_name=element_name) + + tool.Project.update_current_library_page() return {"FINISHED"} def rollback(self, data): diff --git a/src/bonsai/bonsai/bim/module/project/prop.py b/src/bonsai/bonsai/bim/module/project/prop.py index c5a97078d0..ae01fe2bf0 100644 --- a/src/bonsai/bonsai/bim/module/project/prop.py +++ b/src/bonsai/bonsai/bim/module/project/prop.py @@ -67,7 +67,11 @@ def update_library_file(self: "BIMProjectProperties", context: bpy.types.Context def update_selected_project_library(self: "BIMProjectProperties", context: bpy.types.Context) -> None: - bpy.ops.bim.refresh_library() + if self.filter_by_library: + bpy.ops.bim.refresh_library() + else: + # Ensure `.is_declared` up to date. + tool.Project.update_current_library_page() def get_project_libaries(self: "BIMProjectProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]: @@ -76,6 +80,13 @@ def get_project_libaries(self: "BIMProjectProperties", context: bpy.types.Contex return ProjectLibraryData.data["project_libraries_enum"] +def filter_by_library_update(self: "BIMProjectProperties", context: bpy.types.Context) -> None: + if self.filter_by_library and self.selected_project_library == "*": + # Filter is toggled from OFF to ON, so it was showing all elements previously either way. + return + bpy.ops.bim.refresh_library() + + def update_filter_mode(self: "BIMProjectProperties", context: bpy.types.Context) -> None: self.filter_categories.clear() if self.filter_mode == "NONE": @@ -282,6 +293,12 @@ class BIMProjectProperties(PropertyGroup): description="Project library to display elements from", update=update_selected_project_library, ) + filter_by_library: BoolProperty( + name="Filter by Library", + description="Filter library elements based on selected library. If unselected can be used to assign selected library to library elements.", + default=True, + update=filter_by_library_update, + ) use_relative_project_path: BoolProperty(name="Use Relative Project Path", default=False) queried_obj: bpy.props.PointerProperty(type=bpy.types.Object) @@ -348,6 +365,7 @@ class BIMProjectProperties(PropertyGroup): library_file: str selected_project_library: Union[Literal["*", "-"], str] + filter_by_library: bool use_relative_project_path: bool queried_obj: Union[bpy.types.Object, None] diff --git a/src/bonsai/bonsai/bim/module/project/ui.py b/src/bonsai/bonsai/bim/module/project/ui.py index 175c23c391..d1c309df69 100644 --- a/src/bonsai/bonsai/bim/module/project/ui.py +++ b/src/bonsai/bonsai/bim/module/project/ui.py @@ -363,11 +363,17 @@ class BIM_PT_project_library(Panel): row.label(text="No Library Loaded", icon="ASSET_MANAGER") def draw_library_ul(self): + layout = self.layout + if not self.props.library_elements: row = self.layout.row() row.label(text="No Assets Found", icon="ERROR") return - self.layout.prop(self.props, "selected_project_library", text="") + + row = layout.row(align=True) + row.prop(self.props, "selected_project_library", text="") + row.prop(self.props, "filter_by_library", text="", icon="FILTER") + row = self.layout.row(align=True) row.label(text=self.props.active_library_element or "Top Level Assets") if self.props.active_library_element: diff --git a/src/bonsai/bonsai/tool/project.py b/src/bonsai/bonsai/tool/project.py index 81149dc898..4f5ffa8744 100644 --- a/src/bonsai/bonsai/tool/project.py +++ b/src/bonsai/bonsai/tool/project.py @@ -303,12 +303,14 @@ class Project(bonsai.core.tool.Project): library_file = IfcStore.library_file assert library_file - if props.selected_project_library == "*": + selected_project_library = props.selected_project_library if props.filter_by_library else "*" + + if selected_project_library == "*": def condition(elements: list[ifcopenshell.entity_instance]): yield from elements - elif props.selected_project_library == "-": + elif selected_project_library == "-": def condition(elements: list[ifcopenshell.entity_instance]): for element in elements: @@ -316,7 +318,7 @@ class Project(bonsai.core.tool.Project): yield element else: - project_library = library_file.by_id(int(props.selected_project_library)) + project_library = library_file.by_id(int(selected_project_library)) project_library_rels = set(project_library.Declares) if project_library_rels: @@ -333,3 +335,10 @@ class Project(bonsai.core.tool.Project): pass return condition + + @classmethod + def update_current_library_page(cls): + props = cls.get_project_props() + element_name = props.active_library_element + bpy.ops.bim.rewind_library() + bpy.ops.bim.change_library_element(element_name=element_name)