I've got this quick and dirty test I setup where I'm running a thread of QRunners in a QThreadPool one by one in PyQt4 on Python 2.7. Basically it looks like it's running fine, but the threads/pool don't seem to stop the QThreadPool once all threads have completed execution.
I'm wondering if it's as simple as returning some built in method from the QRunnable (ProductImporter
here) when it's done executing it's code, but I can't seem to find anything in the documentation.
I'm moving some code over to this setup from a QThread structure, since I can't have concurrent threads running at the same time.
Any ideas on how I could have the ProductImportTasks aware that it's tasks have all completed, so I can execute more code afterwards, or after that class is called? Any help would be greatly appreciated!
import sys
from PyQt4.QtGui import QApplication
from PyQt4.QtCore import QThreadPool, QObject, QRunnable, pyqtSignal
class WorkerSignals(QObject):
productResult = pyqtSignal(str)
class ProductImporter(QRunnable):
def __init__(self, product):
super(ProductImporter, self).__init__()
self.product = product
self.signals = WorkerSignals()
def run(self):
self.signals.productResult.emit(self.product['name'])
return
class ProductImportTasks(QObject):
def __init__(self, products):
super(ProductImportTasks, self).__init__()
self.products = products
self.pool = QThreadPool()
self.pool.setMaxThreadCount(1)
def process_result(self, product):
return
def start(self):
for product in self.products:
worker = ProductImporter(product)
worker.signals.productResult.connect(view.text)
self.pool.start(worker)
self.pool.waitForDone()
class ViewController(QObject):
def __init__(self, parent=None):
super(ViewController, self).__init__(parent)
#@pyqtSlot(str)
def text(self, message):
print "This is the view.text method: " + message
return
if __name__ == "__main__":
app = QApplication(sys.argv)
view = ViewController()
main = ProductImportTasks([{"name": "test1"}, {"name": "test2"}, {"name": "test3"}])
main.start()
sys.exit(app.exec_())
Here's what your script does:
main.start()
main.start()
Once the event-loop has started, the signals that were emitted by the runnables will be processed, and the messages will be printed. This is because signals sent across thread are queued by default. Normally, signals are sent synchronously, and don't require a running event loop.
If you change the connection type of the signals, and add a few print statements, it should be clear what's going on:
worker.signals.productResult.connect(view.text, Qt.DirectConnection)
self.pool.start(worker)
self.pool.waitForDone()
print('finished')
...
main.start()
print('exec')
sys.exit(app.exec_())