使一个简单的秒表可停止

时间:2018-11-21 05:33:16

标签: python python-3.x pyqt pyqt5

我正在用python(使用pyqt5)编写一个简单的秒表。 到目前为止,启动/暂停/恢复功能可以正常工作,但是问题是当我停止计数器并需要从0开始时,无法再次启动线程,因此我尝试了here的子类,但是它不起作用。

import threading
from PyQt5 import QtWidgets
from stpw_ui import Ui_MainWindow
from time import sleep

# code snippet from stack overflow to stop a thread
class StoppableThread(threading.Thread):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._stop_event = threading.Event()

    def stop(self):
        self._stop_event.set()

    def stopped(self):
        return self._stop_event.is_set()

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # buttons' actions
        self.ui.start.clicked.connect(self.stopwatch)
        self.ui.pause.clicked.connect(self.pse)
        self.ui.stop.clicked.connect(self.stp)
        self.ui.resume.clicked.connect(self.res)
        # self.ui.reset.clicked.connect(self.rst)

        self.t = StoppableThread(target=self.strt)

    def UpdateSec(self,s):
        self.ui.seconds.display(s)

    def UpdateMin(self,m):
        self.ui.minutes.display(m)

    def stopwatch(self):
        self.ui.stacked_buttons.setCurrentIndex(1)
        self.pause = False
        self.t.start()

    pause = False
    second = 0
    minute = 0

    def setpause(self,x):
        self.pause = x

    # start
    def strt(self):
        if (self.pause is True):
            sleep(0.1)
        while self.second <= 59 and self.pause == False:
            self.UpdateSec(self.second)
            self.second += 1
            sleep(1)
        if self.second==59:
            self.minute += 1
            self.UpdateMin(self.minute)
            self.second = 0
        self.strt()

    # pause
    def pse(self):
        self.ui.stacked_buttons.setCurrentIndex(2)
        self.setpause(True)


    # stop
    def stp(self):
        self.setpause(True)
        self.ui.stacked_buttons.setCurrentIndex(0)
        self.t.stop()

    # resume
    def res(self):
        self.ui.stacked_buttons.setCurrentIndex(1)
        self.setpause(False)

    # reset / ignore it for now
    # def rst(self):
    #    self.ui.stacked_buttons.setCurrentIndex(0)
    #    self.setpause(False)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

所以:

  1. 如何使停止按钮起作用? (我在这里做错了什么?)
  2. 是否有更好/简单的方法来制作秒表?因为我觉得我太复杂了。

GUI(stpw_ui.py)文件位于https://www.filedropper.com/stpwui

1 个答案:

答案 0 :(得分:0)

请勿在GUI中使用sleep,因为您可以冻结GUI。另一方面,对于这个问题,使用线程太多了,在GUI中,仅当任务很重时才使用线程是合理的,但每秒增加一对数据并不是一项繁重的任务。

相反,必须使用QTimer来执行重复性任务,而必须使用QTime来获得经过的时间。

对于逻辑,必须实现状态机,在这种情况下,将是StopWatch,它将发出必要的信号来通知对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_())