5
$\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$
6
$\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 Feb 1 '17 at 0:01
  • $\begingroup$ @D.Giunchi Could you provide your whole script to be able to debug it $\endgroup$ – Tak Feb 1 '17 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 Feb 1 '17 at 0:08
  • $\begingroup$ @D.Giunchi I tried this pasteall.org/225375 and it's working totally fine with no errors $\endgroup$ – Tak Feb 1 '17 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 Feb 1 '17 at 0:53
11
$\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
  • 3
    $\begingroup$ I would upvote this answer 5 times if I could... I hate using the ops functions... $\endgroup$ – Noam Peled Aug 17 '18 at 19:40

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.