0
$\begingroup$

I keep getting context is invalid error, when trying to merge all meshes in the scene

### sources: 
### - base code generated by ChatGPT.
### - merging code from: https://blender.stackexchange.com/questions/13986/how-to-join-objects-with-python

import bpy
import os

# Set the directory containing the .3ds files
input_folder = "C:\Database\ModelDB\furniture"
output_folder = "C:\Database\ModelDB\furniture"

# Ensure the output directory exists
if not os.path.exists(output_folder):
  os.makedirs(output_folder)

# Loop through all the .3ds files in the input folder
for file_name in os.listdir(input_folder):
  if file_name.endswith(".3ds"):
    # Full path to the .3ds file
    input_file = os.path.join(input_folder, file_name)
    
    # Define output .fbx file path
    output_file = os.path.join(output_folder, file_name.replace(".3ds", ".fbx"))
    
    # Clear any previous data in Blender
    bpy.ops.wm.read_homefile(use_empty=True)
    
    # Import the .3ds file
    bpy.ops.import_scene.autodesk_3ds(filepath=input_file)
    
    # Select and join all objects
    bpy.ops.object.select_all(action='DESELECT')
    count = 0
    # obs = []
    for ob in bpy.context.scene.objects:
      if ob.type == 'MESH':
        # obs.append(ob)
        ob.select_set(state=True)
        bpy.context.view_layer.objects.active = ob
        count += 1

    if (count > 1):
      # with bpy.context.temp_override(active_object=obs[0], selected_objects=obs):
      bpy.ops.object.join()
    
    # Export to .fbx
    bpy.ops.export_scene.fbx(filepath=output_file, use_selection=False)
    
    print(f"Converted {input_file} to {output_file}")

print("Conversion completed.")

The line that crashes is bpy.ops.object.join() with the following error

RuntimeError: Operator bpy.ops.object.join.poll() failed, context is incorrect

I also tried the commented lines with the ops array, but I can't get it to work. When I hit Ctrl+J in the scene window, at the point where the script crashes, it merges correctly and the objects seem to be selected correctly (All objects are selected and I have an active obejct).

enter image description here

What am I doing wrong?

EDIT: running bpy.ops.object.join() from the python console after the crash also works. Is this some kind of async error, that the selection or something like that hasn't finished yet??

$\endgroup$
1
  • $\begingroup$ Was this code generated by AI? If so you should provide attribution $\endgroup$ Commented Aug 17 at 0:57

2 Answers 2

0
$\begingroup$

The correct override syntax for object.join is :

with bpy.context.temp_override(active_object=obs[0], selected_editable_objects=obs):
    bpy.ops.object.join()

For reference the point of the override is so that you don't need to worry about selecting the objects nor making an object active.

    # Select and join all objects
    bpy.ops.object.select_all(action='DESELECT')
    count = 0
    # obs = []
    for ob in bpy.context.scene.objects:
      if ob.type == 'MESH':
        # obs.append(ob)
        ob.select_set(state=True)
        bpy.context.view_layer.objects.active = ob
        count += 1

    if (count > 1):
      # with bpy.context.temp_override(active_object=obs[0], selected_objects=obs):
      bpy.ops.object.join()

Becomes

    obs = [o for o in bpy.context.scene.objects if o.type == "MESH"]
    if len(obs) > 1:
        with bpy.context.temp_override(active_object=obs[0], selected_editable_objects=obs):
            bpy.ops.object.join()
$\endgroup$
0
$\begingroup$

Any wrong context errors in lines that uses blender tools can be fixed by supplying context "overrides"

for example, for any lines like

bpy.ops.mesh.bevel(offset=1.16208, offset_pct=0, segments=15, affect='EDGES')

or

bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')

or in your case

bpy.ops.object.join()

can be written as follows

override = context_override()
bpy.ops.mesh.bevel(override ,offset=1.16208, offset_pct=0, segments=15, affect='EDGES')
override = context_override()
bpy.ops.mesh.select_mode(override ,use_extend=False, use_expand=False, type='VERT')
override = context_override()
bpy.ops.object.join(override )

where context_override() function typically does the following job:

def context_override():
    for area in bpy.context.screen.areas:
        if area.type == 'VIEW_3D':
            for region in area.regions:
                if region.type == 'WINDOW':
                    override = bpy.context.copy()
                    override['area'] = area
                    override['region'] = region
                    override['space_data'] = area.spaces.active
                    override['edit_object'] = bpy.context.edit_object
                    return override
                    break

override becomes the first argument passed in this case. However post blender 3.2 theres a simpler way of providing context overrides.

example:

area = [area for area in bpy.context.screen.areas if area.type == "VIEW_3D"][0]
 
with bpy.context.temp_override(area=area):
    bpy.ops.object.join()

$\endgroup$

You must log in to answer this question.

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