Move a Pose Bone via Python (bpy.ops and non-bpy.ops solution) 2.6.3.

Hi All,

I have been searching for a way to do this for quite sometime and I have gotten close in the past, but now that 2.6.3 is out I wanted to revisit it to see if there is a way to move a pose bone that does not require switching into Pose mode.

Usage:

1.) Download the boy rig from Blendswap. (this code is linked to his armature and bone names)
2.) Open up the BLEND file with the boy rig.
3.) Paste the code, below, into a new blank text window.
4.) ALT-P to run the code.
5.) Press play and the boys mouth will move up and down.


# An attempt to control bones via code in a frame_change event.
# Atom 05052012.

import bpy
from math import sin

############################################################################
# Globals.
############################################################################
CONSOLE_PREFIX = "RE:Pose "

isBusy = False                      # Global busy flag. Try to avoid events if we are already busy.
isRendering = False                 # Global render flag. Try to detect when we are rendering.
lastFrame = -1

def frameChangePreRePose(passedScene):
    global isBusy, isRendering, lastFrame
    
    print("
frameChangePre #" + str(passedScene.frame_current))

    if passedScene != None:
        cf = passedScene.frame_current
        if isBusy == False:
            if cf != lastFrame:
                # Only process when frames are different.
                isBusy = True
                print(CONSOLE_PREFIX + " FRAME #" + str(cf))
                reviewBones(cf,passedScene)
                lastFrame = cf
                isBusy = False
        else:
            print(CONSOLE_PREFIX + "still BUSY on frame #" + str(cf))
    else:
        print("frameChangePrebone received None as a scene?")

def reviewBones(passedFrame, passedScene, onLoad = False):
    bone_name = "Bone.023"                          # NOTE: Bone must not have keyframes.
    rig = bpy.data.objects.get("garoto_mov_rosto")  # Get Armature
    use_bpy_ops = False
    if use_bpy_ops == True:
        # Use bpy.ops to manage armature mode.
        ob_active = passedScene.objects.active      # Save the active object.
        passedScene.objects.active = rig            # make it the active object for manipulation
        bpy.ops.object.mode_set(mode="POSE")        # need POSE mode to set armature bones
        pb = rig.pose.bones[bone_name]
        pb.location[2] = sin(passedFrame) / 20.0    # Move it.        
        bpy.ops.object.mode_set(mode="POSE")        # need POSE mode to set armature bones
        passedScene.objects.active = ob_active      # Restore the active object. 
    else:
        # Don't use bpy.ops. Try to drive bpy.data directly.
        pb = rig.pose.bones[bone_name]              # Get the bone.
        pb.location[2] = sin(passedFrame) / 20.0    # Move it.

        # Update the rig.
        try:
            # This always generates an error, but the armature refresh still works!
            rig.update_tag(refresh=set('OBJECT')) #{‘OBJECT’, ‘DATA’, ‘TIME’}
        except:
            
            # Exploiting this weakness till it is patched.
            # We have errored out, but the refresh still works, yeah!
            pass
    return

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

The code supports both styes of updating the rig and on my laptop the bpy.ops version is way slower than the non-bpy.ops version. To try both versions run the code with the use_bpy_ops flag set to True or False.

Attachments


Hi atom.

You can try acces via datablock.

Example:

bpy.data.objects["Armature"].pose.bones["Bone"].location=(1,1,1)

Hmm…I guess that does work in a frame_change. It seemed like only a few versions ago this was not possible. I guess the rig.update_tag() is not needed.


import bpy

def frameChange(passedScene):
    y = passedScene.frame_current/2
    bpy.data.objects["Armature"].pose.bones["Bone"].location=(1,y,1)
    
bpy.app.handlers.frame_change_pre.append(frameChange)