I tend to find when someone is new to qt to they block the ui alot espcially in maya.
Here is some examples of decorator you could write to save you some headaches in the future.
__all__ = ("on_qthread", "throttle", "single_shot")
from Qt import QtCore
from functools import wraps
def on_qthread(*decorator_args, signal=None):
"""
Wrap a function so it runs as a QRunnable in QThreadPool.
Optionally emit a signal with the result.
Usage:
@on_qthread
def foo(...): ...
@on_qthread(signal=my_signal)
def bar(...): ...
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
runnable = _Runnable(func, signal, *args, **kwargs)
QtCore.QThreadPool.globalInstance().start(runnable)
return runnable
return wrapper
# If used without () => @on_qthread
if len(decorator_args) == 1 and callable(decorator_args[0]):
return decorator(decorator_args[0])
return decorator
class _Runnable(QtCore.QRunnable):
def __init__(self, func, signal, *args, **kwargs):
super().__init__()
self.func = func
self.signal = signal
self.args = args
self.kwargs = kwargs
def run(self):
result = self.func(*self.args, **self.kwargs)
if self.signal:
# emit() if it's a Qt Signal, call() if it's a Python callable
if hasattr(self.signal, "emit"):
self.signal.emit(result)
else:
self.signal(result)
def throttle(ms=100):
"""Throttle calls to max once every `ms` milliseconds.
Usage:
@throttle(100)
def my_function(...):
...
"""
def decorator(func):
last_call = 0
@wraps(func)
def wrapper(*args, **kwargs):
nonlocal last_call
now = QtCore.QTime.currentTime().msecsSinceStartOfDay()
if now - last_call > ms:
last_call = now
return func(*args, **kwargs)
return wrapper
return decorator
def single_shot(ms):
"""Call function once after `ms` milliseconds.
Usage:
@single_shot(1000)
def my_function(...):
...
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
QtCore.QTimer.singleShot(ms, lambda: func(*args, **kwargs))
return wrapper
return decorator