mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-17 10:59:17 +00:00
initial commit
-Added support for adding and editing enums -changed behaviour of template psets that have enums.
This commit is contained in:
@@ -33,16 +33,26 @@ def draw_attributes(props, layout, copy_operator=None):
|
||||
draw_attribute(attribute, row, copy_operator)
|
||||
|
||||
|
||||
def draw_attribute(attribute, layout, copy_operator=None):
|
||||
def draw_attribute(box, attribute, layout, copy_operator=None):
|
||||
value_name = attribute.get_value_name()
|
||||
if not value_name:
|
||||
layout.label(text=attribute.name)
|
||||
return
|
||||
layout.prop(
|
||||
attribute,
|
||||
value_name,
|
||||
text=attribute.name,
|
||||
)
|
||||
if len(attribute.enumerated_values) != 0:
|
||||
layout.label(text=attribute.name)
|
||||
grid = layout.column_flow(columns=4)
|
||||
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,
|
||||
)
|
||||
if attribute.is_optional:
|
||||
layout.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
if copy_operator:
|
||||
|
||||
@@ -35,6 +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.PsetProperties,
|
||||
prop.MaterialPsetProperties,
|
||||
prop.TaskPsetProperties,
|
||||
|
||||
@@ -133,23 +133,24 @@ class EnablePsetEditing(bpy.types.Operator):
|
||||
# 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"
|
||||
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
|
||||
|
||||
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.data_type = data_type
|
||||
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":
|
||||
@@ -158,22 +159,48 @@ class EnablePsetEditing(bpy.types.Operator):
|
||||
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 = str(data[prop_template.Name])
|
||||
if prop_template.TemplateType == "P_ENUMERATEDVALUE":
|
||||
|
||||
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]
|
||||
|
||||
def load_from_pset_data(self, pset_data):
|
||||
for prop_id in pset_data["Properties"]:
|
||||
prop = Data.properties[prop_id]
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
||||
class DisablePsetEditing(bpy.types.Operator, Operator):
|
||||
@@ -209,7 +236,11 @@ class EditPset(bpy.types.Operator, Operator):
|
||||
else:
|
||||
data = Data.psets if pset_id in Data.psets else Data.qtos
|
||||
for prop in props.properties:
|
||||
properties[prop.name] = prop.get_value()
|
||||
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 pset_id in Data.psets:
|
||||
ifcopenshell.api.run(
|
||||
@@ -381,9 +412,13 @@ class BIM_OT_add_property_to_edit(bpy.types.Operator):
|
||||
bl_idname = "bim.add_property_to_edit"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
option: bpy.props.StringProperty()
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
getattr(context.scene, self.option).add()
|
||||
if self.index == -1:
|
||||
getattr(context.scene, self.option).add()
|
||||
else:
|
||||
getattr(context.scene, self.option)[self.index].enum_values.add()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -392,10 +427,14 @@ class BIM_OT_remove_property_to_edit(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_property_to_edit"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
index: bpy.props.IntProperty()
|
||||
index2: bpy.props.IntProperty(default=-1)
|
||||
option: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
getattr(context.scene, self.option).remove(self.index)
|
||||
if self.index2 == -1:
|
||||
getattr(context.scene, self.option).remove(self.index)
|
||||
else:
|
||||
getattr(context.scene, self.option)[self.index].enum_values.remove(self.index2)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -468,18 +507,39 @@ 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
|
||||
value_ifc_entity = getattr(self.file, f"create{primary_measure_type}")(value)
|
||||
|
||||
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}
|
||||
)
|
||||
|
||||
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)
|
||||
)
|
||||
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
|
||||
)
|
||||
return prop_enum_value
|
||||
|
||||
|
||||
class BIM_OT_bulk_remove_psets(bpy.types.Operator):
|
||||
|
||||
@@ -139,10 +139,26 @@ 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 IfcSimpleProperty(Attribute):
|
||||
#bounded_values:
|
||||
enumerated_values: CollectionProperty(type=EnumerationValues)
|
||||
#list_values:
|
||||
#reference_values:
|
||||
#table_values:
|
||||
|
||||
|
||||
class PsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||
properties: CollectionProperty(name="Properties", type=IfcSimpleProperty)
|
||||
pset_name: EnumProperty(items=getPsetNames, name="Pset Name")
|
||||
qto_name: EnumProperty(items=getQtoNames, name="Qto Name")
|
||||
|
||||
@@ -197,6 +213,14 @@ class AddEditProperties(PropertyGroup):
|
||||
int_value: IntProperty(name="Value")
|
||||
float_value: FloatProperty(name="Value")
|
||||
primary_measure_type: EnumProperty(items=get_primary_measure_type, name="Primary Measure Type")
|
||||
template_type: EnumProperty(
|
||||
items=[
|
||||
("IfcPropertySingleValue","IfcPropertySingleValue","IfcPropertySingleValue"),
|
||||
("IfcPropertyEnumeratedValue","IfcPropertyEnumeratedValue","IfcPropertyEnumeratedValue")
|
||||
],
|
||||
name="Template Type"
|
||||
)
|
||||
enum_values: CollectionProperty(name="Enum Values", type=EnumerationValues)
|
||||
|
||||
def get_value_name(self):
|
||||
ifc_data_type = IfcStore.get_schema().declaration_by_name(self.primary_measure_type)
|
||||
|
||||
@@ -95,7 +95,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_attribute(box, prop, row, copy_operator="bim.copy_property_to_selection")
|
||||
if (
|
||||
"length" in prop.name.lower()
|
||||
or "width" in prop.name.lower()
|
||||
@@ -441,17 +441,35 @@ class BIM_PT_add_edit_custom_properties(Panel):
|
||||
row = layout.row()
|
||||
op = row.operator("bim.add_property_to_edit", icon="ADD")
|
||||
op.option = "AddEditProperties"
|
||||
op.index = -1
|
||||
|
||||
if props:
|
||||
for index, prop in enumerate(props):
|
||||
row = layout.row(align=True)
|
||||
row.prop(prop, "pset_name", text="")
|
||||
row.prop(prop, "property_name", text="")
|
||||
row.prop(prop, prop.get_value_name(), text="")
|
||||
if prop.template_type == "IfcPropertySingleValue":
|
||||
row.prop(prop, prop.get_value_name(), text="")
|
||||
row.prop(prop, "primary_measure_type", text="")
|
||||
row.prop(prop, "template_type", text="")
|
||||
op = row.operator("bim.remove_property_to_edit", icon="X", text="")
|
||||
op.index = index
|
||||
op.option = "AddEditProperties"
|
||||
|
||||
if prop.template_type == "IfcPropertyEnumeratedValue":
|
||||
op = row.operator("bim.add_property_to_edit", icon="ADD", text="Add Enum")
|
||||
op.option = "AddEditProperties"
|
||||
op.index = index
|
||||
for index2, prop2 in enumerate(prop.enum_values):
|
||||
row = layout.row()
|
||||
row.separator()
|
||||
row.separator()
|
||||
row.prop(prop2, prop.get_value_name(), text=f"#{index2}")
|
||||
row.prop(prop2, "is_selected")
|
||||
op = row.operator("bim.remove_property_to_edit", icon="X", text="")
|
||||
op.index = index
|
||||
op.index2 = index2
|
||||
op.option = "AddEditProperties"
|
||||
|
||||
if props:
|
||||
row = layout.row(align=True)
|
||||
@@ -476,7 +494,7 @@ class BIM_PT_delete_psets(Panel):
|
||||
row = layout.row()
|
||||
op = row.operator("bim.add_property_to_edit", icon="ADD")
|
||||
op.option = "DeletePsets"
|
||||
|
||||
|
||||
if props:
|
||||
for index, prop in enumerate(props):
|
||||
row = layout.row(align=True)
|
||||
|
||||
@@ -94,13 +94,15 @@ class Data:
|
||||
elif hasattr(pset, "Properties"):
|
||||
props = pset.Properties or []
|
||||
# TODO: support more than single values
|
||||
data["Properties"] = [p.id() for p in props if p.is_a("IfcPropertySingleValue")]
|
||||
data["Properties"] = [p.id() for p in props if p.is_a("IfcPropertyEnumeratedValue") or p.is_a("IfcPropertySingleValue")]
|
||||
cls.psets[pset.id()] = data
|
||||
cls.products[product_id]["psets"].add(pset.id())
|
||||
for prop in props:
|
||||
# TODO: support more than single values
|
||||
if prop.is_a("IfcPropertySingleValue"):
|
||||
cls.load_prop(prop)
|
||||
elif prop.is_a("IfcPropertyEnumeratedValue"):
|
||||
cls.load_prop_enum(prop)
|
||||
|
||||
@classmethod
|
||||
def load_prop(cls, prop):
|
||||
@@ -116,7 +118,14 @@ class Data:
|
||||
# For convenience, which trumps correctness in this case
|
||||
data["NominalValue"] = prop[3]
|
||||
cls.properties[prop.id()] = data
|
||||
|
||||
|
||||
@classmethod
|
||||
def load_prop_enum(cls, prop):
|
||||
data = prop.get_info()
|
||||
enumerations = [enum.wrappedValue for enum in data["EnumerationValues"]]
|
||||
data["NominalValue"] = str(enumerations)
|
||||
cls.properties[prop.id()] = data
|
||||
|
||||
@classmethod
|
||||
def add_qto(cls, qto, product_id):
|
||||
data = qto.get_info()
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from distutils.command.sdist import sdist
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.pset
|
||||
|
||||
@@ -46,11 +47,40 @@ class Usecase:
|
||||
self.psetqto = ifcopenshell.util.pset.get_template("IFC4")
|
||||
self.pset_template = self.psetqto.get_by_name(self.settings["pset"].Name)
|
||||
|
||||
#TODO - Add support for changing property types?
|
||||
# For example - IfcPropertyEnumeratedValue to
|
||||
# IfcPropertySingleValue. Or maybe the user should
|
||||
# just delete the property first? - vulevukusej
|
||||
def update_existing_properties(self):
|
||||
for prop in self.get_properties():
|
||||
self.update_existing_property(prop)
|
||||
if prop.is_a("IfcPropertyEnumeratedValue"):
|
||||
self.update_existing_enum(prop)
|
||||
else:
|
||||
self.update_existing_property(prop)
|
||||
|
||||
def update_existing_enum(self, prop):
|
||||
if prop.Name not in self.settings["properties"]:
|
||||
return
|
||||
value = self.settings["properties"][prop.Name]
|
||||
if isinstance(value, list):
|
||||
sel_vals = []
|
||||
for val in value:
|
||||
primary_measure_type = prop.EnumerationReference.EnumerationValues[0].is_a() #Only need the first enum type since all enums are of the same type
|
||||
ifc_val = self.file.create_entity(primary_measure_type, val)
|
||||
sel_vals.append(ifc_val)
|
||||
prop.EnumerationValues = tuple(sel_vals)
|
||||
|
||||
def update_existing_property(self, prop):
|
||||
else:
|
||||
if value.EnumerationReference.EnumerationValues == ():
|
||||
prop.EnumerationReference.EnumerationValues = ()
|
||||
prop.EnumerationValues = ()
|
||||
|
||||
elif isinstance(value, ifcopenshell.entity_instance):
|
||||
prop.EnumerationReference.EnumerationValues = value.EnumerationReference.EnumerationValues
|
||||
prop.EnumerationValues = value.EnumerationValues
|
||||
del self.settings["properties"][prop.Name]
|
||||
|
||||
def update_existing_property(self, prop):
|
||||
if prop.Name not in self.settings["properties"]:
|
||||
return
|
||||
value = self.settings["properties"][prop.Name]
|
||||
@@ -73,16 +103,36 @@ class Usecase:
|
||||
continue
|
||||
if isinstance(value, ifcopenshell.entity_instance):
|
||||
nominal_value = value
|
||||
if value.is_a(True) == "IFC4.IfcPropertyEnumeratedValue":
|
||||
properties.append(value)
|
||||
#TODO-The following "elif" is temporary code, will need to refactor at some point - vulevukusej
|
||||
elif isinstance(value, list):
|
||||
for pset_template in self.settings["pset_template"].HasPropertyTemplates:
|
||||
if pset_template.Name == name:
|
||||
prop_enum = self.file.create_entity(
|
||||
"IFCPROPERTYENUMERATION",
|
||||
Name=name,
|
||||
EnumerationValues=pset_template.Enumerators.EnumerationValues
|
||||
)
|
||||
prop_enum_value = self.file.create_entity(
|
||||
"IFCPROPERTYENUMERATEDVALUE",
|
||||
Name=name,
|
||||
EnumerationValues=tuple(self.file.create_entity(
|
||||
pset_template.PrimaryMeasureType, v) for v in value),
|
||||
EnumerationReference=prop_enum
|
||||
)
|
||||
properties.append(prop_enum_value)
|
||||
else:
|
||||
primary_measure_type = self.get_primary_measure_type(name, new_value=value)
|
||||
value = self.cast_value_to_primary_measure_type(value, primary_measure_type)
|
||||
nominal_value = self.file.create_entity(primary_measure_type, value)
|
||||
properties.append(
|
||||
self.file.create_entity(
|
||||
"IfcPropertySingleValue",
|
||||
**{"Name": name, "NominalValue": nominal_value},
|
||||
|
||||
properties.append(
|
||||
self.file.create_entity(
|
||||
"IfcPropertySingleValue",
|
||||
**{"Name": name, "NominalValue": nominal_value},
|
||||
)
|
||||
)
|
||||
)
|
||||
return properties
|
||||
|
||||
def extend_pset_with_new_properties(self, new_properties):
|
||||
|
||||
Submodule src/ifcopenshell-python/ifcopenshell/mvd updated: 83c12fa494...667894b629
@@ -80,6 +80,13 @@ def get_properties(properties):
|
||||
for prop in properties or []:
|
||||
if prop.is_a("IfcPropertySingleValue"):
|
||||
results[prop.Name] = prop.NominalValue.wrappedValue if prop.NominalValue else None
|
||||
for enum in prop.EnumerationValues:
|
||||
values.append(enum.wrappedValue)
|
||||
if values:
|
||||
results[prop.Name] = str(values) if len(values)>1 else values[0]
|
||||
else:
|
||||
results[prop.Name] = None
|
||||
|
||||
elif prop.is_a("IfcComplexProperty"):
|
||||
data = {k: v for k, v in prop.get_info().items() if v is not None and k != "Name"}
|
||||
data["properties"] = get_properties(prop.HasProperties)
|
||||
|
||||
+1
-1
Submodule src/svgfill updated: f9102b1e9f...01a2fb4943
Reference in New Issue
Block a user