Running function when selecting shapekeys

I’m trying to run a function (setting the active shapekey value to 1, and all other shapekeys values to 0) when selecting a shapekey.

Not quite sure how to achieve that. I tried subscribing to msgbus but I’m doing something wrong.

import bpy

def set_active_shape_key(scene, prop):
    obj = scene.objects.active
    active_shape_key_index = obj.active_shape_key_index
    for shape_key in obj.data.shape_keys.key_blocks:
        if shape_key.name != 'Basis':
            shape_key.value = int(shape_key.name == obj.active_shape_key.name)
            print(f'{shape_key.name} set to {shape_key.value}')

bpy.msgbus.subscribe_rna(
    key="object.active_shape_key_index",
    owner=None,
    args=(bpy.context.scene,),
    notify=set_active_shape_key
)

Message Bus (bpy.msgbus) — Blender Python API

Some properties are converted to Python objects when you retrieve them. This needs to be avoided in order to create the subscription, by using datablock.path_resolve("property_name", False)

key=bpy.context.object.path_resolve("active_shape_key_index", False),

1 Like

Awesome, working like a charm. Thank you!

technically I think you want something closer to this:

import bpy

owner = None
subscribe_to = (bpy.types.Object, "active_shape_key_index")
#subscribe_to = bpy.context.object.path_resolve("active_shape_key_index", False)


def set_active_shape_key(*args):
    obj = bpy.context.object
    active_shape_key_index = obj.active_shape_key_index
    print(f'{obj.name} shape key index change {active_shape_key_index}')
    for shape_key in obj.data.shape_keys.key_blocks:
        if shape_key.name != 'Basis':
            shape_key.value = int(shape_key.name == obj.active_shape_key.name)
            print(f'{shape_key.name} set to {shape_key.value}')

bpy.msgbus.subscribe_rna(
    key=subscribe_to,
    owner=owner,
    args=(1, 2, 3),
    notify=set_active_shape_key
)

by using subscribe_to = (bpy.types.Object, "active_shape_key_index") it will work for all objects in a scene if you only use subscribe_to = bpy.context.object.path_resolve("active_shape_key_index", False) you’re only catching messages for the object that was active when you ran the script.

also keep in mind you don’t have a clear in this so you don’t want to run it multiple times.

1 Like

Makes sense, thanks for the pointers, I appreciate it.

The plan is to create a custom bool property to switch this functionality on and off, running this when False -

bpy.msgbus.clear_by_owner(None)

I hope it does. I’m on thin ice with very little experience using the Blender API.