Fix #2304. You can now manage classifications for materials. Bulk classification now supported.

This commit is contained in:
Dion Moult
2022-09-03 15:33:52 +10:00
parent 557d3429eb
commit e34b737428
6 changed files with 186 additions and 59 deletions
@@ -38,6 +38,7 @@ classes = (
prop.BIMClassificationReferenceProperties, prop.BIMClassificationReferenceProperties,
ui.BIM_PT_classifications, ui.BIM_PT_classifications,
ui.BIM_PT_classification_references, ui.BIM_PT_classification_references,
ui.BIM_PT_material_classifications,
ui.BIM_UL_classifications, ui.BIM_UL_classifications,
) )
@@ -27,6 +27,7 @@ from blenderbim.bim.ifc import IfcStore
def refresh(): def refresh():
ClassificationsData.is_loaded = False ClassificationsData.is_loaded = False
ClassificationReferencesData.is_loaded = False ClassificationReferencesData.is_loaded = False
MaterialClassificationsData.is_loaded = False
class ClassificationsData: class ClassificationsData:
@@ -61,7 +62,17 @@ class ClassificationsData:
return [(str(e.id()), e.Name, "") for e in IfcStore.classification_file.by_type("IfcClassification")] return [(str(e.id()), e.Name, "") for e in IfcStore.classification_file.by_type("IfcClassification")]
class ClassificationReferencesData: class ReferencesData:
@classmethod
def is_available_classification_added(cls):
if not IfcStore.classification_file or not IfcStore.classification_file.by_type("IfcClassification"):
return False
props = bpy.context.scene.BIMClassificationProperties
name = IfcStore.classification_file.by_id(int(props.available_classifications)).Name
return name in [e.Name for e in tool.Ifc.get().by_type("IfcClassification")]
class ClassificationReferencesData(ReferencesData):
data = {} data = {}
is_loaded = False is_loaded = False
@@ -82,10 +93,24 @@ class ClassificationReferencesData:
results.append(data) results.append(data)
return results return results
class MaterialClassificationsData(ReferencesData):
data = {}
is_loaded = False
@classmethod @classmethod
def is_available_classification_added(cls): def load(cls):
if not IfcStore.classification_file or not IfcStore.classification_file.by_type("IfcClassification"): cls.is_loaded = True
return False cls.data["references"] = cls.references()
props = bpy.context.scene.BIMClassificationProperties cls.data["is_available_classification_added"] = cls.is_available_classification_added()
name = IfcStore.classification_file.by_id(int(props.available_classifications)).Name
return name in [e.Name for e in tool.Ifc.get().by_type("IfcClassification")] @classmethod
def references(cls):
results = []
element = tool.Ifc.get_entity(bpy.context.active_object.active_material)
if element:
for reference in ifcopenshell.util.classification.get_references(element):
data = reference.get_info()
del data["ReferencedSource"]
results.append(data)
return results
@@ -163,16 +163,33 @@ class RemoveClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
reference: bpy.props.IntProperty() reference: bpy.props.IntProperty()
obj: bpy.props.StringProperty() obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
def _execute(self, context): def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file() if self.obj_type == "Object":
ifcopenshell.api.run( if context.selected_objects:
"classification.remove_reference", objects = [o.name for o in context.selected_objects]
self.file, else:
reference=self.file.by_id(self.reference), objects = [context.active_object.name]
product=self.file.by_id(obj.BIMObjectProperties.ifc_definition_id), else:
) objects = [self.obj]
identification = tool.Ifc.get().by_id(self.reference)[0]
for obj in objects:
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
element = tool.Ifc.get().by_id(ifc_definition_id)
references = ifcopenshell.util.classification.get_references(element, should_inherit=False)
for reference in references:
if reference[0] == identification:
ifcopenshell.api.run(
"classification.remove_reference",
tool.Ifc.get(),
reference=reference,
product=element,
)
break
class EditClassificationReference(bpy.types.Operator, tool.Ifc.Operator): class EditClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
@@ -194,7 +211,8 @@ class EditClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
ifcopenshell.api.run( ifcopenshell.api.run(
"classification.edit_reference", "classification.edit_reference",
self.file, self.file,
reference=self.file.by_id(props.active_reference_id), attributes=attributes, reference=self.file.by_id(props.active_reference_id),
attributes=attributes,
) )
bpy.ops.bim.disable_editing_classification_reference() bpy.ops.bim.disable_editing_classification_reference()
@@ -205,11 +223,16 @@ class AddClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
reference: bpy.props.IntProperty() reference: bpy.props.IntProperty()
obj: bpy.props.StringProperty() obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
def _execute(self, context): def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object if self.obj_type == "Object":
self.file = IfcStore.get_file() if context.selected_objects:
objects = [o.name for o in context.selected_objects]
else:
objects = [context.active_object.name]
else:
objects = [self.obj]
props = context.scene.BIMClassificationProperties props = context.scene.BIMClassificationProperties
classification = None classification = None
classification_name = IfcStore.classification_file.by_id(int(props.available_classifications)).Name classification_name = IfcStore.classification_file.by_id(int(props.available_classifications)).Name
@@ -218,13 +241,17 @@ class AddClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
classification = element classification = element
break break
ifcopenshell.api.run( for obj in objects:
"classification.add_reference", ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
self.file, if not ifc_definition_id:
reference=IfcStore.classification_file.by_id(self.reference), continue
product=tool.Ifc.get_entity(obj), ifcopenshell.api.run(
classification=classification, "classification.add_reference",
) tool.Ifc.get(),
reference=IfcStore.classification_file.by_id(self.reference),
product=tool.Ifc.get().by_id(ifc_definition_id),
classification=classification,
)
class ChangeClassificationLevel(bpy.types.Operator): class ChangeClassificationLevel(bpy.types.Operator):
@@ -32,6 +32,7 @@ from bpy.props import (
CollectionProperty, CollectionProperty,
) )
def get_available_classifications(self, context): def get_available_classifications(self, context):
if not ClassificationsData.is_loaded: if not ClassificationsData.is_loaded:
ClassificationsData.load() ClassificationsData.load()
@@ -21,7 +21,11 @@ import blenderbim.tool as tool
import blenderbim.bim.module.classification.prop as classification_prop import blenderbim.bim.module.classification.prop as classification_prop
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.classification.data import ClassificationsData, ClassificationReferencesData from blenderbim.bim.module.classification.data import (
ClassificationsData,
ClassificationReferencesData,
MaterialClassificationsData,
)
class BIM_PT_classifications(Panel): class BIM_PT_classifications(Panel):
@@ -74,25 +78,8 @@ class BIM_PT_classifications(Panel):
row.operator("bim.remove_classification", text="", icon="X").classification = classification["id"] row.operator("bim.remove_classification", text="", icon="X").classification = classification["id"]
class BIM_PT_classification_references(Panel): class ReferenceUI:
bl_label = "IFC Classification References" def draw_ui(self, context):
bl_idname = "BIM_PT_classification_references"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_parent_id = "BIM_PT_object_metadata"
@classmethod
def poll(cls, context):
if not context.active_object:
return False
return bool(tool.Ifc.get_entity(context.active_object))
def draw(self, context):
if not ClassificationReferencesData.is_loaded:
ClassificationReferencesData.load()
obj = context.active_object obj = context.active_object
self.oprops = obj.BIMObjectProperties self.oprops = obj.BIMObjectProperties
self.sprops = context.scene.BIMClassificationProperties self.sprops = context.scene.BIMClassificationProperties
@@ -101,18 +88,18 @@ class BIM_PT_classification_references(Panel):
self.draw_add_ui(context) self.draw_add_ui(context)
if not ClassificationReferencesData.data["references"]: if not self.data.data["references"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text="No References") row.label(text="No References")
for reference in ClassificationReferencesData.data["references"]: for reference in self.data.data["references"]:
if self.props.active_reference_id == reference["id"]: if self.props.active_reference_id == reference["id"]:
self.draw_editable_ui() self.draw_editable_ui()
else: else:
self.draw_ui(reference) self.draw_reference_ui(reference)
def draw_add_ui(self, context): def draw_add_ui(self, context):
if not ClassificationReferencesData.data["is_available_classification_added"]: if not self.data.data["is_available_classification_added"]:
return return
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.prop(self.sprops, "available_classifications", text="") row.prop(self.sprops, "available_classifications", text="")
@@ -125,6 +112,8 @@ class BIM_PT_classification_references(Panel):
op.parent_id = self.sprops.active_library_referenced_source op.parent_id = self.sprops.active_library_referenced_source
if self.sprops.active_library_reference_index < len(self.sprops.available_library_references): if self.sprops.active_library_reference_index < len(self.sprops.available_library_references):
op = row.operator("bim.add_classification_reference", text="", icon="ADD") op = row.operator("bim.add_classification_reference", text="", icon="ADD")
op.obj = self.obj
op.obj_type = self.obj_type
op.reference = self.sprops.available_library_references[ op.reference = self.sprops.available_library_references[
self.sprops.active_library_reference_index self.sprops.active_library_reference_index
].ifc_definition_id ].ifc_definition_id
@@ -144,7 +133,7 @@ class BIM_PT_classification_references(Panel):
row.operator("bim.disable_editing_classification_reference", text="", icon="CANCEL") row.operator("bim.disable_editing_classification_reference", text="", icon="CANCEL")
blenderbim.bim.helper.draw_attributes(self.props.reference_attributes, self.layout) blenderbim.bim.helper.draw_attributes(self.props.reference_attributes, self.layout)
def draw_ui(self, reference): def draw_reference_ui(self, reference):
row = self.layout.row(align=True) row = self.layout.row(align=True)
if self.file.schema == "IFC2X3": if self.file.schema == "IFC2X3":
name = reference["ItemReference"] or "No Identification" name = reference["ItemReference"] or "No Identification"
@@ -155,7 +144,60 @@ class BIM_PT_classification_references(Panel):
if not self.props.active_reference_id: if not self.props.active_reference_id:
op = row.operator("bim.enable_editing_classification_reference", text="", icon="GREASEPENCIL") op = row.operator("bim.enable_editing_classification_reference", text="", icon="GREASEPENCIL")
op.reference = reference["id"] op.reference = reference["id"]
row.operator("bim.remove_classification_reference", text="", icon="X").reference = reference["id"] op = row.operator("bim.remove_classification_reference", text="", icon="X")
op.reference = reference["id"]
op.obj = self.obj
op.obj_type = self.obj_type
class BIM_PT_classification_references(Panel, ReferenceUI):
bl_label = "IFC Classification References"
bl_idname = "BIM_PT_classification_references"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_parent_id = "BIM_PT_object_metadata"
@classmethod
def poll(cls, context):
if not context.active_object:
return False
return bool(tool.Ifc.get_entity(context.active_object))
def draw(self, context):
if not ClassificationReferencesData.is_loaded:
ClassificationReferencesData.load()
self.data = ClassificationReferencesData
self.obj = context.active_object.name
self.obj_type = "Object"
self.draw_ui(context)
class BIM_PT_material_classifications(Panel, ReferenceUI):
bl_label = "IFC Material Classifications"
bl_idname = "BIM_PT_material_classifications"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "material"
@classmethod
def poll(cls, context):
if not tool.Ifc.get():
return False
try:
return bool(context.active_object.active_material.BIMObjectProperties.ifc_definition_id)
except:
return False
def draw(self, context):
if not MaterialClassificationsData.is_loaded:
MaterialClassificationsData.load()
self.data = MaterialClassificationsData
self.obj = context.active_object.active_material.name
self.obj_type = "Material"
self.draw_ui(context)
class BIM_UL_classifications(UIList): class BIM_UL_classifications(UIList):
@@ -64,7 +64,7 @@ Scenario: Edit classification
When I press "bim.edit_classification" When I press "bim.edit_classification"
Then nothing happens Then nothing happens
Scenario: Add classification reference Scenario: Add classification reference - object
Given an empty IFC project Given an empty IFC project
And I press "bim.load_classification_library(filepath='{cwd}/test/files/classification.ifc')" And I press "bim.load_classification_library(filepath='{cwd}/test/files/classification.ifc')"
And the variable "classification" is "{classification_ifc}.by_type('IfcClassification')[0].id()" And the variable "classification" is "{classification_ifc}.by_type('IfcClassification')[0].id()"
@@ -77,7 +77,7 @@ Scenario: Add classification reference
And I press "bim.assign_class" And I press "bim.assign_class"
And I press "bim.change_classification_level(parent_id={classification})" And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()" And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
When I press "bim.add_classification_reference(reference={reference})" When I press "bim.add_classification_reference(reference={reference}, obj='IfcWallType/Cube', obj_type='Object')"
Then nothing happens Then nothing happens
Scenario: Change classification level Scenario: Change classification level
@@ -124,7 +124,7 @@ Scenario: Enable editing classification reference
And I press "bim.assign_class" And I press "bim.assign_class"
And I press "bim.change_classification_level(parent_id={classification})" And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()" And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.add_classification_reference(reference={reference})" And I press "bim.add_classification_reference(reference={reference}, obj='IfcWallType/Cube', obj_type='Object')"
And the variable "reference" is "{ifc}.by_type('IfcClassificationReference')[0].id()" And the variable "reference" is "{ifc}.by_type('IfcClassificationReference')[0].id()"
When I press "bim.enable_editing_classification_reference(reference={reference})" When I press "bim.enable_editing_classification_reference(reference={reference})"
Then nothing happens Then nothing happens
@@ -142,13 +142,13 @@ Scenario: Disable editing classification reference
And I press "bim.assign_class" And I press "bim.assign_class"
And I press "bim.change_classification_level(parent_id={classification})" And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()" And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.add_classification_reference(reference={reference})" And I press "bim.add_classification_reference(reference={reference}, obj='IfcWallType/Cube', obj_type='Object')"
And the variable "reference" is "{ifc}.by_type('IfcClassificationReference')[0].id()" And the variable "reference" is "{ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.enable_editing_classification_reference(reference={reference})" And I press "bim.enable_editing_classification_reference(reference={reference})"
When I press "bim.disable_editing_classification_reference" When I press "bim.disable_editing_classification_reference"
Then nothing happens Then nothing happens
Scenario: Remove classification reference Scenario: Remove classification reference - object
Given an empty IFC project Given an empty IFC project
And I press "bim.load_classification_library(filepath='{cwd}/test/files/classification.ifc')" And I press "bim.load_classification_library(filepath='{cwd}/test/files/classification.ifc')"
And the variable "classification" is "{classification_ifc}.by_type('IfcClassification')[0].id()" And the variable "classification" is "{classification_ifc}.by_type('IfcClassification')[0].id()"
@@ -161,10 +161,10 @@ Scenario: Remove classification reference
And I press "bim.assign_class" And I press "bim.assign_class"
And I press "bim.change_classification_level(parent_id={classification})" And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()" And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.add_classification_reference(reference={reference})" And I press "bim.add_classification_reference(reference={reference}, obj='IfcWallType/Cube', obj_type='Object')"
And the variable "reference" is "{ifc}.by_type('IfcClassificationReference')[0].id()" And the variable "reference" is "{ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.enable_editing_classification_reference(reference={reference})" And I press "bim.enable_editing_classification_reference(reference={reference})"
When I press "bim.remove_classification_reference(reference={reference})" When I press "bim.remove_classification_reference(reference={reference}, obj='IfcWallType/Cube', obj_type='Object')"
Then nothing happens Then nothing happens
Scenario: Edit classification reference Scenario: Edit classification reference
@@ -180,8 +180,39 @@ Scenario: Edit classification reference
And I press "bim.assign_class" And I press "bim.assign_class"
And I press "bim.change_classification_level(parent_id={classification})" And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()" And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.add_classification_reference(reference={reference})" And I press "bim.add_classification_reference(reference={reference}, obj='IfcWallType/Cube', obj_type='Object')"
And the variable "reference" is "{ifc}.by_type('IfcClassificationReference')[0].id()" And the variable "reference" is "{ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.enable_editing_classification_reference(reference={reference})" And I press "bim.enable_editing_classification_reference(reference={reference})"
When I press "bim.edit_classification_reference" When I press "bim.edit_classification_reference"
Then nothing happens Then nothing happens
Scenario: Add classification reference - material
Given an empty IFC project
And I press "bim.load_classification_library(filepath='{cwd}/test/files/classification.ifc')"
And the variable "classification" is "{classification_ifc}.by_type('IfcClassification')[0].id()"
And I set "scene.BIMClassificationProperties.available_classifications" to "{classification}"
And I press "bim.add_classification"
And I add a cube
And I add a material
And I press "bim.add_material(obj='Material')"
And the variable "material" is "{ifc}.by_type('IfcMaterial')[0].id()"
And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
When I press "bim.add_classification_reference(reference={reference}, obj='Material', obj_type='Material')"
Then nothing happens
Scenario: Remove classification reference - material
Given an empty IFC project
And I press "bim.load_classification_library(filepath='{cwd}/test/files/classification.ifc')"
And the variable "classification" is "{classification_ifc}.by_type('IfcClassification')[0].id()"
And I set "scene.BIMClassificationProperties.available_classifications" to "{classification}"
And I press "bim.add_classification"
And I add a cube
And I add a material
And I press "bim.add_material(obj='Material')"
And the variable "material" is "{ifc}.by_type('IfcMaterial')[0].id()"
And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.add_classification_reference(reference={reference}, obj='Material', obj_type='Material')"
When I press "bim.remove_classification_reference(reference={reference}, obj='Material', obj_type='Material')"
Then nothing happens