3
$\begingroup$

I am struggling with parenting meshes to bones.

Here is the complete runnable script:

import bpy
        
def create_mesh_for_armature(context, name):
    
    counter = 0
    
    armature = context.scene.objects[name]
     
    selected_bones = []

    for pose_bone in armature.pose.bones:
        
        if pose_bone.bone.select:
            selected_bones.append(pose_bone)
            
    for eachSelectedBone in selected_bones:
            
            tail = armature.location + eachSelectedBone.tail
            head = armature.location + eachSelectedBone.head
            
            bpy.ops.mesh.primitive_cube_add()
            ob = context.active_object
            
            bpy.ops.object.select_all(action='DESELECT') #deselect all objects
                    
            if context.object == None:
                print("NOOOOOONE")
            
            ob.select_set(True)
                     
            context.view_layer.objects.active = armature
            
            context.object.data.bones.active = eachSelectedBone.bone
            
            bpy.ops.object.parent_set(type='BONE', keep_transform=True)
            
            counter += 1
    
    
    print(counter, "meshes for bones created")

    
class Create_cubes_for_bones(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.create_cubes_for_bones"
    bl_label = "Create cubes for bones"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        ob = context.active_object
        print("in execute: ob:",ob)
        if ob.type == 'ARMATURE':
            create_mesh_for_armature(context, ob.name)
        return {'FINISHED'}


def menu_func(self, context):
    self.layout.operator(Create_cubes_for_bones.bl_idname, text=Create_cubes_for_bones.bl_label)


# Register and add to the "object" menu (required to also use F3 search "Simple Object Operator" for quick access)
def register():
    bpy.utils.register_class(Create_cubes_for_bones)
    bpy.types.VIEW3D_MT_object.append(menu_func)


def unregister():
    bpy.utils.unregister_class(Create_cubes_for_bones)
    bpy.types.VIEW3D_MT_object.remove(menu_func)


if __name__ == "__main__":
    register()

    bpy.ops.object.create_cubes_for_bones()


class LayoutDemoPanel(bpy.types.Panel):
    """Creates a Panel in the scene context of the properties editor"""
    bl_label = "Layout Demo"
    bl_idname = "SCENE_PT_layout"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

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

        scene = context.scene
       
        row = layout.row(align=True)
        row.scale_y = 3.0
        row.operator("object.create_cubes_for_bones")


def register():
    bpy.utils.register_class(LayoutDemoPanel)


def unregister():
    bpy.utils.unregister_class(LayoutDemoPanel)


if __name__ == "__main__":
    register()

First, select the armature, then select in pose mode minimum 1 bone. Then run the script. It will create cube(s) and parent it to the bones. But...if you go to scene tab and press the button, you will get an error...and i have no clue why...

enter image description here

enter image description here

If i run my add-on/script directly, it works.

If i run it by pressing my custom button, context.object.data is None. (although it should be the armature).

Can anybody tell me how i ensure that context.object.data is set? or how i set it? or am i doing it totally wrong here?

Armature is filled, i checked that by a print statement.

$\endgroup$
2
  • $\begingroup$ This depends on what the context is and whether the currently active object is set / your armature. I would suggest that you edit your post and include a minimal self-contained script that shows how you call it with your button. This way we can provide a specific answer to your problem. $\endgroup$ Commented Apr 11, 2022 at 10:01
  • $\begingroup$ i am sorry, i improved my post. Do i have to add more? blend file? $\endgroup$
    – LuckyOne
    Commented Apr 11, 2022 at 10:46

1 Answer 1

4
$\begingroup$
import bpy

# select and del all object
if bpy.context.object:
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.select_all(action = "SELECT")
    bpy.ops.object.delete(use_global=True, confirm=False)

bpy.ops.object.armature_add(enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
armature = bpy.context.object

bpy.ops.mesh.primitive_cube_add(size=1, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
cube = bpy.context.object

bpy.context.view_layer.objects.active = armature
bpy.ops.object.parent_set(type='BONE', keep_transform=True)

Changed in line 33

import bpy
        
def create_mesh_for_armature(context, name):
    
    counter = 0
    
    armature = context.scene.objects[name]
    
    selected_bones = []

    for pose_bone in armature.pose.bones:
        
        if pose_bone.bone.select:
            selected_bones.append(pose_bone)
            
    for eachSelectedBone in selected_bones:
            
            tail = armature.location + eachSelectedBone.tail
            head = armature.location + eachSelectedBone.head
            
            bpy.ops.mesh.primitive_cube_add()
            ob = context.active_object
            
            bpy.ops.object.select_all(action='DESELECT') #deselect all objects
                    
            if context.object == None:
                print("NOOOOOONE")
            
            ob.select_set(True)
                    
            context.view_layer.objects.active = armature
            
            armature.data.bones.active = eachSelectedBone.bone
            
            bpy.ops.object.parent_set(type='BONE', keep_transform=True)
            
            counter += 1
    
    
    print(counter, "meshes for bones created")

    
class Create_cubes_for_bones(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.create_cubes_for_bones"
    bl_label = "Create cubes for bones"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        ob = context.active_object
        print("in execute: ob:",ob)
        if ob.type == 'ARMATURE':
            create_mesh_for_armature(context, ob.name)
        return {'FINISHED'}


def menu_func(self, context):
    self.layout.operator(Create_cubes_for_bones.bl_idname, text=Create_cubes_for_bones.bl_label)



class LayoutDemoPanel(bpy.types.Panel):
    """Creates a Panel in the scene context of the properties editor"""
    bl_label = "Layout Demo"
    bl_idname = "SCENE_PT_layout"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

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

        scene = context.scene
    
        row = layout.row(align=True)
        row.scale_y = 3.0
        row.operator("object.create_cubes_for_bones")


def register():
    bpy.utils.register_class(Create_cubes_for_bones)
    bpy.types.VIEW3D_MT_object.append(menu_func)
    bpy.utils.register_class(LayoutDemoPanel)


def unregister():
    bpy.utils.unregister_class(Create_cubes_for_bones)
    bpy.types.VIEW3D_MT_object.remove(menu_func)
    bpy.utils.unregister_class(LayoutDemoPanel)


if __name__ == "__main__":
    register()
$\endgroup$
3
  • $\begingroup$ uhm...ok, thank you for your answer. But i am not sure whether i don't get it or you don't get it. My problem isn't to make the code run. It works. It just doesn't work if it is started by a button in an add-on. So i am not sure what i should do with your code now...? $\endgroup$
    – LuckyOne
    Commented Apr 11, 2022 at 13:38
  • 1
    $\begingroup$ It will work fine after modified. $\endgroup$
    – X Y
    Commented Apr 11, 2022 at 14:42
  • $\begingroup$ thank you very much ! works like a charm!!! $\endgroup$
    – LuckyOne
    Commented Apr 11, 2022 at 14:52

You must log in to answer this question.

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