简体   繁体   中英

Multithreading in PyQt5 Window

I got two functions(do_work, do_work2) how do i multithread them? If i use one Thread it works fine if i try to add one or more it wont work. I want to have while True loops in my functions. I already tried to change self.continue_run to: self.continue_run2 for second function but still wont work.

I hope someone can help me

import sys
from PyQt5.QtWidgets import (QWidget,
                         QPushButton, QApplication, QGridLayout)
from PyQt5.QtCore import QThread, QObject, pyqtSignal
import keyboard
import time

class Worker(QObject):

    finished = pyqtSignal()  # give worker class a finished signal

    def __init__(self, parent=None):
        QObject.__init__(self, parent=parent)
        self.continue_run = True  # provide a bool run condition for the class

    def do_work(self):
        while self.continue_run:  # give the loop a stoppable condition
            print("Thread 1")
        self.finished.emit()  # emit the finished signal when the loop is done

    def do_work2(self):
        while self.continue_run:  # give the loop a stoppable condition
            print("Thread 2")
            
        self.finished.emit()  # emit the finished signal when the loop is done

    def stop(self):
        self.continue_run = False  # set the run condition to false on stop


class Gui(QWidget):

    stop_signal = pyqtSignal()  # make a stop signal to communicate with the worker in another thread

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        # Buttons:
        self.btn_start = QPushButton('Start')
        self.btn_start.resize(self.btn_start.sizeHint())
        self.btn_start.move(50, 50)
        self.btn_stop = QPushButton('Stop')
        self.btn_stop.resize(self.btn_stop.sizeHint())
        self.btn_stop.move(150, 50)

        # GUI title, size, etc...
        self.setGeometry(300, 300, 300, 220)
        self.setWindowTitle('ThreadTest')
        self.layout = QGridLayout()
        self.layout.addWidget(self.btn_start, 0, 0)
        self.layout.addWidget(self.btn_stop, 0, 50)
        self.setLayout(self.layout)

        # Thread:
        self.thread = QThread()
        self.worker = Worker()
        self.stop_signal.connect(self.worker.stop)  # connect stop signal to worker stop method
        self.worker.moveToThread(self.thread)

        self.worker.finished.connect(self.thread.quit)  # connect the workers finished signal to stop thread
        self.worker.finished.connect(self.worker.deleteLater)  # connect the workers finished signal to clean up worker
        self.thread.finished.connect(self.thread.deleteLater)  # connect threads finished signal to clean up thread

        self.thread.started.connect(self.worker.do_work)
        self.thread.finished.connect(self.worker.stop)

        self.thread.started.connect(self.worker.do_work2)
        self.thread.finished.connect(self.worker.stop)

        # Start Button action:
        self.btn_start.clicked.connect(self.thread.start)

        # Stop Button action:
        self.btn_stop.clicked.connect(self.stop_thread)

        self.show()

    # When stop_btn is clicked this runs. Terminates the worker and the thread.
    def stop_thread(self):
        self.stop_signal.emit()  # emit the finished signal on stop


if __name__ == '__main__':
    app = QApplication(sys.argv)
    gui = Gui()
    sys.exit(app.exec_())

One of the two main issues with your logic is that you've connected the finished signal to deleteLeter , and the result is that the second function would never be run anyway because you deleted both the thread and the worker.

The first important problem is that moving two functions to a thread doesn't make them run concurrently: a thread can only do one thing at a time, no matter if it's the "main" thread or not.

When the thread is started, it starts the first function the started signal is connected to, and the second will only run as soon as the first has returned (but not in your case, as explained before, since you're deleting both objects).

Consider the following example:

class WorkerTest(QtCore.QObject):
    def firstFunction(self):
        print('starting first function')
        sleep(2)
        print('finished first function')

    def secondFunction(self):
        print('starting second function')
        sleep(2)
        print('finished second function')


class Test(QtWidgets.QPushButton):
    def __init__(self):
        super().__init__('start')
        self.worker = WorkerTest()
        self.thread = QtCore.QThread()
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.firstFunction)
        self.thread.started.connect(self.worker.secondFunction)
        self.clicked.connect(self.thread.start)

If you run the code above, you'll see that the second function will be run only as soon as the first has finished.

If you want to have two concurrent threads, create two threads.

If you want to have another thread to run after (or alternate to) the first, connect the finished signal to a function that disconnects the started from the first function and connects to the second one.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM