python - 单击按钮时如何停止线程(QRunnable)?
问题描述
我阅读了这篇关于 PyQt 中的线程的文章,并尝试将其实现到我自己的程序中。我现在可以启动一个线程,但是当我关闭窗口时,我认为另一个线程会继续运行。我也不能使用 ctrl+c 来中断程序。那么如何停止发送通知的我的线程呢?这是我的代码:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QRunnable, pyqtSlot, QThreadPool
import notify2
import time
class StartTimer(QRunnable):
'''
StartTimer thread
'''
def __init__(self, minutes):
super(StartTimer, self).__init__()
self.minutes = minutes
@pyqtSlot()
def run(self):
notify2.init('Break Timer')
notification = notify2.Notification(
'Success!', 'Break Timer has been started.')
notification.show()
self.start = True
while self.start:
time.sleep(self.minutes * 60)
notify2.init('Break Timer')
notification = notify2.Notification(
'Pause for 1 minute', 'Move and see away from the screen!')
notification.show()
time.sleep(5)
class Ui_break_timer_ui(object):
def __init__(self):
self.threadpool = QThreadPool()
self.start = False
def start_btn_pressed(self):
self.start_timer = StartTimer(self.spinBox_minutes.value())
self.threadpool.start(self.start_timer)
def stop_timer(self):
notify2.init('Break Timer')
notification = notify2.Notification(
'Program ended', 'You will receive no further notifications.')
notification.show()
self.start = False
def setupUi(self, break_timer_ui):
break_timer_ui.setObjectName("break_timer_ui")
break_timer_ui.setWindowIcon(QtGui.QIcon('icon.png'))
break_timer_ui.resize(596, 412)
self.centralwidget = QtWidgets.QWidget(break_timer_ui)
self.centralwidget.setObjectName("centralwidget")
self.btn_start = QtWidgets.QPushButton(self.centralwidget)
self.btn_start.setGeometry(QtCore.QRect(490, 350, 88, 34))
self.btn_start.setObjectName("btn_start")
self.btn_stop = QtWidgets.QPushButton(self.centralwidget)
self.btn_stop.setGeometry(QtCore.QRect(390, 350, 88, 34))
self.btn_stop.setObjectName("btn_stop")
self.label_set_interval = QtWidgets.QLabel(self.centralwidget)
self.label_set_interval.setGeometry(QtCore.QRect(20, 20, 91, 18))
self.label_set_interval.setObjectName("label_set_interval")
self.spinBox_minutes = QtWidgets.QSpinBox(self.centralwidget)
self.spinBox_minutes.setGeometry(QtCore.QRect(20, 50, 52, 32))
self.spinBox_minutes.setMinimum(1)
self.spinBox_minutes.setMaximum(240)
self.spinBox_minutes.setProperty("value", 30)
self.spinBox_minutes.setObjectName("spinBox_minutes")
self.label_minutes = QtWidgets.QLabel(self.centralwidget)
self.label_minutes.setGeometry(QtCore.QRect(80, 60, 58, 18))
self.label_minutes.setObjectName("label_minutes")
break_timer_ui.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(break_timer_ui)
self.statusbar.setObjectName("statusbar")
break_timer_ui.setStatusBar(self.statusbar)
self.btn_stop.clicked.connect(lambda _: self.stop_timer())
self.btn_start.clicked.connect(lambda _: self.start_btn_pressed())
self.retranslateUi(break_timer_ui)
QtCore.QMetaObject.connectSlotsByName(break_timer_ui)
def retranslateUi(self, break_timer_ui):
_translate = QtCore.QCoreApplication.translate
break_timer_ui.setWindowTitle(
_translate("break_timer_ui", "Break Timer"))
self.btn_start.setText(_translate("break_timer_ui", "Start"))
self.btn_stop.setText(_translate("break_timer_ui", "Stop"))
self.label_set_interval.setText(
_translate("break_timer_ui", "Set interval"))
self.label_minutes.setText(_translate("break_timer_ui", "minutes"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
break_timer_ui = QtWidgets.QMainWindow()
ui = Ui_break_timer_ui()
ui.setupUi(break_timer_ui)
break_timer_ui.show()
sys.exit(app.exec_())
解决方案
QRunnable 更适用于运行并完成的代码(即使它是长时间运行的),而不是持久的后台线程。
你想做的事情很少。首先,查看https://doc.qt.io/qt-5/qthread.html。你想要做的是让你的后台任务成为一个 QObject,给它一个 run() 槽,然后将 thread.started 连接到 worker.run。这就是启动该过程的原因。您需要做丑陋的两部分方式,而不仅仅是子类 QThread,因为您希望线程有自己的事件循环。
你仍然有一个问题,你有一个长时间的睡眠,并且睡眠调用阻塞了整个线程,所以你不能在几分钟内取消它。让您的 worker.run() 创建一个 QTimer(因此它位于线程事件循环中,而不是主 GUI 事件循环中),并使用该 QTimer 向您的工作人员上的另一个插槽发出信号,它是时候唤醒了。现在没有长时间的 sleep() 阻塞,只有事件。这意味着当您调用 thread.quit() 时,它将发出完成(您可以连接到您的工作人员以进行任何关闭操作)然后停止线程,无论计时器上还剩多少时间。
综上所述,如果您在这里尝试做的只是您在这里所做的,您可以在主线程中使用 QTimer 并完全避免整个多线程操作,但我认为这只是一些更宏大方案的演示.
推荐阅读
- c# - Spotify Redirect 在参数前添加字符
- eclipse - 在 Pydev 中激活虚拟环境
- php - Google ReCaptcha V2 无法使用旧的 PHP 代码
- python-3.x - Python3 Gtk3 将 GdkPixbuf 附加到 Gtk.ListStore
- python - 在python中获取每个neibours字节的一半
- jquery - 将更改其他 CSS 与类链接
- python - 如何在 Python 中覆盖 ibm_db.exec_immediate(connect, sql) 上的模式使用
- single-page-application - Blazor 页面中的不同 css 文件
- reactjs - 当我包含参数时,为什么我的提取不会完成
- c# - HttpWebRequest.GetResponse 返回 404 但 URL 在浏览器中有效