Help with Qt and Threading!

I am working through some learning materials on Qt and Threading and I have hit a snag with using custom Slots and Signals to pass data between Threads.

Basically I have the main UI Thread and a Worker Thread that should emit periodically emit a signal that can be sent and displayed to the UI.

Here is my code so far:


import sys, time
from PySide import QtCore, QtGui


class LoggingThread(QtCore.QThread):
    
    logMessage = QtCore.Signal(str)
    
    def __init__(self, function, *args, **kwargs):
        QtCore.QThread.__init__(self)
        
        self.function = function
        self.args = args
        self.kwargs = kwargs
    
    def __del__(self):
        self.wait()
    
    def run(self):
        self.function(*self.args, **self.kwargs)
        return


class ThreadingApp(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        
        self.layout = QtGui.QVBoxLayout(self)
        self.startBttn = QtGui.QPushButton('Start!')
        self.startBttn.released.connect(self.startThreadedProcess)
        self.output = QtGui.QPlainTextEdit()
        
        self.layout.addWidget(self.startBttn)
        self.layout.addWidget(self.output)
        
        self.thread = LoggingThread(self.loggingMessages, 'hello', iters=30)
        self.thread.logMessage[str].connect(self.updateOutput)


    def startThreadedProcess(self):
        self.output.clear()
        self.thread.start()


    @QtCore.Slot()
    def updateOutput(self, message):
        print message
        self.output.appendPlainText(message)
    
    

    def loggingMessages(self, message, iters=10):
        self.emit(QtCore.SIGNAL('logMessage(str)'), 'test message')
        for i in range(iters):
            print i, message
            time.sleep(0.3)


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    win = ThreadingApp()
    win.show()
    app.exec_()


everything seems to be working ok, except that I don’t get any updates to my QPlainText Widget…? I can’t seem to get my function to emit the Signal and pass the Message along to the UI…

in the thread, you need to emit the signal:


class LoggingThread(QtCore.QThread):
    
    logMessage = QtCore.Signal(str)
    
    def __init__(self, function, *args, **kwargs):
        QtCore.QThread.__init__(self)
        
        self.function = function
        self.args = args
        self.kwargs = kwargs
    
    def __del__(self):
        self.wait()
    
    def run(self):
        while True:
            result = self.function(*self.args, **self.kwargs)
            self.logMessage.emit(result)
        return

This is assuming your function call returns a string

This person on Stack Overflow has the same problem, but there is no answer… :confused:

I see where I am going wrong, thanks!

is there a way to get the function I am passing to emit the signal as well? The goal is to have a flexible wrapper thread around a variety of functions and each of those functions can periodically emit data back the UI while they are running…

what if i passed the Thread Object to the Function?

you could make each of the functions a generator that yields messages


import time

def myfunc(count=10):
    for i in xrange(count):
        yield 'message for iter(%i)' % i
        time.sleep(1)

class LoggingThread(QtCore.QThread):
    
    logMessage = QtCore.Signal(str)
    
    def __init__(self, function, *args, **kwargs):
        QtCore.QThread.__init__(self)
        
        self.function = function
        self.args = args
        self.kwargs = kwargs
    
    def __del__(self):
        self.wait()
    
    def run(self):
        for msg in myfunc():
            self.logMessage.emit(msg)
        return
...

I wouldnt let the functions log to a thread, they shouldnt have to worry about it. They should just yield/return messages and the thread should emit them. Keep things separate and simple

ya know, I never really understood how to take advantange of yield until this example… you just opened my eyes to a whole new world! :slight_smile:
thanks!

“You have taken your first steps into a larger world…”