I'm currently writing an addon, the purpose of which requires it to render several parts of active scene sequentially, preferably with UI-responsiveness intact. The problem is the UI freezes if I just directly call bpy.ops.render.render(animation=True)
. I get UI-responsiveness by changing the execution context to "INVOKE_DEFAULT"
, but then the code won't wait until a render process finished before executing the next one.
Using threading
Module
Looking for an alternative, I then use Python's threading.Thread
object to encapsulate render function, like this:
def _init_render_thread(self):
self._render_thread = threading.Thread(
target=bpy.ops.render.render, kwargs={'animation':True})
... and check the thread object's is_alive
method at interval set by a Timer
object. Unfortunately it's prone to occasional random crashing, as any thread-related bugs are, so I'm still stuck with this method.
Chaining Handlers
Based on jesterKing's answer, I then chain the render execution using a scene-global property and render handlers with the following code. By chaining, the operator no longer needs to be modal. It's finishing early, then leaving completion to these handlers:
@persistent
def render_pre_handler(dummy):
props = bpy.context.scene.oha_layout_tools
if props.render_marker_infos:
rmi = props.render_marker_infos.pop(0)
marker_scene_settings(bpy.context, rmi)
@persistent
def render_complete_handler(dummy):
props = bpy.context.scene.oha_layout_tools
if props.render_marker_infos:
rmi = props.render_marker_infos.pop(0)
marker_scene_settings(bpy.context, rmi)
bpy.ops.render.render('INVOKE_DEFAULT', animation=True)
else:
bpy.app.handlers.render_pre.remove(render_pre_handler)
bpy.app.handlers.render_complete.remove(render_complete_handler)
props.marker_infos.clear()
props.render_marker_infos.clear()
It still doesn't work. The render_complete
handler promptly executes even while the render thread itself is still in progress.
Is there any thread-safe way to monitor render process, other than spawning new Blender process?