From cf52607528042bd9b36a49227846dd3c6c37d84b Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 24 Jan 2026 14:19:57 +1100 Subject: [PATCH] Fix #7591. Save pset as template now considers all psets in the file, not just the active one. --- src/bonsai/bonsai/bim/module/pset/operator.py | 2 +- src/bonsai/bonsai/tool/pset_template.py | 30 +++++++----- src/bonsai/test/tool/test_pset_template.py | 48 +++++++++++++++++++ 3 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 src/bonsai/test/tool/test_pset_template.py diff --git a/src/bonsai/bonsai/bim/module/pset/operator.py b/src/bonsai/bonsai/bim/module/pset/operator.py index cbd7a7fe77..4e063b4ada 100644 --- a/src/bonsai/bonsai/bim/module/pset/operator.py +++ b/src/bonsai/bonsai/bim/module/pset/operator.py @@ -569,7 +569,7 @@ class SavePsetAsTemplate(bpy.types.Operator, tool.PsetTemplate.PsetTemplateOpera template_file = IfcStore.pset_template_file assert template_file - tool.PsetTemplate.add_pset_as_template(pset, template_file) + tool.PsetTemplate.add_pset_as_template(pset.Name, template_file) template_file.write(IfcStore.pset_template_path) bonsai.bim.handler.refresh_ui_data() diff --git a/src/bonsai/bonsai/tool/pset_template.py b/src/bonsai/bonsai/tool/pset_template.py index 3da6dad2e5..66a65e820c 100644 --- a/src/bonsai/bonsai/tool/pset_template.py +++ b/src/bonsai/bonsai/tool/pset_template.py @@ -61,19 +61,23 @@ class PsetTemplate(bonsai.core.tool.PsetTemplate): tool.Ifc.Operator._execute(self, context) @classmethod - def add_pset_as_template( - cls, pset: ifcopenshell.entity_instance, template_file: ifcopenshell.file - ) -> ifcopenshell.entity_instance: - # TODO: add tests. - pset_template = ifcopenshell.api.pset_template.add_pset_template(template_file, pset.Name) - for property in pset.HasProperties: - ifcopenshell.api.pset_template.add_prop_template( - template_file, - pset_template, - name=property.Name, - description=property.Description, - primary_measure_type=property.NominalValue.is_a(), - ) + def add_pset_as_template(cls, pset_name: str, template_file: ifcopenshell.file) -> ifcopenshell.entity_instance: + added_prop_names = set() + pset_template = ifcopenshell.api.pset_template.add_pset_template(template_file, pset_name) + for pset in tool.Ifc.get().by_type("IfcPropertySet"): + if pset.Name != pset_name: + continue + for prop in pset.HasProperties: + if prop.Name in added_prop_names: + continue + added_prop_names.add(prop.Name) + ifcopenshell.api.pset_template.add_prop_template( + template_file, + pset_template, + name=prop.Name, + description=prop.Description, + primary_measure_type=prop.NominalValue.is_a(), + ) return pset_template @classmethod diff --git a/src/bonsai/test/tool/test_pset_template.py b/src/bonsai/test/tool/test_pset_template.py new file mode 100644 index 0000000000..a104f8baff --- /dev/null +++ b/src/bonsai/test/tool/test_pset_template.py @@ -0,0 +1,48 @@ +# Bonsai - OpenBIM Blender Add-on +# Copyright (C) 2021 Dion Moult +# +# This file is part of Bonsai. +# +# Bonsai is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Bonsai is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Bonsai. If not, see . + +import bpy +import ifcopenshell +import ifcopenshell.api +import ifcopenshell.api.pset +import bonsai.core.tool +import bonsai.tool as tool +from bonsai.tool.pset_template import PsetTemplate as subject +from test.bim.bootstrap import NewFile + + +class TestImplementsTool(NewFile): + def test_run(self): + assert isinstance(subject(), bonsai.core.tool.PsetTemplate) + + +class TestAddPsetAsTemplate(NewFile): + def test_run(self): + ifc = ifcopenshell.file() + tool.Ifc.set(ifc) + element1 = ifc.createIfcWall() + element2 = ifc.createIfcWall() + pset = ifcopenshell.api.pset.add_pset(ifc, product=element1, name="Foo") + prop = ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "a"}) + pset = ifcopenshell.api.pset.add_pset(ifc, product=element2, name="Foo") + prop = ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Bar": "b"}) + library = ifcopenshell.file() + assert (pset_template := subject.add_pset_as_template("Foo", library)) + assert pset_template.is_a("IfcPropertySetTemplate") + assert len(templates := pset_template.HasPropertyTemplates) == 2 + assert set(t.Name for t in templates) == {"Foo", "Bar"}