mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
Fix #2264 refactor property data structures to be separate from attributes
This commit is contained in:
@@ -38,21 +38,11 @@ def draw_attribute(attribute, layout, copy_operator=None):
|
||||
if not value_name:
|
||||
layout.label(text=attribute.name)
|
||||
return
|
||||
if len(attribute.enumerated_values) != 0:
|
||||
layout.label(text=attribute.name)
|
||||
grid = layout.column_flow(columns=3)
|
||||
for e in attribute.enumerated_values:
|
||||
grid.prop(
|
||||
e,
|
||||
"is_selected",
|
||||
text=str(e[value_name])
|
||||
)
|
||||
else:
|
||||
layout.prop(
|
||||
attribute,
|
||||
value_name,
|
||||
text=attribute.name,
|
||||
)
|
||||
layout.prop(
|
||||
attribute,
|
||||
value_name,
|
||||
text=attribute.name,
|
||||
)
|
||||
if attribute.is_optional:
|
||||
layout.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
if copy_operator:
|
||||
|
||||
@@ -35,8 +35,8 @@ classes = (
|
||||
operator.BIM_OT_rename_parameters,
|
||||
operator.BIM_OT_add_edit_custom_property,
|
||||
operator.BIM_OT_bulk_remove_psets,
|
||||
prop.EnumerationValues,
|
||||
prop.IfcSimpleProperty,
|
||||
prop.IfcPropertyEnumeratedValue,
|
||||
prop.IfcProperty,
|
||||
prop.PsetProperties,
|
||||
prop.MaterialPsetProperties,
|
||||
prop.TaskPsetProperties,
|
||||
|
||||
@@ -122,85 +122,91 @@ class EnablePsetEditing(bpy.types.Operator):
|
||||
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 in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
|
||||
data_type = "float"
|
||||
elif prop_template.TemplateType == "Q_COUNT":
|
||||
data_type = "integer"
|
||||
self.load_single_value(prop_template, data)
|
||||
elif prop_template.TemplateType == "P_ENUMERATEDVALUE":
|
||||
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
|
||||
#selected_enum_items = [e.EnumerationValues.wrappedValue for e in Data.properties[e]]
|
||||
data_type = ""
|
||||
else:
|
||||
continue # Other types not yet supported
|
||||
self.load_enumerated_value(prop_template, data)
|
||||
|
||||
new = self.props.properties.add()
|
||||
new.name = prop_template.Name
|
||||
new.is_null = data.get(prop_template.Name, None) is None
|
||||
new.is_optional = True
|
||||
new.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
|
||||
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]
|
||||
if prop_template.TemplateType == "P_ENUMERATEDVALUE":
|
||||
def load_single_value(self, prop_template, data):
|
||||
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.
|
||||
return
|
||||
|
||||
new.set_value(prop_template.Enumerators.EnumerationValues[0].wrappedValue)
|
||||
|
||||
for enum in enum_items:
|
||||
new_enum = new.enumerated_values.add()
|
||||
data_type = new.get_value_name()
|
||||
setattr(new_enum, data_type, enum)
|
||||
if data.get(prop_template.Name):
|
||||
new_enum.is_selected = enum in data[prop_template.Name]
|
||||
prop = self.props.properties.add()
|
||||
prop.value_type = "IfcPropertySingleValue"
|
||||
metadata = prop.metadata
|
||||
metadata.name = prop_template.Name
|
||||
metadata.is_null = data.get(prop_template.Name, None) is None
|
||||
metadata.is_optional = True
|
||||
metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
|
||||
metadata.data_type = data_type
|
||||
|
||||
if data_type == "string":
|
||||
metadata.string_value = "" if metadata.is_null else data[prop_template.Name]
|
||||
elif data_type == "integer":
|
||||
metadata.int_value = 0 if metadata.is_null else data[prop_template.Name]
|
||||
elif data_type == "float":
|
||||
metadata.float_value = 0.0 if metadata.is_null else data[prop_template.Name]
|
||||
elif data_type == "boolean":
|
||||
metadata.bool_value = False if metadata.is_null else data[prop_template.Name]
|
||||
|
||||
def load_enumerated_value(self, prop_template, data):
|
||||
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
|
||||
selected_enum_items = data.get(prop_template.Name, [])
|
||||
|
||||
prop = self.props.properties.add()
|
||||
prop.value_type = "IfcPropertyEnumeratedValue"
|
||||
metadata = prop.metadata
|
||||
metadata.name = prop_template.Name
|
||||
metadata.is_null = data.get(prop_template.Name, None) is None
|
||||
metadata.is_optional = True
|
||||
metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
|
||||
|
||||
# Cute hack to abuse the metadata to find the Blender data_type
|
||||
metadata.set_value(enum_items[0])
|
||||
data_type = metadata.get_value_name()
|
||||
|
||||
for enum in enum_items:
|
||||
new = prop.enumerated_value.enumerated_values.add()
|
||||
setattr(new, data_type, enum)
|
||||
new.is_selected = enum in selected_enum_items
|
||||
|
||||
def load_from_pset_data(self, pset_data):
|
||||
for prop_id in pset_data["Properties"]:
|
||||
prop = Data.properties[prop_id]
|
||||
|
||||
|
||||
if prop["type"] == "IfcPropertyEnumeratedValue":
|
||||
new = self.props.properties.add()
|
||||
new.name = prop["Name"]
|
||||
new.is_null = new.enumerated_values is None
|
||||
new.is_optional = True
|
||||
new.set_value(prop["EnumerationReference"].EnumerationValues[0].wrappedValue)
|
||||
|
||||
enum_ref = [v.wrappedValue for v in prop["EnumerationReference"].EnumerationValues]
|
||||
enum_vals = [v.wrappedValue for v in prop["EnumerationValues"]]
|
||||
for enum in enum_ref:
|
||||
new_enum = new.enumerated_values.add()
|
||||
data_type = new.get_value_name()
|
||||
setattr(new_enum, data_type, enum)
|
||||
if enum in enum_vals:
|
||||
new_enum.is_selected = True
|
||||
else:
|
||||
new_enum.is_selected = False
|
||||
|
||||
simple_prop = self.props.properties.add()
|
||||
simple_prop.value_type = "IfcPropertyEnumeratedValue"
|
||||
metadata = simple_prop.metadata
|
||||
metadata.name = prop["Name"]
|
||||
metadata.is_null = len(simple_prop.enumerated_value.enumerated_values) == 0
|
||||
metadata.is_optional = True
|
||||
metadata.set_value(prop["EnumerationReference"].EnumerationValues[0].wrappedValue)
|
||||
|
||||
enum_items = [v.wrappedValue for v in prop["EnumerationReference"].EnumerationValues]
|
||||
selected_enum_items = [v.wrappedValue for v in prop["EnumerationValues"]]
|
||||
data_type = metadata.get_value_name()
|
||||
|
||||
for enum in enum_items:
|
||||
new = simple_prop.enumerated_value.enumerated_values.add()
|
||||
setattr(new, data_type, enum)
|
||||
new.is_selected = enum in selected_enum_items
|
||||
else:
|
||||
value = prop["NominalValue"]
|
||||
new = self.props.properties.add()
|
||||
new.set_value(value)
|
||||
new.name = prop["Name"]
|
||||
new.is_null = value is None
|
||||
new.is_optional = True
|
||||
new.set_value(new.get_value_default() if new.is_null else value)
|
||||
|
||||
prop = self.props.properties.add()
|
||||
metadata = prop.metadata
|
||||
metadata.set_value(value)
|
||||
metadata.name = prop["Name"]
|
||||
metadata.is_null = value is None
|
||||
metadata.is_optional = True
|
||||
metadata.set_value(metadata.get_value_default() if metadata.is_null else value)
|
||||
|
||||
|
||||
class DisablePsetEditing(bpy.types.Operator, Operator):
|
||||
@@ -236,11 +242,13 @@ class EditPset(bpy.types.Operator, Operator):
|
||||
else:
|
||||
data = Data.psets if pset_id in Data.psets else Data.qtos
|
||||
for prop in props.properties:
|
||||
if len(prop.enumerated_values) != 0:
|
||||
value_name = prop.get_value_name()
|
||||
properties[prop.name] = [e[value_name] for e in prop.enumerated_values if e.is_selected]
|
||||
else:
|
||||
properties[prop.name] = prop.get_value()
|
||||
if prop.value_type == "IfcPropertySingleValue":
|
||||
properties[prop.metadata.name] = prop.metadata.get_value()
|
||||
elif prop.value_type == "IfcPropertyEnumeratedValue":
|
||||
value_name = prop.metadata.get_value_name()
|
||||
properties[prop.metadata.name] = [
|
||||
e[value_name] for e in prop.enumerated_value.enumerated_values if e.is_selected
|
||||
]
|
||||
|
||||
if pset_id in Data.psets:
|
||||
ifcopenshell.api.run(
|
||||
@@ -507,15 +515,15 @@ class BIM_OT_add_edit_custom_property(bpy.types.Operator):
|
||||
continue
|
||||
ifc_element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||
|
||||
for prop in props:
|
||||
for prop in props:
|
||||
value = getattr(prop, prop.get_value_name())
|
||||
primary_measure_type = prop.primary_measure_type
|
||||
|
||||
primary_measure_type = prop.primary_measure_type
|
||||
|
||||
if prop.template_type == "IfcPropertyEnumeratedValue":
|
||||
value_ifc_entity = self.generate_enum_entity(prop)
|
||||
elif prop.template_type == "IfcPropertySingleValue":
|
||||
value_ifc_entity = getattr(self.file, f"create{primary_measure_type}")(value)
|
||||
|
||||
|
||||
new_pset = ifcopenshell.api.run("pset.add_pset", self.file, product=ifc_element, name=prop.pset_name)
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset", self.file, pset=new_pset, properties={prop.property_name: value_ifc_entity}
|
||||
@@ -523,22 +531,26 @@ class BIM_OT_add_edit_custom_property(bpy.types.Operator):
|
||||
Data.load(IfcStore.get_file(), ifc_definition_id)
|
||||
self.report({"INFO"}, "Finished applying changes")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def generate_enum_entity(self, prop):
|
||||
prop_type = prop.get_value_name()
|
||||
prop_enum = self.file.create_entity(
|
||||
"IFCPROPERTYENUMERATION",
|
||||
Name=prop.property_name,
|
||||
EnumerationValues=tuple(self.file.create_entity(
|
||||
prop.primary_measure_type, ev[prop_type]) for ev in prop.enum_values)
|
||||
)
|
||||
"IFCPROPERTYENUMERATION",
|
||||
Name=prop.property_name,
|
||||
EnumerationValues=tuple(
|
||||
self.file.create_entity(prop.primary_measure_type, ev[prop_type]) for ev in prop.enum_values
|
||||
),
|
||||
)
|
||||
prop_enum_value = self.file.create_entity(
|
||||
"IFCPROPERTYENUMERATEDVALUE",
|
||||
Name=prop.property_name,
|
||||
EnumerationValues=tuple(self.file.create_entity(
|
||||
prop.primary_measure_type, ev[prop_type]) for ev in prop.enum_values if ev.is_selected == True),
|
||||
EnumerationReference=prop_enum
|
||||
)
|
||||
"IFCPROPERTYENUMERATEDVALUE",
|
||||
Name=prop.property_name,
|
||||
EnumerationValues=tuple(
|
||||
self.file.create_entity(prop.primary_measure_type, ev[prop_type])
|
||||
for ev in prop.enum_values
|
||||
if ev.is_selected == True
|
||||
),
|
||||
EnumerationReference=prop_enum,
|
||||
)
|
||||
return prop_enum_value
|
||||
|
||||
|
||||
|
||||
@@ -139,26 +139,22 @@ def get_primary_measure_type(self, context):
|
||||
return AddEditCustomPropertiesData.data["primary_measure_type"]
|
||||
|
||||
|
||||
class EnumerationValues(PropertyGroup):
|
||||
string_value: StringProperty(name="Value")
|
||||
bool_value: BoolProperty(name="Value")
|
||||
int_value: IntProperty(name="Value")
|
||||
float_value: FloatProperty(name="Value")
|
||||
is_selected: BoolProperty(default=False)
|
||||
class IfcPropertyEnumeratedValue(PropertyGroup):
|
||||
enumerated_values: CollectionProperty(type=Attribute)
|
||||
|
||||
|
||||
class IfcSimpleProperty(Attribute):
|
||||
# bounded_values:
|
||||
enumerated_values: CollectionProperty(type=EnumerationValues)
|
||||
# list_values:
|
||||
# reference_values:
|
||||
# table_values:
|
||||
class IfcProperty(PropertyGroup):
|
||||
metadata: PointerProperty(type=Attribute)
|
||||
value_type: EnumProperty(
|
||||
items=[(v, v, v) for v in ("IfcPropertySingleValue", "IfcPropertyEnumeratedValue")], name="Value Type"
|
||||
)
|
||||
enumerated_value: PointerProperty(type=IfcPropertyEnumeratedValue)
|
||||
|
||||
|
||||
class PsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
properties: CollectionProperty(name="Properties", type=IfcSimpleProperty)
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=get_pset_names, name="Pset Name")
|
||||
qto_name: EnumProperty(items=get_qto_names, name="Qto Name")
|
||||
|
||||
@@ -225,7 +221,7 @@ class AddEditProperties(PropertyGroup):
|
||||
],
|
||||
name="Template Type",
|
||||
)
|
||||
enum_values: CollectionProperty(name="Enum Values", type=EnumerationValues)
|
||||
enum_values: CollectionProperty(name="Enum Values", type=Attribute)
|
||||
|
||||
getter_enum = {
|
||||
"primary_measure_type": get_primary_measure_type,
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.ui import prop_with_search
|
||||
from blenderbim.bim.helper import draw_attribute
|
||||
from blenderbim.bim.module.pset.data import (
|
||||
ObjectPsetsData,
|
||||
ObjectQtosData,
|
||||
@@ -32,6 +31,45 @@ from blenderbim.bim.module.pset.data import (
|
||||
)
|
||||
|
||||
|
||||
def draw_property(prop, layout, copy_operator=None):
|
||||
if prop.value_type == "IfcPropertySingleValue":
|
||||
draw_single_property(prop, layout, copy_operator)
|
||||
elif prop.value_type == "IfcPropertyEnumeratedValue":
|
||||
draw_enumerated_property(prop, layout, copy_operator)
|
||||
|
||||
|
||||
def draw_single_property(prop, layout, copy_operator=None):
|
||||
value_name = prop.metadata.get_value_name()
|
||||
if not value_name:
|
||||
layout.label(text=prop.metadata.name)
|
||||
return
|
||||
layout.prop(
|
||||
prop.metadata,
|
||||
value_name,
|
||||
text=prop.metadata.name,
|
||||
)
|
||||
if prop.metadata.is_optional:
|
||||
layout.prop(prop.metadata, "is_null", icon="RADIOBUT_OFF" if prop.metadata.is_null else "RADIOBUT_ON", text="")
|
||||
if copy_operator:
|
||||
op = layout.operator(f"{copy_operator}", text="", icon="COPYDOWN")
|
||||
op.name = prop.metadata.name
|
||||
if prop.metadata.is_uri:
|
||||
op = layout.operator("bim.select_uri_prop", text="", icon="FILE_FOLDER")
|
||||
op.data_path = prop.metadata.path_from_id("string_value")
|
||||
|
||||
|
||||
def draw_enumerated_property(prop, layout, copy_operator=None):
|
||||
value_name = prop.metadata.get_value_name()
|
||||
if not value_name:
|
||||
layout.label(text=prop.metadata.name)
|
||||
return
|
||||
if len(prop.enumerated_value.enumerated_values) != 0:
|
||||
layout.label(text=prop.metadata.name)
|
||||
grid = layout.column_flow(columns=3)
|
||||
for e in prop.enumerated_value.enumerated_values:
|
||||
grid.prop(e, "is_selected", text=str(e[value_name]))
|
||||
|
||||
|
||||
def get_active_pset_obj_name(context, obj_type):
|
||||
if obj_type == "Object":
|
||||
return context.active_object.name
|
||||
@@ -96,7 +134,7 @@ 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)
|
||||
draw_attribute(prop, row, copy_operator="bim.copy_property_to_selection")
|
||||
draw_property(prop, row, copy_operator="bim.copy_property_to_selection")
|
||||
if (
|
||||
"length" in prop.name.lower()
|
||||
or "width" in prop.name.lower()
|
||||
|
||||
Reference in New Issue
Block a user