Source code for ilastik.utility.gui.threadRouter
###############################################################################
# ilastik: interactive learning and segmentation toolkit
#
# Copyright (C) 2011-2014, the ilastik developers
# <team@ilastik.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# In addition, as a special exception, the copyright holders of
# ilastik give you permission to combine ilastik with applets,
# workflows and plugins which are not covered under the GNU
# General Public License.
#
# See the LICENSE file for details. License information is also available
# on the ilastik web site at:
# http://ilastik.org/license.html
###############################################################################
import threading
from functools import partial, wraps
from PyQt4.QtCore import QObject, pyqtSignal, Qt
[docs]class ThreadRouter(QObject):
"""
Create an instance of this class called 'threadRouter' to enable the :py:func:`@threadRouted<threadRouted>` decorator for methods of your object.
"""
routeToParent = pyqtSignal(object)
# The main window of the app should set this to True when the
# app is shutting down and thus no gui events should be processed any more.
app_is_shutting_down = False
[docs] def __init__(self, parent):
"""
Construct a threadRouter object. You must call it ``self.threadRouter``.
:param parent: The parent object, which whose thread will be used for all :py:func:`@threadRouted<threadRouted>` functions.
"""
assert parent is not None, "Can't use ThreadRouter without a parent QObject."
QObject.__init__(self, parent=parent)
self.ident = threading.current_thread().ident
self.routeToParent.connect( self.handleRoutedFunc, Qt.BlockingQueuedConnection )
def handleRoutedFunc(self, f):
return f()
def threadRoutedWithRouter(threadRouter):
def _threadRouted(func):
"""
Decorator that routes calls to the given member function into the object's parent thread.
If a member function ``f`` is decorated with ``@threadRouted``, all calls to ``f`` will execute in the GUI thread.
The calling thread will block while ``f`` is executing, so the call will appear synchronous.
This mechanism is slow, and should only be used for functions that MUST execute in the GUI thread.
.. note:: Objects that use the @threadRouted decorator MUST have a :py:class:`ThreadRouter` member called ``self.threadRouter``
"""
@wraps(func)
def routed(*args, **kwargs):
if ThreadRouter.app_is_shutting_down:
return
router = threadRouter
if router is None:
assert len(args) > 0
obj = args[0]
assert isinstance(obj, QObject)
assert hasattr(obj, 'threadRouter'), \
"Can't use the @threadRouted decorator unless your object has a member called self.threadRouter"
router = obj.threadRouter
# If we're already in the parent thread, then we can call the function directly
if router.ident == threading.current_thread().ident:
val = func(*args, **kwargs)
# We rely on Qt signals (below) so it is an error to
# use @threadRouted with a function that gives a return value
assert val is None, "Can't return a value from an @threadRouted function."
# Otherwise, we rely on the Qt BlockingQueuedConnection
# signal behavior to transfer the call to the parent thread.
else:
router.routeToParent.emit( partial(func, *args, **kwargs) )
routed.__wrapped__ = func # Emulate python 3 behavior of @wraps
return routed
return _threadRouted
threadRouted = threadRoutedWithRouter(None)
if __name__ == "__main__":
import time
from PyQt4.QtGui import QApplication
class TestObject(QObject):
def __init__(self):
QObject.__init__(self)
self.threadRouter = ThreadRouter(self)
@threadRouted
def printThreadId(self):
time.sleep(0.5)
print "thread id = ", threading.current_thread().ident
app = QApplication([])
o = TestObject()
def test():
print "thread id = ", threading.current_thread().ident
o.printThreadId()
print "Finished."
th = threading.Thread(target=test)
th.start()
app.exec_()