How do I do a command be repeated whenever the frame changes?

I want the “bpy.ops.anim.keyframe_insert_menu (type = ‘BUILTIN_KSI_LocRot’)” command run on the selected object when the frame changes. How do I make this script?

Hi mano,

Check out frame_change_pre(post) handlers here http://www.blender.org/documentation/blender_python_api_2_69_1/bpy.app.handlers.html

Thanks for the help batFINGER, but I’m newbie in this thing scripting and I missed:
I tried:

import bpy
from persistent import bpy.app.handlers

if bpy.app.handlers.frame_change_pre:
bpy.ops.anim.keyframe_insert_menu (type = ‘BUILTIN_KSI_LocRot’)

Wrap code in tags like so

[noparse]


   blah blah

[/noparse]

Rather than using the operator I used the key_frame_insert.


import bpy


def my_handler(scene):
    print("Frame Change", scene.frame_current)
    for ob in scene.selected_objects:
        l = ob.keyframe_insert("location", group="LocRot", options={'INSERTKEY_NEEDED'})
        r = ob.keyframe_insert("rotation", group="LocRot", options={'INSERTKEY_NEEDED'})
        # l and r flag the success of the inserts.    


bpy.app.handlers.frame_change_pre.append(my_handler)

Thanks, I’m analyzing this code.
I copied and pasted but when “run script” do not know what change it made.

Edit: File “D:\Blender\Pesquisas\alexanderLee_riggedWoman_Wlk 3-2 - Copia.blend\driver_bake.py”, line 6, in my_handlerAttributeError: ‘Scene’ object has no attribute ‘selected_objects’

Thus it works:

import bpy

def my_handler(scene):
    print("Frame Change", scene.frame_current)
    for ob in bpy.context.selected_objects:
        l = ob.keyframe_insert("location", group="LocRot", options={'INSERTKEY_NEEDED'})
        r = ob.keyframe_insert("rotation", group="LocRot", options={'INSERTKEY_NEEDED'})
        # l and r flag the success of the inserts.    


bpy.app.handlers.frame_change_pre.append(my_handler)

Ooops,


import bpy


def my_handler(scene):
    if scene is None:
        return None
    print("Frame Change", scene.frame_current)
    selected_objects = [o for o in scene.objects if o.select]
    for ob in selected_objects:
        l = ob.keyframe_insert("location", group="LocRot")
        ob.keyframe_insert("rotation", group="LocRot")

This working better now! =)
Now I just need to know how to disable the code.

Here is code I use for this


def remove_handlers_by_prefix(prefix):
    handlers = bpy.app.handlers
    my_handlers = [getattr(handlers, name) for name in dir(handlers) if isinstance(getattr(handlers, name), list)]


    for h in my_handlers:
        fs = [f for f in h if callable(f) and f.__name__.startswith(prefix)]
        for f in fs:
            h.remove(f)

In speaker tools all handler method names I prefix with SPEAKER_TOOLS_ then I can remove them in one fell swoop with


    # get rid of any handlers floating around.
    remove_handlers_by_prefix('SPEAKER_TOOLS_')
    bpy.app.handlers.load_post.append(SPEAKER_TOOLS_load)
    bpy.app.handlers.load_pre.append(SPEAKER_TOOLS_unload)

PS

Noticed the name of the file was driver_bake. Here is a snippet from a bake_driver operator I use.
This way can flip thru the ob.animation_data.drivers collection and bake any bloody data_path, array_index combo that you can turn purple in the UI.


        print("BAKING", driver.id_data, driver.data_path, driver.array_index)
        while frame <= frame_end:
            print("#", end="")
            scene.frame_set(frame)
            # quick fix try array, then without
            try:
                driver.id_data.keyframe_insert(driver.data_path,
                                           index=driver.array_index)
            except:
                driver.id_data.keyframe_insert(driver.data_path)
                #print("Error in baking")
            finally:
                frame = frame + 1


            scene.frame_set(scene_frame)
        return {'FINISHED'}

Analyzing …

So: I tried but did not know how to use this fragment. I wanted to bake with the “Automatic insertion for Objects and Bones” (red ball in the Time Line). The previous script runs but does not stop.You can pass me this bake_driver operator you use?

I had to do it this way, the problem is that I have to click “run script” every time I turn off or on the red circle in Timeline:

import bpy

def my_handler(scene):
    if scene is None:
        return None
    print("Frame Change", scene.frame_current)
    selected_objects = [o for o in scene.objects if o.select]
    for ob in selected_objects:
        l = ob.keyframe_insert("location", group="LocRot")
        r = ob.keyframe_insert("rotation", group="LocRot")


if bpy.context.scene.tool_settings.use_keyframe_insert_auto == True:
    bpy.app.handlers.frame_change_pre.append(my_handler)
else:
    for i in range(0,len(bpy.app.handlers.frame_change_pre)):
            bpy.app.handlers.frame_change_pre.pop(i)

Thanks for the help. :slight_smile:

Here are the few changes required.


import bpy


def main(context):
    scene = context.scene
    scene_frame = context.scene.frame_current
    for ob in scene.objects:
        if not hasattr(ob, "animation_data") or ob.animation_data is None:
            continue
        drivers = [d for d in ob.animation_data.drivers]
        for driver in drivers:
            print()
            print("BAKING", repr(driver.id_data), driver.data_path, driver.array_index)
            
            frame, frame_end = scene.frame_start, scene.frame_end
            while frame <= frame_end:
                print("#", end="")
                scene.frame_set(frame)
                # quick fix try array, then without
                try:
                    driver.id_data.keyframe_insert(driver.data_path,
                                               index=driver.array_index)
                except:
                    driver.id_data.keyframe_insert(driver.data_path)
                    #print("Error in baking")
                finally:
                    frame = frame + 1
            print()


            scene.frame_set(scene_frame)
        return {'FINISHED'}




class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"


    @classmethod
    def poll(cls, context):
        return context.active_object is not None


    def execute(self, context):
        return main(context)




def register():
    bpy.utils.register_class(SimpleOperator)




def unregister():
    bpy.utils.unregister_class(SimpleOperator)


if __name__ == "__main__":
    register()


    # test call
    bpy.ops.object.simple_operator()



btw. popping all handlers could make other addons cease to work if they too rely on a frame_change handler.

Sorry but I’m still really newbie. With your script I can now to bake the drivers? How do I do this? Where is SimpleOperator class?

I followed your tip and did this:

import bpy

def my_handler(scene):
    if scene is None:
        return None
    selected_objects = [o for o in scene.objects if o.select]
    if bpy.context.scene.tool_settings.use_keyframe_insert_auto == True:
        for ob in selected_objects:
            l = ob.keyframe_insert("location", group="LocRot")
            r = ob.keyframe_insert("rotation_euler", group="LocRot")


bpy.app.handlers.frame_change_pre.append(my_handler)

Now works perfectly

Edit: It does not work perfectly because it overlaps the previous keyframes