Search code examples
pythonpython-2.7pyqtpyqt4

Timers in PyQt based GUI


I am facing a problem while running the timers simultaneously one by one.The next timer is running and previous one get stopped and after reaching the time the previous one starts. The problematic code is listed below.

import time
from PyQt4 import QtCore, QtGui
import sys


try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)



class Window(QtGui.QMainWindow):

    def __init__(self):

        super(Window, self).__init__()
        self.setGeometry(50, 50, 500, 300)
        self.setWindowTitle("PyQT tuts!")
        self.setWindowIcon(QtGui.QIcon('pythonlogo.png'))
        self.home()


    def home(self):

        self.btn = QtGui.QPushButton(self)
        self.btn.setObjectName(_fromUtf8("pb"))
        self.btn.clicked.connect(self.timer)
        self.btn.setText("Timer1")
        self.btn.resize(65,25)
        self.btn.move(100,100)

        self.btn2 = QtGui.QPushButton(self)
        self.btn2.setObjectName(_fromUtf8("pb2"))
        self.btn2.clicked.connect(self.timer2)
        self.btn2.setText("Timer2")
        self.btn2.resize(65,25)
        self.btn2.move(100,150)

        self.btn3 = QtGui.QPushButton(self)
        self.btn3.setObjectName(_fromUtf8("pb3"))
        self.btn3.clicked.connect(self.timer3)
        self.btn3.setText("Timer3")
        self.btn3.resize(65,25)
        self.btn3.move(100,200)

        self.btn4 = QtGui.QPushButton(self)
        self.btn4.setObjectName(_fromUtf8("pb4"))
        self.btn4.clicked.connect(self.timer4)
        self.btn4.setText("Timer4")
        self.btn4.resize(65,25)
        self.btn4.move(100,250)


        self.show()


    def timer(self):

        # uin = input("enter the time : ")

        when_to_stop = 10 
        # abs(int(uin))

        while when_to_stop > 0:
            m, s = divmod(when_to_stop, 60)
            h, m = divmod(m, 60)
            time_left = str(h).zfill(2) + ":" + str(m).zfill(2) + ":" + str(s).zfill(2)

            # print(time_left+'\r')
            time.sleep(1.0)
            when_to_stop -= 1
            self.btn.setText(str(time_left))
            QtGui.qApp.processEvents()


    def timer2(self):

        # uin = input("enter the time : ")

        when_to_stop = 10 
        # abs(int(uin))

        while when_to_stop > 0:
            m, s = divmod(when_to_stop, 60)
            h, m = divmod(m, 60)
            time_left = str(h).zfill(2) + ":" + str(m).zfill(2) + ":" + str(s).zfill(2)

            # print(time_left+'\r')
            time.sleep(1.0)
            when_to_stop -= 1
            self.btn2.setText(str(time_left))
            QtGui.qApp.processEvents()


    def timer3(self):

        # uin = input("enter the time : ")

        when_to_stop = 10
        # abs(int(uin))

        while when_to_stop > 0:
            m, s = divmod(when_to_stop, 60)
            h, m = divmod(m, 60)
            time_left = str(h).zfill(2) + ":" + str(m).zfill(2) + ":" + str(s).zfill(2)

            # print(time_left+'\r')
            time.sleep(1.0)
            when_to_stop -= 1
            self.btn3.setText(str(time_left))
            QtGui.qApp.processEvents()



     def timer4(self):

        # uin = input("enter the time : ")

        when_to_stop = 10 
        # abs(int(uin))

        while when_to_stop > 0:
            m, s = divmod(when_to_stop, 60)
            h, m = divmod(m, 60)
            time_left = str(h).zfill(2) + ":" + str(m).zfill(2) + ":" + str(s).zfill(2)

            # print(time_left+'\r')
            time.sleep(1.0)
            when_to_stop -= 1
            self.btn4.setText(str(time_left))
            QtGui.qApp.processEvents()




def run():


    app = QtGui.QApplication(sys.argv)
    GUI = Window()
    sys.exit(app.exec_())


run()

Solution

  • time.sleep() should not be used in the GUI because it will block the GUI eventloop causing problems like not updating the GUI, besides using input (), and consider bad practice calling processEvents().

    Qt offers QTimer which is a class specialized in calling a specific task every certain interval of time.

    In the next part I show an example:

    import sys
    from PyQt4 import QtCore, QtGui
    from functools import partial
    
    
    class Window(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(Window, self).__init__(parent)
            centralwidget = QtGui.QWidget()
            self.setCentralWidget(centralwidget)
            layout = QtGui.QVBoxLayout(centralwidget)
    
            for i in range(4):
                button = QtGui.QPushButton("Timer{}".format(i+1))
                layout.addWidget(button)
                button.clicked.connect(partial(self.onClicked, button))
    
        def onClicked(self, button):
            sec = 10 # seconds
            timer =  button.property("timer").toPyObject()
            if timer is None:
                timer = QtCore.QTimer(button)
            timer.stop()
            timer.setInterval(1000)
            timer.setProperty("button", button)
            timer.setProperty("endTime", QtCore.QTime.currentTime().addMSecs(sec*1000+10))
            timer.timeout.connect(partial(self.onTimeout, timer))
            self.onTimeout(timer)
            timer.start()
            button.setProperty("timer", timer)
    
        def onTimeout(self, timer):
            button= timer.property("button")
            if hasattr(button, 'toPyObject'):
                button = button.toPyObject()
            tm = timer.property("endTime").toPyObject()
            if hasattr(tm, 'toPyObject'):
                tm = tm.toPyObject()
            ms = QtCore.QTime.currentTime().msecsTo(tm)
            if ms < 0:
                timer.stop()
            else:
                button.setText(QtCore.QTime().addMSecs(ms).toString())
    
    
    if __name__ == '__main__':
        app = QtGui.QApplication(sys.argv)
        ex = Window()
        ex.show()
        sys.exit(app.exec_())