I have a Worker
object and use its method moveToThread
to put it in a thread.
Now i call its work
method:
- If I invoke the method directly, it is executed in the thread its object is living in
- If I invoke the method using lambda, the method is executed in the main thread
Example:
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class Worker(QObject):
def __init__(self):
super().__init__()
def work(self):
print(self.thread().currentThread())
class Example(QWidget):
def __init__(self):
super().__init__()
self.btnInThread = QPushButton('in thread')
self.btnNotInThread = QPushButton('not in thread')
layout = QVBoxLayout()
layout.addWidget(self.btnInThread)
layout.addWidget(self.btnNotInThread)
self.setLayout(layout)
self.worker = Worker()
self.Thread = QThread()
self.worker.moveToThread(self.Thread)
self.Thread.start()
self.btnInThread.clicked.connect(self.worker.work)
self.btnNotInThread.clicked.connect(lambda: self.worker.work())
self.show()
print('{0} <- Main Thread'.format(self.thread().currentThread()))
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I have done excessive testing (see code in snippet) but i have absolutly no idea what's going on.
So my question is:
Why is work
executed in the main thread and not the thread its object lives in if it is invoked with lambda
? And most important what can i do if i want to call a method of Worker
requiring arguments where i cant spare the lambda
?
from PySide.QtCore import *
from PySide.QtGui import *
from time import sleep
import functools
import sys
class Worker(QObject):
def __init__(self):
super().__init__()
def work(self, name='Nothing'):
print('Thread ID: {1} - {0} start'.format(name, QThread.currentThreadId()))
sleep(1)
print('##### End {0}'.format(name))
class HackPushButton(QPushButton):
clicked_with_arg = Signal(str)
def __init__(self, *args):
super().__init__(*args)
self.argument = None
self.clicked.connect(lambda: self.clicked_with_arg.emit(self.argument))
class Example(QWidget):
def __init__(self):
super().__init__()
self.buttonWithoutLambda = QPushButton('[Works] Call work() without arguments and without lambda')
self.buttonWithLambda = QPushButton('[Blocks] Call work() with arguments and with lambda')
self.buttonWithFunctools = QPushButton('[Blocks] Call work() with arguments and with functools')
self.buttonWithHelperFunctionWithArgument = QPushButton('[Blocks] Call work() with arguments and with helper function')
self.buttonWithHelperFunctionWithoutArgument = QPushButton('[Blocks] Call work() without arguments and with helper function')
self.buttonWithHack = HackPushButton('[Works] Call work() with arguments via dirty hack')
layout = QVBoxLayout()
layout.addWidget(self.buttonWithoutLambda)
layout.addWidget(self.buttonWithLambda)
layout.addWidget(self.buttonWithFunctools)
layout.addWidget(self.buttonWithHelperFunctionWithArgument)
layout.addWidget(self.buttonWithHelperFunctionWithoutArgument)
layout.addWidget(self.buttonWithHack)
self.setLayout(layout)
self.Worker = Worker()
self.Thread = QThread()
self.Worker.moveToThread(self.Thread)
self.Thread.start()
# Doesn't block GUI
self.buttonWithoutLambda.clicked.connect(self.Worker.work)
# Blocks GUI
self.buttonWithLambda.clicked.connect(lambda: self.Worker.work('Lambda'))
# Blocks GUI
self.buttonWithFunctools.clicked.connect(functools.partial(self.Worker.work, 'Functools'))
# Blocks GUI
self.helperFunctionArgument = 'Helper function without arguments'
self.buttonWithHelperFunctionWithArgument.clicked.connect(self.helperFunctionWithArgument)
# Blocks GUI
self.buttonWithHelperFunctionWithoutArgument.clicked.connect(self.helperFunctionWithoutArgument)
# Doesn't block GUI
self.buttonWithHack.argument = 'Hack'
self.buttonWithHack.clicked_with_arg.connect(self.Worker.work)
print('Thread ID: {0}'.format(QThread.currentThreadId()))
self.show()
def helperFunctionWithArgument(self):
self.Worker.work(self.helperFunctionArgument)
def helperFunctionWithoutArgument(self):
self.Worker.work()
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())