waspinator
waspinator

Reputation: 6816

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

I'm trying to write a pyqt5 application with a long running, but not CPU intensive process. I'd like to be able to run it without hanging the UI, so I'm trying to use threading, but since it doesn't seem like I can just run a thread and have it stop after its gone through its code so that it can be run again, I've tried setting up the thread to wait for a variable to change before running.

I know this can't be the correct pattern for running long processes in a pyqt app.

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()

Upvotes: 0

Views: 2133

Answers (1)

ekhumoro
ekhumoro

Reputation: 120588

Below is a simple demo showing how to start and stop a worker thread, and safely comminucate with the gui thread.

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_())

Upvotes: 1

Related Questions