# Is there a way to insert keyframes for a group of frames using python?

I have an object that has a location driver, so the location of this object is constantly changing. I want to keyframe its motion throughout a range of frames, with each frame being a keyframe (essentially creating an fcurve). The method I have been using in python is going frame by frame and inserting a keyframe each frame.

import bpy
from bpy import *

bpy.context.scene.frame_current = 0
i = 0

for i in range(0,1900):
bpy.context.scene.frame_current = bpy.context.scene.frame_current + 1
i += 1


Is there an easier way to keyframe a group of frames via python instead of going frame by frame? The more objects added, and a bigger range of frames means blender getting slower over time to add keyframes one by one, so this method can take many hours depending on how many different objects I have involved.

zeffii's answer is better than using bpy.ops, but if you want to get closer to the metal, it gets a little clumsy. Blender's action API is missing several convenience functions.

It boils down to these steps

• make sure the datablock has animation_data
• get the animation_data.action or .action=bpy.data.actions.new(name)
• find the action_data.fcurves[i] that matches the data_path and array_index, or create the fcurve
• make sure there are enough fcurve.keyframe_points to load our data into
• assign the fcurve.keyframe_points[j].co and possibly .interpolation, .handle_left, .handle_right, etc.

There are a couple of examples embedded in http://web.purplefrog.com/~thoth/blender/python-cookbook/movie-card-stack.html and http://web.purplefrog.com/~thoth/blender/python-cookbook/csv-to-fcurve.html which I will summarize here:

def make_or_get_fcurve(obj, data_path, index=-1):
"""
:param obj: the data block from which we'll get the fcurve
:param data_path:
:param index:
:return:
"""

# freshly created objects don't have animation_data yet.
if obj.animation_data is None:
obj.animation_data_create()
if ad.action is None:

for fc in ad.action.fcurves:
if (fc.data_path != data_path):
continue
if index<0 or index==fc.array_index:
return fc
# the action didn't have the fcurve we needed, yet


and from the other example

def find_or_create_fcurve(action, data_path, array_index= -1):
for fc in action.fcurves:
if fc.data_path == data_path and ( array_index<0 or fc.array_index==array_index):
return fc
fc = action.fcurves.new(data_path, array_index)
return fc

def make_eyeball_look_at(armature, frame):

eyeball = get_eyeball()

cns = find_constraint_track_to(eyeball, armature, "card release")
data_path = 'constraints["%s"].influence' % cns.name
fc = find_or_create_fcurve(eyeball.animation_data.action, data_path)

i0 = len(fc.keyframe_points)
kp0 = fc.keyframe_points[i0]
kp1 = fc.keyframe_points[i0 + 1]
kp2 = fc.keyframe_points[i0 + 2]
kp3 = fc.keyframe_points[i0 + 3]

kp0.handle_left = [frame - 7, 0]
kp0.co = [frame - 6, 0]
kp0.handle_right = [frame - 5, 0]

kp1.handle_left = [frame - 4, 1]
kp1.co = [frame - 3, 1]
kp1.handle_right = [frame - 2, 1]

kp2.handle_left = [frame + 2, 1]
kp2.co = [frame + 3, 1]
kp2.handle_right = [frame + 4, 1]

kp3.handle_left = [frame + 5, 0]
kp3.co = [frame + 6, 0]
kp3.handle_right = [frame + 7, 0]

idx0 = len(fcurve.keyframe_points)
rval = []
for i in range(len(kps)):
kp = fcurve.keyframe_points[idx0 + i]
kp.co = kps[i]
rval.append(kp)
return rval


You should avoid operators if possible and do something like this:

import bpy
from bpy import context

scene = context.scene
ob = context.object  # object to keyframe should be selected/active
# ob = bpy.data.objects["name"]  # or referenced by name

for i in range(0, 1900):
scene.frame_set(i)
ob.keyframe_insert(data_path="location", index=-1)