python qt5 应用程序中线程的正确模式是什么?

What's the correct pattern for threading in a python qt5 application?

我正在尝试编写一个具有较长 运行ning 但不是 CPU 密集过程的 pyqt5 应用程序。我希望能够 运行 它而不挂起 UI,所以我正在尝试使用线程,但因为它似乎不能 运行 一个线程并让它在完成其代码后停止,以便它可以再次 运行,我尝试设置线程以等待变量在 运行ning 之前更改。

我知道这不是 pyqt 应用程序中 运行ning 长进程的正确模式。

import time
import threading
from PyQt5 import QtWidgets, uic


class MyApp(QtWidgets.QMainWindow):
    _run_thread = False

    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.ui = uic.loadUi('myapp.ui', self)

        self.ui.start_thread_button.clicked.connect(self._run_thread_function)

        self._thread = threading.Thread(target=self._run_thread_callback)
        self._thread.daemon = True
        self._thread.start()

        self.ui.show()

    def _run_thread_callback(self):
        while True:
            if self._run_thread:
                print("running thread code...")
                time.sleep(10)
                print("thread code finished")
                self._run_thread = False

    def _run_thread_function(self):
        print("starting thread...")
        self._run_thread = True


def main():
    app = QtWidgets.QApplication(sys.argv)
    MyApp()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

下面是一个简单的演示,展示了如何启动和停止工作线程,以及如何安全地与 gui 线程通信。

import sys
from PyQt5 import QtCore, QtWidgets

class Worker(QtCore.QThread):
    dataSent = QtCore.pyqtSignal(dict)

    def __init__(self, parent=None):
        super(Worker, self).__init__(parent)
        self._stopped = True
        self._mutex = QtCore.QMutex()

    def stop(self):
        self._mutex.lock()
        self._stopped = True
        self._mutex.unlock()

    def run(self):
        self._stopped = False
        for count in range(10):
            if self._stopped:
                break
            self.sleep(1)
            data = {
                'message':'running %d [%d]' % (
                    count, QtCore.QThread.currentThreadId()),
                'time': QtCore.QTime.currentTime(),
                'items': [1, 2, 3],
                }
            self.dataSent.emit(data)

class Window(QtWidgets.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.edit = QtWidgets.QPlainTextEdit()
        self.edit.setReadOnly(True)
        self.button = QtWidgets.QPushButton('Start')
        self.button.clicked.connect(self.handleButton)
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.edit)
        layout.addWidget(self.button)
        self._worker = Worker()
        self._worker.started.connect(self.handleThreadStarted)
        self._worker.finished.connect(self.handleThreadFinished)
        self._worker.dataSent.connect(self.handleDataSent)

    def handleThreadStarted(self):
        self.edit.clear()
        self.button.setText('Stop')
        self.edit.appendPlainText('started')

    def handleThreadFinished(self):
        self.button.setText('Start')
        self.edit.appendPlainText('stopped')

    def handleDataSent(self, data):
        self.edit.appendPlainText('message [%d]' %
            QtCore.QThread.currentThreadId())
        self.edit.appendPlainText(data['message'])
        self.edit.appendPlainText(data['time'].toString())
        self.edit.appendPlainText(repr(data['items']))

    def handleButton(self):
        if self._worker.isRunning():
            self._worker.stop()
        else:
            self._worker.start()

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(500, 100, 400, 400)
    window.show()
    sys.exit(app.exec_())