On load handlers.
One way to approach the load event is with a persistent load post handler. However many side cases will be missed by it. Addon first enabled. An appended object with the old prop.
bl_info = { # fill in to test as addon
}
import bpy
from bpy.props import PointerProperty, BoolProperty, FloatProperty
from bpy.types import PropertyGroup
class Foo(PropertyGroup):
bar : BoolProperty()
foo : FloatProperty()
classes = (Foo,)
@bpy.app.handlers.persistent
def convert(dummy):
old_key = "prop"
new_key = "foo"
print("Convert")
obs = [o for o in bpy.data.objects if o.get(old_key)]
if obs:
print("Converting...")
for o in obs:
if o.get(old_key):
o[new_key] = o[old_key]
del o[old_key]
def draw_test(self, context):
layout = self.layout
layout.prop(context.object.foo, "foo")
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Object.foo = PointerProperty(
type=Foo,
)
bpy.types.TEXT_HT_footer.prepend(draw_test)
bpy.app.handlers.load_post.append(convert)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
bpy.app.handlers.remove(convrt)
if __name__ == "__main__":
register()
Copy Custom Properties from one object to another Can ignore the '_RNA_UI'
in this case, since its members are emulating those defined by the bpy.props
property for drawing the prop in the UI. (name, min, max, soft_min etc).
Consider wiring into panel code.
Instead could roll back the panel to do no more than alert that an old property is set on an object and the fix button.
Example below, a red splash screen button is displayed if an object in the file is given a custom prop named "prop".
import bpy
### hacked in by way of example
class AS_placeholder_properties(bpy.types.PropertyGroup):
foo : bpy.props.BoolProperty()
pass
bpy.utils.register_class(AS_placeholder_properties)
bpy.types.Object.AS_placeholder_properties = bpy.props.PointerProperty(
type=AS_placeholder_properties,
poll=lambda self, obj: obj.get('AS_placeholder') is not None,
)
####
class HelloWorldPanel(bpy.types.Panel):
"""Creates a Panel in the Object properties window"""
bl_label = "Hello World Panel"
bl_idname = "OBJECT_PT_hello"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
@classmethod
def poll(cls, context):
old_prop = "prop"
if any(ob.get(old_prop) is not None for ob in context.blend_data.objects):
cls.draw = cls.convert
else:
cls.draw = cls.draw_normal
return True
def convert(self, context):
layout=self.layout
layout.alert = True
layout.operator("wm.splash") # convert op
def draw_normal(self, context):
layout = self.layout
obj = context.object
row = layout.row()
row.prop(obj.AS_placeholder_properties, "foo")
row = layout.row()
row.operator("mesh.primitive_cube_add")
draw = draw_normal
def register():
bpy.utils.register_class(HelloWorldPanel)
def unregister():
bpy.utils.unregister_class(HelloWorldPanel)
if __name__ == "__main__":
register()
The poll method above could instead test if your convert operator polls
if bpy.ops.object.convert2newpropname.poll():
intrigued by the poll method used for property group above. Would seem to always poll unless class is totally passed in which case will be boolean False. (eg comment out foo boolean above) In as much as the bpy.props
is defining the class and creating the custom prop, IMO always safest to use
ob.get("custom_prop_name")
or
ob.get("custom_prop_name", some_fallback_other_than_None)