I'm trying to make a script where I have to render several images of the same scene at the same frame, and write to disk with appropriate names. The problem is that although this works fine with a non-responsive UI, it doesn't work well when the render progress appears in the Blender UI.
I mean,
bpy.ops.render.render()
uses "EXEC_DEFAULT" when called from a script. This leaves a blocked User Interface until the render finishes, meaning that you can't cancel the process, except by killing Blender. The problem is that the render may be too big in resolution, and the user could want to stop the process when realizing that the render could take hours, so the idea is to cancel this process without losing the currently opened project.
If I call,
bpy.ops.render.render("INVOKE_DEFAULT")
I get a nice preview bar in the Info Window with a clickable "X" button. I want the user to be able to press that button to cancel the operation if needed, but for a series of renders (not animation).
The problem here seems to be when trying to loop this command with the INVOKE_DEFAULT context to write several renders. I made a lot of testing, and this way of calling the render operator seems to continue with whatever is next in the script, so it loops very fast and ends before the rendering actually finishes.
I think that I need some way to stop the script and make it wait until render finishes to continue looping with the next ones. I tried the "render_complete" and "render_cancel" handlers, but I can't use them to start a new render or even make the rest of the script wait until the loop of renders is complete. I tried threading and events without success either. Maybe I'm doing something wrong.
import bpy
render = bpy.context.scene.render
path = render.filepath = "/tmp/"
shots = ["one", "two", "three"]
for x in shots:
render.filepath = path + x
bpy.ops.render.render("INVOKE_DEFAULT", write_still=True) # this doesn't work
# but if I use:
# bpy.ops.render.render(write_still=True)
# without the INVOKE thing, images are written as expected,
# but losing the possibility to see the progress and cancel rendering
register_module(__name__)there is now(un)register_class(Multi_Render)andevent_timer_addneeds to change the second parameter towindow=context.window. – Vit Kovalcik Jan 10 '19 at 19:20context- it's just what I found on the Internet (I was gettingNoneeven before setting that as the default value, so I couldn't read its type). – Markus von Broady Jun 02 '21 at 15:10depsgraphas I understand – Chaos Jun 03 '21 at 16:17render.renderoperator reads the information from the scene before calling the pre handler. This is why in the example, the filepath is set in the modal, just before calling the render operator. On the other hand, I don't see the point of setting the filepath in the post handler. You can just do it before calling the next operator and it should be fine. – Chaos Apr 11 '22 at 19:34