Making script to run on frame range

Hello, I’m kind of new to Blender Python programming and I was working on script that transfers shape keys to poses (source’s shape keys values are used to mix respective poses from library onto the rig). It actually works, but the problem is i want it to be automatized and to work on a frame range (when i run the script on one frame it works just fine, cycling it does nothing).
Here’s the code:

import bpy

mesh = bpy.data.objects['Base v4_mesh']  #source with shape keys
rig = bpy.data.objects['rig'] #target
lib = bpy.data.objects['rig'].pose_library #target's pose library

def getPose(poseCurr):
    pose = []
    b = bpy.context.selected_pose_bones
    for a in b:

        rotway = a.rotation_mode
        rotname = ''
        if rotway in ['QUATERNION']:
            rotname = "rotation_quaternion"
        elif rotway in ['XYZ','XZY','YXZ','YZX','ZYX','ZXY']:
            rotname = "rotation_euler"
        elif rotway in ['AXIS_ANGLE']:
            rotname = 'rotation_axis_angle'
        else:
            rotway = "rotation_quaternion" 
            
        if rotname == 'rotation_axis_angle': 
            pose.append([a.location.copy(), a.rotation_axis_angle, a.scale.copy(), rotname])
        else:
            pose.append([a.location.copy(), getattr(a,rotname).copy(), a.scale.copy(), rotname])
    return pose


def posemix(p_i,influence):
    prePose = getPose(rig.pose)
    bpy.ops.poselib.apply_pose(pose_index=p_i)
    mixToPose(rig, prePose, influence)
    
def mixToPose(ob, pose, value):
    
    bones_select = bpy.context.selected_pose_bones
    for b,p in zip(bones_select,pose):
        b.location[0] =b.location[0]*value + p[0][0]
        b.location[1] =b.location[1]*value + p[0][1]
        b.location[2] =b.location[2]*value + p[0][2]
        b.scale[0] = 1
        b.scale[1] = 1
        b.scale[2] = 1

        if p[3] == "rotation_quaternion" or p[3] == '':
            b.rotation_quaternion[0] = (b.rotation_quaternion[0]*value + p[1][0])
            b.rotation_quaternion[1] = (b.rotation_quaternion[1]*value + p[1][1])
            b.rotation_quaternion[2] = (b.rotation_quaternion[2]*value + p[1][2])
            b.rotation_quaternion[3] = (b.rotation_quaternion[3]*value + p[1][3])
        elif p[3] == "rotation_euler":
            b.rotation_euler[0] = (b.rotation_euler[0]*value+ p[1][0])
            b.rotation_euler[1] = (b.rotation_euler[1]*value+ p[1][1])
            b.rotation_euler[2] = (b.rotation_euler[2]*value+ p[1][2])
        else:
            print("ERROR!")

    bpy.ops.anim.keyframe_insert(type='BUILTIN_KSI_LocRot')
        
        

keyBlocks = mesh.data.shape_keys.key_blocks
bpy.ops.poselib.apply_pose(pose_index=0)
for block in keyBlocks:
    index = [i for i in range(len(lib.pose_markers))
    if lib.pose_markers[i].name ==block.name]
    if index and block.value>0.1:
        index=index[0]
        lib.pose_markers.active_index = index
        posemix(index,block.value)

Can anyone help?

Did you ever figure this out? I’m at the exact point with my script. My script works for one frame but I need the action to repeat through my current frame range.

searching online, mostly stack exchange, I’m not finding what I’m needing.

Thanks.

Good day, yes I figured it out, by using handlers

def set_keyframes(scene):
    keyBlocks = mesh.data.shape_keys.key_blocks
    bpy.ops.poselib.apply_pose(pose_index=0)
    for block in keyBlocks:
        index = [i for i in range(len(lib.pose_markers))
        if lib.pose_markers[i].name ==block.name]
        if index and block.value>0.1:
            index=index[0]
            lib.pose_markers.active_index = index
            posemix(index,block.value)
                        

def register():
    bpy.app.handlers.frame_change_post.append(set_keyframes)

def unregister():
    bpy.app.handlers.frame_change_post.remove(set_keyframes)

So I turn on the handler and play through the frame range for the script to make the keyframes, and then turn it off

1 Like

Thank you, I’ll try this out. At the moment I have to open the script and manually enter the frame range and re-run it, which is a pain.