I\'m writing a simple stopwatch with python (using pyqt5). So far, start/pause/resume functions are working fine but the problem is when I stop the counter and need to start fro
Do not use sleep in a GUI because you can freeze the GUI. On the other hand using a thread is too much for this question, in a GUI the use of thread is only justified if the task is heavy, but increasing a pair of data every second is not a heavy task.
Instead, you must use QTimer to perform repetitive tasks and QTime to get the elapsed time.
For the logic a state machine must be implemented, in this case it will be StopWatch that will emit the necessary signals to notify the changes to the GUI.
from PyQt5 import QtCore, QtWidgets
from stpw_ui import Ui_MainWindow
class State:
STOPPED = 0
PAUSE = 1
RUNNING = 1 << 1
class StopWatch(QtCore.QObject, State):
State = State
QtCore.Q_ENUMS(State)
secondChanged = QtCore.pyqtSignal(int)
minuteChanged = QtCore.pyqtSignal(int)
stateChanged = QtCore.pyqtSignal(State)
def __init__(self, parent=None):
super(StopWatch, self).__init__(parent)
self._current_state = State.STOPPED
self._time = QtCore.QTime()
self._timer = QtCore.QTimer(self, interval=100, timeout=self.on_timeout)
self._delta = 0
self._seconds = 0
self._minutes = 0
def setCurrentState(self, state):
self._current_state = state
self.stateChanged.emit(state)
@QtCore.pyqtSlot()
def start(self):
self._delta = 0
self._timer.start()
self._time.start()
self.setCurrentState(State.RUNNING)
@QtCore.pyqtSlot()
def stop(self):
if self._current_state != State.STOPPED:
self._timer.stop()
self.setCurrentState(State.STOPPED)
@QtCore.pyqtSlot()
def pause(self):
if self._current_state == State.RUNNING:
self._timer.stop()
self.setCurrentState(State.PAUSE)
self._delta += self._time.elapsed()
@QtCore.pyqtSlot()
def resume(self):
if self._current_state == State.PAUSE:
self._timer.start()
self._time = QtCore.QTime()
self._time.start()
self.setCurrentState(State.RUNNING)
@QtCore.pyqtSlot()
def on_timeout(self):
t = QtCore.QTime.fromMSecsSinceStartOfDay(self._delta + self._time.elapsed())
s, m = t.second(), t.minute()
if self._seconds != s:
self._seconds = s
self.secondChanged.emit(s)
if self._minutes != m:
self._minutes = m
self.minuteChanged.emit(m)
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self._stop_watch = StopWatch(self)
self.start.clicked.connect(self._stop_watch.start)
self.pause.clicked.connect(self._stop_watch.pause)
self.stop.clicked.connect(self._stop_watch.stop)
self.resume.clicked.connect(self._stop_watch.resume)
self._stop_watch.secondChanged.connect(self.seconds.display)
self._stop_watch.minuteChanged.connect(self.minutes.display)
self._stop_watch.stateChanged.connect(self.on_stateChanged)
@QtCore.pyqtSlot(StopWatch.State)
def on_stateChanged(self, state):
if state == StopWatch.RUNNING:
self.stacked_buttons.setCurrentIndex(1)
elif state == StopWatch.PAUSE:
self.stacked_buttons.setCurrentIndex(2)
elif state == StopWatch.STOPPED:
self.stacked_buttons.setCurrentIndex(0)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())