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