Help with renaming custom properties and associated data

Hey, there guys. Blender seems to have a problem in which it doesn’t correctly update references to custom properties when their names are changed. To deal with this I am attempting to right a renaming script. The user will input the name of the property they want to change and the new name and the script will then go through and rename references to that name in animation curves and driver property variables. I feel like what I have so far should be working but I am not getting expected results, ie, the data is not changing. On a related note, I don’t think that I am accessing the custom properties animation data correctly in the “Update Property Animation Curves” section. I’m sure I am doing most of this wrong/the hard way so any help would be appreciated.

#----------------------RENAME PROPERTIES-----------------------#
bpy.types.Scene.OldPropertyName = bpy.props.StringProperty(default="Property To Change")
bpy.types.Scene.RenameProperties = bpy.props.StringProperty(default="New Name")

class renameProperties (bpy.types.Operator):
    bl_idname = "object.rename_properties"
    bl_label = "Rename Properties"
    bl_options = {"REGISTER", "UNDO"}
    def execute(self,context):
        objects = bpy.context.selected_objects[:]
        old_name = bpy.context.scene.OldPropertyName
        new_name = bpy.context.scene.RenameProperties

        for obj in objects:           
            if obj == bpy.context.active_object:
                
                #--------Add Property Names to A List---------#
                for key in obj.keys():
                    if key == old_name:
                        if key != "_RNA_UI":
                            print("old key is ", key)
                            key = new_name
                            print("new key is ", key)

        #-------Get Actions In Scene--------#
        actions = bpy.data.actions
                            
        #-------Get FCurves-------#
        for action in actions:
            act_curve = bpy.data.actions[action.name].groups
            for curve in act_curve:
                #print("curves", curve.name)
                
                #--------Update Property Animation Curves ------#
                if curve.name == old_name:
                    print("old curve name", curve.name)
                    curve.name = new_name
                    print("new curve name", curve.name)
            
        #--------Get Driver Datapaths--------#
        drivers = bpy.context.object.animation_data.drivers
        for driver in range(len(drivers)):
            variables = drivers[driver].driver.variables
            for var in range(len(variables)):
                target = variables[var].targets
                for path in range(len(target)):
                    path_name = target[path].data_path
                    
                    #--------Update Driver Variable Path--------#
                    if path_name == "[\"{0}\"]".format(old_name):
                        print(True, var_name)
                        print("new_name", new_name)
                        path_name == new_name
                        print("new var name", var_name)                
                    
        return {'FINISHED'}

Today I am back at it trying to find a solution to this problem. Today I worked on renaming FCurves. I thought I had made some progress but it looks like my solution doesn’t work. Here is what I did today:

import bpy

actions = bpy.data.actions 
for action in actions:
curves = bpy.data.actions[action.name].fcurves
for path in curves:
if "upperarm.L" in str(path.data_path):
old_path = str(path.data_path)
new_path = old_path.replace("upperarm.L", "UpArmL")

Any help would be appreciated!

Would probably be best if you renamed properties with a custom operator in the first place:

import bpy

class SimpleOperator(bpy.types.Operator):
    """Change name of custom property"""
    bl_idname = "object.property_rename"
    bl_label = "Rename custom property"

    @classmethod
    def poll(cls, context):
        return len(context.object.keys()) > 1

    def execute(self, context):
        wm = context.window_manager
        name_old = wm.object_property_enum
        name_new = wm.object_property_name
        
        if name_old == name_new:
            return {'CANCELLED'}
        
        for ob in bpy.context.selected_objects:
            prop = ob.get(name_old)
            if prop is not None:
                ob[name_new] = prop
                ob["_RNA_UI"][name_new] = {}
                for k in ob["_RNA_UI"].get(name_old):
                    ob["_RNA_UI"][name_new][k] = ob["_RNA_UI"][name_old][k]
                del ob[name_old]
                try:
                    del ob["_RNA_UI"][name_old]
                except KeyError:
                    pass
                
                # rename other stuff here
                
        wm.object_property_enum = name_new

        return {'FINISHED'}

def draw_func(self, context):
    layout = self.layout
    ob = context.object
    wm = context.window_manager
    name_old = wm.object_property_enum
    name_new = wm.object_property_name

    row = layout.row(align=True)
    row.enabled = len(ob.keys()) > 1
    row.prop(wm, "object_property_enum", text="")
    col = row.column(align=True)
    col.alert = name_old == name_new
    col.prop(wm, "object_property_name", text="")
    row.operator("object.property_rename", text="", icon="GREASEPENCIL")
    
def property_items(self, context):
    ob = context.object
    return [(key, key, "") for key in ob.keys() if key != "_RNA_UI"]


def register():
    bpy.utils.register_module(__name__)
    bpy.types.OBJECT_PT_custom_props.prepend(draw_func)
    bpy.types.WindowManager.object_property_enum = bpy.props.EnumProperty(
        name="Custom Properties",
        items=property_items,
    )
    bpy.types.WindowManager.object_property_name = bpy.props.StringProperty()

def unregister():
    bpy.utils.unregister_module(__name__)
    bpy.types.OBJECT_PT_custom_props.remove(draw_func)
    del bpy.types.WindowManager.object_property_enum
    del bpy.types.WindowManager.object_property_name

if __name__ == "__main__":
    register()

Could you provide an example blend with drivers and fcurves, so I can test some renaming code?

I am going to be going over this code soon but I wanted to reply. First off thanks for the response. Secondly, there are few things that you’ve done that I just don’t understand yet. Specifically, I don’t understand what property_enum is. This is one of a number of aspects of the blender api that just seem so confusing to me. bpy.types.EnumProperty(Property) this is what I see when I look at the docs and it just doesn’t tell me at all how its used. There just seems to be so many ways to try and approach the data and I am missing some fundamental understanding of how Blender api is structured.

Anyway, here is a link to a simple file with two animated custom properties approximately as I will use them.
https://drive.google.com/file/d/0Bygg2UIJw9VAb1lmTFZtVzBJYlU/view?usp=sharing

CoDEmanX

I am just getting back to this problem after a while. The code you posted only renames the custom properties themselves and doesn’t fix associated data paths. I have attempted a number of times to write something myself that does so but have not come up with anything that works properly. When I print out what’s going on it looks like paths are being update but really aren’t. I am getting to the point where I am starting to think that this is a bug in the python implementation. Before I submit a bug report I would ask you to take a look at the blend file I am linking and the code in it to see if it should be renaming the paths to the animation data and drivers. I appreciate this a bunch.

https://drive.google.com/file/d/0Bygg2UIJw9VAVVB2VXVtTUNQdUk/view?usp=sharing

Do you mean the data_paths in fcurves and drivers? Well, that’s rather a known limitation than a bug…

Can you describe specifically what the limitation is?

Well, name changes are only tracked for built-in properties which store a reference to an ID datablock. A python-defined StringProperty that refers to a datablock will become invalid if the referenced datablock is renamed. There is no way to define ID properties with Python.

What I want to do is repair/redirect broken f-curve and driver data_paths via python. Are you saying this is not possible?

technically, it is possible to change f-curve data_paths and driver paths, but seems pretty tricky to me to implement a script that works reliably. Unless you let the user do the mapping in a GUI…

I don’t really need a GUI. This is really for my own use. Although I would imagine a tool like that would be useful to the wider community. Its hard for me to understand how this hasn’t been addressed before. Well, I’d be willing to pay for some help with on this issue. I have a lot of animated data that needs to be redirected and soon.

Is it already “broken”, as in driver expressions and f-curve data paths are already out of sync? Or do you plan on changing names at the moment? For the latter case, it would be so much easier to solve, you would only need a UI to rename stuff and a function to not only to to rename objects, but also data paths and driver expressions.

Things aren’t “broken” yet. I have a bunch of custom properties that I am exporting with their associated animations. I need to rename the custom properties so that they follow a naming convention. When I rename the properties I need the animation data to be relinked as well as the drivers that refer to those properties.