Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cancel all outstanding QTimer events

Tags:

qt

pyqt

I am looking for a way to setup a number of timed function calls in pyqt, with a way to cancel all pending calls when needed.

I was looking at using QtCore.QTimer to setup the function calls, but I don't know of any good way to cancel these calls. Any ideas?



I can't seem to find any way to cancel after using QtCore.QTimer.singleShot(1000, self.function). If instead I create a list of QTimer objects I can stop them, but then I am forced to manage a list of timer objects (creation, deletion, which are active, etc.), which I would like to avoid.

# Setup a timer object.
timer = QtCore.QTimer(self)
timer.timeout.connect(self.function)
timer.setSingleShot(True)
timer.start(1000)

# To stop the timer object at some later point.
timer.stop()

I could also manage my own queue of pending function calls, which I would also like to avoid if possible (for the sake of simplicity).



Here is some dummy code showing the kind of thing I am trying to do:

import sys
from PyQt4 import QtCore

class Test(QtCore.QObject):

    def __init__(self, parent=None):
        QtCore.QObject.__init__(self, parent)
        

    def addDelayedCall(self, time, function):
        # Do something here.
        pass


    def clearPendingCalls(self):
        print('  Clearing pending function calls.')
        # Do something here.


    def setupCalls(self):
        self.addDelayedCall(500, self.dummy)
        self.addDelayedCall(1000, self.dummy)
        self.addDelayedCall(1500, self.dummy)
        self.addDelayedCall(2000, self.dummy)


    def dummy(self):
        print('dummy just got called.')


if __name__ == '__main__':
    app = QtCore.QCoreApplication(sys.argv)
    test = Test(app)

    QtCore.QTimer.singleShot(0, test.setupCalls)
    QtCore.QTimer.singleShot(1250, test.clearPendingCalls)
                   
    QtCore.QTimer.singleShot(5000, app.quit)            
    sys.exit(app.exec_())
like image 253
amicitas Avatar asked Feb 21 '11 21:02

amicitas


2 Answers

I have come up with a solution by directly using the timer functionality provided by QObject. I suspect that there is a more elegant solution, but this will probably work for what I need.

import sys
from PyQt4 import QtCore


class Test(QtCore.QObject):

    def __init__(self, parent=None):
        QtCore.QObject.__init__(self, parent)

        self.timers = {}


    def timerEvent(self, event):
        function = self.timers.pop(event.timerId())
        self.killTimer(event.timerId())
        function()


    def addDelayedCall(self, time, function):
        timer_id = self.startTimer(time)

        self.timers[timer_id] = function


    def clearPendingCalls(self):
        print('  Clearing pending function calls.')

        while self.timers:
            timer_id, function = self.timers.popitem()
            self.killTimer(timer_id)


    def setupCalls(self):
        self.addDelayedCall(500, self.dummy)
        self.addDelayedCall(1000, self.dummy)
        self.addDelayedCall(1500, self.dummy)
        self.addDelayedCall(2000, self.dummy)


    def dummy(self):
        print('dummy just got called.')




if __name__ == '__main__':
    app = QtCore.QCoreApplication(sys.argv)
    test = Test(app)

    QtCore.QTimer.singleShot(0, test.setupCalls)
    QtCore.QTimer.singleShot(1250, test.clearPendingCalls)

    QtCore.QTimer.singleShot(5000, app.quit)            
    sys.exit(app.exec_())
like image 178
amicitas Avatar answered Nov 06 '22 05:11

amicitas


you can disconnect from the timer's timeout signal and connect back whenever you need. Once disconnected you stop receiving any calls from the timer even though it still remains active. Below is a small example:

import sys
from PyQt4 import QtGui, QtCore

class MainForm(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)

        self.button = QtGui.QPushButton("button", self)       
        self.button.resize(100, 30)

        self.connect(self.button, QtCore.SIGNAL('clicked()'), self.on_button_click)

        self.connected = True
        self.timer = QtCore.QTimer(self)
        self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.on_timeout)
        self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.on_timeout_test)
        self.timer.start(1000)

    def on_button_click(self):
        if self.connected:
            self.disconnect(self.timer, QtCore.SIGNAL('timeout()'), self.on_timeout_test)
        else:
            self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.on_timeout_test)
        self.connected = not self.connected

    def on_timeout(self):
        print 'on_timeout'

    def on_timeout_test(self):
        print 'on_timeout_test'

def main():
    app = QtGui.QApplication(sys.argv)
    form = MainForm()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()

once timer started both slots on_timeout and on_timeout_test are getting called, when you click the button on_timeout_test slot gets disconnected and then connected back if button is clicked again.

hope this helps, regards

like image 36
serge_gubenko Avatar answered Nov 06 '22 07:11

serge_gubenko