bulk property editor (#1896)

* first revision

* update incorporating review comments

* added a new feature - remove psets

* type dropdown now references ifc data types

* adopt new solution for enumerations and umlauts

* Revert "adopt new solution for enumerations and umlauts"

This reverts commit 203a31a2bf.
This commit is contained in:
Vukas Pajic
2022-01-04 02:59:55 +01:00
committed by GitHub
parent 03f5feca88
commit 0625e4153c
6 changed files with 443 additions and 114 deletions
@@ -29,12 +29,21 @@ classes = (
operator.GuessQuantity,
operator.RemovePset,
operator.TogglePsetExpansion,
operator.BIM_OT_add_property_to_edit,
operator.BIM_OT_remove_property_to_edit,
operator.BIM_OT_clear_list,
operator.BIM_OT_rename_parameters,
operator.BIM_OT_add_edit_custom_property,
operator.BIM_OT_bulk_remove_psets,
prop.PsetProperties,
prop.MaterialPsetProperties,
prop.TaskPsetProperties,
prop.ResourcePsetProperties,
prop.ProfilePsetProperties,
prop.WorkSchedulePsetProperties,
prop.RenameProperties,
prop.AddEditProperties,
prop.DeletePsets,
ui.BIM_PT_object_psets,
ui.BIM_PT_object_qtos,
ui.BIM_PT_material_psets,
@@ -43,6 +52,10 @@ classes = (
ui.BIM_PT_resource_psets,
ui.BIM_PT_profile_psets,
ui.BIM_PT_work_schedule_psets,
ui.BIM_PT_bulk_property_editor,
ui.BIM_PT_rename_parameters,
ui.BIM_PT_add_edit_custom_properties,
ui.BIM_PT_delete_psets
)
@@ -53,7 +66,9 @@ 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.RenameProperties = bpy.props.CollectionProperty(type=prop.RenameProperties)
bpy.types.Scene.AddEditProperties = bpy.props.CollectionProperty(type=prop.AddEditProperties)
bpy.types.Scene.DeletePsets = bpy.props.CollectionProperty(type=prop.DeletePsets)
def unregister():
del bpy.types.Object.PsetProperties
@@ -62,3 +77,6 @@ def unregister():
del bpy.types.Scene.ResourcePsetProperties
del bpy.types.Scene.ProfilePsetProperties
del bpy.types.Scene.WorkSchedulePsetProperties
del bpy.types.Scene.RenameProperties
del bpy.types.Scene.AddEditProperties
del bpy.types.Scene.DeletePsets
@@ -101,7 +101,6 @@ class EnablePsetEditing(bpy.types.Operator):
def execute(self, context):
self.props = get_pset_props(context, self.obj, self.obj_type)
self.props.properties.clear()
data = Data.psets if self.pset_id in Data.psets else Data.qtos
@@ -374,3 +373,159 @@ class CopyPropertyToSelection(bpy.types.Operator, Operator):
prop_name=self.name,
prop_value=prop_value,
)
class BIM_OT_add_property_to_edit(bpy.types.Operator):
bl_label = "Add new row"
bl_idname = "bim.add_property_to_edit"
bl_options = {"REGISTER", "UNDO"}
option: bpy.props.StringProperty()
def execute(self, context):
getattr(context.scene, self.option).add()
return {"FINISHED"}
class BIM_OT_remove_property_to_edit(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()
option: bpy.props.StringProperty()
def execute(self, context):
getattr(context.scene, self.option).remove(self.index)
return {"FINISHED"}
class BIM_OT_clear_list(bpy.types.Operator):
bl_label = "Clear list of properties"
bl_idname = "bim.clear_list"
bl_options = {"REGISTER", "UNDO"}
option: bpy.props.StringProperty()
def execute(self, context):
getattr(context.scene, self.option).clear()
return {"FINISHED"}
class BIM_OT_rename_parameters(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.RenameProperties
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 BIM_OT_add_edit_custom_property(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.AddEditProperties
for obj in selected_objects:
ifc_definition_id = obj.BIMObjectProperties.ifc_definition_id
if not ifc_definition_id:
continue
ifc_element = tool.Ifc.get().by_id(ifc_definition_id)
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)
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}
)
self.report({'INFO'}, 'Finished applying changes')
return {"FINISHED"}
class BIM_OT_bulk_remove_psets(bpy.types.Operator):
bl_label = "Bulk remove psets from selected objects"
bl_idname = "bim.bulk_remove_psets"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Bulk remove psets from selected objects"
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.DeletePsets
for obj in selected_objects:
ifc_definition_id = obj.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:
pset = prop.pset_name
if pset in psets:
try:
ifcopenshell.api.run(
"pset.remove_pset",
self.file,
**{
"product": self.file.by_id(ifc_definition_id),
"pset": self.file.by_id(psets[pset]["id"]),
},
)
except KeyError:
pass #Sometimes the pset id is not found, I'm not sure why this happens though. - vulevukusej
Data.load(IfcStore.get_file(), ifc_definition_id)
self.report({'INFO'}, 'Finished applying changes')
return {"FINISHED"}
@@ -18,7 +18,8 @@
import bpy
import blenderbim.bim.schema
from blenderbim.bim.prop import Attribute
from blenderbim.bim.prop import Attribute, PRIMARY_MEASURE_TYPE
import ifcopenshell
from ifcopenshell.api.pset.data import Data
from blenderbim.bim.ifc import IfcStore
from bpy.types import PropertyGroup
@@ -173,3 +174,42 @@ 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 RenameProperties(PropertyGroup):
pset_name: StringProperty(name="Pset")
existing_property_name: StringProperty(name="Existing Property Name")
new_property_name: StringProperty(name="New Property Name")
class AddEditProperties(PropertyGroup):
pset_name: StringProperty(name="Pset")
property_name: StringProperty(name="Property")
string_value: StringProperty(name="Value")
bool_value: BoolProperty(name="Value")
int_value: IntProperty(name="Value")
float_value: FloatProperty(name="Value")
primary_measure_type: EnumProperty(
items=[
(x, x, "")
for x in PRIMARY_MEASURE_TYPE
],
default="IfcLabel",
name="Primary Measure Type"
)
def get_value_name(self):
ifc_data_type = IfcStore.get_schema().declaration_by_name(self.primary_measure_type)
data_type = ifcopenshell.util.attribute.get_primitive_type(ifc_data_type)
if data_type == "string":
return "string_value"
elif data_type == "boolean":
return "bool_value"
elif data_type == "integer":
return "int_value"
elif data_type == "float":
return "float_value"
class DeletePsets(PropertyGroup):
pset_name: StringProperty(name="Pset")
@@ -375,3 +375,117 @@ 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_bulk_property_editor(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_rename_parameters(Panel):
bl_label = "Rename all building elements"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
bl_parent_id = "BIM_PT_bulk_property_editor"
bl_options = {"DEFAULT_CLOSED"}
bl_order = 0
def draw(self, context):
layout = self.layout
props = context.scene.RenameProperties
if props:
for index, prop in enumerate(props):
row = layout.row()
row.prop(prop, "pset_name", text="")
row.prop(prop, "existing_property_name", text="")
row.prop(prop, "new_property_name", text="")
op = row.operator("bim.remove_property_to_edit",icon="PANEL_CLOSE", text="")
op.index = index
op.option = "RenameProperties"
row = layout.row()
row.label()
op = row.operator("bim.add_property_to_edit", icon="ADD",text="")
op.option = "RenameProperties"
if props:
row = layout.row()
clear = row.operator("bim.clear_list")
clear.option = "RenameProperties"
row.operator("bim.rename_parameters")
class BIM_PT_add_edit_custom_properties(Panel):
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_bulk_property_editor"
bl_options = {"DEFAULT_CLOSED"}
bl_order = 1
def draw(self, context):
layout = self.layout
props = context.scene.AddEditProperties
if props:
for index, prop in enumerate(props):
row = layout.row()
row.prop(prop, "pset_name", text="")
row.prop(prop, "property_name", text="")
row.prop(prop, prop.get_value_name(), text="")
row.prop(prop, "primary_measure_type", text="")
op = row.operator("bim.remove_property_to_edit",icon="PANEL_CLOSE", text="")
op.index = index
op.option = "AddEditProperties"
row = layout.row()
row.label()
op = row.operator("bim.add_property_to_edit", icon="ADD",text="")
op.option = "AddEditProperties"
if props:
row = layout.row()
clear = row.operator("bim.clear_list")
clear.option = "AddEditProperties"
op = row.operator("bim.add_edit_custom_property",icon="ADD", text="Apply Changes")
class BIM_PT_delete_psets(Panel):
bl_label = "Bulk remove psets from selected objects"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
bl_parent_id = "BIM_PT_bulk_property_editor"
bl_options = {"DEFAULT_CLOSED"}
bl_order = 2
def draw(self, context):
layout = self.layout
props = context.scene.DeletePsets
if props:
for index, prop in enumerate(props):
row = layout.row()
row.prop(prop, "pset_name", text="Pset")
op = row.operator("bim.remove_property_to_edit",icon="PANEL_CLOSE", text="")
op.index = index
op.option = "DeletePsets"
row = layout.row(align=True)
row.label()
op = row.operator("bim.add_property_to_edit", icon="ADD",text="")
op.option = "DeletePsets"
if props:
row = layout.row()
clear = row.operator("bim.clear_list")
clear.option = "DeletePsets"
op = row.operator("bim.bulk_remove_psets", text="Apply Changes")
@@ -19,7 +19,7 @@
import os
import bpy
import ifcopenshell
from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.prop import StrProperty, Attribute, PRIMARY_MEASURE_TYPE
from blenderbim.bim.ifc import IfcStore
from bpy.types import PropertyGroup
from bpy.props import (
@@ -146,116 +146,7 @@ class PropTemplate(PropertyGroup):
primary_measure_type: EnumProperty(
items=[
(x, x, "")
for x in [
"IfcInteger",
"IfcReal",
"IfcBoolean",
"IfcIdentifier",
"IfcText",
"IfcLabel",
"IfcLogical",
"IfcDateTime",
"IfcDate",
"IfcTime",
"IfcDuration",
"IfcTimeStamp",
"IfcPositiveInteger",
"IfcBinary",
"IfcVolumeMeasure",
"IfcTimeMeasure",
"IfcThermodynamicTemperatureMeasure",
"IfcSolidAngleMeasure",
"IfcPositiveRatioMeasure",
"IfcRatioMeasure",
"IfcPositivePlaneAngleMeasure",
"IfcPlaneAngleMeasure",
"IfcParameterValue",
"IfcNumericMeasure",
"IfcMassMeasure",
"IfcPositiveLengthMeasure",
"IfcLengthMeasure",
"IfcElectricCurrentMeasure",
"IfcDescriptiveMeasure",
"IfcCountMeasure",
"IfcContextDependentMeasure",
"IfcAreaMeasure",
"IfcAmountOfSubstanceMeasure",
"IfcLuminousIntensityMeasure",
"IfcNormalisedRatioMeasure",
"IfcComplexNumber",
"IfcNonNegativeLengthMeasure",
"IfcAbsorbedDoseMeasure",
"IfcAccelerationMeasure",
"IfcAngularVelocityMeasure",
"IfcAreaDensityMeasure",
"IfcCompoundPlaneAngleMeasure",
"IfcCurvatureMeasure",
"IfcDoseEquivalentMeasure",
"IfcDynamicViscosityMeasure",
"IfcElectricCapacitanceMeasure",
"IfcElectricChargeMeasure",
"IfcElectricConductanceMeasure",
"IfcElectricResistanceMeasure",
"IfcElectricVoltageMeasure",
"IfcEnergyMeasure",
"IfcForceMeasure",
"IfcFrequencyMeasure",
"IfcHeatFluxDensityMeasure",
"IfcHeatingValueMeasure",
"IfcIlluminanceMeasure",
"IfcInductanceMeasure",
"IfcIntegerCountRateMeasure",
"IfcIonConcentrationMeasure",
"IfcIsothermalMoistureCapacityMeasure",
"IfcKinematicViscosityMeasure",
"IfcLinearForceMeasure",
"IfcLinearMomentMeasure",
"IfcLinearStiffnessMeasure",
"IfcLinearVelocityMeasure",
"IfcLuminousFluxMeasure",
"IfcLuminousIntensityDistributionMeasure",
"IfcMagneticFluxDensityMeasure",
"IfcMagneticFluxMeasure",
"IfcMassDensityMeasure",
"IfcMassFlowRateMeasure",
"IfcMassPerLengthMeasure",
"IfcModulusOfElasticityMeasure",
"IfcModulusOfLinearSubgradeReactionMeasure",
"IfcModulusOfRotationalSubgradeReactionMeasure",
"IfcModulusOfSubgradeReactionMeasure",
"IfcMoistureDiffusivityMeasure",
"IfcMolecularWeightMeasure",
"IfcMomentOfInertiaMeasure",
"IfcMonetaryMeasure",
"IfcPHMeasure",
"IfcPlanarForceMeasure",
"IfcPowerMeasure",
"IfcPressureMeasure",
"IfcRadioActivityMeasure",
"IfcRotationalFrequencyMeasure",
"IfcRotationalMassMeasure",
"IfcRotationalStiffnessMeasure",
"IfcSectionModulusMeasure",
"IfcSectionalAreaIntegralMeasure",
"IfcShearModulusMeasure",
"IfcSoundPowerLevelMeasure",
"IfcSoundPowerMeasure",
"IfcSoundPressureLevelMeasure",
"IfcSoundPressureMeasure",
"IfcSpecificHeatCapacityMeasure",
"IfcTemperatureGradientMeasure",
"IfcTemperatureRateOfChangeMeasure",
"IfcThermalAdmittanceMeasure",
"IfcThermalConductivityMeasure",
"IfcThermalExpansionCoefficientMeasure",
"IfcThermalResistanceMeasure",
"IfcThermalTransmittanceMeasure",
"IfcTorqueMeasure",
"IfcVaporPermeabilityMeasure",
"IfcVolumetricFlowRateMeasure",
"IfcWarpingConstantMeasure",
"IfcWarpingMomentMeasure",
]
for x in PRIMARY_MEASURE_TYPE
],
name="Primary Measure Type",
)
+111
View File
@@ -40,6 +40,117 @@ cwd = os.path.dirname(os.path.realpath(__file__))
materialpsetnames_enum = []
PRIMARY_MEASURE_TYPE = [
"IfcInteger",
"IfcReal",
"IfcBoolean",
"IfcIdentifier",
"IfcText",
"IfcLabel",
"IfcLogical",
"IfcDateTime",
"IfcDate",
"IfcTime",
"IfcDuration",
"IfcTimeStamp",
"IfcPositiveInteger",
"IfcBinary",
"IfcVolumeMeasure",
"IfcTimeMeasure",
"IfcThermodynamicTemperatureMeasure",
"IfcSolidAngleMeasure",
"IfcPositiveRatioMeasure",
"IfcRatioMeasure",
"IfcPositivePlaneAngleMeasure",
"IfcPlaneAngleMeasure",
"IfcParameterValue",
"IfcNumericMeasure",
"IfcMassMeasure",
"IfcPositiveLengthMeasure",
"IfcLengthMeasure",
"IfcElectricCurrentMeasure",
"IfcDescriptiveMeasure",
"IfcCountMeasure",
"IfcContextDependentMeasure",
"IfcAreaMeasure",
"IfcAmountOfSubstanceMeasure",
"IfcLuminousIntensityMeasure",
"IfcNormalisedRatioMeasure",
"IfcComplexNumber",
"IfcNonNegativeLengthMeasure",
"IfcAbsorbedDoseMeasure",
"IfcAccelerationMeasure",
"IfcAngularVelocityMeasure",
"IfcAreaDensityMeasure",
"IfcCompoundPlaneAngleMeasure",
"IfcCurvatureMeasure",
"IfcDoseEquivalentMeasure",
"IfcDynamicViscosityMeasure",
"IfcElectricCapacitanceMeasure",
"IfcElectricChargeMeasure",
"IfcElectricConductanceMeasure",
"IfcElectricResistanceMeasure",
"IfcElectricVoltageMeasure",
"IfcEnergyMeasure",
"IfcForceMeasure",
"IfcFrequencyMeasure",
"IfcHeatFluxDensityMeasure",
"IfcHeatingValueMeasure",
"IfcIlluminanceMeasure",
"IfcInductanceMeasure",
"IfcIntegerCountRateMeasure",
"IfcIonConcentrationMeasure",
"IfcIsothermalMoistureCapacityMeasure",
"IfcKinematicViscosityMeasure",
"IfcLinearForceMeasure",
"IfcLinearMomentMeasure",
"IfcLinearStiffnessMeasure",
"IfcLinearVelocityMeasure",
"IfcLuminousFluxMeasure",
"IfcLuminousIntensityDistributionMeasure",
"IfcMagneticFluxDensityMeasure",
"IfcMagneticFluxMeasure",
"IfcMassDensityMeasure",
"IfcMassFlowRateMeasure",
"IfcMassPerLengthMeasure",
"IfcModulusOfElasticityMeasure",
"IfcModulusOfLinearSubgradeReactionMeasure",
"IfcModulusOfRotationalSubgradeReactionMeasure",
"IfcModulusOfSubgradeReactionMeasure",
"IfcMoistureDiffusivityMeasure",
"IfcMolecularWeightMeasure",
"IfcMomentOfInertiaMeasure",
"IfcMonetaryMeasure",
"IfcPHMeasure",
"IfcPlanarForceMeasure",
"IfcPowerMeasure",
"IfcPressureMeasure",
"IfcRadioActivityMeasure",
"IfcRotationalFrequencyMeasure",
"IfcRotationalMassMeasure",
"IfcRotationalStiffnessMeasure",
"IfcSectionModulusMeasure",
"IfcSectionalAreaIntegralMeasure",
"IfcShearModulusMeasure",
"IfcSoundPowerLevelMeasure",
"IfcSoundPowerMeasure",
"IfcSoundPressureLevelMeasure",
"IfcSoundPressureMeasure",
"IfcSpecificHeatCapacityMeasure",
"IfcTemperatureGradientMeasure",
"IfcTemperatureRateOfChangeMeasure",
"IfcThermalAdmittanceMeasure",
"IfcThermalConductivityMeasure",
"IfcThermalExpansionCoefficientMeasure",
"IfcThermalResistanceMeasure",
"IfcThermalTransmittanceMeasure",
"IfcTorqueMeasure",
"IfcVaporPermeabilityMeasure",
"IfcVolumetricFlowRateMeasure",
"IfcWarpingConstantMeasure",
"IfcWarpingMomentMeasure",
]
def update_preset(self, context):
from blenderbim.bim.data.ui.presets import presets