A common issue for folks trying to do expensive work at Maya startup is how to do background work properly. Maya is very finicky about work done outside the main thread – even seemingly innocent calls like print()
can be problematic when they issue from another thread, and touching the UI or the scene from another thread is almost guaranteed to cause problems.
Current Python provides some nice ways to wrap this up more neatly than the traditional method of creating a thread and polling an output queue. This is an example of a simple way to run jobs outside the main Maya thread and get thread-safe callback at the end without much extra work:
import concurrent.futures
import maya.utils
import contextlib
import maya.cmds as cmds
class DeferredJob (object):
def __init__(self, max_workers = 4):
self.exect = concurrent.futures.ThreadPoolExecutor(max_workers)
self.futures = []
def add(self, job, callback = None):
job_future = self.exect.submit(job)
if callback:
def maya_cb(*_):
maya.utils.executeDeferred(callback, job_future.result())
job_future.add_done_callback(maya_cb)
self.futures.append(job_future)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.exect.shutdown(False)
def results(self):
return tuple(self.futures)
This creates a context manager which will run all submitted jobs as parallel threads. If you need safe notifications about a completed job, add a callback to the “add” call. That callback will fire on the main thread when the job completes with the future
object representing the job as an argument. You can poll the result()
of the future or check to see what errors it might have generated.
Simple example:
with DeferredJob() as test:
# you would not want to do this in regular threads - it would lock up your maya !
def example():
time.sleep(1)
return cmds.polyCube()
def example2():
time.sleep(2)
return cmds.polySphere()
test.add(example, print )
test.add(example2, print)
# after a second:
# ['pCube2', 'polyCube2']
# another second later
# ['pSphere2', 'polySphere2']