3
$\begingroup$

I know bones can be arranged by layers in the armature properties. Is it possible to somehow "lock" a layer so that all bones in that layer cannot be selected?

$\endgroup$

1 Answer 1

0
$\begingroup$

As far as I know, this function does not exist. Generally all collections, objects and bones can be set to non-selectable with the outliner filter Selectable which looks like an arrowhead. Manual

enter image description here

The filter acts on both modes at once - pose and editmode. Unfortunately you have to navigate through the armature hierarchy to find all the bones of one a layer and set the selectability individually.

You may want to give the following script a chance. It adds a menu to the skeleton panel on top of the layer field that allows to lock and unlock all or just the bones of the active layer(s).

enter image description here

import bpy
from bpy.types import Menu, Operator

# special menu
class DATA_MT_skeleton_specials(Menu):
    bl_idname = "DATA_MT_skeleton_specials"
    bl_label = "Armature Layer Specials"

    def draw(self, _context):
        layout = self.layout

        layout.label(text="Selectable Bones")

        op = layout.operator('data.skeleton_specials_op', text="Lock All", icon='RESTRICT_SELECT_ON')
        op.do = "lock_all"
        op.descript = "Make all Bones non-selectable"

        op = layout.operator('data.skeleton_specials_op', text="Lock Layer", icon='RESTRICT_SELECT_ON')
        op.do = "lock_layer"
        op.descript = "Make Bones in active layer(s) non-selectable"

        op = layout.operator('data.skeleton_specials_op', text="Unlock All", icon='RESTRICT_SELECT_OFF')
        op.do = "unlock_all"
        op.descript = "Make all Bones selectable"

        op = layout.operator('data.skeleton_specials_op', text="Unlock Layer", icon='RESTRICT_SELECT_OFF')
        op.do = "unlock_layer"
        op.descript = "Make Bones in active layer(s) selectable"

# special op
class DATA_OT_skeleton_specials(Operator):
    bl_idname = 'data.skeleton_specials_op'
    bl_label = ""    
    do : bpy.props.StringProperty(default="")
    descript : bpy.props.StringProperty()  

    def execute(self, context):
        if context.mode == 'EDIT_ARMATURE':
            bone_data = context.armature.edit_bones
        else:
            bone_data = context.armature.bones

        bones = []
        layers = []

        set = False 
        if self.do.startswith('lock'):
            set = True

        if self.do.endswith('all'):
            bones = bone_data

        else:
            for idx, layer in enumerate(context.armature.layers):
                if layer:
                    layers.append(idx)                           

            for bone in bone_data:
                for layer in layers:
                    if bone.layers[layer]:
                        bones.append(bone)

        for bone in bones:
            bone_data[bone.name].hide_select = set

        return {'FINISHED'}  

    @classmethod
    def description(cls, context, properties):
        return properties.descript

### REGISTRY

def register():

    skeleton_draw_old = bpy.types.DATA_PT_skeleton.draw # store original

    bpy.utils.register_class(DATA_MT_skeleton_specials)     
    bpy.utils.register_class(DATA_OT_skeleton_specials)     

    def skeleton_draw_new(self, context):
        layout = self.layout

        arm = context.armature

        layout.row().prop(arm, "pose_position", expand=True)

        col = layout.column()
        row = col.row()
        row.label(text="Layers:") 
        row.menu('DATA_MT_skeleton_specials', text="", icon='DOWNARROW_HLT')
        col.prop(arm, "layers", text="")
        col.label(text="Protected Layers:")
        col.prop(arm, "layers_protected", text="")    

    bpy.types.DATA_PT_skeleton.draw = skeleton_draw_new   # replace draw
            
def unregister():
    bpy.utils.unregister_class(DATA_MT_skeleton_specials) 
    bpy.utils.unregister_class(DATA_OT_skeleton_specials) 
    bpy.types.DATA_PT_skeleton.draw = skeleton_draw_old   # restore draw            
    
if __name__ == "__main__":
    register()

Open a new text block in Scripting workspace, copy the whole script into it and press Run. The script is stored with the blenderfile but needs to be run again.

enter image description here

(tested in 2.93, 3.32, 3.51)

$\endgroup$

You must log in to answer this question.

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