mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 19:07:57 +00:00
WIP psets now use partial editing with new features! See full message. See #1222.
* Psets are now sensitive to data type * Psets now support null vs empty * Psets now respect shared STEP IDs
This commit is contained in:
@@ -14,6 +14,7 @@ if bpy is not None:
|
||||
import blenderbim.bim.module.geometry as module_geometry
|
||||
import blenderbim.bim.module.model as module_model
|
||||
import blenderbim.bim.module.project as module_project
|
||||
import blenderbim.bim.module.pset as module_pset
|
||||
import blenderbim.bim.module.spatial as module_spatial
|
||||
import blenderbim.bim.module.style as module_style
|
||||
import blenderbim.bim.module.type as module_type
|
||||
@@ -52,8 +53,6 @@ if bpy is not None:
|
||||
operator.SelectSweptSolidInnerCurves,
|
||||
operator.AssignSweptSolidExtrusion,
|
||||
operator.SelectSweptSolidExtrusion,
|
||||
operator.AddPset,
|
||||
operator.RemovePset,
|
||||
operator.AddQto,
|
||||
operator.RemoveQto,
|
||||
operator.AddMaterialPset,
|
||||
@@ -286,7 +285,6 @@ if bpy is not None:
|
||||
ui.BIM_PT_material,
|
||||
ui.BIM_PT_presentation_layer_data,
|
||||
ui.BIM_PT_object_material,
|
||||
ui.BIM_PT_object_psets,
|
||||
ui.BIM_PT_object_qto,
|
||||
ui.BIM_PT_classification_references,
|
||||
ui.BIM_PT_documents,
|
||||
@@ -320,6 +318,7 @@ if bpy is not None:
|
||||
classes.extend(module_geometry.classes)
|
||||
classes.extend(module_model.classes)
|
||||
classes.extend(module_project.classes)
|
||||
classes.extend(module_pset.classes)
|
||||
classes.extend(module_spatial.classes)
|
||||
classes.extend(module_style.classes)
|
||||
classes.extend(module_type.classes)
|
||||
@@ -364,6 +363,7 @@ if bpy is not None:
|
||||
module_geometry.register()
|
||||
module_model.register()
|
||||
module_project.register()
|
||||
module_pset.register()
|
||||
module_spatial.register()
|
||||
module_style.register()
|
||||
module_type.register()
|
||||
@@ -393,6 +393,7 @@ if bpy is not None:
|
||||
module_type.unregister()
|
||||
module_style.unregister()
|
||||
module_spatial.unregister()
|
||||
module_pset.unregister()
|
||||
module_project.unregister()
|
||||
module_model.unregister()
|
||||
module_geometry.unregister()
|
||||
|
||||
@@ -849,7 +849,6 @@ class IfcImporter:
|
||||
self.material_creator.create(element, obj, mesh)
|
||||
self.add_element_classifications(element, obj)
|
||||
self.add_element_document_relations(element, obj)
|
||||
self.add_type_product_psets(element, obj)
|
||||
self.type_collection.objects.link(obj)
|
||||
self.type_products[element.GlobalId] = obj
|
||||
|
||||
@@ -1364,18 +1363,9 @@ class IfcImporter:
|
||||
for definition in element.IsDefinedBy:
|
||||
if not definition.is_a("IfcRelDefinesByProperties"):
|
||||
continue
|
||||
if definition.RelatingPropertyDefinition.is_a("IfcPropertySet"):
|
||||
self.add_pset(definition.RelatingPropertyDefinition, obj.BIMObjectProperties)
|
||||
elif definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"):
|
||||
if definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"):
|
||||
self.add_qto(definition.RelatingPropertyDefinition, obj)
|
||||
|
||||
def add_type_product_psets(self, element, obj):
|
||||
if not hasattr(element, "HasPropertySets") or not element.HasPropertySets:
|
||||
return
|
||||
for definition in element.HasPropertySets:
|
||||
if definition.is_a("IfcPropertySet"):
|
||||
self.add_pset(definition, obj.BIMObjectProperties)
|
||||
|
||||
def add_pset(self, pset, props):
|
||||
new_pset = props.psets.add()
|
||||
new_pset.name = pset.Name
|
||||
|
||||
@@ -12,7 +12,6 @@ class Data:
|
||||
return
|
||||
product = file.by_id(product_id)
|
||||
cls.products[product_id] = []
|
||||
attributes = product.get_info()
|
||||
declaration = IfcStore.get_schema().declaration_by_name(product.is_a())
|
||||
for attribute in declaration.all_attributes():
|
||||
data_type = str(attribute.type_of_attribute())
|
||||
|
||||
@@ -76,11 +76,10 @@ class EditAttributes(bpy.types.Operator):
|
||||
attributes[attribute["name"]] = blender_attribute.float_value
|
||||
elif attribute["type"] == "enum":
|
||||
attributes[attribute["name"]] = blender_attribute.enum_value
|
||||
usecase = edit_attributes.Usecase(self.file, {
|
||||
edit_attributes.Usecase(self.file, {
|
||||
"product": self.file.by_id(props.ifc_definition_id),
|
||||
"attributes": attributes
|
||||
})
|
||||
usecase.execute()
|
||||
}).execute()
|
||||
Data.load(props.ifc_definition_id)
|
||||
bpy.ops.bim.disable_editing_attributes()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -20,9 +20,6 @@ class BIM_PT_attributes(Panel):
|
||||
if props.ifc_definition_id not in Data.products:
|
||||
Data.load(props.ifc_definition_id)
|
||||
|
||||
props = context.active_object.BIMObjectProperties
|
||||
|
||||
|
||||
if props.is_editing_attributes:
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_attributes", icon="CHECKMARK", text="Save Attributes")
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import bpy
|
||||
from . import ui, operator
|
||||
|
||||
classes = (
|
||||
operator.TogglePsetExpansion,
|
||||
operator.EnablePsetEditing,
|
||||
operator.DisablePsetEditing,
|
||||
operator.EditPset,
|
||||
operator.RemovePset,
|
||||
operator.AddPset,
|
||||
ui.BIM_PT_object_psets,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
pass
|
||||
|
||||
|
||||
def unregister():
|
||||
pass
|
||||
@@ -0,0 +1,41 @@
|
||||
import ifcopenshell
|
||||
import blenderbim.bim.schema # TODO: refactor
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {"product": None, "Name": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
if self.settings["product"].is_a("IfcObject"):
|
||||
pset = self.file.create_entity(
|
||||
"IfcPropertySet", **{"GlobalId": ifcopenshell.guid.new(), "Name": self.settings["Name"]}
|
||||
)
|
||||
self.file.create_entity(
|
||||
"IfcRelDefinesByProperties",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
# TODO: owner history
|
||||
"RelatedObjects": [self.settings["product"]],
|
||||
"RelatingPropertyDefinition": pset,
|
||||
}
|
||||
)
|
||||
elif self.settings["product"].is_a("IfcTypeObject"):
|
||||
pset = self.file.create_entity(
|
||||
"IfcPropertySet", **{"GlobalId": ifcopenshell.guid.new(), "Name": self.settings["Name"]}
|
||||
)
|
||||
has_property_sets = list(self.settings["product"].HasPropertySets)
|
||||
has_property_sets.append(pset)
|
||||
self.settings["product"].HasPropertySets = has_property_sets
|
||||
elif self.settings["product"].is_a("IfcMaterialDefinition"):
|
||||
pset = self.file.create_entity(
|
||||
"IfcPropertySet",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"Name": self.settings["Name"],
|
||||
"Material": self.settings["product"],
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,129 @@
|
||||
import ifcopenshell
|
||||
import blenderbim.bim.schema # TODO: refactor elsewhere
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class Data:
|
||||
products = {}
|
||||
psets = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls, product_id):
|
||||
file = IfcStore.get_file()
|
||||
if not file:
|
||||
return
|
||||
product = file.by_id(product_id)
|
||||
cls.products[product_id] = set()
|
||||
if product.is_a("IfcElementType"):
|
||||
cls.add_type_product_psets(product, product_id)
|
||||
else:
|
||||
cls.add_product_psets(product, product_id)
|
||||
|
||||
@classmethod
|
||||
def add_type_product_psets(cls, product, product_id):
|
||||
if not hasattr(product, "HasPropertySets") or not product.HasPropertySets:
|
||||
return # TODO
|
||||
for definition in product.HasPropertySets:
|
||||
if definition.is_a("IfcPropertySet"):
|
||||
cls.add_pset(definition, product_id)
|
||||
|
||||
@classmethod
|
||||
def add_product_psets(cls, product, product_id):
|
||||
if not hasattr(product, "IsDefinedBy") or not product.IsDefinedBy:
|
||||
return
|
||||
for definition in product.IsDefinedBy:
|
||||
if not definition.is_a("IfcRelDefinesByProperties"):
|
||||
continue
|
||||
if definition.RelatingPropertyDefinition.is_a("IfcPropertySet"):
|
||||
cls.add_pset(definition.RelatingPropertyDefinition, product_id)
|
||||
|
||||
@classmethod
|
||||
def add_pset(cls, pset, product_id):
|
||||
new_pset = {
|
||||
"Name": pset.Name,
|
||||
"is_expanded": True,
|
||||
"Properties": []
|
||||
}
|
||||
cls.products[product_id].add(int(pset.id()))
|
||||
cls.psets[int(pset.id())] = new_pset
|
||||
pset_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(pset.Name)
|
||||
if pset_template:
|
||||
for prop_template in pset_template.HasPropertyTemplates:
|
||||
if not prop_template.is_a("IfcSimplePropertyTemplate"):
|
||||
continue # Other types not yet supported
|
||||
enum_items = []
|
||||
if prop_template.TemplateType == "P_SINGLEVALUE":
|
||||
data_type = str(IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType))
|
||||
elif prop_template.TemplateType == "P_ENUMERATEDVALUE":
|
||||
data_type = "enum"
|
||||
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
|
||||
else:
|
||||
continue # Other types not yet supported
|
||||
if "<string>" in data_type:
|
||||
data_type = "string"
|
||||
elif "<real>" in data_type:
|
||||
data_type = "float"
|
||||
elif "<integer>" in data_type:
|
||||
data_type = "integer"
|
||||
elif "<boolean>" in data_type:
|
||||
data_type = "boolean"
|
||||
elif "<enumeration" in data_type:
|
||||
data_type = "enum"
|
||||
enum_items = attribute.type_of_attribute().declared_type().enumeration_items()
|
||||
new_pset["Properties"].append({
|
||||
"Name": prop_template.Name,
|
||||
"value": None,
|
||||
"type": data_type,
|
||||
"enum_items": enum_items,
|
||||
"is_null": True
|
||||
})
|
||||
try:
|
||||
if hasattr(pset, "HasProperties"):
|
||||
props = pset.HasProperties
|
||||
elif hasattr(pset, "Properties"): # For IfcMaterialProperties
|
||||
props = pset.Properties
|
||||
except:
|
||||
return # I've seen ArchiCAD produce invalid IFCs with empty data
|
||||
# Invalid IFC, but some vendors like Solidworks do this so we accomodate it
|
||||
if not props:
|
||||
return
|
||||
for prop in props:
|
||||
if prop.is_a("IfcPropertySingleValue") and prop.NominalValue:
|
||||
has_existing_prop = False
|
||||
for existing_prop in new_pset["Properties"]:
|
||||
if existing_prop["Name"] == prop.Name:
|
||||
if prop.NominalValue is None:
|
||||
existing_prop["value"] = None
|
||||
elif existing_prop["type"] == "string":
|
||||
existing_prop["value"] = str(prop.NominalValue.wrappedValue)
|
||||
elif existing_prop["type"] == "float":
|
||||
existing_prop["value"] = float(prop.NominalValue.wrappedValue)
|
||||
elif existing_prop["type"] == "integer":
|
||||
existing_prop["value"] = int(prop.NominalValue.wrappedValue)
|
||||
elif existing_prop["type"] == "boolean":
|
||||
existing_prop["value"] = bool(prop.NominalValue.wrappedValue)
|
||||
elif existing_prop["type"] == "enum":
|
||||
existing_prop["value"] = str(prop.NominalValue.wrappedValue)
|
||||
existing_prop["is_null"] = prop.NominalValue.wrappedValue is None
|
||||
has_existing_prop = True
|
||||
break
|
||||
if not has_existing_prop:
|
||||
value = prop.NominalValue.wrappedValue
|
||||
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_pset["Properties"].append({
|
||||
"Name": prop.Name,
|
||||
"value": value,
|
||||
"type": data_type,
|
||||
"enum_items": [],
|
||||
"is_null": prop.NominalValue.wrappedValue is None
|
||||
})
|
||||
@@ -0,0 +1,55 @@
|
||||
import blenderbim.bim.schema # TODO: refactor
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {"pset": None, "Name": None, "Properties": {}}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
if hasattr(self.settings["pset"], "HasProperties"):
|
||||
attr_name = "HasProperties"
|
||||
elif hasattr(self.settings["pset"], "Properties"): # For IfcMaterialProperties
|
||||
attr_name = "Properties"
|
||||
props = getattr(self.settings["pset"], attr_name) or []
|
||||
|
||||
if self.settings["Name"]:
|
||||
self.settings["pset"].Name = self.settings["Name"]
|
||||
|
||||
pset_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(self.settings["pset"].Name)
|
||||
|
||||
for prop in props:
|
||||
if prop.Name in self.settings["Properties"]:
|
||||
value = self.settings["Properties"][prop.Name]
|
||||
primary_measure_type = self.get_primary_measure_type(
|
||||
pset_template, prop.Name, previous_value=prop.NominalValue
|
||||
)
|
||||
if value is not None:
|
||||
prop.NominalValue = self.file.create_entity(primary_measure_type, value)
|
||||
else:
|
||||
prop.NominalValue = None
|
||||
del self.settings["Properties"][prop.Name]
|
||||
properties = []
|
||||
for name, value in self.settings["Properties"].items():
|
||||
if value is None:
|
||||
continue
|
||||
primary_measure_type = self.get_primary_measure_type(pset_template, name)
|
||||
properties.append(
|
||||
self.file.create_entity(
|
||||
"IfcPropertySingleValue",
|
||||
**{"Name": name, "NominalValue": self.file.create_entity(primary_measure_type, value)},
|
||||
)
|
||||
)
|
||||
props = list(props)
|
||||
props.extend(properties)
|
||||
setattr(self.settings["pset"], attr_name, props)
|
||||
|
||||
def get_primary_measure_type(self, pset_template, name, previous_value=None):
|
||||
if not pset_template:
|
||||
return previous_value.is_a() if previous_value else "IfcLabel"
|
||||
for prop_template in pset_template.HasPropertyTemplates:
|
||||
if prop_template.Name == name:
|
||||
return prop_template.PrimaryMeasureType
|
||||
return previous_value.is_a() if previous_value else "IfcLabel"
|
||||
@@ -0,0 +1,132 @@
|
||||
import bpy
|
||||
import json
|
||||
import blenderbim.bim.module.pset.add_pset as add_pset
|
||||
import blenderbim.bim.module.pset.edit_pset as edit_pset
|
||||
import blenderbim.bim.module.pset.remove_pset as remove_pset
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.pset.data import Data
|
||||
|
||||
|
||||
class TogglePsetExpansion(bpy.types.Operator):
|
||||
bl_idname = "bim.toggle_pset_expansion"
|
||||
bl_label = "Toggle Pset Expansion"
|
||||
pset_id: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.context.active_object
|
||||
props = obj.BIMObjectProperties
|
||||
Data.psets[self.pset_id]["is_expanded"] = not Data.psets[self.pset_id]["is_expanded"]
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnablePsetEditing(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_pset_editing"
|
||||
bl_label = "Enable Pset Editing"
|
||||
pset_id: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.context.active_object
|
||||
props = obj.BIMObjectProperties
|
||||
|
||||
while len(props.properties) > 0:
|
||||
props.properties.remove(0)
|
||||
|
||||
props.active_pset_name = Data.psets[self.pset_id]["Name"]
|
||||
for prop in Data.psets[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.
|
||||
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"]
|
||||
|
||||
props.active_pset_id = self.pset_id
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisablePsetEditing(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_pset_editing"
|
||||
bl_label = "Disable Pset Editing"
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.context.active_object
|
||||
props = obj.BIMObjectProperties
|
||||
props.active_pset_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditPset(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_pset"
|
||||
bl_label = "Edit Pset"
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
obj = bpy.context.active_object
|
||||
props = obj.BIMObjectProperties
|
||||
properties = {}
|
||||
for prop in Data.psets[props.active_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
|
||||
edit_pset.Usecase(self.file, {
|
||||
"pset": self.file.by_id(props.active_pset_id),
|
||||
"Name": props.active_pset_name,
|
||||
"Properties": properties
|
||||
}).execute()
|
||||
Data.load(props.ifc_definition_id)
|
||||
bpy.ops.bim.disable_pset_editing()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemovePset(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_pset"
|
||||
bl_label = "Remove Pset"
|
||||
pset_id: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
obj = bpy.context.active_object
|
||||
props = obj.BIMObjectProperties
|
||||
remove_pset.Usecase(self.file, {
|
||||
"product": self.file.by_id(props.ifc_definition_id),
|
||||
"pset": self.file.by_id(self.pset_id),
|
||||
}).execute()
|
||||
Data.load(props.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddPset(bpy.types.Operator):
|
||||
bl_idname = "bim.add_pset"
|
||||
bl_label = "Add Pset"
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
obj = bpy.context.active_object
|
||||
props = obj.BIMObjectProperties
|
||||
add_pset.Usecase(self.file, {
|
||||
"product": self.file.by_id(props.ifc_definition_id),
|
||||
"Name": props.pset_name,
|
||||
}).execute()
|
||||
Data.load(props.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
@@ -0,0 +1,27 @@
|
||||
import blenderbim.bim.schema # TODO: refactor
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {"product": None, "pset": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
to_purge = []
|
||||
should_remove_pset = True
|
||||
for inverse in self.file.get_inverse(self.settings["pset"]):
|
||||
if inverse.is_a("IfcRelDefinesByProperties"):
|
||||
if not inverse.RelatedObjects or len(inverse.RelatedObjects) == 1:
|
||||
to_purge.append(inverse)
|
||||
else:
|
||||
related_objects = list(inverse.RelatedObjects)
|
||||
related_objects.remove(self.settings["product"])
|
||||
inverse.RelatedObjects = related_objects
|
||||
should_remove_pset = False
|
||||
if should_remove_pset:
|
||||
self.file.remove(self.settings["pset"])
|
||||
for element in to_purge:
|
||||
self.file.remove(element)
|
||||
# TODO: implement deep purging
|
||||
@@ -0,0 +1,74 @@
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.module.pset.data import Data
|
||||
|
||||
|
||||
class BIM_PT_object_psets(Panel):
|
||||
bl_label = "IFC Object Property Sets"
|
||||
bl_idname = "BIM_PT_object_psets"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object is not None and hasattr(context.active_object, "BIMObjectProperties")
|
||||
|
||||
def draw(self, context):
|
||||
props = context.active_object.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
return
|
||||
if props.ifc_definition_id not in Data.products:
|
||||
Data.load(props.ifc_definition_id)
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "pset_name", text="")
|
||||
row.operator("bim.add_pset", icon="ADD", text="")
|
||||
|
||||
self.draw_psets_ui(props, Data.products[props.ifc_definition_id])
|
||||
|
||||
# TODO reimplement. See #1222.
|
||||
#if props.relating_type and props.relating_type.BIMObjectProperties.psets:
|
||||
# self.layout.label(text="Inherited Psets:")
|
||||
# self.draw_psets_ui(props.relating_type.BIMObjectProperties, enabled=False)
|
||||
|
||||
def draw_psets_ui(self, props, pset_ids):
|
||||
for pset_id in pset_ids:
|
||||
pset = Data.psets[pset_id]
|
||||
box = self.layout.box()
|
||||
row = box.row(align=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:
|
||||
row.label(text=pset["Name"], icon="COPY_ID")
|
||||
row.operator("bim.enable_pset_editing", icon="GREASEPENCIL", text="").pset_id = pset_id
|
||||
row.operator("bim.remove_pset", icon="X", text="").pset_id = pset_id
|
||||
elif props.active_pset_id != pset_id:
|
||||
row.label(text=pset["Name"], icon="COPY_ID")
|
||||
row.operator("bim.remove_pset", icon="X", text="").pset_id = pset_id
|
||||
elif props.active_pset_id == pset_id:
|
||||
row.prop(props, "active_pset_name", icon="COPY_ID", text="")
|
||||
row.operator("bim.edit_pset", icon="CHECKMARK", text="")
|
||||
row.operator("bim.disable_pset_editing", icon="X", text="")
|
||||
if pset["is_expanded"]:
|
||||
if props.active_pset_id == pset_id:
|
||||
for prop in pset["Properties"]:
|
||||
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="")
|
||||
else:
|
||||
for prop in pset["Properties"]:
|
||||
if prop["value"] is None or prop["value"] == "":
|
||||
continue
|
||||
row = box.row(align=True)
|
||||
row.scale_y = 0.8
|
||||
row.label(text=prop["Name"])
|
||||
row.label(text=str(prop["value"]))
|
||||
@@ -484,45 +484,6 @@ class AddQto(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddPset(bpy.types.Operator):
|
||||
bl_idname = "bim.add_pset"
|
||||
bl_label = "Add Pset"
|
||||
|
||||
def execute(self, context):
|
||||
name = bpy.context.active_object.BIMObjectProperties.pset_name
|
||||
pset_template = schema.ifc.psetqto.get_by_name(name)
|
||||
if not pset_template:
|
||||
return {"FINISHED"}
|
||||
for obj in bpy.context.selected_objects:
|
||||
if "/" not in obj.name or obj.BIMObjectProperties.psets.find(name) != -1:
|
||||
continue
|
||||
applicable_psets = schema.ifc.psetqto.get_applicable_names(obj.name.split("/")[0], pset_only=True)
|
||||
if name not in applicable_psets:
|
||||
continue
|
||||
pset = obj.BIMObjectProperties.psets.add()
|
||||
pset.name = name
|
||||
for prop_name in (p.Name for p in pset_template.HasPropertyTemplates):
|
||||
prop = pset.properties.add()
|
||||
prop.name = prop_name
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemovePset(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_pset"
|
||||
bl_label = "Remove Pset"
|
||||
pset_index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
name = bpy.context.active_object.BIMObjectProperties.psets[self.pset_index].name
|
||||
for obj in bpy.context.selected_objects:
|
||||
if "/" not in obj.name:
|
||||
continue
|
||||
index = obj.BIMObjectProperties.psets.find(name)
|
||||
if index != -1:
|
||||
obj.BIMObjectProperties.psets.remove(index)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveQto(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_qto"
|
||||
bl_label = "Remove Qto"
|
||||
|
||||
@@ -40,7 +40,7 @@ psettemplatefiles_enum = []
|
||||
propertysettemplates_enum = []
|
||||
classification_enum = []
|
||||
attributes_enum = []
|
||||
psetnames_enum = []
|
||||
psetnames = {}
|
||||
qtonames_enum = []
|
||||
materialattributes_enum = []
|
||||
materialtypes_enum = []
|
||||
@@ -435,12 +435,14 @@ def refreshReferences(self, context):
|
||||
|
||||
|
||||
def getPsetNames(self, context):
|
||||
global psetnames_enum
|
||||
psetnames_enum.clear()
|
||||
if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements:
|
||||
pset_names = schema.ifc.psetqto.get_applicable_names(context.active_object.name.split("/")[0], pset_only=True)
|
||||
psetnames_enum.extend([(p, p, "") for p in pset_names])
|
||||
return psetnames_enum
|
||||
global psetnames
|
||||
if "/" in context.active_object.name:
|
||||
ifc_class = context.active_object.name.split("/")[0]
|
||||
if ifc_class not in psetnames:
|
||||
psets = schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
||||
psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
||||
return psetnames[ifc_class]
|
||||
return []
|
||||
|
||||
|
||||
def getMaterialPsetNames(self, context):
|
||||
@@ -1483,6 +1485,9 @@ class BIMObjectProperties(PropertyGroup):
|
||||
is_editing_type: BoolProperty(name="Is Editing Type")
|
||||
relating_type: PointerProperty(name="Type Product", type=bpy.types.Object)
|
||||
relating_structure: PointerProperty(name="Spatial Container", type=bpy.types.Object)
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||
psets: CollectionProperty(name="Psets", type=PsetQto)
|
||||
qtos: CollectionProperty(name="Qtos", type=PsetQto)
|
||||
document_references: CollectionProperty(name="Document References", type=DocumentReference)
|
||||
|
||||
@@ -29,6 +29,7 @@ class IfcSchema:
|
||||
|
||||
self.property_files = []
|
||||
property_paths = self.data_dir.joinpath("pset").glob("*.ifc")
|
||||
# TODO: add IFC2X3 PsetQto template support
|
||||
self.psetqto = ifcopenshell.util.pset.PsetQto("IFC4")
|
||||
for path in property_paths:
|
||||
self.psetqto.templates.append(ifcopenshell.open(path))
|
||||
|
||||
@@ -143,73 +143,6 @@ class BIM_PT_object_material(Panel):
|
||||
row.prop(attribute, "string_value", text="")
|
||||
|
||||
|
||||
class BIM_PT_object_psets(Panel):
|
||||
bl_label = "IFC Object Property Sets"
|
||||
bl_idname = "BIM_PT_object_psets"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object is not None and hasattr(context.active_object, "BIMObjectProperties")
|
||||
|
||||
def draw(self, context):
|
||||
if context.active_object is None:
|
||||
return
|
||||
layout = self.layout
|
||||
props = context.active_object.BIMObjectProperties
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "pset_name", text="")
|
||||
row.operator("bim.add_pset")
|
||||
|
||||
self.draw_psets_ui(props)
|
||||
|
||||
if props.relating_type and props.relating_type.BIMObjectProperties.psets:
|
||||
layout.label(text="Inherited Psets:")
|
||||
self.draw_psets_ui(props.relating_type.BIMObjectProperties, enabled=False)
|
||||
|
||||
def draw_psets_ui(self, props, enabled=True):
|
||||
for index, pset in enumerate(props.psets):
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.prop(
|
||||
pset,
|
||||
"is_expanded",
|
||||
icon="TRIA_DOWN" if pset.is_expanded else "TRIA_RIGHT",
|
||||
icon_only=True,
|
||||
emboss=False,
|
||||
)
|
||||
row2 = row.row(align=True)
|
||||
row2.enabled = enabled
|
||||
row2.prop(pset, "name", text="", icon="COPY_ID", emboss=pset.is_editable)
|
||||
|
||||
if enabled:
|
||||
row.prop(pset, "is_editable", icon="CHECKMARK" if pset.is_editable else "GREASEPENCIL", icon_only=True)
|
||||
row.operator("bim.remove_pset", icon="X", text="").pset_index = index
|
||||
if not pset.is_expanded:
|
||||
continue
|
||||
if pset.is_editable:
|
||||
for prop in pset.properties:
|
||||
row = box.row(align=True)
|
||||
row.enabled = enabled
|
||||
row.prop(prop, "name", text="")
|
||||
row.prop(prop, "string_value", text="")
|
||||
if not enabled:
|
||||
continue
|
||||
op = row.operator("bim.copy_property_to_selection", icon="COPYDOWN", text="")
|
||||
op.pset_name = pset.name
|
||||
op.prop_name = prop.name
|
||||
op.prop_value = prop.string_value
|
||||
else:
|
||||
col = box.column(align=True)
|
||||
for prop in pset.properties:
|
||||
if not prop.string_value:
|
||||
continue
|
||||
col.enabled = enabled
|
||||
col.prop(prop, "string_value", text=prop.name)
|
||||
|
||||
|
||||
class BIM_PT_object_qto(Panel):
|
||||
bl_label = "IFC Object Quantity Sets"
|
||||
bl_idname = "BIM_PT_object_qto"
|
||||
|
||||
Reference in New Issue
Block a user