I have been writing a Nuke render manager in Python and came across a problem:
nuke.cancel()
doesn't seem to work right.
My python script creates a main class and connects to a server (written in Java) and then enters an infinite loop, each time getting a new task (in the form of a JSON object). Each loop it gets opens a script? finds the write nodes and executes them. To report progress back to the server I have added callbacks:
nuke.addBeforeFrameRender(lambda: self._pre_frame_render())
and
nuke.addAfterFrameRender(lambda: self._post_frame_render())
where
def _pre_frame_render(self):
self._host_socket.send(struct.pack('>b', 1))
self._host_socket.send(struct.pack('>i', nuke.frame()))
def _post_frame_render(self):
self._host_socket.send(struct.pack('>b', 2))
self._host_socket.send(struct.pack('>i', nuke.frame()))
To enable a cancel
method on the server, I have a separate thread that runs while the rendering is active and waits for a 'cancel' request
class CancelHandler(threading.Thread):
def __init__(self, input_socket, cancel_callback):
threading.Thread.__init__(self)
self.cancel_set = threading.Event()
self.run_set = threading.Event()
self._input_socket = input_socket
self._cancel_callback = cancel_callback
def run(self):
while True:
if self.run_set.is_set():
msg_raw = recv_msg(self._input_socket)
msg = json.loads(msg_raw)
if 'mode' in msg and msg['mode'] == 'cancel':
print msg_raw
self.cancel_set.set()
self.run_set.clear()
self._cancel_callback()
The cancel_callback
is defined as
def _cancel_callback(self):
nuke.cancel()
self._is_canceled = True
Before the loop starts I start the CancelHandler
thread and on each iteration the run_set
action is set, and after - cleared to let the main thread communicate. In between that at different stages I check if cancel_set
has been set in case a 'cancel' request has arrived.
All of the above seems to work fine except for one thing: nuke.execute()
does absolutely nothing - the render just continues. I have tried putting it in the nuke.addBeforeFrameRender
and nuke.addAfterFrameRender
callbacks but that does nothing. Also I thought it might be executing in the wrong thread so I tried nuke.executeInMainThread(nuke.cancel)
with no luck.
The documentation reads:
execute(nameOrNode, start, end, incr, views, continueOnError= False) ... If Nuke is run with the GUI up, this will pop up a progress meter. If the user hits the cancel button this command will return 'cancelled' error. If Nuke is run from the nuke command line (ie nuke was started with the -t switch) execute() prints a text percentage as it progresses. If the user types ^C it will aborting the execute() and return a 'cancelled' error.
so I tried replacing nuke.cancel()
with os.kill(os.getpid(), signal.CTRL_C_EVENT)
That actually stopped the render but execute()
never returned so the script couldn't continue
Since execute()
should
return a 'cancelled' error
I tried to catch the nuke.CancelledError
exception both from the execute()
and the os.kill(os.getpid(), signal.CTRL_C_EVENT)
code but it wasn't thrown.
Curiously, when I ran this callback:
def _cancel_callback(self):
self._is_canceled = True
os.kill(os.getpid(), signal.CTRL_C_EVENT)
print "Cancelled"
the print
line got executed
I am fairly new to python so it might be a noob's mistake but does anyone know what I might be doing wrong or if it's a bug in the API? Or maybe someone cam come up with a better solution to the problem - that would be greatly appreciated!
So far the only way out of this situation I see is to simply shut down the worker process and then launch a new one, but that would be kind of an ugly thing to do each time someone wants to cancel a job.
Using Nuke 9.0v3 Full source code can be found at https://bitbucket.org/andrey_glebov/nukerendermanager/src in PYTHON directory