Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I have a GUI that implements 3 buttons
One of the buttons has the "start" function tied, which contains 2 processes, in fact, these are two cycles that should work multithreaded, but I need to add more code to the "start" function that, for example, changes some widgets.
The problem is that if I add some code after
self.threadpool.start(thread2) self.threadpool.start(thread3)
, then it is not executed upon completion of these threads, but before that, or not at all.
I wrote a toy example that fully describes my requirements from the program
import sys
import time
from PySide2.QtCore import QThreadPool, QRunnable, Slot
from PySide2.QtWidgets import QApplication, QWidget, QPushButton
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.threadpool = QThreadPool()
self.btn.clicked.connect(lambda: self.start())
def start(self):
t1 = Worker(th1)
t2 = Worker(th2)
self.btn.setEnabled(False)
self.threadpool.start(t1)
self.threadpool.start(t2)
self.btn.setEnabled(True)
def initUI(self):
self.btn = QPushButton('Start', self)
self.btn.resize(self.btn.sizeHint())
self.btn.move(50, 50)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('HELP')
self.show()
class Worker(QRunnable):
def __init__(self, fn):
super(Worker, self).__init__()
self.fn = fn
@Slot() # QtCore.Slot
def run(self):
self.fn()
def th1():
time.sleep(5)
print("th1")
def th2():
time.sleep(10)
print("th2")
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
I need the button to turn off before the streams start and turn on only after their completion.
The solution should be fair for any code I want to use after streams.
First, using the Slot decorator in the methods of a QRunnable is useless since it is not a QObject.
On the other hand, the idea of implementing a multithreading logic is that the main thread does not block, so you should not expect that after invoking start() it will be executed after it finishes executing the threads.
The solution is to create a QObject that emits a signal before and after the execution of the thread, and with that logic implement the GUI update:
import sys
import time
from PySide2.QtCore import QObject, QThreadPool, QRunnable, Signal, Slot
from PySide2.QtWidgets import QApplication, QWidget, QPushButton
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.active_workers = 0
self.threadpool = QThreadPool()
self.btn.clicked.connect(self.start)
@Slot()
def start(self):
t1 = Worker(th1)
t1.signaller.started.connect(self.on_started)
t1.signaller.finished.connect(self.on_finished)
t2 = Worker(th2)
t2.signaller.started.connect(self.on_started)
t2.signaller.finished.connect(self.on_finished)
self.btn.setEnabled(False)
self.threadpool.start(t1)
self.threadpool.start(t2)
def initUI(self):
self.btn = QPushButton("Start", self)
self.btn.resize(self.btn.sizeHint())
self.btn.move(50, 50)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle("HELP")
self.show()
@Slot()
def on_started(self):
self.active_workers += 1
@Slot()
def on_finished(self):
self.active_workers -= 1
self.btn.setEnabled(self.active_workers == 0)
class Signaller(QObject):
started = Signal()
finished = Signal()
class Worker(QRunnable):
def __init__(self, fn):
super(Worker, self).__init__()
self.signaller = Signaller()
self.fn = fn
def run(self):
self.signaller.started.emit()
self.fn()
self.signaller.finished.emit()
def th1():
time.sleep(5)
print("th1")
def th2():
time.sleep(10)
print("th2")
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.