New feature to show inherited element type psets.

This commit is contained in:
Dion Moult
2021-10-25 16:21:40 +11:00
parent 54fa45850a
commit 58ee049a8f
3 changed files with 219 additions and 86 deletions
@@ -0,0 +1,157 @@
# 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 ifcopenshell
import blenderbim.tool as tool
# TODO: Should this cache belong here? Dunno. Maybe.
is_expanded = {}
def refresh():
ObjectPsetsData.is_loaded = False
ObjectQtosData.is_loaded = False
MaterialPsetsData.is_loaded = False
TaskQtosData.is_loaded = False
ResourceQtosData.is_loaded = False
ResourcePsetsData.is_loaded = False
ProfilePsetsData.is_loaded = False
WorkSchedulePsetsData.is_loaded = False
class Data:
@classmethod
def psetqtos(cls, element, psets_only=False, qtos_only=False):
results = []
psetqtos = ifcopenshell.util.element.get_psets(element, psets_only=psets_only, qtos_only=qtos_only)
for name, data in psetqtos.items():
results.append(
{
"id": data["id"],
"Name": name,
"is_expanded": is_expanded.get(data["id"], True),
"Properties": [{"Name": k, "NominalValue": v} for k, v in data.items() if k != "id"],
}
)
return sorted(results, key=lambda v: v["Name"])
class ObjectPsetsData(Data):
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"psets": cls.psetqtos(tool.Ifc.get_entity(bpy.context.active_object), psets_only=True),
"inherited_psets": cls.inherited_psets(),
}
cls.is_loaded = True
@classmethod
def inherited_psets(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
if element.is_a("IfcTypeObject"):
return
element_type = ifcopenshell.util.element.get_type(element)
return cls.psetqtos(element_type)
class ObjectQtosData(Data):
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"qtos": cls.psetqtos(tool.Ifc.get_entity(bpy.context.active_object), qtos_only=True)}
cls.is_loaded = True
class MaterialPsetsData(Data):
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"psets": cls.psetqtos(tool.Ifc.get_entity(bpy.context.active_object.active_material))}
cls.is_loaded = True
class TaskQtosData(Data):
data = {}
is_loaded = False
@classmethod
def load(cls):
wprops = bpy.context.scene.BIMWorkScheduleProperties
tprops = bpy.context.scene.BIMTaskTreeProperties
ifc_definition_id = tprops.tasks[wprops.active_task_index].ifc_definition_id
cls.data = {"qtos": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), qtos_only=True)}
cls.is_loaded = True
class ResourceQtosData(Data):
data = {}
is_loaded = False
@classmethod
def load(cls):
rprops = bpy.context.scene.BIMResourceProperties
rtprops = bpy.context.scene.BIMResourceTreeProperties
ifc_definition_id = rtprops.resources[rprops.active_resource_index].ifc_definition_id
cls.data = {"qtos": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), qtos_only=True)}
cls.is_loaded = True
class ResourcePsetsData(Data):
data = {}
is_loaded = False
@classmethod
def load(cls):
rprops = bpy.context.scene.BIMResourceProperties
rtprops = bpy.context.scene.BIMResourceTreeProperties
ifc_definition_id = rtprops.resources[rprops.active_resource_index].ifc_definition_id
cls.data = {"psets": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), psets_only=True)}
cls.is_loaded = True
class ProfilePsetsData(Data):
data = {}
is_loaded = False
@classmethod
def load(cls):
pprops = bpy.context.scene.BIMProfileProperties
ifc_definition_id = pprops.profiles[pprops.active_profile_index].ifc_definition_id
cls.data = {"psets": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), psets_only=True)}
cls.is_loaded = True
class WorkSchedulePsetsData(Data):
data = {}
is_loaded = False
@classmethod
def load(cls):
props = bpy.context.scene.WorkSchedulePsetProperties
ifc_definition_id = bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id
cls.data = {"psets": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), psets_only=True)}
cls.is_loaded = True
@@ -23,8 +23,10 @@ import ifcopenshell.util.unit
import ifcopenshell.util.pset
import ifcopenshell.util.attribute
import blenderbim.bim.schema
import blenderbim.bim.handler
import blenderbim.tool as tool
import blenderbim.core.pset as core
import blenderbim.bim.module.pset.data
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.pset.data import Data
from ifcopenshell.api.cost.data import Data as CostData
@@ -78,16 +80,15 @@ def get_pset_obj_ifc_definition_id(context, obj, obj_type):
return context.scene.BIMWorkScheduleProperties.active_work_schedule_id
class TogglePsetExpansion(bpy.types.Operator):
class TogglePsetExpansion(bpy.types.Operator, Operator):
bl_idname = "bim.toggle_pset_expansion"
bl_label = "Toggle Pset Expansion"
pset_id: bpy.props.IntProperty()
def execute(self, context):
obj = context.active_object
data = Data.psets if self.pset_id in Data.psets else Data.qtos
data[self.pset_id]["is_expanded"] = not data[self.pset_id]["is_expanded"]
return {"FINISHED"}
def _execute(self, context):
blenderbim.bim.module.pset.data.is_expanded[
self.pset_id
] = not blenderbim.bim.module.pset.data.is_expanded.setdefault(self.pset_id, True)
class EnablePsetEditing(bpy.types.Operator):
+55 -80
View File
@@ -17,9 +17,18 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from bpy.types import Panel
from ifcopenshell.api.pset.data import Data
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.helper import draw_attribute
from blenderbim.bim.module.pset.data import (
ObjectPsetsData,
ObjectQtosData,
MaterialPsetsData,
TaskQtosData,
ResourceQtosData,
ResourcePsetsData,
ProfilePsetsData,
WorkSchedulePsetsData,
)
def get_active_pset_obj_name(context, obj_type):
@@ -68,8 +77,7 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type):
draw_psetqto_editable_ui(box, props, prop)
else:
has_props_displayed = False
for prop_id in pset["Properties"]:
prop = Data.properties[prop_id]
for prop in pset["Properties"]:
if context.preferences.addons["blenderbim"].preferences.should_hide_empty_props and (
prop["NominalValue"] is None or prop["NominalValue"] == ""
):
@@ -121,33 +129,26 @@ class BIM_PT_object_psets(Panel):
return False
if not IfcStore.get_element(props.ifc_definition_id):
return False
if props.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), props.ifc_definition_id)
if not Data.products[props.ifc_definition_id]:
return False
return True
def draw(self, context):
oprops = context.active_object.BIMObjectProperties
if not ObjectPsetsData.is_loaded:
ObjectPsetsData.load()
props = context.active_object.PsetProperties
if not oprops.ifc_definition_id:
return
if oprops.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
row = self.layout.row(align=True)
row.prop(props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
op.obj = context.active_object.name
op.obj_type = "Object"
psets = [(pset_id, Data.psets[pset_id]) for pset_id in Data.products[oprops.ifc_definition_id]["psets"]]
for pset_id, pset in sorted(psets, key=lambda v: v[1]["Name"]):
draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Object")
for pset in ObjectPsetsData.data["psets"]:
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Object")
# TODO reimplement. See #1222.
# if props.relating_type and props.relating_type.PsetProperties.psets:
# self.layout.label(text="Inherited Psets:")
# self.draw_psets_ui(props.relating_type.PsetProperties, enabled=False)
if ObjectPsetsData.data["inherited_psets"]:
self.layout.label(text="Inherited Psets:")
for pset in ObjectPsetsData.data["inherited_psets"]:
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Object")
class BIM_PT_object_qtos(Panel):
@@ -166,28 +167,21 @@ class BIM_PT_object_qtos(Panel):
return False
if not IfcStore.get_element(props.ifc_definition_id):
return False
if props.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), props.ifc_definition_id)
if not Data.products[props.ifc_definition_id]:
return False
return True
def draw(self, context):
oprops = context.active_object.BIMObjectProperties
if not ObjectQtosData.is_loaded:
ObjectQtosData.load()
props = context.active_object.PsetProperties
if not oprops.ifc_definition_id:
return
if oprops.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
row = self.layout.row(align=True)
row.prop(props, "qto_name", text="")
op = row.operator("bim.add_qto", icon="ADD", text="")
op.obj = context.active_object.name
op.obj_type = "Object"
qtos = [(qto_id, Data.qtos[qto_id]) for qto_id in Data.products[oprops.ifc_definition_id]["qtos"]]
for qto_id, qto in sorted(qtos, key=lambda v: v[1]["Name"]):
draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Object")
for qto in ObjectQtosData.data["qtos"]:
draw_psetqto_ui(context, qto["id"], qto, props, self.layout, "Object")
class BIM_PT_material_psets(Panel):
@@ -209,28 +203,21 @@ class BIM_PT_material_psets(Panel):
file = IfcStore.get_file()
if not file or file.schema == "IFC2X3":
return False # We don't support material psets in IFC2X3 because they suck
if props.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), props.ifc_definition_id)
if not Data.products[props.ifc_definition_id]:
return False
return True
def draw(self, context):
oprops = context.active_object.active_material.BIMObjectProperties
if not MaterialPsetsData.is_loaded:
MaterialPsetsData.load()
props = context.active_object.active_material.PsetProperties
if not oprops.ifc_definition_id:
return
if oprops.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
row = self.layout.row(align=True)
row.prop(props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
op.obj = context.active_object.active_material.name
op.obj_type = "Material"
psets = [(pset_id, Data.psets[pset_id]) for pset_id in Data.products[oprops.ifc_definition_id]["psets"]]
for pset_id, pset in sorted(psets, key=lambda v: v[1]["Name"]):
draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Material")
for pset in MaterialPsetsData.data["psets"]:
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Material")
class BIM_PT_task_qtos(Panel):
@@ -253,20 +240,17 @@ class BIM_PT_task_qtos(Panel):
return False
def draw(self, context):
if not TaskQtosData.is_loaded:
TaskQtosData.load()
props = context.scene.TaskPsetProperties
wprops = context.scene.BIMWorkScheduleProperties
tprops = context.scene.BIMTaskTreeProperties
ifc_definition_id = tprops.tasks[wprops.active_task_index].ifc_definition_id
if ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), ifc_definition_id)
row = self.layout.row(align=True)
row.prop(props, "qto_name", text="")
op = row.operator("bim.add_qto", icon="ADD", text="")
op.obj_type = "Task"
qtos = [(qto_id, Data.qtos[qto_id]) for qto_id in Data.products[ifc_definition_id]["qtos"]]
for qto_id, qto in sorted(qtos, key=lambda v: v[1]["Name"]):
draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Task")
for qto in TaskQtosData.data["qtos"]:
draw_psetqto_ui(context, qto["id"], qto, props, self.layout, "Task")
class BIM_PT_resource_qtos(Panel):
@@ -287,20 +271,17 @@ class BIM_PT_resource_qtos(Panel):
return False
def draw(self, context):
if not ResourceQtosData.is_loaded:
ResourceQtosData.load()
props = context.scene.ResourcePsetProperties
rprops = context.scene.BIMResourceProperties
rtprops = context.scene.BIMResourceTreeProperties
ifc_definition_id = rtprops.resources[rprops.active_resource_index].ifc_definition_id
if ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), ifc_definition_id)
row = self.layout.row(align=True)
row.prop(props, "qto_name", text="")
op = row.operator("bim.add_qto", icon="ADD", text="")
op.obj_type = "Resource"
qtos = [(qto_id, Data.qtos[qto_id]) for qto_id in Data.products[ifc_definition_id]["qtos"]]
for qto_id, qto in sorted(qtos, key=lambda v: v[1]["Name"]):
draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Resource")
for qto in ResourceQtosData.data["qtos"]:
draw_psetqto_ui(context, qto["id"], qto, props, self.layout, "Resource")
class BIM_PT_resource_psets(Panel):
@@ -321,20 +302,17 @@ class BIM_PT_resource_psets(Panel):
return False
def draw(self, context):
if not ResourcePsetsData.is_loaded:
ResourcePsetsData.load()
props = context.scene.ResourcePsetProperties
rprops = context.scene.BIMResourceProperties
rtprops = context.scene.BIMResourceTreeProperties
ifc_definition_id = rtprops.resources[rprops.active_resource_index].ifc_definition_id
if ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), ifc_definition_id)
row = self.layout.row(align=True)
row.prop(props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
op.obj_type = "Resource"
psets = [(pset_id, Data.psets[pset_id]) for pset_id in Data.products[ifc_definition_id]["psets"]]
for pset_id, pset in sorted(psets, key=lambda v: v[1]["Name"]):
draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Resource")
for pset in ResourcePsetsData.data["psets"]:
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Resource")
class BIM_PT_profile_psets(Panel):
@@ -357,19 +335,17 @@ class BIM_PT_profile_psets(Panel):
return False
def draw(self, context):
if not ProfilePsetsData.is_loaded:
ProfilePsetsData.load()
props = context.scene.ProfilePsetProperties
pprops = context.scene.BIMProfileProperties
ifc_definition_id = pprops.profiles[pprops.active_profile_index].ifc_definition_id
if ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), ifc_definition_id)
row = self.layout.row(align=True)
row.prop(props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
op.obj_type = "Profile"
psets = [(pset_id, Data.psets[pset_id]) for pset_id in Data.products[ifc_definition_id]["psets"]]
for pset_id, pset in sorted(psets, key=lambda v: v[1]["Name"]):
draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Profile")
for pset in ProfilePsetsData.data["psets"]:
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Profile")
class BIM_PT_work_schedule_psets(Panel):
@@ -388,15 +364,14 @@ class BIM_PT_work_schedule_psets(Panel):
return True
def draw(self, context):
if not WorkSchedulePsetsData.is_loaded:
WorkSchedulePsetsData.load()
props = context.scene.WorkSchedulePsetProperties
ifc_definition_id = context.scene.BIMWorkScheduleProperties.active_work_schedule_id
if ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), ifc_definition_id)
row = self.layout.row(align=True)
row.prop(props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
op.obj_type = "WorkSchedule"
psets = [(pset_id, Data.psets[pset_id]) for pset_id in Data.products[ifc_definition_id]["psets"]]
for pset_id, pset in sorted(psets, key=lambda v: v[1]["Name"]):
draw_psetqto_ui(context, pset_id, pset, props, self.layout, "WorkSchedule")
for pset in WorkSchedulePsetsData.data["psets"]:
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "WorkSchedule")