Files
IfcOpenShell/src/bonsai/test/tool/test_material.py
T
Petru Conduraru 297d8c981f Bonsai: refresh material data unconditionally instead of forcing a redraw
falken10vdl reviewed 16b1b4e7b1 on #8843 and pointed out that tagging
every area for redraw was overkill. The actual problem was that the
Object Material panel and the scene Materials list read from plain
python caches (ObjectMaterialData and MaterialsData) that only get
invalidated when the Materials editing UI list is reloaded, which
never happens while you are not in editing mode. The redraw itself was
never the issue, closing the rename dialog already triggers one.

Removed the tag_redraw loop from RenameMaterial and instead call the
existing bonsai.bim.module.material.data.refresh() function from
core.rename_material, unconditionally, through a new tool.Material.refresh()
method. This is the same invalidate-on-next-load mechanism already used
by every other module's Data classes, just wired up for this operator
too, instead of introducing a new one.

Also updates the core tests to prescribe the new unconditional refresh()
call, and adds tool-layer coverage for tool.Material.refresh().

Generated with the assistance of an AI coding tool.

(cherry picked from commit 7ab0628c54)
2026-07-25 23:18:32 +10:00

222 lines
8.5 KiB
Python

# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# 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 <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.material
import ifcopenshell.api.pset
import ifcopenshell.api.root
import ifcopenshell.api.style
import bonsai.core.tool
import bonsai.tool as tool
from bonsai.tool.material import Material as subject
from test.bim.bootstrap import NewFile
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), bonsai.core.tool.Material)
class TestDisableEditingMaterials(NewFile):
def test_run(self):
props = tool.Material.get_material_props()
props.is_editing = True
subject.disable_editing_materials()
assert props.is_editing is False
class TestEnableEditingMaterials(NewFile):
def test_run(self):
props = tool.Material.get_material_props()
props.is_editing = False
subject.enable_editing_materials()
assert props.is_editing is True
class TestGetActiveMaterialType(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
props = tool.Material.get_material_props()
props.material_type = "IfcMaterial"
assert subject.get_active_material_type() == "IfcMaterial"
props.material_type = "IfcMaterialLayerSet"
assert subject.get_active_material_type() == "IfcMaterialLayerSet"
class TestGetElementsByMaterial(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
material = ifcopenshell.api.material.add_material(ifc)
ifcopenshell.api.material.assign_material(ifc, products=[element], material=material)
assert subject.get_elements_by_material(material) == {element}
class TestImportMaterialDefinitions(NewFile):
def test_import_materials_grouped_by_categories(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterial(Name="Name", Category="Category")
subject.import_material_definitions("IfcMaterial")
props = tool.Material.get_material_props()
assert props.materials[0].ifc_definition_id == 0
assert props.materials[0].name == "Category"
assert props.materials[0].is_category is True
assert props.materials[0].is_expanded is False
assert len(props.materials) == 1
def test_importing_expanded_material_categories(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterial(Name="Name", Category="Category")
subject.import_material_definitions("IfcMaterial")
props = tool.Material.get_material_props()
props.materials[0].is_expanded = True
subject.import_material_definitions("IfcMaterial")
assert len(props.materials) == 2
assert props.materials[1].ifc_definition_id == material.id()
assert props.materials[1].name == "Name"
assert props.materials[1].total_elements == 0
def test_import_material_layer_sets(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterialLayerSet(LayerSetName="Name")
subject.import_material_definitions("IfcMaterialLayerSet")
props = tool.Material.get_material_props()
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Name"
assert props.materials[0].total_elements == 0
def test_import_material_profile_sets(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterialProfileSet(Name="Name")
subject.import_material_definitions("IfcMaterialProfileSet")
props = tool.Material.get_material_props()
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Name"
assert props.materials[0].total_elements == 0
def test_import_material_constituent_sets(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterialConstituentSet(Name="Name")
subject.import_material_definitions("IfcMaterialConstituentSet")
props = tool.Material.get_material_props()
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Name"
assert props.materials[0].total_elements == 0
def test_import_material_lists(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterialList()
subject.import_material_definitions("IfcMaterialList")
props = tool.Material.get_material_props()
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Unnamed"
assert props.materials[0].total_elements == 0
class TestRefresh(NewFile):
def test_run(self):
from bonsai.bim.module.material.data import MaterialsData, ObjectMaterialData
MaterialsData.is_loaded = True
ObjectMaterialData.is_loaded = True
subject.refresh()
assert MaterialsData.is_loaded is False
assert ObjectMaterialData.is_loaded is False
class TestIsEditingMaterials(NewFile):
def test_run(self):
props = tool.Material.get_material_props()
props.is_editing = False
assert subject.is_editing_materials() is False
props.is_editing = True
assert subject.is_editing_materials() is True
class TestIsMaterialUsedInSets(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material_set = ifc.createIfcMaterialLayerSet()
material_set_item = ifc.createIfcMaterialLayer()
material = ifc.createIfcMaterial()
assert subject.is_material_used_in_sets(material) is False
material_set.MaterialLayers = [material_set_item]
material_set_item.Material = material
assert subject.is_material_used_in_sets(material) is True
class TestEnsureNewMaterialSetIsValid(NewFile):
def test_material_constituent_set(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material_set = ifc.create_entity("IfcMaterialConstituentSet")
subject.ensure_new_material_set_is_valid(material_set)
assert len(constituents := material_set.MaterialConstituents) == 1
assert constituents[0].Material
def test_material_layer_set(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material_set = ifc.create_entity("IfcMaterialLayerSet")
subject.ensure_new_material_set_is_valid(material_set)
assert len(layers := material_set.MaterialLayers) == 1
assert layers[0].Material
def test_material_profile_set(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material_set = ifc.create_entity("IfcMaterialProfileSet")
subject.ensure_new_material_set_is_valid(material_set)
assert len(profiles := material_set.MaterialProfiles) == 1
assert profiles[0].Profile
def test_material_list(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material_set = ifc.create_entity("IfcMaterialList")
subject.ensure_new_material_set_is_valid(material_set)
assert len(material_set.Materials) == 1
class TestPurgeUnusedMaterials(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifcopenshell.api.material.add_material(ifc)
# Add pset.
ifcopenshell.api.pset.add_pset(ifc, material, "Foo")
# Add style.
style = ifcopenshell.api.style.add_style(ifc)
context = ifc.create_entity("IfcRepresentationContext")
ifcopenshell.api.style.assign_material_style(ifc, material, style, context)
assert subject.purge_unused_materials() == 1
assert not ifc.by_type("IfcMaterial")