python - 制作一个可停止的简单秒表

标签 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

最佳答案

不要在 GUI 中使用 sleep ,因为这样可能会卡住 GUI。另一方面,对于这个问题来说,使用线程太过分了,在 GUI 中,只有在任务繁重时才使用线程,但每秒增加一对数据并不是一项繁重的任务。

相反,您必须使用 QTimer 来执行重复任务,并使用 QTime 来获取耗时。

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

关于python - 制作一个可停止的简单秒表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53405789/

相关文章:

python - 在 python 中定义一组(可能是分层的)常量的最佳方法是什么?

python - 尝试将分层字符串结构放入字典

Python-firebase 与 firebase-storage 一起工作

qt - 通过 eventFilter 连接/断开信号

python - 加载网页

python - 副窗无槽

python - Python 中的 Mclust (R) 等效包

python - sendfile() 失败 (32 : Broken pipe) while sending request to upstream nginx 502

python - 如何使用 eyes3 和 python2.7 从 mp3 文件访问发布日期或年份

python - 嵌套字典/json的分解与解码