PyQt5:单击按钮时如何停止线程(QRunnable)?

问题描述 投票:0回答:1

我阅读了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_())
python multithreading pyqt pyqt5 qrunnable
1个回答
0
投票

[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并完全避免整个多线程,但是我认为这只是一些示例更大的计划。

© www.soinside.com 2019 - 2024. All rights reserved.