James Mackenzie
James Mackenzie

Reputation: 88

Timer in a Child Thread in PySide

First off, I'm very new to Python and Pyside. In order to do a bit of self-improvement, I'm trying to get a QTimer to execute every second in a child thread of my PySide program (at the moment I just want it to print "hi!" to a terminal every second without freezing the main window).

I tried converting the example I found on the Qt Wiki from C++ to Python/PySide, but since I don't really know C++ I assume I converted it incorrectly and that's why it's not working properly.

At the moment, the doWork() function only seems to execute once and then never again. What am I doing wrong? Is there a better way to execute a function every second in PySide without freezing the main window?

Here's the code (I have removed some main window code to increase clarity):

from PySide import QtGui
from PySide import QtCore
from client_gui import Ui_MainWindow

statsThread = QtCore.QThread()

class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        #setup GUI
        self.setupUi(self)
        #start thread to update GUI
        self.statsThread = updateStatsThread()
        self.statsThread.start(QtCore.QThread.TimeCriticalPriority)

class updateGuiWithStats(QtCore.QObject):
    def Worker(self):
        timer = QtCore.QTimer()
        timer.timeout.connect(self.doWork())
        timer.start(1000)

    def doWork(self):
        print "hi!"

class updateStatsThread (QtCore.QThread):
    def run(self):
        updater = updateGuiWithStats()
        updater.Worker()
        self.exec_()

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    frame = MainWindow()
    frame.show()
    sys.exit(app.exec_())

Upvotes: 3

Views: 4776

Answers (2)

jdi
jdi

Reputation: 92559

@Masci already pointed out the fix you needed for your timer.timeout.connect, but I see more issues than just that.

No need to create a global QThread that is never used:

statsThread = QtCore.QThread()

Your QTimer is being garbage collected right away because its created without a parent, and you aren't saving it within your class. This is why even after you fix your timer connection, it will still not work... Try:

class UpdateGuiWithStats(QtCore.QObject):

    def startWorker(self):
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.doWork)
        self.timer.start(1000)

Also, use UpperCase for the first letter of classes, and camelCase for methods. You are doing a mixture of both ways.

A couple of notes based on that link you provided, your example, and other comments on here... You can use just a QTimer as a solution if your doWork() is very light and will not block your main event loop with a bunch of data crunching, sleeping, etc. If it does, then doWork() will need to be moved to a QThread, as your example is doing. But at that point it is somewhat unnecessary to use an event loop, and a QTimer in a separate class that calls its own work. This all could be consolidated into a single class, something like:

class UpdateStatsThread(QtCore.QThread):

    def __init__(self, parent=None):
        super(UpdateStatsThread, self).__init__(parent)
        self._running = False

    def run(self):
        self._running = True
        while self._running:
            self.doWork()
            self.msleep(1000)

    def stop(self, wait=False):
        self._running = False
        if wait:
            self.wait()

    def doWork(self):
        print "hi!"

Upvotes: 4

Masci
Masci

Reputation: 6074

in updateGuiWithStats class, Worker method:

timer.timeout.connect(self.doWork())

should be

timer.timeout.connect(self.doWork)

You are connecting timeout signal to None (the return value of doWork() method), and I think this is why it is executed only once: doWork is called during the connection and nomore. When you make connections, remember to connect the function name (in Pythonics words, the callable object) and not the function call.

By the way, even if the above solved your problem, you should avoid using threads since QTimer already does by its own you need. In the docs you linked, the first answer to the When shouldn’t I use threads? question is: Timers.

Upvotes: 1

Related Questions