3
$\begingroup$

I'm trying to change poses on my armature with Python and Pose Library, but I'm getting incorrect context errors and to be frank I don't even know what exactly am I supposed to use the operators from the docs on.

In the documentation I found this: bpy.ops.poselib.apply_pose(pose_index=- 1) described as "Apply specified Pose Library pose to the rig; Pose, Index of the pose to apply (-2 for no change to pose, -1 for poselib active pose)" which sounds like what I need.

How do I get the correct context? Pose-mode didn't seem to be it.

$\endgroup$
4
  • $\begingroup$ context errors usually mean you're trying to run the ops command from the wrong context, ie, from the Python console when it need to run in the 3D Viewport. You can override context for most ops commands and that will usually be sufficient or you can wrap them in operators that you only invoke from the correct context. $\endgroup$ Mar 29, 2022 at 19:02
  • $\begingroup$ @MartyFouts Thank you, I prepared a simple test .blend file on 3.0.1. I'm completely new to context overrides. I understand the general concept, but it's hard for me to come up with working solutions. poselib_operators.blend $\endgroup$
    – AlexM
    Mar 29, 2022 at 20:10
  • $\begingroup$ @MartyFouts are you saying it's not possible? I'm using that Pose Library addon shipped with Blender. Is there no way to actually apply its poses with Python? When I check the Info panel it says bpy.ops.poselib.apply_pose_asset(blend_factor=1, flipped=False) so it's definitely using these operators somehow $\endgroup$
    – AlexM
    Mar 29, 2022 at 21:06
  • $\begingroup$ Once I enabled the addon, I was able to figure out the correct context. See my answer. $\endgroup$ Mar 29, 2022 at 22:34

2 Answers 2

1
$\begingroup$

EDIT: pointed out in a comment: my code needed some error handling and I didn't demonstrate how to use an override with arguments.

Here is an example of how to use an override to allow you to invoke apply_pose_asset:

win = bpy.context.window
scr = win.screen
assets = [area for area in scr.areas if area.type == 'FILE_BROWSER']
if len(assets):
    region = [region for region in assets[0].regions if region.type == 'TOOLS']
    if len(region):
        override = {
            'window': win,
            'screen': scr,
            'area'  : assets[0],
            'region': region[0],
            'scene' : bpy.context.scene,
        }
        bpy.ops.poselib.apply_pose_asset(override, blend_factor=1, flipped=False)
    else:
        print("Current Pose Library does not have a tools menu open.")
else:
    print("There is no Pose LIbrary open.")

The override is inserted into the function call as the first argument, all other arguments remaining the same.

The override argument takes the form of a Python dictionary. Each entry represents one piece of context to override. The above example shows the complete override for the command. Note that the area type is FILE_BROWSER. That is the area type of the editor window that the command expects to run in.

Note: The command itself uses the first valid entry in the catalog if none are selected, or the active entry if one is selected.


bpy.ops.poselib.apply_pose(pose_index=- 1) is currently an operator designed for the deprecated (but still functional) separate version of the Pose Library system, soon to be removed post Blender 3.1. It exists in the Properties sidebar as a tab in Armature's settings. It requires its own pose entries and 'PROPERTIES' area type context to be executed. Like so:

import bpy
bpy.ops.poselib.apply_pose({'area':'PROPERTIES'}, pose_index=0)

3.2 Update

Context overrides are deprecated in Blender 3.2 and are scheduled to be removed in Blender 3.3 The replacement is temp_override. The manual has examples of how to use the new function.

$\endgroup$
4
  • $\begingroup$ Thank you for this detailed response, but I can't get your code to work. I got errors. I was using your code on the .blend file I provided earlier. The first code gave me IndexError: list index out of range on line 6, which was the region = [...] line. The second code gave me context is incorrect on the bpy.ops line. I understand that your code searches for windows of certain type and uses them as context for operators, right? How could I override context on bpy.ops.poselib.apply_pose(pose_index=- 1) but still control the parameter value? Thank you! $\endgroup$
    – AlexM
    Apr 4, 2022 at 11:34
  • $\begingroup$ The error on line 6 is because the search on line 5 didn't turn up any open FILE_BROWSER windows. I should have checked the len of assets before getting region. If you don't have a window of that type there's nothing you can do. arguments go after the override, so you would use bpy.ops.poselib.apply_pose(override, pose_index=- 1). I'll change the answer to reflect both of those things. $\endgroup$ Apr 4, 2022 at 14:30
  • $\begingroup$ I'm so close to getting this to work! I believe there's a small mistake in the code above, it's supposed to be .apply_pose() not .apply_pose_asset() for this particular function. Nevertheless, your code almost works for me with the bpy.ops.poselib.apply_pose_asset(override, blend_factor=1, flipped=False) it actually changes poses! But I have no idea how to change the active(highlighted) pose. I need something like bpy.data.workspaces["Layout"].active_pose_asset_index = 2 but for the asset browser. Sidenote, the .apply_pose() throws "incorrect context" for some reason. $\endgroup$
    – AlexM
    Apr 29, 2022 at 12:14
  • $\begingroup$ @AlexM Thanks for the correction. $\endgroup$ May 3, 2022 at 16:04
0
$\begingroup$

Just to complete an awesome response by Marty Fouts, here is a code snippet on how to choose an appropriate asset from the asset browser (tested with Marty's example only)

Inject the original code with the following:

space = assets[0].spaces[0]
space.deselect_all()
space.activate_asset_by_id(bpy.data.actions[pose_id])

A full example would be (with minor irrelevant changes):

def apply_pose(pose_id, blend_factor=1):
    #  https://blender.stackexchange.com/questions/258952/how-to-properly-execute-poselib-operators-pose-library/258970#258970?s=d03c6944322f4034875046fad6b365de
    
    win = bpy.context.window
    scr = win.screen
    assets = [area for area in scr.areas if area.type == 'FILE_BROWSER']
    if len(assets):
        region = [region for region in assets[0].regions if region.type == 'TOOLS']
        space = assets[0].spaces[0]
        space.deselect_all()
        space.activate_asset_by_id(bpy.data.actions[pose_id])
        if len(region):
            override = {
                'window': win,
                'screen': scr,
                'area'  : assets[0],
                'region': region[0],
                'scene' : bpy.context.scene,
            }

            bpy.ops.poselib.blend_pose_asset(
                override,
                blend_factor=blend_factor,
                flipped=False,release_confirm=True)
        else:
            print("Current Pose Library does not have a tools menu open.")
    else:
        print("There is no Pose LIbrary open.")

I came up with a solution when accidentally found this https://docs.blender.org/api/current/bpy.types.SpaceFileBrowser.html#bpy.types.SpaceFileBrowser.activate_asset_by_id

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .