Fix #1855. Bug where authoring type dropdowns didn't refresh.

This commit is contained in:
Dion Moult
2021-11-08 21:12:27 +11:00
parent 7fbf317ad2
commit de7c54b771
10 changed files with 176 additions and 106 deletions
@@ -0,0 +1,59 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on 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.
#
# BlenderBIM Add-on 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 BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import blenderbim.tool as tool
def refresh():
AuthoringData.is_loaded = False
WorkspaceData.is_loaded = False
class AuthoringData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"ifc_classes": cls.ifc_classes(),
"relating_types": cls.relating_types(),
}
cls.is_loaded = True
@classmethod
def ifc_classes(cls):
results = []
classes = set([e.is_a() for e in tool.Ifc.get().by_type("IfcElementType")])
results.extend([(c, c, "") for c in sorted(classes)])
return results
@classmethod
def relating_types(cls):
results = []
ifc_class = bpy.context.scene.BIMModelProperties.ifc_class
if ifc_class:
elements = [(str(e.id()), e.Name, "") for e in tool.Ifc.get().by_type(ifc_class)]
results.extend(sorted(elements, key=lambda s: s[1]))
return results
return []
class WorkspaceData(AuthoringData):
pass
@@ -63,9 +63,9 @@ class AddTypeInstance(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context) return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
tprops = context.scene.BIMTypeProperties props = context.scene.BIMModelProperties
ifc_class = self.ifc_class or tprops.ifc_class ifc_class = self.ifc_class or props.ifc_class
relating_type_id = self.relating_type or tprops.relating_type relating_type_id = self.relating_type or props.relating_type
if not ifc_class or not relating_type_id: if not ifc_class or not relating_type_id:
return {"FINISHED"} return {"FINISHED"}
@@ -118,7 +118,7 @@ class AddTypeInstance(bpy.types.Operator):
tool.Ifc, tool.Ifc,
tool.Type, tool.Type,
element=tool.Ifc.get_entity(obj), element=tool.Ifc.get_entity(obj),
type=tool.Ifc.get().by_id(int(tprops.relating_type)), type=tool.Ifc.get().by_id(int(props.relating_type)),
) )
if building_obj: if building_obj:
@@ -18,6 +18,7 @@
import bpy import bpy
import ifcopenshell.util.type import ifcopenshell.util.type
from blenderbim.bim.module.model.data import AuthoringData
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
@@ -32,21 +33,23 @@ from bpy.props import (
CollectionProperty, CollectionProperty,
) )
relating_types_enum = []
def get_ifc_class(self, context):
if not AuthoringData.is_loaded:
AuthoringData.load()
return AuthoringData.data["ifc_classes"]
def purge(): def get_relating_type(self, context):
global relating_types_enum if not AuthoringData.is_loaded:
relating_types_enum = [] AuthoringData.load()
return AuthoringData.data["relating_types"]
def getRelatingTypes(self, context): def update_ifc_class(self, context):
global relating_types_enum AuthoringData.is_loaded = False
if len(relating_types_enum) < 1:
elements = IfcStore.get_file().by_type("IfcWallType")
relating_types_enum.extend((str(e.id()), e.Name, "") for e in elements)
return relating_types_enum
class BIMModelProperties(PropertyGroup): class BIMModelProperties(PropertyGroup):
relating_type: EnumProperty(items=getRelatingTypes, name="Relating Type") ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="IFC Class", update=update_ifc_class)
relating_type: bpy.props.EnumProperty(items=get_relating_type, name="Relating Type")
@@ -19,6 +19,7 @@
import blenderbim.bim.module.type.prop as type_prop import blenderbim.bim.module.type.prop as type_prop
from bpy.types import Panel from bpy.types import Panel
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.model.data import AuthoringData
class BIM_PT_authoring(Panel): class BIM_PT_authoring(Panel):
@@ -33,17 +34,20 @@ class BIM_PT_authoring(Panel):
return IfcStore.get_file() return IfcStore.get_file()
def draw(self, context): def draw(self, context):
tprops = context.scene.BIMTypeProperties if not AuthoringData.is_loaded:
AuthoringData.load()
props = context.scene.BIMModelProperties
col = self.layout.column(align=True) col = self.layout.column(align=True)
enabled = True enabled = True
if type_prop.getIfcTypes(tprops, context): if AuthoringData.data["ifc_classes"]:
col.prop(tprops, "ifc_class", text="", icon="FILE_VOLUME") col.prop(props, "ifc_class", text="", icon="FILE_VOLUME")
else: else:
col.label(text="No IFC Class", icon="FILE_VOLUME") col.label(text="No IFC Class", icon="FILE_VOLUME")
enabled = False enabled = False
if type_prop.get_relating_type(tprops, context): if AuthoringData.data["relating_types"]:
col.prop(tprops, "relating_type", text="", icon="FILE_3D") col.prop(props, "relating_type", text="", icon="FILE_3D")
else: else:
col.label(text="No Relating Type", icon="FILE_3D") col.label(text="No Relating Type", icon="FILE_3D")
enabled = False enabled = False
@@ -21,6 +21,7 @@ import bpy
import blenderbim.bim.module.type.prop as type_prop import blenderbim.bim.module.type.prop as type_prop
from bpy.types import WorkSpaceTool from bpy.types import WorkSpaceTool
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.model.data import WorkspaceData
class BimTool(WorkSpaceTool): class BimTool(WorkSpaceTool):
@@ -51,17 +52,19 @@ class BimTool(WorkSpaceTool):
) )
def draw_settings(context, layout, tool): def draw_settings(context, layout, tool):
if not WorkspaceData.is_loaded:
WorkspaceData.load()
row = layout.row(align=True) row = layout.row(align=True)
if not IfcStore.get_file(): if not IfcStore.get_file():
row.label(text="No IFC Project", icon="ERROR") row.label(text="No IFC Project", icon="ERROR")
return return
props = context.scene.BIMTypeProperties props = context.scene.BIMModelProperties
ifc_classes_is_empty = not bool(type_prop.getIfcTypes(props, context)) if WorkspaceData.data["ifc_classes"]:
if ifc_classes_is_empty:
row.label(text="No IFC Class")
else:
row.prop(props, "ifc_class", text="") row.prop(props, "ifc_class", text="")
if type_prop.get_relating_type(props, context): else:
row.label(text="No IFC Class")
if WorkspaceData.data["relating_types"]:
row.prop(props, "relating_type", text="") row.prop(props, "relating_type", text="")
else: else:
row.label(text="No Relating Type") row.label(text="No Relating Type")
@@ -143,8 +146,12 @@ class Hotkey(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context) return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.props = context.scene.BIMTypeProperties self.props = context.scene.BIMModelProperties
self.ifc_classes_is_empty = not bool(type_prop.getIfcTypes(self.props, context)) self.ifc_classes_is_empty = True
try:
self.ifc_classes_is_empty = bool(self.props.ifc_class)
except:
pass
getattr(self, f"hotkey_{self.hotkey}")() getattr(self, f"hotkey_{self.hotkey}")()
return {"FINISHED"} return {"FINISHED"}
@@ -28,16 +28,13 @@ classes = (
operator.SelectTypeObjects, operator.SelectTypeObjects,
operator.SelectType, operator.SelectType,
prop.BIMTypeProperties, prop.BIMTypeProperties,
prop.BIMTypeObjectProperties,
ui.BIM_PT_type, ui.BIM_PT_type,
) )
def register(): def register():
bpy.types.Scene.BIMTypeProperties = bpy.props.PointerProperty(type=prop.BIMTypeProperties) bpy.types.Object.BIMTypeProperties = bpy.props.PointerProperty(type=prop.BIMTypeProperties)
bpy.types.Object.BIMTypeProperties = bpy.props.PointerProperty(type=prop.BIMTypeObjectProperties)
def unregister(): def unregister():
del bpy.types.Scene.BIMTypeProperties
del bpy.types.Object.BIMTypeProperties del bpy.types.Object.BIMTypeProperties
@@ -0,0 +1,53 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on 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.
#
# BlenderBIM Add-on 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 BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import blenderbim.tool as tool
def refresh():
TypeData.is_loaded = False
class TypeData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"relating_type_classes": cls.relating_type_classes(),
"relating_types": cls.relating_types(),
}
cls.is_loaded = True
@classmethod
def relating_type_classes(cls):
results = []
element = tool.Ifc.get_entity(bpy.context.active_object)
types = ifcopenshell.util.type.get_applicable_types(element.is_a(), schema=tool.Ifc.get_schema())
applicable_types_enum.extend((t, t, "") for t in types)
return results
@classmethod
def relating_types(cls):
results = []
elements = tool.Ifc.get().by_type(bpy.context.active_object.BIMTypeProperties.relating_type_class)
elements = [(str(e.id()), e.Name, "") for e in elements]
relating_types_enum.extend(sorted(elements, key=lambda s: s[1]))
return results
@@ -18,6 +18,7 @@
import bpy import bpy
import ifcopenshell.util.type import ifcopenshell.util.type
from blenderbim.bim.module.type.data import TypeData
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
@@ -32,78 +33,24 @@ from bpy.props import (
CollectionProperty, CollectionProperty,
) )
applicable_types_enum = []
relating_types_enum = []
type_classes_enum = []
available_types_enum = []
def get_relating_type_class(self, context):
def purge(): if not TypeData.is_loaded:
global applicable_types_enum TypeData.load()
global relating_types_enum return TypeData.data["relating_type_classes"]
global type_classes_enum
global available_types_enum
applicable_types_enum = []
relating_types_enum = []
type_classes_enum = []
available_types_enum = []
def getIfcTypes(self, context):
global type_classes_enum
file = IfcStore.get_file()
if len(type_classes_enum) < 1 and file:
classes = set([e.is_a() for e in file.by_type("IfcElementType")])
type_classes_enum.extend([(c, c, "") for c in sorted(classes)])
return type_classes_enum
def get_relating_type(self, context): def get_relating_type(self, context):
global available_types_enum if not TypeData.is_loaded:
if len(available_types_enum) < 1 and getIfcTypes(self, context): TypeData.load()
elements = [(str(e.id()), e.Name, "") for e in IfcStore.get_file().by_type(self.ifc_class)] return TypeData.data["relating_types"]
available_types_enum.extend(sorted(elements, key=lambda s: s[1]))
return available_types_enum
def updateTypeInstanceIfcClass(self, context): def update_is_editing_type(self, context):
global type_classes_enum TypeData.is_loaded = False
global available_types_enum
type_classes_enum.clear()
available_types_enum.clear()
def getApplicableTypes(self, context):
global applicable_types_enum
if len(applicable_types_enum) < 1:
element = IfcStore.get_file().by_id(context.active_object.BIMObjectProperties.ifc_definition_id)
types = ifcopenshell.util.type.get_applicable_types(element.is_a(), schema=IfcStore.get_file().schema)
applicable_types_enum.extend((t, t, "") for t in types)
return applicable_types_enum
def get_object_relating_type(self, context):
global relating_types_enum
if len(relating_types_enum) < 1:
elements = IfcStore.get_file().by_type(context.active_object.BIMTypeProperties.relating_type_class)
elements = [(str(e.id()), e.Name, "") for e in elements]
relating_types_enum.extend(sorted(elements, key=lambda s: s[1]))
return relating_types_enum
def updateTypeDropdowns(self, context):
global applicable_types_enum
global relating_types_enum
applicable_types_enum.clear()
relating_types_enum.clear()
class BIMTypeProperties(PropertyGroup): class BIMTypeProperties(PropertyGroup):
ifc_class: bpy.props.EnumProperty(items=getIfcTypes, name="IFC Class", update=updateTypeInstanceIfcClass) is_editing_type: BoolProperty(name="Is Editing Type", update=update_is_editing_type)
relating_type: bpy.props.EnumProperty(items=get_relating_type, name="Relating Type") relating_type_class: EnumProperty(items=get_relating_type_class, name="Relating Type Class")
relating_type: EnumProperty(items=get_relating_type, name="Relating Type")
class BIMTypeObjectProperties(PropertyGroup):
is_editing_type: BoolProperty(name="Is Editing Type", update=updateTypeDropdowns)
relating_type_class: EnumProperty(items=getApplicableTypes, name="Relating Type Class")
relating_type: EnumProperty(items=get_object_relating_type, name="Relating Type")
@@ -275,9 +275,9 @@ Scenario: Override duplicate move - copying a type instance with a representatio
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class" And I press "bim.assign_class"
And I set "scene.BIMTypeProperties.ifc_class" to "IfcWallType" And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
And I set "scene.BIMTypeProperties.relating_type" to "{cube}" And I set "scene.BIMModelProperties.relating_type" to "{cube}"
And I press "bim.add_type_instance" And I press "bim.add_type_instance"
And the object "IfcWall/Instance" is selected And the object "IfcWall/Instance" is selected
When I press "object.duplicate_move" When I press "object.duplicate_move"
@@ -8,9 +8,9 @@ Scenario: Add type instance - add from a mesh
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class" And I press "bim.assign_class"
And I set "scene.BIMTypeProperties.ifc_class" to "IfcWallType" And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
And I set "scene.BIMTypeProperties.relating_type" to "{cube}" And I set "scene.BIMModelProperties.relating_type" to "{cube}"
When I press "bim.add_type_instance" When I press "bim.add_type_instance"
Then the object "IfcWall/Instance" exists Then the object "IfcWall/Instance" exists
@@ -21,9 +21,9 @@ Scenario: Add type instance - add from an empty
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class" And I press "bim.assign_class"
And I set "scene.BIMTypeProperties.ifc_class" to "IfcWallType" And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
And the variable "empty" is "{ifc}.by_type('IfcWallType')[0].id()" And the variable "empty" is "{ifc}.by_type('IfcWallType')[0].id()"
And I set "scene.BIMTypeProperties.relating_type" to "{empty}" And I set "scene.BIMModelProperties.relating_type" to "{empty}"
When I press "bim.add_type_instance" When I press "bim.add_type_instance"
Then the object "IfcWall/Instance" exists Then the object "IfcWall/Instance" exists
@@ -34,9 +34,9 @@ Scenario: Add type instance - add a mesh where existing instances have changed c
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType" And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class" And I press "bim.assign_class"
And I set "scene.BIMTypeProperties.ifc_class" to "IfcWallType" And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()" And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
And I set "scene.BIMTypeProperties.relating_type" to "{cube}" And I set "scene.BIMModelProperties.relating_type" to "{cube}"
And I press "bim.add_type_instance" And I press "bim.add_type_instance"
And the object "IfcWall/Instance" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW" And the object "IfcWall/Instance" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
And the object "IfcWall/Instance" is selected And the object "IfcWall/Instance" is selected