Search code examples
pythonpysideqthreadqtimer

QTimer and QThread odd behavior


from PySide.QtGui import *
from PySide.QtCore import *
import sys
from time import sleep

class MyWorkerThread(QThread):
    def __init__(self, parent=None):
        super(MyWorkerThread, self).__init__(parent)

    def run(self):
        timer1 = QTimer()
        timer1.singleShot(1000, self.alarm_goes1)
        print "Timer 1 Start"
        timer1.start()

    def alarm_goes1(self):
        print "goes off 1"


class MainFrame(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.thread = MyWorkerThread(self)
        print "Thread Start"
        self.thread.start()

        timer2 = QTimer()
        timer2.singleShot(1000, self.alarm_goes2)
        print "Timer 2 Start"
        timer2.start()

    def alarm_goes2(self):
        print 'goes off 2'

if __name__ == "__main__":
    app = QApplication(sys.argv)
    main = MainFrame()
    main.show()
    sys.exit(app.exec_())

Output:

Thread Start
Timer 2 Start
Timer 1 Start
goes off 2

Please, explain why alarm_goes1 doesn't execute. And how can I make it do it?


EDIT:

def run(self):
    timer1 = QTimer()
    timer1.singleShot(2000, self.alarm_goes1)
    print "Timer 1 Start"
    timer1.start()
    self.exec_()
    self._alive = True
    while self._alive:
        print 'this part will not execute'
        self.sleep(1)

With the self.exec_() both timers will work fine, but the part inside while loop won't. Without the self.exec_(), just one of the timers work, but the while loop will work.

I want both the timers and the while loop working together.

I tried moving the self.exec_() inside the while loop, but it will only run once.


Solution

  • The timer posts a QTimerEvent, but there is no event-loop running in the thread to process it.

    So you need to do something like this:

        def run(self):
            timer1 = QTimer()
            timer1.singleShot(1000, self.alarm_goes1)
            print "Timer 1 Start"
            timer1.start()
            # start the clock
            clock = QTimer()
            clock.start(1000)
            clock.timeout.connect(self.tick)
            # start the thread's event loop
            self.exec_()
    
        def tick(self):
            print 'tick'
    

    and it would probably be wise to add this to the MainFrame class:

        def closeEvent(self, event):
            # exit the thread's event loop
            self.thread.quit()