From 559636ef0b117036afc316b4f9c6e627ba0d0809 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 15 Sep 2025 18:16:35 +0500 Subject: [PATCH] IFC Header UI - display author and additional info in case if it's available --- src/bonsai/bonsai/bim/module/project/data.py | 7 +++ .../bonsai/bim/module/project/operator.py | 26 +++------- src/bonsai/bonsai/bim/module/project/prop.py | 8 +++ src/bonsai/bonsai/bim/module/project/ui.py | 47 +++++++++--------- src/bonsai/bonsai/tool/project.py | 49 ++++++++++++++++++- 5 files changed, 91 insertions(+), 46 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/project/data.py b/src/bonsai/bonsai/bim/module/project/data.py index 9ced81da06..1f51737205 100644 --- a/src/bonsai/bonsai/bim/module/project/data.py +++ b/src/bonsai/bonsai/bim/module/project/data.py @@ -44,6 +44,7 @@ class ProjectData: "library_file": cls.library_file(), "last_saved": cls.last_saved(), "total_elements": cls.total_elements(), + "header_info": cls.header_info(), } # After export_schema. cls.data["template_file"] = cls.template_file() @@ -108,6 +109,12 @@ class ProjectData: return len(ifc.by_type("IfcElement")) return 0 + @classmethod + def header_info(cls) -> Union[tool.Project.HeaderData, None]: + if not tool.Ifc.get(): + return None + return tool.Project.get_header_data() + class ProjectLibraryData: data: dict[str, Any] = {} diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 609611e910..8da0593b24 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -849,26 +849,8 @@ class EnableEditingHeader(bpy.types.Operator): self.file = tool.Ifc.get() props = tool.Project.get_project_props() props.is_editing = True - - mvd = "".join(tool.Ifc.get().header.file_description.description) - if "[" in mvd: - props.mvd = mvd.split("[")[1][0:-1] - else: - props.mvd = "" - - author = self.file.header.file_name.author - if author: - props.author_name = author[0] - if len(author) > 1: - props.author_email = author[1] - - organisation = self.file.header.file_name.organization - if organisation: - props.organisation_name = organisation[0] - if len(organisation) > 1: - props.organisation_email = organisation[1] - - props.authorisation = self.file.header.file_name.authorization or "" + header_data = tool.Project.get_header_data() + props.load_header_data(header_data) return {"FINISHED"} @@ -883,6 +865,9 @@ class EditHeader(bpy.types.Operator): return tool.Ifc.get() def execute(self, context): + # NOTE: Though header entities are now generic `entity_instance` + # we still have a special undo system in place for this operator + # since general undo system tracks only elements with ids != 0. IfcStore.begin_transaction(self) self.transaction_data = {} self.transaction_data["old"] = self.record_state() @@ -890,6 +875,7 @@ class EditHeader(bpy.types.Operator): self.transaction_data["new"] = self.record_state() IfcStore.add_transaction_operation(self) IfcStore.end_transaction(self) + bonsai.bim.handler.refresh_ui_data() return result def _execute(self, context): diff --git a/src/bonsai/bonsai/bim/module/project/prop.py b/src/bonsai/bonsai/bim/module/project/prop.py index 2def006065..5682c00c81 100644 --- a/src/bonsai/bonsai/bim/module/project/prop.py +++ b/src/bonsai/bonsai/bim/module/project/prop.py @@ -446,6 +446,14 @@ class BIMProjectProperties(PropertyGroup): def get_library_element_index(self, lib_element: LibraryElement) -> int: return next(i for i in range(len(self.library_elements)) if self.library_elements[i] == lib_element) + def load_header_data(self, header_data: tool.Project.HeaderData) -> None: + self.mvd = header_data.mvd + self.author_name = header_data.author_name + self.author_email = header_data.author_email + self.organisation_name = header_data.organization_name + self.organisation_email = header_data.organization_email + self.authorisation = header_data.authorization + if TYPE_CHECKING: is_editing: bool is_loading: bool diff --git a/src/bonsai/bonsai/bim/module/project/ui.py b/src/bonsai/bonsai/bim/module/project/ui.py index 341f463981..75d114e204 100644 --- a/src/bonsai/bonsai/bim/module/project/ui.py +++ b/src/bonsai/bonsai/bim/module/project/ui.py @@ -266,33 +266,30 @@ class BIM_PT_project(Panel): row.label(text="IFC Schema", icon="FILE_CACHE") row.label(text=ifc_file.schema) - if pprops.is_editing: - row = self.layout.row(align=True) - row.prop(pprops, "mvd") + if not ProjectData.is_loaded: + ProjectData.load() - row = self.layout.row(align=True) - row.prop(pprops, "author_name") - row = self.layout.row(align=True) - row.prop(pprops, "author_email") - - row = self.layout.row(align=True) - row.prop(pprops, "organisation_name") - row = self.layout.row(align=True) - row.prop(pprops, "organisation_email") - - row = self.layout.row(align=True) - row.prop(pprops, "authorisation") - else: - row = self.layout.row(align=True) - row.label(text="IFC MVD", icon="FILE_HIDDEN") - if isinstance(ifc_file, ifcopenshell.sqlite): - mvd = ifc_file.mvd_str + headers_props_and_icons = [ + ("mvd", "FILE_HIDDEN"), + ("author_name", None), + ("author_email", None), + ("organisation_name", None), + ("organisation_email", None), + ("authorisation", None), + ] + rna_props = pprops.bl_rna.properties + for prop_name, prop_icon in headers_props_and_icons: + if pprops.is_editing: + row = self.layout.row(align=True) + row.prop(pprops, prop_name) else: - mvd = "".join(ifc_file.header.file_description.description) - if "[" in mvd: - mvd = mvd.split("[")[1][0:-1] - row.label(text=mvd) - + value = getattr(ProjectData.data["header_info"], prop_name) + if not value: + continue + row = self.layout.row(align=True) + icon = {} if prop_icon is None else {"icon": prop_icon} + row.label(text=rna_props[prop_name].name, **icon) + row.label(text=value) else: row = self.layout.row(align=True) row.label(text="File Not Loaded", icon="ERROR") diff --git a/src/bonsai/bonsai/tool/project.py b/src/bonsai/bonsai/tool/project.py index f8c13e93b5..af6167a7f8 100644 --- a/src/bonsai/bonsai/tool/project.py +++ b/src/bonsai/bonsai/tool/project.py @@ -36,7 +36,7 @@ from collections import defaultdict from bonsai.bim.ifc import IfcStore from ifcopenshell.api.project.append_asset import APPENDABLE_ASSET_TYPES from pathlib import Path -from typing import Optional, Union, TYPE_CHECKING +from typing import NamedTuple, Optional, Union, TYPE_CHECKING if TYPE_CHECKING: from bonsai.bim.module.project.prop import BIMProjectProperties, MeasureToolSettings @@ -437,3 +437,50 @@ class Project(bonsai.core.tool.Project): remove_root(rel) else: assert False, f"Shouldn't be here, {rel}" + + class HeaderData(NamedTuple): + mvd: str + author_name: str + author_email: str + organization_name: str + organization_email: str + authorization: str + + @classmethod + def get_header_data(cls) -> HeaderData: + if not (ifc_file := tool.Ifc.get()): + return {} + + # MVD. + if isinstance(ifc_file, ifcopenshell.sqlite): + mvd = ifc_file.mvd_str + else: + mvd = "".join(ifc_file.header.file_description.description) + if f"[" in mvd: + mvd = mvd.split("[")[1][0:-1] + + # Author. + author = ifc_file.header.file_name.author + author_name, author_email = "", "" + if author: + author_name = author[0] + if len(author) > 1: + author_email = author[1] + + # Organization. + organization_name, organization_email = "", "" + organization = ifc_file.header.file_name.organization + if organization: + organization_name = organization[0] + if len(organization) > 1: + organization_email = organization[1] + + authorization = ifc_file.header.file_name.authorization or "" + return cls.HeaderData( + mvd=mvd, + author_name=author_name, + author_email=author_email, + organization_name=organization_name, + organization_email=organization_email, + authorization=authorization, + )