Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I kill a single shot QtCore.QTimer in PyQt4?

So, in my application, I create a single QtCore.QTimer object and then call the singleShot method on it to evoke a function after say 60 secs. Now, at any given point in time, if I would need to call the singleShot method on it again and prevent the previous singleShot method from taking effect (that is preventing it from calling the caller passed to it, if the second time the singleShot is invoked before the first 60 seconds), what do I need to do? How can I 'kill' the previous QTimer and completely forget about it and work only with the current QTimer?

Can someone help me out with this?

Here's just a sample code:

def main():
    q = QtCore.QTimer()
    q.singleShot(4000, print_hello)
    q.killTimer(id)     ##how can I get the value of 'id' so that print_hello() is not called before the end of the 4 seconds?

def print_hello():
    print 'hello'

Thanks

like image 437
gravetii Avatar asked Jan 12 '14 20:01

gravetii


3 Answers

The problem is that QTimer.singleShot() does not return a reference to the QTimer. I don't know of anyway to get the Timer ID so you can kill it using that method. However, you can instantiate a normal QTimer and make it a single-shot timer (this is not what you have done in your code provided, calling singleShot on an instance of QTimer creates a new QTimer which you do not have access to.)

However, all is not lost. You can create a normal QTimer and convert it to a single shot timer using setSingleShot(True). This allows you to call the stop() method if you wish to abort the timer. See the code example below which does what you require, on a 3 second timeout. You can push the button as many times as you like in rapid succession, and it will print "hello" once, 3 seconds after you stop. If you push it once, wait 4 seconds, and push it again, it will of course print out twice!

Hope that helps!

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *


class MyApp(QWidget):
    def __init__(self,*args,**kwargs):
        QWidget.__init__(self,*args,**kwargs)
        self.current_timer = None
        self.layout = QVBoxLayout(self)
        self.button = QPushButton('start timer')
        self.button.clicked.connect(self.start_timer)
        self.layout.addWidget(self.button)

    def start_timer(self):
        if self.current_timer:
            self.current_timer.stop()
            self.current_timer.deleteLater()
        self.current_timer = QTimer()
        self.current_timer.timeout.connect(self.print_hello)
        self.current_timer.setSingleShot(True)
        self.current_timer.start(3000)

    def print_hello(self):
        print 'hello'


# Create QApplication and QWidget
qapp = QApplication(sys.argv)  
app = MyApp()
app.show()
qapp.exec_()
like image 163
three_pineapples Avatar answered Oct 31 '22 06:10

three_pineapples


Building on @three_pineapples's answer, and simplifying the code some.
There is no need to create a new QTimer on every button click, you only need to call .start() on the existing timer, and it will be stopped and started again.

See the PyQt4 documentation for QTimer.start().

import sys

from PyQt4.QtCore import QTimer
from PyQt4.QtGui import (
    QApplication,
    QWidget,
    QVBoxLayout,
    QPushButton,
)

class MyApp(QWidget):
    def __init__(self,*args,**kwargs):
        QWidget.__init__(self,*args,**kwargs)

        self.layout = QVBoxLayout(self)

        self.button = QPushButton('Start timer')
        self.button.clicked.connect(self.start_timer)

        self.layout.addWidget(self.button)

        self.timer = QTimer()
        self.timer.timeout.connect(self.hello)
        self.timer.setSingleShot(True)

    def start_timer(self):
        self.timer.start(3000)

    def hello(self):
        print('Hello!')

# Create QApplication and QWidget
qapp = QApplication(sys.argv)  
app = MyApp()
app.show()
qapp.exec_()
like image 4
Oddstr13 Avatar answered Oct 31 '22 08:10

Oddstr13


If the creation of a second QTimer is entirely necessary then your approach is decent enough. What you could do it create a function or class that would such bookkeeping.

You could use the QBasicTimer. The doc:

The QTimer class provides a high-level programming interface with single-shot timers and timer signals instead of events. There is also a QBasicTimer class that is more lightweight than QTimer and less clumsy than using timer IDs directly.

If you want to get rid of the current_timer entirely then I would suggest you call the current_timer.deleteLater function. Make sure you assign a new QTimer to it as soon as you call this function or del/delete it by del current_timer or else if you reference any it's attributes later it will raise an error saying something like C++ object not found.

like image 1
Bleeding Fingers Avatar answered Oct 31 '22 07:10

Bleeding Fingers