How to bone parent an object using Python?

I would really like to parent objects to bones using Python, but the only way I can find to do this is using bpy.ops.object.parent_set(type=‘BONE’). This doesn’t take any more parameters other than the parenting type though. It only parents the selected objects to the active bone.

import bpy

print('
#######################
')

bpy.ops.object.select_all(action='DESELECT')
bpy.ops.object.select_name(name='Cube', extend=False)
bpy.ops.object.select_name(name='Armature', extend=True)
bpy.ops.object.mode_set(mode='POSE')
bpy.ops.pose.select_all(action='DESELECT')

bones = bpy.context.object.data.bones
pose_bones = bpy.context.object.pose.bones
edit_bones = bpy.context.object.data.edit_bones

for bone in pose_bones:
    bone.select = False
for bone in bones:
    bone.select = False
bpy.context.object.data.bones['Bone.005'].select = True
bpy.context.object.pose.bones['Bone.005'].select = True
for bone in pose_bones:
    if not bone.name == 'Bone.005':
        bone.select = False
for bone in bones:
    if not bone.name == 'Bone.005':
        bone.select = False
bpy.ops.object.parent_set(type='BONE')

The script above should deselect all bones in both object.pose.bones and object.data.bones (just to make sure), and then select a single bone, double checking by deselecting all bones except that single bone.

But if you deselect all bones but one through Python then run bpy.context.active_pose_bone or bpy.context.active_bone then regardless of the way that you set the bone.select values in Python, the last bone that was selected in the 3d view is still the active bone. :mad:

It appears to be impossible to choose which bone is the active bone using Python.

Just noticed a bug in bpy.ops.object.select_name() too… extend does the same thing when set to both True and False, it always extends the selection. :frowning:

Perhaps there’s an oversight in the operators and bpy.ops.pose.select_name(name=‘STRING’,extend=‘BOOL’) is missing? The way to change the active object using Python is using bpy.ops.object.select_name(name=‘STRING’,extend=‘DOES_NOT_WORK’)

Am I missing something or is this a bug report in the making?

Try the script on the default scene with an armature chain and you’ll see that the parenting is always to the last bone selected by mouse even if it is subsequently deselected.

Yep, seems to be a bug…

I just checked, and it only uses the current selected bone in edit mode for parenting, not pose mode.

Do you need to be in edit mode to modify the edit mode selection? It also looks like you left the armature object in pose mode.

@kitsu:

It’s not a problem to switch modes as much as necessary in the script, I just need some method or some quirky hack that enables me to parent an object to each bone in an armature on a single script run. I’ve been playing about with the different modes, but still can’t parent an object to a bone that hasn’t first been selected with the mouse. Have you managed to find a way to do it? I would be very interested to know. :slight_smile:

I thought that either pose mode or object mode was necessary for the bone parenting to work. I get a “no active bone” error when running bpy.ops.object.parent_set(type=‘BONE’) in edit mode.

My latest script. It’s easy to check out the different modes:


import bpy

print('
#######################
')
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.object.select_name(name='Cube', extend=False)
bpy.ops.object.select_name(name='Armature', extend=True)

'''
bpy.ops.object.mode_set(mode='POSE')
pose_bones = bpy.context.object.pose.bones
for bone in pose_bones:
    if not bone.name == 'Bone.005':
        bone.select = False
bpy.context.object.pose.bones['Bone.005'].select = True
bpy.context.scene.update()

bpy.ops.object.parent_set(type='BONE')
'''

'''
bpy.ops.object.mode_set(mode='EDIT')
edit_bones = bpy.context.object.data.edit_bones
for bone in edit_bones:
    if not bone.name == 'Bone.005':
        bone.select = False
bpy.context.object.data.edit_bones['Bone.005'].select = True
bpy.context.scene.update()

bpy.ops.object.parent_set(type='BONE')
'''

bpy.ops.object.mode_set(mode='OBJECT')
bones = bpy.context.object.data.bones
for bone in bones:
    if not bone.name == 'Bone.005':
        bone.select = False
bpy.context.object.data.bones['Bone.005'].select = True
bpy.context.scene.update()

bpy.ops.object.parent_set(type='BONE')


hmm…


def parent_set(object, armature, bone):
    object.parent = armature
    object.parent_bone = bone
    object.parent_type = 'BONE'


obj = bpy.data.objects
parent_set(obj['Cube'], obj['Armature'], 'Bone')

1 Like

OMG!

Very nice. :smiley:

It was staring me in the face. I thought that using the operators was really messy, but never made the mental link between parent, parent_bone and parent_type.

Thanks a lot for this, Uncle Entity.

It seems to do something weird with the object location and orientation when the parenting is applied, but this can most likely be solved. If there is a way then it can be done. :slight_smile:

This was the final piece of the puzzle to fully automate the current implementation of my script. Just gotta put all the pieces together now… :confused:

Hi,

I also want to parent on object to a specific bone in an armature. If I try the method above the object I want to parent changes it rotation according to the rotation of the bone I want to parent it to.
Is there any way to make sure that the child objects retains it rotation?

Note: After the parenting is done I want the child to follow the rotation, etc of the parent bone. I just don’t want the child to rotate when I instantiate the parent-child relation.

The main thing to remember is that a bone’s y axis always points down it’s length from tail to head (or is it head to tail?.. should be easy to figure out). The object that you make the child of the bone will also follow this direction. If you rotate your object before parenting then this rotation will be kept after parenting.

There are two options…

1: Make sure that without any object rotation (Alt+R in object mode), the object mesh is orientated so that the part of the mesh you want to point down the length of the bone is pointing down the object’s y axis (rotate the mesh in edit mode).

2: Allow for any object rotation using rotation matrices or quaternion rotations to work out what direction you need the object to point in and use the mathutils module to work it out.

Personally, I went for the first option. :wink:

If you parent an object in blender via ctrl-P, it doesn’t rotate.
ctrl-P uses the parent_set function. So I rather try to use this. But the problem is that I don’t know how to make a specific bone active so I can parent to the right bone.

If I really can’t find a solution to make a specific bone active, I think I’m going to use one of your solutions. But It would be nicer if I can do it trough the parent_set function.

You don’t happen to know how to make a specific bone active?

Has anyone found a solution for this parenting issue not related to bones / armatures?

In the simplest example, I just want to parent one object to another using parent_set(). It correctly parents to the active object in the scene, just as Ctrl+P does, but I can’t find any way to change the active object through the API.

Using object.select = True/False works just fine for selections but the active object remains! Only when I use the mouse to select an object in the 3D view or outliner will the active object change.

Help please!

Nevermind I think I figured it out. Took me a while to find out that you can just set the active object like so…

If you have an object cube that you get:

cube = bpy.data.objects[0]

Then you can assign it to be the active object

bpy.context.scene.objects.active = cube

I’m sure there’s a great reason that bpy.context.active_object is read only, but I was trying to set that instead.

I meant to reply to this before, but didn’t have time before work.

Provided that you have references to both the object that you want to be parent and the object that you want to be child, you can probably just set the parent property on an object. I haven’t tried this, but it should work since the first line of the bone parenting function provided by Uncle Entity above uses this too.

ob1.parent = ob2

Concerning active_object, I also would like to know if there’s a way to change this through Python without using operators. Operators have great functionality, but when coding I prefer to avoid them when possible because they are context sensitive so if you want to do some complex stuff you really have to micromanage the context switching.

Just noticed that this is your first time to the forum, so welcome to Blenderartists! :cool:

I would really like to use this solution from Uncle Entity but just cannot get it to work. I just want to parent one mesh to a bone. Using CTRL P it just does not work for some reason. If I can manage to do it with python it would be great. My problem is making sense of the above scripts for my particular problem. My object is a mesh titled “MeshrightRetrLL”.
The Armature is “rightRetrLL” and the bone which I want to make the parent of the mesh is “rightRetrLLDeployment”
so how should the above be modified for this situation. I am using Blender 2.49. Hope someone can help.

@longbreak… what works for me is the following code:

    bpy.ops.object.select_all(action='DESELECT')
    bpy.data.objects[MeshrightRetrLL'].select=True
    bpy.data.objects['rightRetrLLDeployment'].select=True
    bpy.context.scene.objects.active = bpy.data.objects['rightRetrLLDeployment'] 
    bpy.ops.object.parent_set(type='OBJECT', keep_transform=False)

The activ object (mesh or armature) will become the parent, i.e. here “rightRetrLLDeployment” should be the parent after the code run through.

1 Like

Thank you for your suggestion eauxfolles. You have pointed me in a better direction. I now realize that part of my problem is that I am dealing with a different version of Blender, namely 2.49 which does not recognize bpy.ops or select_all.

I should have mentioned that an error comes up when I try to export to xplane object file where there is no parent for the mesh and for some reason I just cannot fix it in blender so I am trying to do it with python. I can’t even see what the result of CTRL P is in Blender 2.49 but I can open the project with 2.68 and see that it is indeed not a child of the bone. In 2.68 I can simply use a menu to hook it onto the bone but unfortunately 2.68 does not export properly to xplane object. That is briefly why I need to continue with 2.49.
For selection of objects in 2.49 I apparently need to use soething like this:

lender.Object.GetSelected()[0].sel = False #think this does the same as bpy.ops.object.select_all(action=‘DESELET’)

so that is a start. If I get this thing working I will post the result back here.

No sorry cannot figure this out. It only applies to animation of a tiny door covering a light on a fuselage so will just leave it as fixed mesh and delete the animation. Everything more or less works up to the last line so it is hopeless cause and not worth any more effort trying to get it to work.

myobj = bpy.data.objects[‘MeshrightRetrLL’]
myobj.sel = True
obj = bpy.data.objects[“rightRetrLL”] # this is the armature
obj.sel = True
am = Armature.Get(“rightRetrLL”) # just another possibility

obj.makeEditable() - throws error object has no attribute ‘makeEditable’

obj.makeParentBone([myobj], am.bones, noninverse=0, fast=0)

last line throws error - makeParentBone() takes no keyword arguments

wow then what does it take? I’ll never know.