I want to access the keyframes for a specific bone in animation_data.action, just so I can reset them to a default value. My main problem is I dont know how to just access a specific bone.

The general purpose of this is to automatically reset the hand positions of bvh animation loaded onto a manuelbastionilab model. The bvh animation loads it in fine, but because there isnt really any hand data, it sets the animation to a default splayed out position. And because it assigns it to the wrong bones, it comes in with a crooked little finger. So I just want a simple python script to reset any animations I load in to a good default.

I think I should be able to get to the animation data for the keyframes with

myaction = bpy.context.active_object.animation_data.action

But I dont know what comes next, I dont know how to access the fcurves for a specific bone like a joint / bone in my little finger called pinky02_L

This seems incredibly basic but Ive spent ages googling it and failed miserably.

I suspect I might have problems with setting the data too ! I am using quaternions and so I want to access say the z rotation quaternion. So how do I access the z rot channel, I think its some array offset for the fcurve or something ? So once I have found the fcurve for pinky02l (pinky02lfcurve = ?), is it something like curve = pinky02lfcurve[6] to access the z rotation channel ? Or have I got it all horribly wrong ?

keyframePoints = curve.keyframe_points
    for keyframe in keyframePoints:

And Once I have a keyframe how do I set it to a value like :

keyframe.value = math.radians(8.0)

. Is it that simple ? I bet its not!

Any answers to any parts of this would be great ! p.s. thank you to batFinger and Scott Milner for answering my question about it setting it to the wrong degrees the other day.

So from the post lemon refers too I if I am understanding it correctly if I use:

for curve in pinky02lfcurve 
       if curve.data_path.endswith('rotation_quaternion') 
        if curve.array_index == 2 # or is it 3 for z with quaternions ?
            keyframePoints = curve.keyframe_points
            for keyframe in keyframePoints:
                keyframe.co[1] = math.radians(8.0)

Ok so re writing to take into account batFingers post I think the following should do what I want !

myaction = bpy.context.active_object.animation_data.action
fcurves = myaction.fcurves
rot = Euler(0,0,8)
myquat = rot.to_quaternion()

pinkyb = bpy.data.objects['MyArmature'].pose.bones['pinky02_L']
dpath = pinkyb.path_from_id("rotation_quaternion")
fc = fcurves.find(dpath, index=0)
keyframePoints = fc.keyframe_points
for keyframe in keyframePoints:
    keyframe.co[1] = myquat.w 

fc = fcurves.find(dpath, index=1)
keyframePoints = fc.keyframe_points
for keyframe in keyframePoints:
    keyframe.co[1] = myquat.x 

fc = fcurves.find(dpath, index=2)
keyframePoints = fc.keyframe_points
for keyframe in keyframePoints:
    keyframe.co[1] = myquat.y 

fc = fcurves.find(dpath, index=3)
keyframePoints = fc.keyframe_points
for keyframe in keyframePoints:
    keyframe.co[1] = myquat.z 

So I hope this should be good apart from the curve control points which are still at their original positions I think. Though I also still need to work out what angle I actually want to set it too ! Thanks again batFinger and lemon.

  • $\begingroup$ related blender.stackexchange.com/questions/74773/… $\endgroup$
    – lemon
    Jun 22 '18 at 14:16
  • $\begingroup$ Thanks lemon, if I am understanding it correctlyif fc.data_path.endswith(('location','rotation_euler','rotation_quaternion' $\endgroup$
    – MajorTom
    Jun 22 '18 at 17:38
  • $\begingroup$ Didn't used it myself with quaternion (so you'll have to check how it is organized... 4 data rows??). But yes 'rotation_quaternion', it should be. To check that, simply print the curve names. $\endgroup$
    – lemon
    Jun 22 '18 at 17:56
  • $\begingroup$ Just to say my last comment didnt go quite as planned, I pressed return to add a line break and arrange some code and it submitted the comment, then I deleted all the characters and submitted that and it ignored me and left the comment there ! Anyway I amended the end of my original question, though I think lemon might have replied to the comment that went wrong. So Im still hazy on the usage of data_path.endswith (is it used with curves or fcurves or are they the same thing) and Im still after a way of finding the fcurve(s) associated with pinky02_L. Thanks $\endgroup$
    – MajorTom
    Jun 22 '18 at 18:47

Some py console code.

Below are some ways to find an fcurve for specific pose bone, and pose bone property. In this example the context pose bone, and rotation_quaternion.

>>> ob = C.object
>>> action = ob.animation_data.action
>>> pb =  context.active_pose_bone
>>> pb.name

>>> # could query the pose bone  from collection via its name
>>> ob.pose.bones.get(pb.name)

>>> print(pb.path_from_id.__doc__)
.. method:: path_from_id(property="")

   Returns the data path from the ID to this object (string).

   :arg property: Optional property name which can be used if the path is
      to a property of this object.
   :type property: string
   :return: The path from :class:`bpy.types.bpy_struct.id_data`
      to this struct and property (when given).
   :rtype: str

# get the path from pb.id_data 
>>> dp = pb.path_from_id("rotation_quaternion")
>>> dp

>>> # could also build this from name
>>> 'pose.bones["%s"].rotation_quaternion' % pb.name

>>> # use find for one offs
>>> print(action.fcurves.find.__doc__)
ActionFCurves.find(data_path, index=0)
Find an F-Curve. Note that this function performs a linear scan of all F-Curves in the action.
# find rotquot[3] aka rotquot.z
>>> fc = action.fcurves.find(dp, index=3)
>>> fc

>>> fc.array_index
# use list comp to find them all for for each index
>>> ["%s[%d]" % (dp, fc.array_index) for fc in action.fcurves if fc.data_path == dp]
['pose.bones["Bone"].rotation_quaternion[0]', 'pose.bones["Bone"].rotation_quaternion[1]', 'pose.bones["Bone"].rotation_quaternion[2]', 'pose.bones["Bone"].rotation_quaternion[3]']

>>> # get the value from ob using dp 
>>> ob.path_resolve("%s[%d]" % (dp, fc.array_index))
# ie ob.pose.bones["Bone"].rotation_quaternion[3] = -0.43
>>> pb.id_data.path_resolve("%s[%d]" % (dp, fc.array_index))


Get bones associated with specific action in python

How can I tell if a bone property has a keyframe on the current frame using python?

How can I access bone's 'animation_data.action'?

On rotations, here is the quaternion for an xyz Euler rotation of degrees (8, 8, 8)

>>> rot = Euler((radians(8) for axis in "xyz"))
>>> rot.to_quaternion()
Quaternion((0.9930493831634521, 0.06456293165683746, 0.07427114993333817, 0.06456293165683746)) # wxyz

To set the defaults to above would set each keyframe.y of keyframes in fcurve fc with array_index=3 to 0.06.. etc.

  • $\begingroup$ Thanks batFinger I think that sort of works, though boy is it confusing. Ill amend my orig post with what I think is working code in a bit. The one prob I still appear to have is that the curve control points are maybe still at their original positions. So despite setting all the points to the same value I dont end up with a straight line ! Getting the data path from the pose bone and using it to find the fcurve in the action doesnt make a lot of sense to me, but it appears to work, so thank you. P.s. thanks for the tip about quaternions as Im not sure I ever did fully understand them :) $\endgroup$
    – MajorTom
    Jun 22 '18 at 20:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.