diff --git a/src/ifcblenderexport/io_export_ifc/ui.py b/src/ifcblenderexport/io_export_ifc/ui.py index ea51fc9049..b8c22cc8d3 100644 --- a/src/ifcblenderexport/io_export_ifc/ui.py +++ b/src/ifcblenderexport/io_export_ifc/ui.py @@ -3,247 +3,317 @@ import json import os import csv from pathlib import Path +from bpy.types import PropertyGroup, Panel +from bpy.props import ( + StringProperty, + EnumProperty, + BoolProperty, + IntProperty, + FloatProperty, + CollectionProperty +) + cwd = os.path.dirname(os.path.realpath(__file__)) + os.path.sep class IfcSchema(): def __init__(self): - with open('{}ifc_elements_IFC4.json'.format(cwd + 'schema/')) as f: + with open('{}schema{}ifc_elements_IFC4.json'.format(cwd, os.path.sep)) as f: self.elements = json.load(f) ifc_schema = IfcSchema() -class BIMProperties(bpy.types.PropertyGroup): - def getIfcClasses(self, context): - return [(e, e, '') for e in ifc_schema.elements] +# hold reference to dynamic enum +classes_enum = [] +types_enum = [] +psetnames_enum = [] +psetfiles_enum = [] +classification_enum = [] +attributes_enum = [] +documents_enum = [] - def getIfcPredefinedTypes(self, context): - results = [] - for name, data in ifc_schema.elements.items(): - if name != bpy.context.scene.BIMProperties.ifc_class: +# Note: unless we need dynamic behaviour +# enums should be cached especially when file io is involved +# we may clear on need to regen + +def getIfcPredefinedTypes(self): + global types_enum + types_enum.clear() + for name, data in ifc_schema.elements.items(): + if name != bpy.context.scene.BIMProperties.ifc_class: + continue + for attribute in data['attributes']: + if attribute['name'] != 'PredefinedType': continue - for attribute in data['attributes']: - if attribute['name'] != 'PredefinedType': - continue - return [(e, e, '') for e in attribute['enum_values']] + types_enum.extend([(e, e, '') for e in attribute['enum_values']]) + return types_enum - def getPsetNames(self, context): - files = os.listdir(bpy.context.scene.BIMProperties.data_dir + 'pset/') - return [(f, f, '') for f in files] +def getIfcClasses(self): + global classes_enum + classes_enum.clear() + classes_enum.extend([(e, e, '') for e in ifc_schema.elements]) + return classes_enum - def getPsetFiles(self, context): - if not bpy.context.scene.BIMProperties.pset_name: - return [] - files = os.listdir(bpy.context.scene.BIMProperties.data_dir + 'pset/{}/'.format(bpy.context.scene.BIMProperties.pset_name)) - return [(f.replace('.csv', ''), f.replace('.csv', ''), '') for f in files] +def getPsetNames(self): + global psetnames_enum + psetnames_enum.clear() + files = os.listdir(bpy.context.scene.BIMProperties.data_dir + 'pset' + os.path.sep) + psetnames_enum.extend([(f, f, '') for f in files]) + return psetnames_enum - def getClassifications(self, context): - with open(bpy.context.scene.BIMProperties.data_dir + 'class/classifications.csv', 'r') as f: - data = list(csv.reader(f)) - keys = data.pop(0) - index = keys.index('Name') - return [(str(i), d[index], '') for i, d in enumerate(data)] +def getPsetFiles(self): + global psetfiles_enum + psetfiles_enum.clear() + if not bpy.context.scene.BIMProperties.pset_name: + return psetfiles_enum + files = os.listdir( + bpy.context.scene.BIMProperties.data_dir + 'pset{}{}{}'.format( + os.path.sep, + bpy.context.scene.BIMProperties.pset_name, + os.path.sep)) + psetfiles_enum.extend([(f.replace('.csv', ''), f.replace('.csv', ''), '') for f in files]) + return psetfiles_enum - def getReferences(self, context): - if not bpy.context.scene.BIMProperties.classification: - return [] - with open(bpy.context.scene.BIMProperties.data_dir + 'class/references.csv', 'r') as f: - data = list(csv.reader(f)) - keys = data.pop(0) - return [(d[0], '{} - {}'.format(d[0], d[1]), '') for d in data if - d[2] == bpy.context.scene.BIMProperties.classification] +def getClassifications(self): + global classification_enum + classification_enum.clear() + with open(bpy.context.scene.BIMProperties.data_dir + 'class/classifications.csv', 'r') as f: + data = list(csv.reader(f)) + keys = data.pop(0) + index = keys.index('Name') + classification_enum.extend([(str(i), d[index], '') for i, d in enumerate(data)]) + return classification_enum - schema_dir: bpy.props.StringProperty(default=cwd + 'schema/', name="Schema Directory") - data_dir: bpy.props.StringProperty(default=cwd + 'data/', name="Data Directory") - ifc_class: bpy.props.EnumProperty(items = getIfcClasses, name="Class") - ifc_predefined_type: bpy.props.EnumProperty( +def getReferences(self): + global reference_enum + reference_enum.clear() + if not bpy.context.scene.BIMProperties.classification: + return reference_enum + with open(bpy.context.scene.BIMProperties.data_dir + 'class' + os.path.sep + 'references.csv', 'r') as f: + data = list(csv.reader(f)) + keys = data.pop(0) + reference_enum.extend([(d[0], '{} - {}'.format(d[0], d[1]), '') for d in data if + d[2] == bpy.context.scene.BIMProperties.classification]) + return reference_enum + +def getApplicableAttributes(self): + global attributes_enum + attributes_enum.clear() + if '/' in bpy.context.active_object.name \ + and bpy.context.active_object.name.split('/')[0] in ifc_schema.elements: + attributes_enum.extend([(a['name'], a['name'], '') for a in + ifc_schema.elements[bpy.context.active_object.name.split('/')[0]]['attributes'] + if bpy.context.active_object.BIMObjectProperties.attributes.find(a['name']) == -1]) + return attributes_enum + +def getApplicableDocuments(self): + global documents_enum + documents_enum.clear() + doc_path = bpy.context.scene.BIMProperties.data_dir + 'doc' + os.path.sep + for filename in Path(doc_path).glob('**/*'): + uri = str(filename.relative_to(doc_path).as_posix()) + documents_enum.append((uri, uri, '')) + return documents_enum + + +class BIMProperties(PropertyGroup): + + schema_dir: StringProperty(default=cwd + 'schema' + os.path.sep, name="Schema Directory") + data_dir: StringProperty(default=cwd + 'data' + os.path.sep, name="Data Directory") + ifc_class: EnumProperty(items = getIfcClasses, name="Class") + ifc_predefined_type: EnumProperty( items = getIfcPredefinedTypes, name="Predefined Type", default=None) - ifc_userdefined_type: bpy.props.StringProperty(name="Userdefined Type") - export_has_representations: bpy.props.BoolProperty(name="Export Representations", default=True) - export_should_export_all_materials_as_styled_items: bpy.props.BoolProperty(name="Export All Materials as Styled Items", default=False) - export_should_use_presentation_style_assignment: bpy.props.BoolProperty(name="Export with Presentation Style Assignment", default=False) - import_should_ignore_site_coordinates: bpy.props.BoolProperty(name="Import Ignoring Site Coordinates", default=False) - import_should_import_curves: bpy.props.BoolProperty(name="Import Curves", default=False) - qa_reject_element_reason: bpy.props.StringProperty(name="Element Rejection Reason") - pset_name: bpy.props.EnumProperty(items=getPsetNames, name="Pset Name") - pset_file: bpy.props.EnumProperty(items=getPsetFiles, name="Pset File") - has_georeferencing: bpy.props.BoolProperty(name="Has Georeferencing", default=False) - global_id: bpy.props.StringProperty(name="GlobalId") - features_dir: bpy.props.StringProperty(default='', name="Features Directory") - diff_json_file: bpy.props.StringProperty(default='', name="Diff JSON File") - aggregate_class: bpy.props.EnumProperty(items = getIfcClasses, name="Aggregate Class") - aggregate_name: bpy.props.StringProperty(name="Aggregate Name") - classification: bpy.props.EnumProperty(items = getClassifications, name="Classification") - reference: bpy.props.EnumProperty(items = getReferences, name="Reference") + ifc_userdefined_type: StringProperty(name="Userdefined Type") + export_has_representations: BoolProperty(name="Export Representations", default=True) + export_should_export_all_materials_as_styled_items: BoolProperty(name="Export All Materials as Styled Items", default=False) + export_should_use_presentation_style_assignment: BoolProperty(name="Export with Presentation Style Assignment", default=False) + import_should_ignore_site_coordinates: BoolProperty(name="Import Ignoring Site Coordinates", default=False) + import_should_import_curves: BoolProperty(name="Import Curves", default=False) + qa_reject_element_reason: StringProperty(name="Element Rejection Reason") + pset_name: EnumProperty(items=getPsetNames, name="Pset Name") + pset_file: EnumProperty(items=getPsetFiles, name="Pset File") + has_georeferencing: BoolProperty(name="Has Georeferencing", default=False) + global_id: StringProperty(name="GlobalId") + features_dir: StringProperty(default='', name="Features Directory") + diff_json_file: StringProperty(default='', name="Diff JSON File") + aggregate_class: EnumProperty(items = getIfcClasses, name="Aggregate Class") + aggregate_name: StringProperty(name="Aggregate Name") + classification: EnumProperty(items = getClassifications, name="Classification") + reference: EnumProperty(items = getReferences, name="Reference") -class MapConversion(bpy.types.PropertyGroup): - eastings: bpy.props.StringProperty(name="Eastings") - northings: bpy.props.StringProperty(name="Northings") - orthogonal_height: bpy.props.StringProperty(name="Orthogonal Height") - x_axis_abscissa: bpy.props.StringProperty(name="X Axis Abscissa") - x_axis_ordinate: bpy.props.StringProperty(name="X Axis Ordinate") - scale: bpy.props.StringProperty(name="Scale") +class MapConversion(PropertyGroup): + eastings: StringProperty(name="Eastings") + northings: StringProperty(name="Northings") + orthogonal_height: StringProperty(name="Orthogonal Height") + x_axis_abscissa: StringProperty(name="X Axis Abscissa") + x_axis_ordinate: StringProperty(name="X Axis Ordinate") + scale: StringProperty(name="Scale") -class TargetCRS(bpy.types.PropertyGroup): - name: bpy.props.StringProperty(name="Name") - description: bpy.props.StringProperty(name="Description") - geodetic_datum: bpy.props.StringProperty(name="Geodetic Datum") - vertical_datum: bpy.props.StringProperty(name="Vertical Datum") - map_projection: bpy.props.StringProperty(name="Map Projection") - map_zone: bpy.props.StringProperty(name="Map Zone") - map_unit: bpy.props.StringProperty(name="Map Unit") +class TargetCRS(PropertyGroup): + name: StringProperty(name="Name") + description: StringProperty(name="Description") + geodetic_datum: StringProperty(name="Geodetic Datum") + vertical_datum: StringProperty(name="Vertical Datum") + map_projection: StringProperty(name="Map Projection") + map_zone: StringProperty(name="Map Zone") + map_unit: StringProperty(name="Map Unit") -class Attribute(bpy.types.PropertyGroup): - name: bpy.props.StringProperty(name="Name") - data_type: bpy.props.StringProperty(name="Data Type") - string_value: bpy.props.StringProperty(name="Value") - bool_value: bpy.props.BoolProperty(name="Value") - int_value: bpy.props.IntProperty(name="Value") - float_value: bpy.props.FloatProperty(name="Value") +class Attribute(PropertyGroup): + name: StringProperty(name="Name") + data_type: StringProperty(name="Data Type") + string_value: StringProperty(name="Value") + bool_value: BoolProperty(name="Value") + int_value: IntProperty(name="Value") + float_value: FloatProperty(name="Value") -class Pset(bpy.types.PropertyGroup): - name: bpy.props.StringProperty(name="Name") - file: bpy.props.StringProperty(name="File") +class Pset(PropertyGroup): + name: StringProperty(name="Name") + file: StringProperty(name="File") -class Document(bpy.types.PropertyGroup): - file: bpy.props.StringProperty(name="File") +class Document(PropertyGroup): + file: StringProperty(name="File") -class Classification(bpy.types.PropertyGroup): - name: bpy.props.StringProperty(name="Name") - identification: bpy.props.StringProperty(name="Identification") +class Classification(PropertyGroup): + name: StringProperty(name="Name") + identification: StringProperty(name="Identification") -class GlobalId(bpy.types.PropertyGroup): - name: bpy.props.StringProperty(name="Name") -class BIMObjectProperties(bpy.types.PropertyGroup): - def getApplicableAttributes(self, context): - if '/' in bpy.context.active_object.name \ - and bpy.context.active_object.name.split('/')[0] in ifc_schema.elements: - return [(a['name'], a['name'], '') for a in - ifc_schema.elements[bpy.context.active_object.name.split('/')[0]]['attributes'] - if bpy.context.active_object.BIMObjectProperties.attributes.find(a['name']) == -1] - return [] +class GlobalId(PropertyGroup): + name: StringProperty(name="Name") - def getApplicableDocuments(self, context): - results = [] - doc_path = bpy.context.scene.BIMProperties.data_dir + 'doc/' - for filename in Path(doc_path).glob('**/*'): - uri = str(filename.relative_to(doc_path).as_posix()) - results.append((uri, uri, '')) - return results - global_ids: bpy.props.CollectionProperty(name="GlobalIds", type=GlobalId) - attributes: bpy.props.CollectionProperty(name="Attributes", type=Attribute) - psets: bpy.props.CollectionProperty(name="Psets", type=Pset) - applicable_attributes: bpy.props.EnumProperty(items=getApplicableAttributes, name="Attribute Names") - documents: bpy.props.CollectionProperty(name="Documents", type=Document) - applicable_documents: bpy.props.EnumProperty(items=getApplicableDocuments, name="Available Documents") - classifications: bpy.props.CollectionProperty(name="Classifications", type=Classification) +class BIMObjectProperties(PropertyGroup): -class BIMMaterialProperties(bpy.types.PropertyGroup): - is_external: bpy.props.BoolProperty(name="Has External Definition") - location: bpy.props.StringProperty(name="Location") - identification: bpy.props.StringProperty(name="Identification") - name: bpy.props.StringProperty(name="Name") -class SweptSolid(bpy.types.PropertyGroup): - name: bpy.props.StringProperty(name="Name") - outer_curve: bpy.props.StringProperty(name="Outer Curve") - inner_curves: bpy.props.StringProperty(name="Inner Curves") - extrusion: bpy.props.StringProperty(name="Extrusion") + global_ids: CollectionProperty(name="GlobalIds", type=GlobalId) + attributes: CollectionProperty(name="Attributes", type=Attribute) + psets: CollectionProperty(name="Psets", type=Pset) + applicable_attributes: EnumProperty(items=getApplicableAttributes, name="Attribute Names") + documents: CollectionProperty(name="Documents", type=Document) + applicable_documents: EnumProperty(items=getApplicableDocuments, name="Available Documents") + classifications: CollectionProperty(name="Classifications", type=Classification) -class BIMMeshProperties(bpy.types.PropertyGroup): - is_wireframe: bpy.props.BoolProperty(name="Is Wireframe") - is_swept_solid: bpy.props.BoolProperty(name="Is Swept Solid") - swept_solids: bpy.props.CollectionProperty(name="Swept Solids", type=SweptSolid) -class ObjectPanel(bpy.types.Panel): +class BIMMaterialProperties(PropertyGroup): + is_external: BoolProperty(name="Has External Definition") + location: StringProperty(name="Location") + identification: StringProperty(name="Identification") + name: StringProperty(name="Name") + + +class SweptSolid(PropertyGroup): + name: StringProperty(name="Name") + outer_curve: StringProperty(name="Outer Curve") + inner_curves: StringProperty(name="Inner Curves") + extrusion: StringProperty(name="Extrusion") + + +class BIMMeshProperties(PropertyGroup): + is_wireframe: BoolProperty(name="Is Wireframe") + is_swept_solid: BoolProperty(name="Is Swept Solid") + swept_solids: CollectionProperty(name="Swept Solids", type=SweptSolid) + + +class BIM_PT_object(Panel): bl_label = 'IFC Object' bl_idname = 'BIM_PT_object' bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = 'object' - + + @classmethod + def poll(cls, context): + return context.active_object is not None and hasattr(context.active_object, "BIMObjectProperties") + def draw(self, context): - if not bpy.context.active_object: + + if context.active_object is None: return layout = self.layout + props = context.active_object.BIMObjectProperties + layout.label(text="Software Identity:") row = layout.row() row.operator('bim.generate_global_id') layout.label(text="Attributes:") row = layout.row(align=True) - row.prop(bpy.context.active_object.BIMObjectProperties, 'applicable_attributes', text='') + row.prop(props, 'applicable_attributes', text='') row.operator('bim.add_attribute') - for index, attribute in enumerate(bpy.context.active_object.BIMObjectProperties.attributes): + for index, attribute in enumerate(props.attributes): row = layout.row(align=True) row.prop(attribute, 'name', text='') row.prop(attribute, 'string_value', text='') row.operator('bim.remove_attribute', icon='X', text='').attribute_index = index row = layout.row() - row.prop(bpy.context.active_object.BIMObjectProperties, 'attributes') + row.prop(props, 'attributes') layout.label(text="Property Sets:") row = layout.row() row.operator('bim.add_pset') - for index, pset in enumerate(bpy.context.active_object.BIMObjectProperties.psets): + for index, pset in enumerate(props.psets): row = layout.row(align=True) row.prop(pset, 'name', text='') row.prop(pset, 'file', text='') row.operator('bim.remove_pset', icon='X', text='').pset_index = index row = layout.row() - row.prop(bpy.context.active_object.BIMObjectProperties, 'psets') + row.prop(props, 'psets') layout.label(text="Documents:") row = layout.row(align=True) - row.prop(bpy.context.active_object.BIMObjectProperties, 'applicable_documents', text='') + row.prop(props, 'applicable_documents', text='') row.operator('bim.add_document') - for index, document in enumerate(bpy.context.active_object.BIMObjectProperties.documents): + for index, document in enumerate(props.documents): row = layout.row(align=True) row.prop(document, 'file', text='') row.operator('bim.remove_document', icon='X', text='').document_index = index row = layout.row() - row.prop(bpy.context.active_object.BIMObjectProperties, 'documents') + row.prop(props, 'documents') layout.label(text="Classification:") - for index, classification in enumerate(bpy.context.active_object.BIMObjectProperties.classifications): + for index, classification in enumerate(props.classifications): row = layout.row(align=True) row.prop(classification, 'identification', text='') row.prop(classification, 'name', text='') row.operator('bim.remove_classification', icon='X', text='').classification_index = index row = layout.row() - row.prop(bpy.context.active_object.BIMObjectProperties, 'classifications') + row.prop(props, 'classifications') -class MeshPanel(bpy.types.Panel): +class BIM_PT_mesh(Panel): bl_label = 'IFC Representations' bl_idname = 'BIM_PT_mesh' bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = 'data' + @classmethod + def poll(cls, context): + return context.active_object is not None and context.active_object.type == "MESH" and \ + hasattr(context.active_object.data, "BIMMeshProperties") + def draw(self, context): - if not bpy.context.active_object.data: + if not context.active_object.data: return layout = self.layout + props = context.active_object.data.BIMMeshProperties row = layout.row() - row.prop(bpy.context.active_object.data.BIMMeshProperties, 'is_wireframe') + row.prop(props, 'is_wireframe') row = layout.row() - row.prop(bpy.context.active_object.data.BIMMeshProperties, 'is_swept_solid') + row.prop(props, 'is_swept_solid') row = layout.row() row.operator('bim.add_swept_solid') - for index, swept_solid in enumerate(bpy.context.active_object.data.BIMMeshProperties.swept_solids): + for index, swept_solid in enumerate(props.swept_solids): row = layout.row(align=True) row.prop(swept_solid, 'name', text='') row.operator('bim.remove_swept_solid', icon='X', text='').index = index @@ -258,30 +328,37 @@ class MeshPanel(bpy.types.Panel): row.operator('bim.assign_swept_solid_extrusion').index = index row.operator('bim.select_swept_solid_extrusion', icon='RESTRICT_SELECT_OFF', text='').index = index row = layout.row() - row.prop(bpy.context.active_object.data.BIMMeshProperties, 'swept_solids') + row.prop(props, 'swept_solids') -class MaterialPanel(bpy.types.Panel): + +class BIM_PT_material(Panel): bl_label = 'IFC Materials' bl_idname = 'BIM_PT_material' bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = 'material' - + + @classmethod + def poll(cls, context): + return context.active_object is not None and context.active_object.active_material is not None + def draw(self, context): if not bpy.context.active_object.active_material: return + props = context.active_object.active_material.BIMMaterialProperties layout = self.layout row = layout.row() - row.prop(bpy.context.active_object.active_material.BIMMaterialProperties, 'is_external') + row.prop(props, 'is_external') row = layout.row(align=True) - row.prop(bpy.context.active_object.active_material.BIMMaterialProperties, 'location') + row.prop(props, 'location') row.operator('bim.select_external_material_dir', icon="FILE_FOLDER", text="") row = layout.row() - row.prop(bpy.context.active_object.active_material.BIMMaterialProperties, 'identification') + row.prop(props, 'identification') row = layout.row() - row.prop(bpy.context.active_object.active_material.BIMMaterialProperties, 'name') + row.prop(props, 'name') -class GISPanel(bpy.types.Panel): + +class BIM_PT_gis(Panel): bl_label = 'IFC Georeferencing' bl_idname = "BIM_PT_gis" bl_space_type = 'PROPERTIES' @@ -291,27 +368,28 @@ class GISPanel(bpy.types.Panel): def draw(self, context): layout = self.layout layout.use_property_split = True - - layout.row().prop(bpy.context.scene.BIMProperties, 'has_georeferencing') + scene = context.scene + layout.row().prop(scene.BIMProperties, 'has_georeferencing') layout.label(text="Map Conversion:") - layout.row().prop(bpy.context.scene.MapConversion, 'eastings') - layout.row().prop(bpy.context.scene.MapConversion, 'northings') - layout.row().prop(bpy.context.scene.MapConversion, 'orthogonal_height') - layout.row().prop(bpy.context.scene.MapConversion, 'x_axis_abscissa') - layout.row().prop(bpy.context.scene.MapConversion, 'x_axis_ordinate') - layout.row().prop(bpy.context.scene.MapConversion, 'scale') + layout.row().prop(scene.MapConversion, 'eastings') + layout.row().prop(scene.MapConversion, 'northings') + layout.row().prop(scene.MapConversion, 'orthogonal_height') + layout.row().prop(scene.MapConversion, 'x_axis_abscissa') + layout.row().prop(scene.MapConversion, 'x_axis_ordinate') + layout.row().prop(scene.MapConversion, 'scale') layout.label(text="Target CRS:") - layout.row().prop(bpy.context.scene.TargetCRS, 'name') - layout.row().prop(bpy.context.scene.TargetCRS, 'description') - layout.row().prop(bpy.context.scene.TargetCRS, 'geodetic_datum') - layout.row().prop(bpy.context.scene.TargetCRS, 'vertical_datum') - layout.row().prop(bpy.context.scene.TargetCRS, 'map_projection') - layout.row().prop(bpy.context.scene.TargetCRS, 'map_zone') - layout.row().prop(bpy.context.scene.TargetCRS, 'map_unit') + layout.row().prop(scene.TargetCRS, 'name') + layout.row().prop(scene.TargetCRS, 'description') + layout.row().prop(scene.TargetCRS, 'geodetic_datum') + layout.row().prop(scene.TargetCRS, 'vertical_datum') + layout.row().prop(scene.TargetCRS, 'map_projection') + layout.row().prop(scene.TargetCRS, 'map_zone') + layout.row().prop(scene.TargetCRS, 'map_unit') -class BIMPanel(bpy.types.Panel): + +class BIM_PT_bim(Panel): bl_label = "Building Information Modeling" bl_idname = "BIM_PT_bim" bl_space_type = 'PROPERTIES' @@ -323,7 +401,7 @@ class BIMPanel(bpy.types.Panel): layout.use_property_split = True scene = context.scene - bim_properties = bpy.context.scene.BIMProperties + bim_properties = scene.BIMProperties layout.label(text="System Setup:") @@ -394,7 +472,8 @@ class BIMPanel(bpy.types.Panel): row.operator("bim.assign_classification") row.operator("bim.unassign_classification") -class QAPanel(bpy.types.Panel): + +class BIM_PT_qa(Panel): bl_label = "BIMTester Quality Auditing" bl_idname = "BIM_PT_qa" bl_space_type = 'PROPERTIES' @@ -432,7 +511,8 @@ class QAPanel(bpy.types.Panel): row = layout.row() row.operator("bim.select_audited") -class DiffPanel(bpy.types.Panel): + +class BIM_PT_diff(Panel): bl_label = "IFC Diff" bl_idname = "BIM_PT_diff" bl_space_type = 'PROPERTIES' @@ -444,7 +524,7 @@ class DiffPanel(bpy.types.Panel): layout.use_property_split = True scene = context.scene - bim_properties = bpy.context.scene.BIMProperties + bim_properties = scene.BIMProperties layout.label(text="IFC Diff Setup:") @@ -452,7 +532,8 @@ class DiffPanel(bpy.types.Panel): row.prop(bim_properties, "diff_json_file") row.operator("bim.select_diff_json_file", icon="FILE_FOLDER", text="") -class MVDPanel(bpy.types.Panel): + +class BIM_PT_mvd(Panel): bl_label = "Model View Definitions" bl_idname = "BIM_PT_mvd" bl_space_type = 'PROPERTIES' @@ -463,7 +544,7 @@ class MVDPanel(bpy.types.Panel): layout = self.layout scene = context.scene - bim_properties = bpy.context.scene.BIMProperties + bim_properties = scene.BIMProperties layout.label(text="Custom MVD:")