11
$\begingroup$

I try to do this:

bpy.ops.object.select_all()
bpy.ops.object.join()

in my script. It selects all the stuff like CtrlA but fails on join() (CtrlJ) with the following error message:

Traceback (most recent call last): File "", line 1,
in File "C:\Program Files\Blender
Foundation\Blender\2.71\scripts\modules\bpy\ops.py", line 188, in
__ call __
ret = op_call(self.idname_py(), None, kw) RuntimeError: Operator bpy.ops.object.join.poll() failed, context is incorrect

the message is from console.

How can I make this work? CtrlA, CtrlJ actually works.

$\endgroup$
  • 1
    $\begingroup$ TO join an object you must have one object as the active object. If there is no active object selected it will not work. Try setting one object as the selected object. $\endgroup$ – Vader Jul 12 '14 at 13:07
  • $\begingroup$ Works nice, thank you. Would you put this as an answer so I can flag it? $\endgroup$ – Zéiksz Jul 12 '14 at 13:15
8
$\begingroup$

To join an object you must have one object as the active object. If there is no active object selected it will not work. Try setting one object as the selected object.

An example would be

bpy.context.scene.objects.active = bpy.data.objects["Cube"]

$\endgroup$
18
$\begingroup$

If you don't want to change the real selection states, you may wanna use an override:

import bpy

scene = bpy.context.scene

obs = []
for ob in scene.objects:
    # whatever objects you want to join...
    if ob.type == 'MESH':
        obs.append(ob)

ctx = bpy.context.copy()

# one of the objects to join
ctx['active_object'] = obs[0]

ctx['selected_objects'] = obs

# We need the scene bases as well for joining.
# Remove this line in Blender >= 2.80!
ctx['selected_editable_bases'] = [scene.object_bases[ob.name] for ob in obs]

bpy.ops.object.join(ctx)
$\endgroup$
  • 1
    $\begingroup$ Your code is much much faster than the proposal of stacker when joining some hundred or thousand objects. Could you please explain the concept? Why do you make a copy of context? $\endgroup$ – Peter Hilgers Dec 5 '16 at 19:43
  • $\begingroup$ The performance difference can probably be explained like this: blender.stackexchange.com/a/7360/1363 The context override was only supposed to keep the original selection intact AFAIR. If you use a copy of the context, you can change some attributes and pass it to an operator without affecting the actual context. $\endgroup$ – CoDEmanX Dec 5 '16 at 19:48
  • 1
    $\begingroup$ Oh my God this is lightning fast! Thank you so much for this. Joining 400-ish objects was the main timesucker in my project, and it has been reduced to almost zero $\endgroup$ – Davy Herben Feb 8 '18 at 16:02
  • $\begingroup$ Any recommendation on making this work for 2.8: AttributeError: 'Scene' object has no attribute 'object_bases'? $\endgroup$ – oneiros Nov 22 at 14:05
  • 1
    $\begingroup$ @oneiros Just delete that line, it's not required anymore (the collection system changed things). $\endgroup$ – CoDEmanX Nov 23 at 15:02
5
$\begingroup$

A full script for later reference:

import bpy

for ob in bpy.context.scene.objects:
    if ob.type == 'MESH':
        ob.select = True
        bpy.context.scene.objects.active = ob
    else:
        ob.select = False
bpy.ops.object.join()
$\endgroup$
  • $\begingroup$ Given the comments below CoDEmanX 's answer re speed, perhaps set active outside loop. $\endgroup$ – batFINGER Feb 27 at 14:50
4
$\begingroup$

No need for bases in 2.8 join operator override.

Thought I would also post this here. It appears at 2.80 beta version, there is no need to have the selected editable bases context member, instead use selected editable objects

Testing this in python console. Have duped the default cube 3 times, the last dupe "Cube.003" is active and only object selected.

>>> C.object
bpy.data.objects['Cube.003']


>>> C.selected_objects
[bpy.data.objects['Cube.003']]

A list of all the mesh objects in scene to join

>>> obs = [o for o in C.scene.objects if o.type == 'MESH']
>>> obs
[bpy.data.objects['Cube'], bpy.data.objects['Cube.001'], bpy.data.objects['Cube.002'], bpy.data.objects['Cube.003']]

Make a context override dictionary, with only object, active_object, selected_objects, selected_editable_objects as members

>>> c = {}

>>> c["object"] = c["active_object"] = C.object
>>> c["selected_objects"] = c["selected_editable_objects"] = obs

Run the operator with this override

>>> bpy.ops.object.join(c)
{'FINISHED'}

Resulting scene after running. All four cubes are joined as one "Cube.003", The result desired

>>> C.scene.objects[:]
[bpy.data.objects['Lamp'], bpy.data.objects['Camera'], bpy.data.objects['Cube.003']]

Risky look at obs since three of the objects no longer exist.

>>> obs
[<bpy_struct, Object invalid>, <bpy_struct, Object invalid>, <bpy_struct, Object invalid>, bpy.data.objects['Cube.003']]

Further to this, can make object copies into the data collection and join them. Here i am copying the object 3 times, and then joining all mesh objects into file as one.

>> for i in range(3):
...     o.copy()
...  

bpy.data.objects['Cube.000']
bpy.data.objects['Cube.001']
bpy.data.objects['Cube.002']

>>> obs = [o for o in D.objects if o.type == 'MESH']

"Array them to see result"

>> for  o in obs:
...     o.location.z += ob.dimensions.z
...     
>>> c = {}
>>> c["object"] = c["active_object"] = C.object
>>> c["selected_objects"] = c["selected_editable_objects"] = obs

>>> bpy.ops.object.join(c)
{'FINISHED'}

the dupes will remain in data, the object "Cube.003" will be a mesh made of all others.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.