1

I have a simple GUI App, it's a timer with LCD display I have problem with delaying one function that's responsible for counting down the time it's should count down every second, but it's count the minute in one second, it for loop function here the demo of the project

from PySide2 import QtWidgets , QtCore
import sys
from clock_gui import Ui_clock # this is the GUI components

class clockApp(QtWidgets.QMainWindow, Ui_clock):
    def __init__(self):
        super(clockApp,self).__init__()
        self.setupUi(self) # it set up the UI from Ui_clock
        self.min = self.spinBox_min.value()
        self.sec = self.spinBox_sec.value()
        self.button.clicked.connect(self._buttonFunction) # the button 
        self.show() # render the GUI


    def _buttonFunction(self): # the function that counts down after the button clicked 
        self.total = (self.min *60) + self.sec # take minutes and seconds to one variable  
        if self.total >=1:
           for i in range(self.total):
               self.total -= 1            
               # here the problem I want the delay to start here
               # I can use time.sleep but I want to use Qtimer I try it in different ways, but didn't work there is no error but the Qtimer didn't delay anything 


               




app = QtWidgets.QApplication(sys.argv)
window  = clockApp()
app.exec_()

I try setInterval and I try singleShot but nothing works, maybe I use them wrong?

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
Mustafa
  • 198
  • 1
  • 7
  • Your question is unclear. Are you asking how to call `_buttonFunction()` *after* the timeout, or some other function? Also, right now `self.total` will always have the same value based on the default values of those spin boxes: declaring `self.min` and `self.sec` at startup doesn't make any sense, you have to check those values *when* the function is called: `self.total = self.spinBox_min.value() * 60 + self.spinBox_sec.value()` – musicamante Sep 14 '22 at 15:14

1 Answers1

2

Using a QTimer is the right approach, but you need to use the timeout signal instead of a for-loop (which will block GUI updates until it ends). It might also help to use a QTime object to count down the seconds, as this will make it easier to format the dsiplay for the LCD widget.

Below is a simple demo that shows one way to implement this. Hopefully you can see how to adapt this code to suit your own requirements:

screenshot

from PySide2 import QtCore, QtWidgets

class Window(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.timer = QtCore.QTimer(self)
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.handleTimeout)
        self.buttonStart = QtWidgets.QPushButton('Start')
        self.buttonStart.clicked.connect(self.handleStart)
        self.buttonStop = QtWidgets.QPushButton('Stop')
        self.buttonStop.clicked.connect(self.timer.stop)
        self.min = QtWidgets.QSpinBox()
        self.min.setRange(0, 10)
        self.sec = QtWidgets.QSpinBox()
        self.sec.setRange(0, 59)
        self.lcd = QtWidgets.QLCDNumber()
        layout = QtWidgets.QGridLayout(self)
        layout.addWidget(self.lcd, 0, 0, 1, 2)
        layout.addWidget(self.min, 1, 0)
        layout.addWidget(self.sec, 1, 1)
        layout.addWidget(self.buttonStart, 2, 0)
        layout.addWidget(self.buttonStop, 2, 1)
        self.time = QtCore.QTime(0, 0)
        self.handleTimeout()

    def handleStart(self):
        if not self.timer.isActive():
            self.time.setHMS(0, self.min.value(), self.sec.value())
            self.handleTimeout()
            self.timer.start()

    def handleTimeout(self):
        self.lcd.display(self.time.toString('m:ss'))
        if self.time.minute() or self.time.second():
            self.time = self.time.addSecs(-1)
        else:
            self.timer.stop()

if __name__ == '__main__':

    app = QtWidgets.QApplication(['Test'])
    window = Window()
    window.setGeometry(600, 100, 300, 200)
    window.show()
    app.exec_()
ekhumoro
  • 115,249
  • 20
  • 229
  • 336