Refactor pset module to follow the same design patterns as other modules, in preparation for complex property / quantity handling in the future

This commit is contained in:
Dion Moult
2021-05-13 11:55:25 +10:00
parent 75ed4dcdd8
commit 5334d2b754
3 changed files with 173 additions and 192 deletions
@@ -2,6 +2,8 @@ import bpy
import json
import ifcopenshell.api
import ifcopenshell.util.unit
import ifcopenshell.util.pset
import ifcopenshell.util.attribute
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.pset.data import Data
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
@@ -31,33 +33,100 @@ class EnablePsetEditing(bpy.types.Operator):
obj = bpy.data.objects.get(self.obj)
elif self.obj_type == "Material":
obj = bpy.data.materials.get(self.obj)
props = obj.PsetProperties
self.props = obj.PsetProperties
while len(props.properties) > 0:
props.properties.remove(0)
while len(self.props.properties) > 0:
self.props.properties.remove(0)
data = Data.psets if self.pset_id in Data.psets else Data.qtos
props.active_pset_name = data[self.pset_id]["Name"]
for prop in data[self.pset_id]["Properties"]:
new = props.properties.add()
new.name = prop["Name"]
new.is_null = prop["is_null"]
if prop["type"] == "string":
new.string_value = prop["value"] or ""
elif prop["type"] == "integer":
new.int_value = prop["value"] or 0
elif prop["type"] == "float":
new.float_value = prop["value"] or 0.0
elif prop["type"] == "boolean":
new.bool_value = prop["value"] or False
elif prop["type"] == "enum":
new.enum_items = json.dumps(prop["enum_items"])
if prop["value"]:
new.enum_value = prop["value"]
pset_data = data[self.pset_id]
self.props.active_pset_name = pset_data["Name"]
props.active_pset_id = self.pset_id
templates = ifcopenshell.util.pset.get_template("IFC4")
pset_template = templates.get_by_name(pset_data["Name"])
if pset_template:
self.load_from_pset_template(pset_template, pset_data)
else:
self.load_from_pset_data(pset_data)
self.props.active_pset_id = self.pset_id
return {"FINISHED"}
def load_from_pset_template(self, pset_template, pset_data):
data = {Data.properties[p]["Name"]: Data.properties[p]["NominalValue"] for p in pset_data["Properties"]}
for prop_template in pset_template.HasPropertyTemplates:
if not prop_template.is_a("IfcSimplePropertyTemplate"):
continue # Other types not yet supported
if prop_template.TemplateType == "P_SINGLEVALUE":
try:
data_type = ifcopenshell.util.attribute.get_primitive_type(
IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType or "IfcLabel")
)
except:
# TODO: Occurs if the data type is something that exists in IFC4 and not in IFC2X3. To fully fix
# this we need to generate the IFC2X3 pset template definitions.
continue
elif prop_template.TemplateType == "P_ENUMERATEDVALUE":
data_type = "enum"
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
elif prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
data_type = "float"
elif prop_template.TemplateType == "Q_COUNT":
data_type = "integer"
else:
continue # Other types not yet supported
new = self.props.properties.add()
new.name = prop_template.Name
new.is_null = data.get(prop_template.Name, None) is None
new.data_type = data_type
if data_type == "string":
new.string_value = "" if new.is_null else data[prop_template.Name]
elif data_type == "integer":
new.int_value = 0 if new.is_null else data[prop_template.Name]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else data[prop_template.Name]
elif data_type == "boolean":
new.bool_value = False if new.is_null else data[prop_template.Name]
elif data_type == "enum":
new.enum_items = json.dumps(enum_items)
if data.get(prop_template.Name):
new.enum_value = data[prop_template.Name]
def load_from_pset_data(self, pset_data):
for prop_id in pset_data["Properties"]:
prop = Data.properties[prop_id]
value = prop["NominalValue"]
if isinstance(value, str):
data_type = "string"
elif isinstance(value, float):
data_type = "float"
elif isinstance(value, bool):
data_type = "boolean"
elif isinstance(value, int):
data_type = "integer"
else:
data_type = "string"
value = str(value)
new = self.props.properties.add()
new.name = prop["Name"]
new.is_null = prop["NominalValue"] is None
new.data_type = data_type
if data_type == "string":
new.string_value = "" if new.is_null else value
elif data_type == "integer":
new.int_value = 0 if new.is_null else value
elif data_type == "float":
new.float_value = 0.0 if new.is_null else value
elif data_type == "boolean":
new.bool_value = False if new.is_null else value
class DisablePsetEditing(bpy.types.Operator):
bl_idname = "bim.disable_pset_editing"
@@ -98,22 +167,19 @@ class EditPset(bpy.types.Operator):
properties = json.loads(self.properties)
else:
data = Data.psets if pset_id in Data.psets else Data.qtos
for prop in data[pset_id]["Properties"]:
blender_prop = props.properties.get(prop["Name"])
if not blender_prop:
continue
if blender_prop.is_null:
properties[prop["Name"]] = None
elif prop["type"] == "string":
properties[prop["Name"]] = blender_prop.string_value
elif prop["type"] == "boolean":
properties[prop["Name"]] = blender_prop.bool_value
elif prop["type"] == "integer":
properties[prop["Name"]] = blender_prop.int_value
elif prop["type"] == "float":
properties[prop["Name"]] = blender_prop.float_value
elif prop["type"] == "enum":
properties[prop["Name"]] = blender_prop.enum_value
for prop in props.properties:
if prop.is_null:
properties[prop.name] = None
elif prop.data_type == "string":
properties[prop.name] = prop.string_value
elif prop.data_type == "boolean":
properties[prop.name] = prop.bool_value
elif prop.data_type == "integer":
properties[prop.name] = prop.int_value
elif prop.data_type == "float":
properties[prop.name] = prop.float_value
elif prop.data_type == "enum":
properties[prop.name] = prop.enum_value
if pset_id in Data.psets:
ifcopenshell.api.run(
+28 -26
View File
@@ -6,6 +6,8 @@ from blenderbim.bim.ifc import IfcStore
def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type):
box = layout.box()
row = box.row(align=True)
if "is_expanded" not in pset:
pset["is_expanded"] = True
icon = "TRIA_DOWN" if pset["is_expanded"] else "TRIA_RIGHT"
row.operator("bim.toggle_pset_expansion", icon=icon, text="", emboss=False).pset_id = pset_id
if not props.active_pset_id:
@@ -34,20 +36,21 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type):
op.obj_type = obj_type
if pset["is_expanded"]:
if props.active_pset_id == pset_id:
for prop in pset["Properties"]:
for prop in props.properties:
draw_psetqto_editable_ui(box, props, prop)
else:
has_props_displayed = False
for prop in pset["Properties"]:
for prop_id in pset["Properties"]:
prop = Data.properties[prop_id]
if context.preferences.addons["blenderbim"].preferences.should_hide_empty_props and (
prop["value"] is None or prop["value"] == ""
prop["NominalValue"] is None or prop["NominalValue"] == ""
):
continue
has_props_displayed = True
row = box.row(align=True)
row.scale_y = 0.8
row.label(text=prop["Name"])
row.label(text=str(prop["value"]))
row.label(text=str(prop["NominalValue"]))
if not has_props_displayed:
row = box.row()
row.scale_y = 0.8
@@ -56,33 +59,32 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type):
def draw_psetqto_editable_ui(box, props, prop):
row = box.row(align=True)
blender_prop = props.properties.get(prop["Name"])
if prop["type"] == "string":
row.prop(blender_prop, "string_value", text=prop["Name"])
elif prop["type"] == "integer":
row.prop(blender_prop, "int_value", text=prop["Name"])
elif prop["type"] == "float":
row.prop(blender_prop, "float_value", text=prop["Name"])
elif prop["type"] == "boolean":
row.prop(blender_prop, "bool_value", text=prop["Name"])
elif prop["type"] == "enum":
row.prop(blender_prop, "enum_value", text=prop["Name"])
row.prop(blender_prop, "is_null", icon="RADIOBUT_OFF" if blender_prop.is_null else "RADIOBUT_ON", text="")
if prop.data_type == "string":
row.prop(prop, "string_value", text=prop.name)
elif prop.data_type == "integer":
row.prop(prop, "int_value", text=prop.name)
elif prop.data_type == "float":
row.prop(prop, "float_value", text=prop.name)
elif prop.data_type == "boolean":
row.prop(prop, "bool_value", text=prop.name)
elif prop.data_type == "enum":
row.prop(prop, "enum_value", text=prop.name)
row.prop(prop, "is_null", icon="RADIOBUT_OFF" if prop.is_null else "RADIOBUT_ON", text="")
if (
"length" in prop["Name"].lower()
or "width" in prop["Name"].lower()
or "height" in prop["Name"].lower()
or "depth" in prop["Name"].lower()
or "perimeter" in prop["Name"].lower()
"length" in prop.name.lower()
or "width" in prop.name.lower()
or "height" in prop.name.lower()
or "depth" in prop.name.lower()
or "perimeter" in prop.name.lower()
):
op = row.operator("bim.guess_quantity", icon="IPO_EASE_IN_OUT", text="")
op.prop = prop["Name"]
elif "area" in prop["Name"].lower():
op.prop = prop.name
elif "area" in prop.name.lower():
op = row.operator("bim.guess_quantity", icon="MESH_CIRCLE", text="")
op.prop = prop["Name"]
elif "volume" in prop["Name"].lower():
op.prop = prop.name
elif "volume" in prop.name.lower():
op = row.operator("bim.guess_quantity", icon="SPHERE", text="")
op.prop = prop["Name"]
op.prop = prop.name
class BIM_PT_object_psets(Panel):