Passing thread to threaded object
Question: Quick question on the use of `QThread` in PyQt4 and Python 2.7. I am creating
a process inherited from `QObject`, and assigning this to a `Qthread` I have
created in a separate class (also inherited from `QObject`).

Is it safe to pass the `QThread` object to the process object, so that I can
call `thread.msleep(mseconds)` from within the process itself?

I want to be able to make the thread wait or sleep, but I have read that
time.sleep(seconds) is dodgy when used with PyQt multi-threading.

I did try to send a signal from the process object to a slot in the main
thread (attached to `thread.msleep(mseconds)` for that process object), but I
found that this failed to work; the process object continued executing until
complete, with the slot only being executed after this time. Even after
adjusting priorities, this continued to happen. This is unacceptable since I
want the process loop to run continuously.

Any other recommendations?


Answer: I eventually managed to alter my code to achieve the functionality that I
required in my question: namely the ability to make a thread wait or sleep for
a specified amount of time.

Firstly, my research seems to show that one of the main reasons subclassing
`QThread` became ill-advised in Qt was that a thread should not be able to
manage itself. Though there is no official documentation on my question, I can
only surmise that passing the thread object to the process object running on
it would also be ill-advised, because the thread would again be able to
control itself directly.

The solution I have found is to dispense with `msleep()` altogether. Qt
documentation on QThread recommends that `sleep()` and `wait()` functions are
avoided because they do not fit well with the event driven nature of Qt. They
recommend that `QTimer()` is used to call a function via a signal after it
times out, in place of `msleep()`. By default `QTimer()` is used to send a
repeating signal every time interval, but can also send a signal once using
`QTimer.singleShot()`. It is also stated in the documentation that it is safe
to call `QSleep()` from within a thread.

I only use a repeating `QTimer` to call a single slot `foo()` multiple times,
but to add a delay within `foo()`, `QTimer.singleShot()` could be used to call
a second function `moo()` after a set number of milliseconds.

EDIT: I have decided to include my threading code, which subclasses QObject
and QThread to perform a task on a thread in a continual loop every given time
interval. It is, as far as I can tell, fully functional, though could do with
a little work.

    
    
    # -*- coding: utf-8 -*-
    import sys
    from PyQt4 import QtCore, QtGui
    
    # Class to be assigned to a thread.  
    # This should be subclassed to provide new functionality.
    class GenericLoop(QtCore.QObject):
        def __init__(self):
            super(GenericLoop, self).__init__()
    
        # We use this signal to tell the main thread 
        # when this thread is finished.
        finished_Sig = QtCore.pyqtSignal()
    
        # Default timeout is 0, i.e. do work on thread after 
        # other events have been dealt with
        __timeout = 0
        __processTimer = None
        __args = None
        __kwargs = None
    
        # We use this function to set the arguments used by run(),
        # if we want to change them mid-execution
        @QtCore.pyqtSlot(tuple, dict)
        def changeArgs(self, args, kwargs):
            self.__args = args
            self.__kwargs = kwargs
    
        # We can change the timeout used to make the thread run 
        # at given intervals.  Note that the timing is not exact, 
        # since this is impossible with a real time operating system
        @QtCore.pyqtSlot(int)
        def setTimeout(self, mseconds): 
            self.__timeout = int(mseconds)
    
        # Call either a singleShot QTimer (one execution), 
        # or a normal QTimer (repeated), to start the loop
        @QtCore.pyqtSlot(bool, tuple, dict)
        def startTimer(self, singleShot, args, kwargs):
            self.__processTimer = QtCore.QTimer()
            # We can't pass args and kwargs directly because QTimer.timeout 
            # emits a signal with allowing no contained variables
            # so we copy args and kwargs to local variables instead
            self.changeArgs(args, kwargs)
            if singleShot:
                self.__processTimer.singleShot(self.__timeout, self.callRun)
            else:
                self.__processTimer.timeout.connect(self.callRun)
                self.__processTimer.start(self.__timeout)
    
        # Call finish from within subclass using self.finish(), or 
        # from another thread using signals.  finish() will stop the 
        # QTimer causing execution of the loop.  The loop can be started again
        # by calling startTimer() or stopTimer() from another thread
        @QtCore.pyqtSlot()
        def stopTimer(self):
            if self.__processTimer.isActive():
                self.__processTimer.stop()
            else:
                print "ERROR: stopTimer() has been called but no timer is running!"
    
        # We call this to delete the thread.
        @QtCore.pyqtSlot()
        def deleteThread(self):
            self.finished_Sig.emit()
    
        # This calls run(), in order to enable the passing of 
        # command line arguments to the loop
        @QtCore.pyqtSlot()
        def callRun(self):
            self.run(self.__args, self.__kwargs)
    
        # run() can be called directly from another thread if required
        @QtCore.pyqtSlot(tuple, dict)
        def run(self, args, kwargs):
            print "ERROR: run() has not been defined!  Stopping thread..."
            self.stopTimer()
    
    # Class for creating threads
    class GenericThread(QtCore.QObject):
    
        # Private variables include the thread.  
        __sendArguments_Sig = QtCore.pyqtSignal(tuple, dict)
        __startTimer_Sig = QtCore.pyqtSignal(int, tuple, dict)
        __setTimeout_Sig = QtCore.pyqtSignal(int)
        __obj = None
        __finished_Sig = None
        __thread = QtCore.QThread()
    
        # Object to be threaded must be specified when 
        # creating a GenericThread object
        def __init__(self, obj): 
            super(GenericThread, self).__init__()
            self.__obj = obj
            self.moreInit()
    
        # Set up object on thread
        def moreInit(self):
            self.__thread = QtCore.QThread()
            self.__obj.moveToThread(self.__thread)
    
            # Allows thread to delete itself when done
            self.__obj.finished_Sig.connect(self.__thread.deleteLater)
    
            self.__sendArguments_Sig.connect(self.__obj.changeArgs)
            self.__startTimer_Sig.connect(self.__obj.startTimer)
            self.__setTimeout_Sig.connect(self.__obj.setTimeout)
            self.__thread.start()
    
        # Sets the QTimer timeout and does some checking 
        # to make sure that types are as they should be
        def setTimeout(self, mseconds):
            if mseconds >= 0 and type(mseconds) is type(int()):
                self.__setTimeout_Sig.emit(mseconds)
            elif mseconds < 0 and type(mseconds) is type(int()):
                print "Error: timeout of below 0 ms specified."
            else:
                print "Error: timeout period is specified with a type other than int."
    
        # Starts a function in the thread via signals, and can pass 
        # it arguments if required. Function executes until QTimer is stopped
        def startLoop(self, *args, **kwargs):
            if (self.__thread == None):
                print "ERROR: Thread has been deleted!"
            else:
                self.__startTimer_Sig.emit(False, args, kwargs)
    
        # Starts a function in the thread via signals, once
        def startOnce(self, *args, **kwargs):
            if (self.__thread == None):
                print "ERROR: Thread has been deleted!"
            else:
                self.__startTimer_Sig.emit(True, args, kwargs)
    
    # Calls a very simple GUI just to show that the program is responsive
    class GUIBox(QtGui.QWidget):
    
        def __init__(self):
            super(GUIBox, self).__init__()
    
            self.initUI()
    
        def initUI(self):
    
            self.resize(250, 150)
    
            self.setWindowTitle('Threading!')
            self.show()
    
    # Subclass GenericLoop to reimplement run and such.
    class SubClassedLoop(GenericLoop):
        def __init__(self):
            super(SubClassedLoop, self).__init__()
    
        __i = 0
    
        @QtCore.pyqtSlot(tuple, dict)
        def run(self, args, kwargs):
            if self.__i>=50:
                self.stopTimer()
                return
            print self.__i, args
            self.__i += 1
    
    app = QtGui.QApplication(sys.argv)
    
    ex = GUIBox()
    
    # Create 3 worker objects to do the actual calculation
    worker1 = SubClassedLoop()
    worker2 = SubClassedLoop()
    worker3 = SubClassedLoop()
    
    # Create 3 thread managing objects to do the thread control
    thread1 = GenericThread(worker1)
    thread2 = GenericThread(worker2)
    thread3 = GenericThread(worker3)
    
    # Set the threads to execute as soon as there is no work to do
    thread1.setTimeout(125)
    thread2.setTimeout(125)
    thread3.setTimeout(125)
    
    # Start threads
    thread1.startLoop(1)
    thread2.startLoop(2)
    thread3.startLoop(3)
    
    # Quit the program when the GUI window is closed
    sys.exit( app.exec_() )
    

