0
$\begingroup$

I'm making an add-on to boost my workflow. I want to add custom shapes to the bones of the armature selected in the PointerProperty from the panel. But according to my knowledge, I can only add custom shapes in the posebones of the armature which can be accessed by bpy.context.object.pose.bones but taking an armature like this I've already accessed bpy.context.object.data. Any help would be greatly appreciated.

Here is the code:

import bpy

class CUSTOM_PT_PANEL(bpy.types.Panel):
    bl_label = "Custom"
    bl_idname = "CUSTOM_PT_PANEL"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_category = "Custom"
     
    def draw (self, context):
        layout = self.layout
        layout.prop(context.scene, 'select_rig')
        layout.operator('add.custom_shape')
        
class CUSTOM_OT_OPERATOR(bpy.types.Operator):
    bl_label = "Add custom shae"
    bl_idname = "add.custom_shape"
    
    def execute(self,context):
        armature = bpy.context.scene.select_rig
        
        #Custom Shape
        bpy.ops.mesh.primitive_cube_add()
        shape = bpy.context.active_object
        bpy.ops.object.mode_set(mode='EDIT')
        shape.scale.y = 0.4
        shape.scale.z = 0.4
        bpy.ops.mesh.delete(type='ONLY_FACE')
        bpy.ops.object.mode_set(mode='OBJECT')
        
        return {'FINISHED'}
    
classes = [CUSTOM_PT_PANEL, CUSTOM_OT_OPERATOR]

def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.select_rig = bpy.props.PointerProperty(name='Select Rig',type=bpy.types.Armature)
def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)

if __name__ == "__main__":
    register()
```
$\endgroup$
0

1 Answer 1

1
$\begingroup$

The mistake here is that you use type=bpy.types.Armature for the PointerProperty. This allows the user to pick an Armature which sounds right. But this data block has no pose bones. The Object of type 'ARMATURE' has actually the pose data. You can see this in the outliner.

type=bpy.types.Armature: aramature selection

What you really want is the Mannequin object (type=bpy.types.Object): objects

Now the user can pick one of all objects. Of course, you don't want that the Camera can be picked which makes no sense. To limit the selection to Objects of type ARMATURE you can use a poll function and check the type of the object in question. This works like a filter: pick an armature object

Here's the modified script. The validation check (lines 22 to 25) is just for testing and never should fail when the filter is correct.

import bpy

class CUSTOM_PT_PANEL(bpy.types.Panel):
    bl_label = "Custom"
    bl_idname = "CUSTOM_PT_PANEL"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_category = "Custom"
     
    def draw (self, context):
        layout = self.layout
        layout.prop(context.scene, 'select_rig')
        layout.operator('add.custom_shape')
        
class CUSTOM_OT_OPERATOR(bpy.types.Operator):
    bl_label = "Add custom shae"
    bl_idname = "add.custom_shape"
    
    def execute(self,context):
        armature = bpy.context.scene.select_rig
        
        # this validation check should never fail because scene_mychosenobject_poll() 
        # will limit the selection
        if armature.type != 'ARMATURE':
            raise ValueError(f'Select an armature not {armature.type}')
        
        #Custom Shape
        bpy.ops.mesh.primitive_cube_add()
        shape = bpy.context.active_object
        bpy.ops.object.mode_set(mode='EDIT')
        shape.scale.y = 0.4
        shape.scale.z = 0.4
        bpy.ops.mesh.delete(type='ONLY_FACE')
        bpy.ops.object.mode_set(mode='OBJECT')
        
        # assign the custom shape to a bone 
        armature.pose.bones['LowerLeg.L'].custom_shape = shape   # <== adjust the bone name!
        
        return {'FINISHED'}


# filter the selection
def scene_mychosenobject_poll(self, object):
    return object.type == 'ARMATURE'

  
classes = [CUSTOM_PT_PANEL, CUSTOM_OT_OPERATOR]

def register():
    for cls in classes:
        bpy.utils.register_class(cls)

    # select_rig should point to an Object of type 'ARMATURE'
    bpy.types.Scene.select_rig = bpy.props.PointerProperty(name='Select Rig',
        type=bpy.types.Object, poll=scene_mychosenobject_poll)


def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)
        

if __name__ == "__main__":
    register()

Related questions:

$\endgroup$
1
  • 1
    $\begingroup$ Thank you very much. $\endgroup$
    – Muzammil
    Commented Dec 15, 2022 at 16:44

You must log in to answer this question.

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