How to collect keys created with keyframe_insert()?

I’m working on a script to duplicate keyframes on the current frame and begin moving them, using a modal operator. This is all done in the timeline.

So keyframe_insert() works for this, but what’s the best way to collect those new keys? keyframe_insert() returns a boolean rather than the new keyframe (which is SO inconvenient right about now).

I can have it create the new keys on the next frame, and loop through the fcurves to find the keyframes on frame_current + 1, but then what if there are already other keys on that frame? They’ll get moved as well… dilemma.

Perhaps collect f-curve indices in a list? Or use a custom color when you insert the keyframe so later you can detect if any f-curve in the larger pool is managed by your code. I don’t think f-curves have names…?

Here is something I put together a while back.


############################################################################
# f-Curve code.
############################################################################
def makeSinglePointFCurve(passedName,passedDataPath, passedTarget, passedValue):
    # Use code generated f-curve solution.
    # Check for presence of f-curve.
    try:
        action = bpy.data.actions[passedName]
        can_proceed = True
    except:
        can_proceed = False
    if can_proceed == False:
        # Perform this one time setup of an f-curve.
        # Create a single point f-curve to act as a constant.
        print("Creating action [" + passedName + "] to control " + passedDataPath + ".")
        action = bpy.data.actions.new(passedName)
        
        action.fcurves.new(passedDataPath, action_group="managed by code")
        action.fcurves[0].keyframe_points.insert(1, 0.0)    #frame, value, option=set('FAST')
           
        passedTarget.animation_data_create()
        passedTarget.animation_data.action=action
    else:
        #print (action)
        pass
      
    # Assign value by changing frame #1 of the fcurve.
    print("Assigning " + passedDataPath + " " +str(passedValue) + " to particle [" + passedName +"].")
    action.fcurves[0].keyframe_points.insert(1, passedValue)
    action.fcurves[0].color_mode = 'CUSTOM'
    action.fcurves[0].extrapolation = 'LINEAR'
    action.fcurves[0].color = (0.6,0.0, 1.0)            # Code managed f-curves will have their own custom color.
    action.fcurves[0].lock = False

I’m not sure I follow. What I’m currently doing is using something like


fcurves = context.object.animation_data.action.fcurves
bones = context.selected_pose_bones
for bone in bones:
    for fcu in fcurves:
        if fcu.data_path.split('"')[1] == bone.name:
            bone.keyframe_insert(data_path = fcu.data_path.split('.')[-1], frame = scene.frame_current + 1)

to create the new keyframes. Then I’m using something to the effect of


self._keys = {}
for fcu in fcurves:
    if fcu:
        for kp in fcu.keyframe_points:
            if kp.co.x == scene.frame_current + 1:
                self._keys[kp] = (kp.handle_left.x - kp.co.x, kp.handle_right.x - kp.co.x)

to store the keyframes I’m going to move. I thought that, before doing any of this, I could collect any keyframes on

scene.frame_current + 1

and store them, and then exclude those keys from moving… but then I’d either have to exclude those fcurves from the keyframe_insert() part, or overwrite them in order to include the fcurves in the operation.

When you insert keyframes you can mark them as ‘yours’ with a color. Use the color as a way include/exclude a set of fcurves from processing when you scan the list later.


fcurves = context.object.animation_data.action.fcurves
bones = context.selected_pose_bones
for bone in bones:
    for fcu in fcurves:
        if fcu.data_path.split('"')[1] == bone.name:
            bone.keyframe_insert(data_path = fcu.data_path.split('.')[-1], frame = scene.frame_current + 1)
            fcu.color = (0.6,0.0, 1.0)  # My color.

It might be more memory efficient to just store the index into the fcurve list and pass that along to your review routine. Instead of storing the keyframe parts.


self._keys = {}
self._indcies = []
for fcu in fcurves:
    if fcu:
        count = 0
        for kp in fcu.keyframe_points:
            if kp.co.x == scene.frame_current + 1:
                #self._keys[kp] = (kp.handle_left.x - kp.co.x, kp.handle_right.x - kp.co.x)
                self._indicies.append(count)
            count = count + 1

Wow, that’s a really neat idea, I’ll have to keep that in mind for future reference.

However, the more I’ve thought about it, the more I realize it may be pretty moot to even try to keep from overwriting keys on the next frame when calling keyframe_insert()…

Atom - While we’re talking about this though, do you have any ideas about how to move more than one keyframe in the timeline? Is it possible to add something like a box-select operator for the timeline?

you should work with keyframe_points of fcurves and not keyframes in timeline, 'cause that’s the actual data. It might be possible to use timeline ops, but that will be inconvenient or even unsafe and slow.

I’m not sure I’m understanding what you’re meaning. I am working with keyframe_points, in general. :slight_smile: What specifically are you referring to?

Edit: Ohhh, you’re referring to post #6? Ah, I should probably ask that question in a different thread, 'cause it’s kinda-but-not-completely unrelated. I should clarify what I’m trying to do.

I’ve got an addon for moving keyframes directly in the timeline instead of having to open up the dopesheet or graph editor. Now I’m looking for a way to move all the keyframe_points on multiple frames, instead of only the keyframe_points on scene.frame_current. That’s all working with keyframe_points directly.

What I was curious about was whether there’s a way to add an operator to the timeline to select a range of frames. Or maybe I should just use an IntProperty so the user can tell the operator how many subsequent frames to move? Or maybe a frame range?