Why context is incorrect?

I try to render with freestyle wireframe, so i use mark_freestyle_edge in my script. But when i create after-render handler and try to clear marked edges i get that error: bpy.ops.object.mode_set.poll() failed, context is incorrect… How to fix it?

Here is part of my code:


@persistent
def onRenderFinished(scene):
    objects = bpy.context.scene.objects
    for object in objects:
        for i, l in enumerate(bpy.context.scene.layers):
            if object.layers[i] == True and object.layers[i] == l:
                if object.type == "MESH":
                    bpy.context.scene.objects.active = object
                    bpy.ops.object.mode_set(mode='EDIT')    <b># ERROR DISPATCHES HERE</b>
                    bpy.ops.mesh.mark_freestyle_edge(clear=True)
                    bpy.ops.object.mode_set(mode='OBJECT')
    return


class WRender(bpy.types.Operator):
    def execute(self, context):
        objects = bpy.context.scene.objects
        for object in objects:
            for i, l in enumerate(bpy.context.scene.layers):
                if object.layers[i] == True and object.layers[i] == l:
                    if object.type == "MESH":
                        bpy.context.scene.objects.active = object
                        bpy.ops.object.mode_set(mode='EDIT')
                        bpy.ops.mesh.mark_freestyle_edge(clear=False)
                        bpy.ops.object.mode_set(mode='OBJECT')
        renderCompleteHadler = bpy.app.handlers.render_complete
        global onRenderFinished
        if onRenderFinished not in renderCompleteHadler:
            renderCompleteHadler.append(onRenderFinished)
        bpy.ops.render.render('INVOKE_DEFAULT')
        return {'FINISHED'}


def register():
    bpy.utils.register_class(WRender)


def unregister():
    bpy.utils.unregister_class(WRender)


if __name__ == "__main__":
    register()

1 Like

after going to edit mode, it is required to select edges:

bpy.ops.mesh.select_all(action=‘SELECT’)

I tryed to add this - no result.
An error dispatches when i trying to go to edit mode on: bpy.ops.object.mode_set(mode=‘EDIT’)

How about using execute, instead of invoke?
I wonder if editing process is invoked in rendering, may cause context poll check failure.

Testing code is here, very messy.

import bpy

# add menu and button in toolprops region
class ToolPropsPanel(bpy.types.Panel):
  bl_label = "my Buttons in Tool props"
  bl_space_type = "VIEW_3D"
  bl_region_type = "TOOL_PROPS"
  def draw(self, context):
    self.layout.operator("clear.mark_freestyle_edge")


def onRenderFinished(scene):
    objects = bpy.context.scene.objects
    bpy.ops.object.select_all(action='DESELECT')
    return # dummy, pass rest of pard of program for testing
    for object in objects:
        for i, l in enumerate(bpy.context.scene.layers):
            if object.layers[i] == True and object.layers[i] == l:
                if object.type == "MESH":
                    bpy.context.scene.objects.active = object
                    print("Before error")
                    object.select = True
                    print(object.name)
                    bpy.ops.object.mode_set(mode='EDIT')    # ERROR DISPATCHES HERE
                    print("After error")
                    bpy.ops.mesh.mark_freestyle_edge(clear=True)
                    bpy.ops.object.mode_set(mode='OBJECT')
    return

class onRenderFinished(bpy.types.Operator):
  bl_label = "RenderFinished"
  bl_idname = "on_render.finished"

  def  execute(self, context):
    objects = bpy.context.scene.objects
    bpy.ops.object.select_all(action='DESELECT')
    for object in objects:
        for i, l in enumerate(bpy.context.scene.layers):
            if object.layers[i] == True and object.layers[i] == l:
                if object.type == "MESH":
                    bpy.context.scene.objects.active = object
                    print("Before error")
                    object.select = True
                    print(object.name)
                    bpy.ops.object.mode_set(mode='EDIT')    # ERROR DISPATCHES HERE
                    print("After error")
                    bpy.ops.mesh.mark_freestyle_edge(clear=True)
                    bpy.ops.object.mode_set(mode='OBJECT')
    return {'FINISHED'}

class WRender(bpy.types.Operator):
    bl_label = "Clear Mark freestyle edge"
    bl_idname = "clear.mark_freestyle_edge"

    def execute(self, context):
        # only set edge select mode on for mesh edit 
        bpy.context.tool_settings.mesh_select_mode = [False, True, False]
        objects = bpy.context.scene.objects
        for object in objects:
            for i, l in enumerate(bpy.context.scene.layers):
                if object.layers[i] == True and object.layers[i] == l:
                    if object.type == "MESH":
                        bpy.context.scene.objects.active = object
                        object.select = True
                        bpy.ops.object.mode_set(mode='EDIT')
                        bpy.ops.mesh.mark_freestyle_edge(clear=False)
                        bpy.ops.object.mode_set(mode='OBJECT')
                        object.select = False
        renderCompleteHadler = bpy.app.handlers.render_complete
        global onRenderFinished
        if onRenderFinished not in renderCompleteHadler:
            renderCompleteHadler.append(onRenderFinished)
        bpy.ops.render.render('INVOKE_DEFAULT')
        
        bpy.ops.on_render.finished() # directly execute
        return {'FINISHED'}


def register():
    bpy.utils.register_class(WRender)

def unregister():
    bpy.utils.unregister_class(WRender)


if __name__ == "__main__":
    bpy.utils.register_class(onRenderFinished)
    # register menu and button
    bpy.utils.register_module(__name__)

Sorry, may be i dont anderstand the idea. If

bpy.ops.on_render.finished() # directly execute

executes directly after render starts, without waiting for its completion, all freestyle marked edges clears before render finised and so dont draws on render result. I need to clear freestyle marked edges after render completed otherwise i get wrong result - marked edges are not visible.

Korchy, you are right. Need a flag to indicate rendering is finished or not.

import bpy
import time

# add menu and button in toolprops region
class ToolPropsPanel(bpy.types.Panel):
  bl_label = "my Buttons in Tool props"
  bl_space_type = "VIEW_3D"
  bl_region_type = "TOOL_PROPS"
  def draw(self, context):
    self.layout.operator("clear.mark_freestyle_edge")

def onRenderFinished(scene):
    global render_finished
    render_finished = True
    print("render_finished", render_finished)
    return # dummy, bypass rest of pard of program for testing

    objects = bpy.context.scene.objects
    bpy.ops.object.select_all(action='DESELECT')
    for object in objects:
        for i, l in enumerate(bpy.context.scene.layers):
            if object.layers[i] == True and object.layers[i] == l:
                if object.type == "MESH":
                    bpy.context.scene.objects.active = object
                    print("Before error")
                    object.select = True
                    print(object.name)
                    bpy.ops.object.mode_set(mode='EDIT')    # ERROR DISPATCHES HERE
                    print("After error")
                    bpy.ops.mesh.mark_freestyle_edge(clear=True)
                    bpy.ops.object.mode_set(mode='OBJECT')
    return

class onRenderFinishedNew(bpy.types.Operator):
  bl_label = "RenderFinished"
  bl_idname = "on_render.finished"

  def  execute(self, context):
    objects = bpy.context.scene.objects
    bpy.ops.object.select_all(action='DESELECT')
    for object in objects:
        for i, l in enumerate(bpy.context.scene.layers):
            if object.layers[i] == True and object.layers[i] == l:
                if object.type == "MESH":
                    bpy.context.scene.objects.active = object
                    object.select = True
                    print(object.name)
                    bpy.ops.object.mode_set(mode='EDIT')    # ERROR DISPATCHES HERE
                    bpy.ops.mesh.mark_freestyle_edge(clear=True)
                    bpy.ops.object.mode_set(mode='OBJECT')
    return {'FINISHED'}

class WRender(bpy.types.Operator):
    bl_label = "Clear Mark freestyle edge"
    bl_idname = "clear.mark_freestyle_edge"

    def execute(self, context):
        # only set edge select mode on for mesh edit 
        bpy.context.tool_settings.mesh_select_mode = [False, True, False]
        objects = bpy.context.scene.objects
        for object in objects:
            for i, l in enumerate(bpy.context.scene.layers):
                if object.layers[i] == True and object.layers[i] == l:
                    if object.type == "MESH":
                        bpy.context.scene.objects.active = object
                        object.select = True
                        bpy.ops.object.mode_set(mode='EDIT')
                        bpy.ops.mesh.mark_freestyle_edge(clear=False)
                        bpy.ops.object.mode_set(mode='OBJECT')
                        object.select = False
        renderCompleteHadler = bpy.app.handlers.render_complete
        global onRenderFinished
        if onRenderFinished not in renderCompleteHadler:
            renderCompleteHadler.append(onRenderFinished)
        global render_finished
        render_finished = False
        print("render_finished", render_finished)
        bpy.ops.render.render('INVOKE_DEFAULT')
        
        while render_finished == False:
          print("Waiting rendering")        
          print("render_finished", render_finished)
          time.sleep(.200) # pause for a while (0.2 sec) to let render running           

        print("Do clear mark job after render_finished")
        bpy.ops.on_render.finished() # directly execute

        return {'FINISHED'}


def register():
    bpy.utils.register_class(WRender)

def unregister():
    bpy.utils.unregister_class(WRender)


if __name__ == "__main__":
    bpy.utils.register_class(onRenderFinishedNew)
    # register menu and button
    bpy.utils.register_module(__name__)

Korchy, you are right. Need a flag to indicate rendering is finished or not. <br>
<br>

import bpy&lt;br&gt;
import time&lt;br&gt;
&lt;br&gt;
# add menu and button in toolprops region&lt;br&gt;
class ToolPropsPanel(bpy.types.Panel):&lt;br&gt;
  bl_label = "my Buttons in Tool props"&lt;br&gt;
  bl_space_type = "VIEW_3D"&lt;br&gt;
  bl_region_type = "TOOL_PROPS"&lt;br&gt;
  def draw(self, context):&lt;br&gt;
    self.layout.operator("clear.mark_freestyle_edge")&lt;br&gt;
&lt;br&gt;
def onRenderFinished(scene):&lt;br&gt;
    global render_finished #flag&lt;br&gt;
    render_finished = True&lt;br&gt;
    print("render_finished", render_finished)&lt;br&gt;
    return # dummy, bypass rest of pard of program for testing&lt;br&gt;
&lt;br&gt;
    objects = bpy.context.scene.objects&lt;br&gt;
    bpy.ops.object.select_all(action='DESELECT')&lt;br&gt;
    for object in objects:&lt;br&gt;
        for i, l in enumerate(bpy.context.scene.layers):&lt;br&gt;
            if object.layers[i] == True and object.layers[i] == l:&lt;br&gt;
                if object.type == "MESH":&lt;br&gt;
                    bpy.context.scene.objects.active = object&lt;br&gt;
                    print("Before error")&lt;br&gt;
                    object.select = True&lt;br&gt;
                    print(object.name)&lt;br&gt;
                    bpy.ops.object.mode_set(mode='EDIT')    # ERROR DISPATCHES HERE&lt;br&gt;
                    print("After error")&lt;br&gt;
                    bpy.ops.mesh.mark_freestyle_edge(clear=True)&lt;br&gt;
                    bpy.ops.object.mode_set(mode='OBJECT')&lt;br&gt;
    return&lt;br&gt;
&lt;br&gt;
class onRenderFinishedNew(bpy.types.Operator):&lt;br&gt;
  bl_label = "RenderFinished"&lt;br&gt;
  bl_idname = "on_render.finished"&lt;br&gt;
&lt;br&gt;
  def  execute(self, context):&lt;br&gt;
    objects = bpy.context.scene.objects&lt;br&gt;
    bpy.ops.object.select_all(action='DESELECT')&lt;br&gt;
    for object in objects:&lt;br&gt;
        for i, l in enumerate(bpy.context.scene.layers):&lt;br&gt;
            if object.layers[i] == True and object.layers[i] == l:&lt;br&gt;
                if object.type == "MESH":&lt;br&gt;
                    bpy.context.scene.objects.active = object&lt;br&gt;
                    object.select = True&lt;br&gt;
                    print(object.name)&lt;br&gt;
                    bpy.ops.object.mode_set(mode='EDIT')    # ERROR DISPATCHES HERE&lt;br&gt;
                    bpy.ops.mesh.mark_freestyle_edge(clear=True)&lt;br&gt;
                    bpy.ops.object.mode_set(mode='OBJECT')&lt;br&gt;
    return {'FINISHED'}&lt;br&gt;
&lt;br&gt;
class WRender(bpy.types.Operator):&lt;br&gt;
    bl_label = "Clear Mark freestyle edge"&lt;br&gt;
    bl_idname = "clear.mark_freestyle_edge"&lt;br&gt;
&lt;br&gt;
    def execute(self, context):&lt;br&gt;
        # only set edge select mode on for mesh edit &lt;br&gt;
        bpy.context.tool_settings.mesh_select_mode = [False, True, False]&lt;br&gt;
        objects = bpy.context.scene.objects&lt;br&gt;
        for object in objects:&lt;br&gt;
            for i, l in enumerate(bpy.context.scene.layers):&lt;br&gt;
                if object.layers[i] == True and object.layers[i] == l:&lt;br&gt;
                    if object.type == "MESH":&lt;br&gt;
                        bpy.context.scene.objects.active = object&lt;br&gt;
                        object.select = True&lt;br&gt;
                        bpy.ops.object.mode_set(mode='EDIT')&lt;br&gt;
                        bpy.ops.mesh.mark_freestyle_edge(clear=False)&lt;br&gt;
                        bpy.ops.object.mode_set(mode='OBJECT')&lt;br&gt;
                        object.select = False&lt;br&gt;
        renderCompleteHadler = bpy.app.handlers.render_complete&lt;br&gt;
        global onRenderFinished&lt;br&gt;
        if onRenderFinished not in renderCompleteHadler:&lt;br&gt;
            renderCompleteHadler.append(onRenderFinished)&lt;br&gt;
        global render_finished&lt;br&gt;
        render_finished = False&lt;br&gt;
        print("render_finished", render_finished)&lt;br&gt;
        bpy.ops.render.render('INVOKE_DEFAULT')&lt;br&gt;
        &lt;br&gt;
        while render_finished == False:&lt;br&gt;
          print("Waiting rendering")        &lt;br&gt;
          print("render_finished", render_finished)&lt;br&gt;
          time.sleep(.200) # pause for a while (0.2 sec) to let render running           &lt;br&gt;
&lt;br&gt;
        print("Do clear mark job after render_finished")&lt;br&gt;
        bpy.ops.on_render.finished() # directly execute&lt;br&gt;
&lt;br&gt;
        return {'FINISHED'}&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
def register():&lt;br&gt;
    bpy.utils.register_class(WRender)&lt;br&gt;
&lt;br&gt;
def unregister():&lt;br&gt;
    bpy.utils.unregister_class(WRender)&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
if __name__ == "__main__":&lt;br&gt;
    bpy.utils.register_class(onRenderFinishedNew)&lt;br&gt;
    # register menu and button&lt;br&gt;
    bpy.utils.register_module(__name__)

Grammer, interesting decision. It works, but not the way i need - render process is not shown that case, only render result. If not to show render process bpy.ops.render.render(write_still=True) can be used. But i want to follow the rendering process - it is the main reason, i used bpy.ops.render.render(‘INVOKE_DEFAULT’).
Is it any other way to display render process with controlling its completion without using while loop?

Korchy, thank you for let me more about your program.
After checked Blender 2.72a source, knew that global flag ‘is_rendering’ is still True when a render complete handler is triggered. When ‘is_rendering’ is still True,context is not objects editing related. Editing can be done after rendering has finished and ‘is_rendering’ has turned False. Found’scene_update’ handler can be used. The temporary method summary:

  • add scene_update handler in render_complete handler, and
  • do objects editing in scene update handler.
    As a result, there is no while loop and rendering process will be shown normally on screen.
    Code is based on your original one. And for test convenient, panel and button are added.
# add menu and button in toolprops region
class ToolPropsPanel(bpy.types.Panel):
  bl_label = "my Buttons in Tool props"
  bl_space_type = "VIEW_3D"
  bl_region_type = "TOOL_PROPS"
  def draw(self, context):
    self.layout.operator("clear.mark_freestyle_edge")


def onRenderFinished(scene):
    # Rendering is finished, but the global flag 'is_rendering' is still True.
    # let's put editing process into scene update handler list which will be
    # executed after 'is_rendering' is False.
    sceneUpdatePostHadler = bpy.app.handlers.scene_update_post 
    global onSceneUpdatePost
    if onSceneUpdatePost not in sceneUpdatePostHadler:
        sceneUpdatePostHadler.append(onSceneUpdatePost)
    return

def onSceneUpdatePost(scene):
    # scene update too often, to avoid run this handler more than once, remove it 
    global onSceneUpdatePost
    bpy.app.handlers.scene_update_post.remove(onSceneUpdatePost)

    print("Context mode:", bpy.context.mode)
    objects = bpy.context.scene.objects
    for object in objects:
        for i, l in enumerate(bpy.context.scene.layers):
            if object.layers[i] == True and object.layers[i] == l:
                if object.type == "MESH":
                    print("Object:", object.name)
                    bpy.context.scene.objects.active = object
                    bpy.ops.object.mode_set(mode='EDIT')    # ERROR DISPATCHES HERE
                    bpy.ops.mesh.mark_freestyle_edge(clear=True)
                    bpy.ops.object.mode_set(mode='OBJECT')
    return


class WRender(bpy.types.Operator):
    bl_label = "Clear Mark freestyle edge"
    bl_idname = "clear.mark_freestyle_edge"

    def execute(self, context):
        objects = bpy.context.scene.objects
        for object in objects:
            for i, l in enumerate(bpy.context.scene.layers):
                if object.layers[i] == True and object.layers[i] == l:
                    if object.type == "MESH":
                        bpy.context.scene.objects.active = object
                        bpy.ops.object.mode_set(mode='EDIT')
                        bpy.ops.mesh.mark_freestyle_edge(clear=False)
                        bpy.ops.object.mode_set(mode='OBJECT')
        renderCompleteHadler = bpy.app.handlers.render_complete
        global onRenderFinished
        if onRenderFinished not in renderCompleteHadler:
            renderCompleteHadler.append(onRenderFinished)
        bpy.ops.render.render('INVOKE_DEFAULT')

        return {'FINISHED'}


def register():
    bpy.utils.register_class(WRender)
    bpy.utils.register_class(ToolPropsPanel)

def unregister():
    bpy.utils.unregister_class(WRender)
    bpy.utils.unregister_class(ToolPropsPanel)


if __name__ == "__main__":
    register()

Grammer, big thanks! Simple and ingenious solution. It works fine!

@Korchy , thank you too for sharing code and ideas! I learned a lot.

@Korchy , amazing! Wire add-on is very useful.