Need help scripting pose.bones Please!!

I have a model that has 4 track with 48 track pads per track. I’ve animated a complete cycle for each set. I decided to rename the bones in the 4 sets after I completed the animation and did it via a rename script. That work great, however, it totally disconnected the animations. My bones are named something new and the animations retained the old curve/pose.bone names. I’ve struggled through a bunch of code, found a way to change the curve names to the new bone name but the embedded pose.bones[“trk_pad_R.008”].location, pose.bones[“trk_pad_R.008”].rotation_quaternion, etc pose.bone name retained the old names.

I know that if I can change these names the animations will work again because I’ve proved it manually. I’m hoping to do this through code because each pad has a xyz location, scale and a wxyz quaternion rotation that have to be changed.

If someone can just show me how to change this list bones code to list the pose.bones, I might be able to work out the rest.

import bpy
for object in bpy.context.selected_objects[0].data.bones:
    print(object.name)

I changed the bone names using a Dictionary of names and this code


import bpybones = bpy.context.selected_objects[0].data.bones
if (bones):
    for bone in bones:
        newName = names.get(bone.name)
        if (newName):
            bone.name = newName

Then modified that to change the curve names. (which changed the names of the main bones/curves in the Animation Editor as well.)

I just need to change the Pose.Bone names now and everything should work. Every method, object or property I’ve tried results in an Error Such and such does not have a pose.bone attribute.

Thanks

Pte Jack

Hi,

The pose bones collection is on the armature object, rather than the armature data object.


import bpy

context = bpy.context
ob = context.object

for pb in ob.pose.bones:
    print(pb.name)

Thanks for the quick response batFINGER, I did find the armature element of the pose.bone though playing around with some code, however, the settings in the amature element will report the correct pose.bones as the names have been changed in the armature.

This is a sample of a return from your code


trk_pad_LI.001
trk_pad_LI.002
trk_pad_LI.003
trk_pad_LI.004
trk_pad_LI.005

The armature pose_bone names are correct…

However, I think I need to access the armature animation data via the fcurves or through the Animation Editor to get to the data that has yet to be changed. I read in an old thread that said the BVH Importer script might give a clue, and I found this, but have no idea on how to access it.


        if bvh_node.has_loc:
            # Not sure if there is a way to query this or access it in the
            # PoseBone structure.
            data_path = 'pose.bones["%s"].location' % pose_bone.name ##<--
            location = [(0.0, 0.0, 0.0)] * num_frame
            for frame_i in range(num_frame):
                bvh_loc = bvh_node.anim_data[frame_i + skip_frame][:3]
                bone_translate_matrix = Matrix.Translation(
                        Vector(bvh_loc) - bvh_node.rest_head_local)
                location[frame_i] = (bone_rest_matrix_inv *
                                     bone_translate_matrix).to_translation()
            # For each location x, y, z.
            for axis_i in range(3):
                curve = action.fcurves.new(data_path=data_path, index=axis_i)
                keyframe_points = curve.keyframe_points
                keyframe_points.add(num_frame)
                for frame_i in range(num_frame):
                    keyframe_points[frame_i].co = \
                            (time[frame_i], location[frame_i][axis_i])
        if bvh_node.has_rot:
            data_path = None
            rotate = None
            if 'QUATERNION' == rotate_mode:
                rotate = [(1.0, 0.0, 0.0, 0.0)] * num_frame
                data_path = ('pose.bones["%s"].rotation_quaternion'  
                           % pose_bone.name)  ##<---
            else:
                rotate = [(0.0, 0.0, 0.0)] * num_frame
                data_path = ('pose.bones["%s"].rotation_euler' % 
                             pose_bone.name)   ##<---
            prev_euler = Euler((0.0, 0.0, 0.0))
            for frame_i in range(num_frame):
                bvh_rot = bvh_node.anim_data[frame_i + skip_frame][3:]


The lines I’ve added the ##<— I believe contain the data that I need to change. The BVH_Node looks like an user defined class, and my coding skill just aren’t there yet to be able to follow the script and decipher exactly where those 2 data paths lead, and I’m still not sure what the %s variable passes (I presume the pose.bone name) or how it obtains its value (with the , % pose_bone.name on the right of the equation I think). I think I would need to find the data_path = (‘pose.bones["%s"].scale’ % pose_bone.name) and the the data_path = (‘pose.bones["%s"].location’ % pose_bone.name) if they exist.


I think this is a picture of the data elements being changed and definitely the ones I need to access and change to get this project back on track.


print('pose.bones["%s"].location' % name)

is an example of string formatting where %s is a string and is replaced with the variable name, which must be a string.

Have you tried “Revive Disabled FCurves” from the channel menu.

otherwise you will need to loop thru your fcurves and change the bone name to the one you have now


import bpy
action = bpy.data.actions["anim_Track_Inner_R"]
lookup = some  dictionary of names
for fcurve in action.fcurves:
    newname = lookup["oldname"]
    fcurve.data_path = fcurve.data_path.replace(oldname, newname)


or similar, should do the trick. (as long as there are no bones named pose, bone, rotation etc.

batFINGER!!! You are a coding wizard!!

Almost perfect, I guess it all depends on which animation I have loaded, but all I can say is THANK YOU!!!

Running this code…


import bpy
action = bpy.data.actions["anim_TrackInner_R"]  ##(&lt;-- This is the animation currently loaded)
for fcurve in action.fcurves:
 print (fcurve.data_path)

Returns exactly what I’m looking for


pose.bones["trk_pad_R.023"].rotation_quaternion
pose.bones["trk_pad_R.023"].rotation_quaternion
pose.bones["trk_pad_R.023"].scale
pose.bones["trk_pad_R.023"].scale
pose.bones["trk_pad_R.023"].scale
----&gt;8 snip 8&lt;----
etc

Now to set up a dictionary and see if I can program the change…

Again, PERFECT!!! And Thanks for all the help!!!

{tosses a bottle or thermos of your preferred drink your way!!}

Pte Jack

Quick note, I just attempted the revive, but it failed, the new bone names must be too different for Blender to figure out what goes where. The tracks were individual armatures that I joined in to one master armature, which is why I did the bone rename in the first place. Current armature expanded from 50 bones to 278 bones when I added the tracks.

Just in the off chance that you might be wondering what I’m working on… lol


Cool I have the very same, or similar, with the intention of rigging, animating etc, but alas… I do have the driver setup to light his “voice box” based on audio, backed up somewhere.

For those looking at the thread for solutions…

This is the Script that I came up with to get all fcurve.datapaths from all animations and throw them into animation specific text files by animation name on the current user’s desktop.


# Script will:
# List all data_path objects in all animations
# Send output from Script to Animation specific text files for editing 
# import required modules
import getpass
import bpy
import os
# get the current user and and send the created text file to the User's Desktop
user = getpass.getuser() 
os.chdir("C:\\users\\" + str(user) + "[\\Desktop\\](file://\\Desktop\\)")
# find the animations in the armature and create a new text file based on the animation name
action = bpy.data.actions
for a in action:
 print (a.name)
 aname = str(a.name)
 myFile = open(aname + '.txt','w')
# write the animation data_path to the file 
# myFile.write('
' + aname + '
')
#uncomment the above line if you want the animation name to be written to the file
 for fcurve in action[a.name].fcurves:
     print (fcurve.data_path)
     e = str(fcurve.data_path)
     myFile.write(e + '
')
# close the file
 myFile.close()

Step one complete!!! You have no idea how excited I am about this.