Search code examples
pythonpyqtpyqt5concurrent.futures

How to emit a pyqtsignal through multi-threading task


I am trying to emit a pyqtsignal through multi-threading fashion. I created a function that performs the computations (e.g func). And another function that takes that task and run it in multiple threads (e.g Function).

The code works well when I use the parent thread. But, when I use the multiple threads, computations works well, but the signal is not emitted.

I need to use the multithreading, since the functions I am writing perform computational expensive tasks.

Please find below the sample code (I have used simple print function in this example)

from PyQt5.QtCore import QObject, pyqtSignal,pyqtSlot
import time
from threading import Thread
import sys
import math
import concurrent.futures


class Plot2D(QObject):
    finish=pyqtSignal(float)

    def __init__(self):
        super(Plot2D,self).__init__()

    def Function(self):
        st = time.time()

        # Using parent thread
        # self.func()

        # Using multi-thread 1
        #t=Thread(target=self.func)
        #t.start()
        #t.join()

        # Using multi-thread 2
        with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
           f = executor.submit(self.func)
        en = time.time()
        print(en-st)

    def func(self):
        n=10
        v=(1*100/(n-1))

        for i in range(n):
            print('thread')
            self.finish.emit(v)

    def fprint(self):
        print('works')

obj=Plot2D()
obj.finish.connect(obj.fprint)
obj.Function()

Solution

  • You have to be clear about the following concept: The signals need an event loop for the signals to work.

    Considering the above, the solutions are:

    • threading.Thread

    You should not use join() because it blocks the main thread where the event-loop lives and because of the above the signals will not work.

    from PyQt5 import QtCore
    from threading import Thread
    
    
    class Plot2D(QtCore.QObject):
        finished = QtCore.pyqtSignal(float)
    
        def Function(self):
            Thread(target=self.func).start()
    
        def func(self):
            n = 10
            v = 1 * 100 / (n - 1)
    
            for i in range(n):
                print("thread")
                self.finished.emit(v)
    
        @QtCore.pyqtSlot()
        def fprint(self):
            print("works")
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtCore.QCoreApplication(sys.argv)
        obj = Plot2D()
        obj.finished.connect(obj.fprint)
        obj.Function()
        sys.exit(app.exec_())
    

    Output:

    thread
    thread
    thread
    works
    thread
    works
    thread
    works
    thread
    thread
    works
    thread
    works
    thread
    thread
    works
    works
    works
    works
    works
    
    • concurrent.futures

    Do not use with since it will make the executor block the main thread (and we already know what problem it generates), it also calls executor.shutdown(wait = False)

    from PyQt5 import QtCore
    import concurrent.futures
    
    
    class Plot2D(QtCore.QObject):
        finished = QtCore.pyqtSignal(float)
    
        def Function(self):
            executor = concurrent.futures.ThreadPoolExecutor(max_workers=3)
            f = executor.submit(self.func)
            executor.shutdown(wait=False)
    
        def func(self):
            n = 10
            v = 1 * 100 / (n - 1)
    
            for i in range(n):
                print("thread")
                self.finished.emit(v)
    
        @QtCore.pyqtSlot()
        def fprint(self):
            print("works")
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtCore.QCoreApplication(sys.argv)
        obj = Plot2D()
        obj.finished.connect(obj.fprint)
        obj.Function()
        sys.exit(app.exec_())
    

    Output:

    thread
    thread
    works
    thread
    works
    thread
    works
    thread
    works
    thread
    thread
    works
    thread
    works
    thread
    works
    thread
    works
    works
    works