Make a simple stopwatch stoppable

后端 未结 1 604
野性不改
野性不改 2021-01-24 02:26

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

相关标签:
1条回答
  • 2021-01-24 02:45

    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_())
    
    0 讨论(0)
提交回复
热议问题