diff --git a/src/blenderbim/blenderbim/bim/module/pset/__init__.py b/src/blenderbim/blenderbim/bim/module/pset/__init__.py index b1dcfa4ec2..52b6ada3e4 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/pset/__init__.py @@ -29,12 +29,19 @@ classes = ( operator.GuessQuantity, operator.RemovePset, operator.TogglePsetExpansion, + operator.AddPropertyToEdit, + operator.RemovePropertyToEdit, + operator.ClearList, + operator.RenameParameters, + operator.AddEditCustomProperty, prop.PsetProperties, prop.MaterialPsetProperties, prop.TaskPsetProperties, prop.ResourcePsetProperties, prop.ProfilePsetProperties, prop.WorkSchedulePsetProperties, + prop.PropertiesToRename, + prop.PropertiesToAddOrEdit, ui.BIM_PT_object_psets, ui.BIM_PT_object_qtos, ui.BIM_PT_material_psets, @@ -43,6 +50,9 @@ classes = ( ui.BIM_PT_resource_psets, ui.BIM_PT_profile_psets, ui.BIM_PT_work_schedule_psets, + ui.BIM_PT_BulkPropertyEditor, + ui.BIM_PT_RenameParameters, + ui.BIM_PT_AddPropertiesOrEditValues ) @@ -53,6 +63,8 @@ def register(): bpy.types.Scene.ResourcePsetProperties = bpy.props.PointerProperty(type=prop.ResourcePsetProperties) bpy.types.Scene.ProfilePsetProperties = bpy.props.PointerProperty(type=prop.ProfilePsetProperties) bpy.types.Scene.WorkSchedulePsetProperties = bpy.props.PointerProperty(type=prop.WorkSchedulePsetProperties) + bpy.types.Scene.PropertiesToRename = bpy.props.CollectionProperty(type=prop.PropertiesToRename) + bpy.types.Scene.PropertiesToAddOrEdit = bpy.props.CollectionProperty(type=prop.PropertiesToAddOrEdit) def unregister(): @@ -62,3 +74,5 @@ def unregister(): del bpy.types.Scene.ResourcePsetProperties del bpy.types.Scene.ProfilePsetProperties del bpy.types.Scene.WorkSchedulePsetProperties + del bpy.types.Scene.PropertiesToRename + del bpy.types.Scene.PropertiesToAddOrEdit diff --git a/src/blenderbim/blenderbim/bim/module/pset/operator.py b/src/blenderbim/blenderbim/bim/module/pset/operator.py index b306ebba76..3ca2b228a8 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/operator.py +++ b/src/blenderbim/blenderbim/bim/module/pset/operator.py @@ -374,3 +374,147 @@ class CopyPropertyToSelection(bpy.types.Operator, Operator): prop_name=self.name, prop_value=prop_value, ) + + +class AddPropertyToEdit(bpy.types.Operator): + bl_label = "Add new row" + bl_idname = "bim.add_property_to_edit" + bl_options = {"REGISTER", "UNDO"} + type: bpy.props.StringProperty() + + def execute(self, context): + if self.type == "PropertiesToRename": + props = context.scene.PropertiesToRename + props.add() + elif self.type == "PropertiesToAddOrEdit": + props = context.scene.PropertiesToAddOrEdit + props.add() + return {"FINISHED"} + + +class RemovePropertyToEdit(bpy.types.Operator): + bl_label = "Remove property to be renamed" + bl_idname = "bim.remove_property_to_edit" + bl_options = {"REGISTER", "UNDO"} + index: bpy.props.IntProperty() + type: bpy.props.StringProperty() + + def execute(self, context): + if self.type == "PropertiesToRename": + props = context.scene.PropertiesToRename + props.remove(self.index) + if self.type == "PropertiesToAddOrEdit": + props = context.scene.PropertiesToAddOrEdit + props.remove(self.index) + return {"FINISHED"} + + +class ClearList(bpy.types.Operator): + bl_label = "Clear list of properties" + bl_idname = "bim.clear_list" + bl_options = {"REGISTER", "UNDO"} + type: bpy.props.StringProperty() + + def execute(self, context): + if self.type == "PropertiesToRename": + props = context.scene.PropertiesToRename + props.clear() + if self.type == "PropertiesToAddOrEdit": + props = context.scene.PropertiesToAddOrEdit + props.clear() + return {"FINISHED"} + + +class RenameParameters(bpy.types.Operator): + bl_label = "Rename Parameters" + bl_idname = "bim.rename_parameters" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Rename parameters that are subclasses of IfcElement" + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + props_to_map = context.scene.PropertiesToRename + ifc_file = IfcStore.get_file() + all_ifc_elements = ifc_file.by_type("IfcElement") + + for ifc_element in all_ifc_elements: + for definition in ifc_element.IsDefinedBy: + if definition.is_a('IfcRelDefinesByProperties'): + prop_set = definition.RelatingPropertyDefinition + self.rename_property(prop_set, props_to_map, ifc_element) + + self.report({'INFO'}, 'Finished applying changes') + return {"FINISHED"} + + def rename_property(self, property_set, properties_to_map, ifc_element): + if property_set.is_a() == "IfcPropertySet": + property_container = property_set.HasProperties + elif property_set.is_a() == "IfcElementQuantity": + property_container = property_set.Quantities + + for obj_prop in property_container: + for prop2map in properties_to_map: + if prop2map.pset_name != property_set.Name: + continue + if prop2map.existing_property_name == obj_prop.Name: + obj_prop.Name = prop2map.new_property_name + Data.load(IfcStore.get_file(), ifc_element.id()) + + +class AddEditCustomProperty(bpy.types.Operator): + bl_label = "Add or edit a custom property" + bl_idname = "bim.add_edit_custom_property" + bl_options = {"REGISTER", "UNDO"} + index: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + self.file = IfcStore.get_file() + selected_objects = context.selected_objects + props = context.scene.PropertiesToAddOrEdit + + for object in selected_objects: + ifc_definition_id = object.BIMObjectProperties.ifc_definition_id + if not ifc_definition_id: + continue + ifc_element = tool.Ifc.get().by_id(ifc_definition_id) + psets = ifcopenshell.util.element.get_psets(ifc_element) + for prop in props: + if prop.value_type == "String": + value = prop.string_value + elif prop.value_type == "Boolean": + value = prop.bool_value + elif prop.value_type == "Integer": + value = prop.int_value + elif prop.value_type == "Number": + value = prop.float_value + + if prop.pset_name not in psets: + 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}) + + else:#1) delete the existing property first, in case the value type has also been changed, and then + #2) re-add the property + #TODOUpdate this code to incorporate the dion's latest commit + self.delete_ifc_property(ifc_element, prop) + ifcopenshell.api.run("pset.edit_pset", self.file, pset=self.file.by_id(psets[prop.pset_name]["id"]), properties={prop.property_name:value}) + + self.report({'INFO'}, 'Finished applying changes') + return {"FINISHED"} + + def delete_ifc_property(self, element, property): + for definition in element.IsDefinedBy: + if definition.is_a('IfcRelDefinesByProperties'): + prop_set = definition.RelatingPropertyDefinition + if prop_set.Name != property.pset_name: + continue + for hasprop in prop_set.HasProperties: + if hasprop.Name != property.property_name: + continue + self.file.remove(hasprop) + Data.load(IfcStore.get_file(), element.id()) + return diff --git a/src/blenderbim/blenderbim/bim/module/pset/prop.py b/src/blenderbim/blenderbim/bim/module/pset/prop.py index ba96a34555..fd7814025b 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/prop.py +++ b/src/blenderbim/blenderbim/bim/module/pset/prop.py @@ -173,3 +173,26 @@ class WorkSchedulePsetProperties(PropertyGroup): active_pset_name: StringProperty(name="Pset Name") properties: CollectionProperty(name="Properties", type=Attribute) pset_name: EnumProperty(items=getWorkSchedulePsetNames, name="Pset Name") + + +class PropertiesToRename(PropertyGroup): + pset_name: StringProperty(name="Pset") + existing_property_name: StringProperty(name="Existing Property Name") + new_property_name: StringProperty(name="New Property Name") + + +class PropertiesToAddOrEdit(PropertyGroup): + pset_name: StringProperty(name="Pset") + property_name: StringProperty(name="Property") + string_value: StringProperty(name="Value") + bool_value: BoolProperty(name="Property Value") + int_value: IntProperty(name="Property Value") + float_value: FloatProperty(name="Property Value") + value_type: EnumProperty( + items=[ + ("String", "String", "" ), + ("Boolean", "True/False", "" ), + ("Integer", "Integer", "" ), + ("Number", "Number", "" )], + default="String" + ) diff --git a/src/blenderbim/blenderbim/bim/module/pset/ui.py b/src/blenderbim/blenderbim/bim/module/pset/ui.py index a7247daf0f..e04f0ca225 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/ui.py +++ b/src/blenderbim/blenderbim/bim/module/pset/ui.py @@ -375,3 +375,92 @@ class BIM_PT_work_schedule_psets(Panel): for pset in WorkSchedulePsetsData.data["psets"]: draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "WorkSchedule") + + +class BIM_PT_BulkPropertyEditor(Panel): + bl_label = "IFC Bulk Property Editor" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "object" + + def draw(self, context): + pass + + +class BIM_PT_RenameParameters(Panel): + bl_label = "Rename all building elements" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "object" + bl_parent_id = "BIM_PT_BulkPropertyEditor" + bl_options = {"DEFAULT_CLOSED"} + bl_order = 0 + + def draw(self, context): + layout = self.layout + props = context.scene.PropertiesToRename + + if props: + for index, property in enumerate(props): + row = layout.row() + row.prop(property, "pset_name", text="") + row.prop(property, "existing_property_name", text="") + row.prop(property, "new_property_name", text="") + op = row.operator("bim.remove_property_to_edit",icon="PANEL_CLOSE", text="") + op.index = index + op.type = "properties_to_map" + + row = layout.row() + row.label() + op = row.operator("bim.add_property_to_edit", icon="ADD",text="") + op.type = "PropertiesToRename" + + if props: + row = layout.row() + clear = row.operator("bim.clear_list") + clear.type = "PropertiesToRename" + row.operator("bim.rename_parameters") + + +class BIM_PT_AddPropertiesOrEditValues(Panel): + """Creates a Panel in the Object properties window""" + bl_label = "Add/Edit Custom Properties and Values (to selected objects)" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "object" + bl_parent_id = "BIM_PT_BulkPropertyEditor" + bl_options = {"DEFAULT_CLOSED"} + bl_order = 1 + + def draw(self, context): + layout = self.layout + props = context.scene.PropertiesToAddOrEdit + + if props: + for index, property in enumerate(props): + row = layout.row() + row.prop(property, "pset_name", text="") + row.prop(property, "property_name", text="") + if property.value_type == "String": + row.prop(property, "string_value", text="") + elif property.value_type == "Boolean": + row.prop(property, "bool_value", text="") + elif property.value_type == "Integer": + row.prop(property, "int_value", text="") + elif property.value_type == "Number": + row.prop(property, "float_value", text="") + row.prop(property, "value_type", text="") + op = row.operator("bim.remove_property_to_edit",icon="PANEL_CLOSE", text="") + op.index = index + op.type = "PropertiesToAddOrEdit" + + row = layout.row() + row.label() + op = row.operator("bim.add_property_to_edit", icon="ADD",text="") + op.type = "PropertiesToAddOrEdit" + + if props: + row = layout.row() + clear = row.operator("bim.clear_list") + clear.type = "PropertiesToAddOrEdit" + op = row.operator("bim.add_edit_custom_property",icon="ADD", text="Apply Changes")