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:
Dion Moult
2021-01-07 19:09:55 +11:00
parent fcc2b9ee13
commit d673e845f6
16 changed files with 498 additions and 134 deletions
@@ -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"
+12 -7
View File
@@ -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))
-67
View File
@@ -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"