9

My script runs through a for loop, each time rendering a frame. How can I abort the script?

Normally, I'd use CTL+C in the terminal Blender was launched from, but this doesn't work. It just waits until the current frame is done and then raises a KeyboardInterrupt exception in some file called .../2.69/scripts/addons/cycles/engine.py, but continues on to the next frame.

Here is a simplified version of my script:

import bpy

framesToRender = (1, 3, 4, 6, 7)

for frame in framesToRender:
    bpy.context.scene.frame_set(frame)
    bpy.context.scene.render.filepath = "/home/garrett/Desktop/" + str(frame).zfill(4)
    bpy.ops.render.render(write_still=True)    


EDIT:

The only way I've figured out to stop it is by killing Blender from the System Monitor (for Ubuntu), or close the terminal Blender was launched from. Using xkill on Blender kills the user interface, but doesn't stop the script.

CodeManX
  • 29,298
  • 3
  • 89
  • 128
Garrett
  • 6,596
  • 6
  • 47
  • 75
  • I am not sure in what part of the script you want to abort, but you can try raiseing and Exception manually in your code. – Vader Feb 05 '14 at 15:48
  • 1
    The tiny x button in the info header during a rendering process is hardcoded, it's unfortunately not an operator you could call from python :( – CodeManX Feb 05 '14 at 16:14
  • 1
    @Vader, thanks, but I'm trying to stop the script running using a command entered into the terminal Blender was called from (since the script can take many hours to finish), not from a command coded into the script. – Garrett Feb 05 '14 at 21:55
  • 1
    The reason why Ctrl+C doesn't work is because it sends SIGINT, which is used to stop renders (hence the cycles reference). If you don't mind killing blender too, either killall blender or kill $(pgrep blender) should stop the script as well. – gandalf3 Feb 07 '14 at 11:17
  • Couldn't you add a simple if stop to your render loop and set it to true on KeyboardInterrupt? – CodeManX Feb 07 '14 at 14:03
  • The exception does not reach the executing for loop. It is consumed before and not raised again. Seems like the render operator always returns 'FINISHED'. So you can not check for that, neither. – pink vertex Feb 07 '14 at 14:29
  • I wonder where it's consumed, looks like a bug or misconcepted operator if it's impossible to catch. – CodeManX Feb 11 '14 at 00:45
  • @CoDEmanX, good point. I can't even seem to intercept the console output by changing sys.stdout. If I could, then I could search through it for KeyboardInterrupt before printing it to the console. – Garrett Feb 11 '14 at 01:26

1 Answers1

5

To stop the loop you can install your own handler for the SIGINT signal which is executed by the main thread. (see signal module)

The rendering of the current frame will not abort.

import bpy
import signal

abort = None

def hndl(signum, frame):
    global abort
    abort = True
    print("\nAbort")
    #uncomment to call the default handler
    #raising the KeyboardInterruption exception
    #signal.default_int_handler(signum, frame)

def run():
    global abort
    signal.signal(signal.SIGINT, hndl)
    count = 0
    abort = False
    while not abort and count < 10:
        count += 1
        print(count, abort)
        bpy.ops.render.render()

def reset():   
    signal.signal(
        signal.SIGINT, 
        signal.default_int_handler
        )

if __name__== "__main__":
    run()
    reset()
pink vertex
  • 9,896
  • 1
  • 25
  • 44