6
$\begingroup$

I have a script that allows to import and export .obj files which is working fine. However, I'd like to perform some operations in between these calls, for example select the mesh and recalculate the normals (outside). If I try that, I get a context error and I'm not sure why...?

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

    bpy.ops.import_scene.obj(filepath=source_to_file)
    
    print("Starting--------------------------------------------")
    for obj in bpy.data.objects:
        bpy.context.scene.objects.active = obj
        # go edit mode
        bpy.ops.object.mode_set(mode='EDIT')
        # select al faces
        bpy.ops.mesh.select_all(action='SELECT')
        # recalculate outside normals 
        bpy.ops.mesh.normals_make_consistent(inside=False)
        # go object mode again
        bpy.ops.object.editmode_toggle()
    print("Ending--------------------------------------------")
    
    bpy.ops.export_scene.obj(filepath=target_to_file,use_materials=True)

How is it possible to know the object imported by any operator (there is no return value) and how can I recalculate the normals per object that is triggered by a button?

$\endgroup$

2 Answers 2

8
$\begingroup$

You can use bpy.context.selected_objects[:] to get all imported objects. Iterate through the list, set the select state of each object to True and make it the active object as well in order to call operators like mesh.normals_make_consistent():

Blender 2.8+

import bpy

bpy.ops.import_scene.obj(filepath=source_to_file)
obj_objects = bpy.context.selected_objects[:]

for obj in obj_objects:
    bpy.ops.object.select_all(action='DESELECT')
    obj.select_set(True)
    bpy.context.view_layer.objects.active = obj
    # go edit mode
    bpy.ops.object.mode_set(mode='EDIT')
    # select al faces
    bpy.ops.mesh.select_all(action='SELECT')
    # recalculate outside normals 
    bpy.ops.mesh.normals_make_consistent(inside=False)
    # go object mode again
    bpy.ops.object.editmode_toggle()

Blender 2.7x

import bpy

bpy.ops.import_scene.obj(filepath=source_to_file)
obj_objects = bpy.context.selected_objects[:]

for obj in obj_objects:
    bpy.ops.object.select_all(action='DESELECT')
    obj.select = True
    bpy.context.scene.objects.active = obj
    # go edit mode
    bpy.ops.object.mode_set(mode='EDIT')
    # select al faces
    bpy.ops.mesh.select_all(action='SELECT')
    # recalculate outside normals 
    bpy.ops.mesh.normals_make_consistent(inside=False)
    # go object mode again
    bpy.ops.object.editmode_toggle()

Updated .blend file can be found here:

$\endgroup$
19
  • $\begingroup$ I have this error: AttributeError: 'Context' object has no attribute 'selected_objects' (I use 2.78a) $\endgroup$
    – D.Giunchi
    Commented Feb 1, 2017 at 0:01
  • $\begingroup$ @D.Giunchi Could you provide your whole script to be able to debug it $\endgroup$
    – Tak
    Commented Feb 1, 2017 at 0:02
  • $\begingroup$ pastebin.com/vpyi29Nq , I also used obj_objects = [ o for o in bpy.context.scene.objects if o.select ] but I got the first error. $\endgroup$
    – D.Giunchi
    Commented Feb 1, 2017 at 0:08
  • $\begingroup$ @D.Giunchi I tried this pasteall.org/225375 and it's working totally fine with no errors $\endgroup$
    – Tak
    Commented Feb 1, 2017 at 0:20
  • $\begingroup$ in the console I can see this: location: <unknown location>:-1 Error: Traceback (most recent call last): File "\fix.py", line 73, in <module> File "\fixpy", line 52, in main File "C:\Program Files\Blender Foundation\Blender\2.78\scripts\modules\bpy\ops.py", line 189, in call ret = op_call(self.idname_py(), None, kw) RuntimeError: Operator bpy.ops.object.mode_set.poll() failed, context is incorrect location: <unknown location>:-1 Error: Python script fail, look in the console for now... <!> event has invalid window $\endgroup$
    – D.Giunchi
    Commented Feb 1, 2017 at 0:53
14
$\begingroup$

Making normals outside can also be done with the bmesh module operator recalc_face_normals(bm, faces)

Sample script

import bpy
import bmesh

from bpy import context

#bpy.ops.import_scene.obj(filepath=source_to_file)
bm = bmesh.new()
meshes = set(o.data for o in context.selected_objects
            if o.type == 'MESH')
for mesh in meshes:
    bm.from_mesh(mesh)
    bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
    bm.to_mesh(mesh)
    bm.clear()
    mesh.update()
    
bm.free()
#bpy.ops.export_scene.obj(filepath=target_to_file, use_materials=True)

Saves the need to flip mode and context object.

$\endgroup$
1
  • 4
    $\begingroup$ I would upvote this answer 5 times if I could... I hate using the ops functions... $\endgroup$
    – Noam Peled
    Commented Aug 17, 2018 at 19:40

You must log in to answer this question.

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