我正在用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_())
所以:
GUI(stpw_ui.py)文件位于https://www.filedropper.com/stpwui
答案 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_())