I am trying to building a very simple GUI for a stopwatch with PyQt5 for Python 3.8. I created a sort of finite state machine with 3 states: Running, Stop and Reset. When I start the clock the first time, it seems running correctly. Whenever I stop the timer (also for resetting), the new start of the clock seem to accelerate the clock. I don't understand why, neither how to debug what is happening or to solve the problem. Can you help me?
from PyQt5.QtCore import QSize, Qt, QTimer
from PyQt5.QtWidgets import *
# Only needed for access to command line arguments
import sys
import time
from enum import Enum
class ClockState(Enum):
STOP = 0
RUNNING = 1
RESET = 2
def __str__(self):
return self.name
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Clock GUI')
# Defining the size:
self.setFixedSize(QSize(600, 400))
self.State = ClockState.STOP
self.millisecond = 0
self.second = 0
self.minute = 0
self.layout = QVBoxLayout()
self.timerlayout = QHBoxLayout()
self.buttonlayout = QHBoxLayout()
self.timer = QTimer()
self.timer.setInterval(100)
self.timer.setSingleShot(False)
self.numberMS = QLCDNumber()
self.numberMS.setNumDigits(3)
self.numberS = QLCDNumber()
self.numberS.setNumDigits(2)
self.numberM = QLCDNumber()
self.numberM.setNumDigits(2)
# Inizializzo a zero cosa i timer e con il numero di cifre che voglio
self.numberMS.display(f'{self.millisecond:03}')
self.numberS.display(f'{self.second:02}')
self.numberM.display(f'{self.minute:02}')
self.timerlayout.addWidget(self.numberM)
self.timerlayout.addWidget(self.numberS)
self.timerlayout.addWidget(self.numberMS)
self.start_button = QPushButton('Start')
self.reset_button = QPushButton('Reset')
self.stop_button = QPushButton('Stop')
# self.start_button.setCheckable(True)
# self.reset_button.setCheckable(False)
# self.stop_button.setCheckable(True)
self.buttonlayout.addWidget(self.start_button)
self.buttonlayout.addWidget(self.reset_button)
self.buttonlayout.addWidget(self.stop_button)
# self.button_checked = True
self.start_button.clicked.connect(self.running_timer)
self.reset_button.clicked.connect(self.reset_timer)
self.stop_button.clicked.connect(self.stop_timer)
self.layout.addLayout(self.timerlayout)
self.layout.addLayout(self.buttonlayout)
self.container = QWidget()
self.container.setLayout(self.layout)
self.setCentralWidget(self.container)
def update_time(self):
self.millisecond += 100
if self.millisecond > 900:
self.millisecond = 0
self.second += 1
if self.second > 59:
self.millisecond = 0
self.second = 0
self.minute += 1
self.numberMS.display(f'{self.millisecond:03}')
self.numberS.display(f'{self.second:02}')
self.numberM.display(f'{self.minute:02}')
def running_timer(self):
self.State = ClockState.RUNNING
self.start_button.setEnabled(False)
self.reset_button.setEnabled(False)
self.stop_button.setEnabled(True)
print(f"State: {self.State}")
if self.State == ClockState.RUNNING:
self.timer.setInterval(100)
self.timer.start()
self.timer.timeout.connect(self.update_time)
else:
print(f"Checking if wrong state happen: {self.State}")
# print(f"Final time: {self.minute:02}:{self.second:02}.{self.millisecond:03}")
# self.timer.stop()
# self.millisecond = 0
# self.second = 0
# self.minute = 0
def reset_timer(self):
if self.State == ClockState.STOP:
self.State = ClockState.RESET
print(f"Final time: {self.minute:02}:{self.second:02}.{self.millisecond:03}")
self.timer.stop()
print(f'State: {self.State} -- {self.timer.isActive()}')
self.millisecond = 0
self.second = 0
self.minute = 0
self.numberMS.display(f'{self.millisecond:03}')
self.numberS.display(f'{self.second:02}')
self.numberM.display(f'{self.minute:02}')
def stop_timer(self):
self.State = ClockState.STOP
self.stop_button.setEnabled(False)
self.start_button.setEnabled(True)
self.reset_button.setEnabled(True)
self.timer.stop()
print(f'State: {self.State}')
print(f"Actual time: {self.minute:02}:{self.second:02}.{self.millisecond:03}")
def main():
# You need one (and only one) QApplication instance per application.
# Pass in sys.argv to allow command line arguments for your app.
# If you know you won't use command line arguments QApplication([]) works too.
app = QApplication(sys.argv)
# Create a Qt widget, which will be our window.
window = MainWindow()
window.show() # IMPORTANT!!!!! Windows are hidden by default.
# Start the event loop.
app.exec_()
if __name__ == '__main__':
main()
I thought that the problem was in interval setting. So I set again the timer interval before start the clock. But nothing changed.
You connect the timer slot with every call of def running_timer()
.
This means that the slot is called up several times for each cycle.
You should connect the slot only in constructor or use disconnect .