我阅读了this关于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通知您的工作器上的另一个插槽该唤醒。现在不再需要long sleep()阻塞,而只是事件。这意味着,当您调用thread.quit()时,它将发出完成信号(您可以将其连接到您的工作程序以执行任何关闭操作),然后停止线程无论如何]剩下多少时间在计时器上。
话虽如此,如果您要在这里做的就是在这里做的,您可以只在主线程中使用QTimer并完全避免整个多线程,但是我认为这只是一些示例更大的计划。