>The goal of this is to have a QThread start its event loop and then execute slots from that event loop. Ill include a sample program that demonstrates the differences in connection types.
Ill also include a minimal example that involves some psuedo code.
#!/usr/bin/env python from PyQt4 import QtCore,QtGui import sys, time class DisplayWindow(QtGui.QMainWindow): progress = QtCore.pyqtSignal(['int']) def __init__(self, parent=None): QtGui.QMainWindow.__init__(self, parent) self.progress.connect(self.showID) self.label = QtGui.QLabel("0") #connected to the thread itself. buttonA = QtGui.QPushButton("Direct") #connected to the object created during thread exec. buttonB = QtGui.QPushButton("Connected") #connected to a local function buttonC = QtGui.QPushButton("No Thread") self.buttonB = buttonB self.proc = ConnectedThread(self) #Connect buttonB after the thread has started self.proc.started.connect(self.connectButtonB) self.proc.start() buttonA.clicked.connect( self.proc.stopNWait, type=QtCore.Qt.QueuedConnection ) buttonC.clicked.connect(self.sleepNoThread) #layout widge = QtGui.QWidget() layout = QtGui.QVBoxLayout() button_layout = QtGui.QHBoxLayout() button_layout.addWidget(buttonA) button_layout.addWidget(buttonB) button_layout.addWidget(buttonC) layout.addWidget(self.label) layout.addLayout(button_layout) widge.setLayout(layout) self.setCentralWidget(widge) @QtCore.pyqtSlot() def sleepNoThread(self): self.progress.emit(QtCore.QThread.currentThreadId()) QtCore.QThread.sleep(1) @QtCore.pyqtSlot() def xFinished(self): print QtCore.QThread.currentThreadId() self.ok = True @QtCore.pyqtSlot() def connectButtonB(self): self.proc.queuedConnect(self.buttonB) @QtCore.pyqtSlot("int") def showID(self, s): self.label.setText("id: %s"%s); def cleanUp(self): self.proc.quit() self.proc.wait() class ConnectedThread(QtCore.QThread): """ This is a simple thread class to show how to connect a signal across threads. The key is creating a new object during the QThreads event loop """ def __init__(self, parent=None): QtCore.QThread.__init__(self,parent) self.p = parent def run(self): self.o = Starter() self.o.progress.connect(self.p.showID) self.o.progress.emit(QtCore.QThread.currentThreadId()) self.exec_() @QtCore.pyqtSlot() def stopNWait(self): self.o.stopNWait() def queuedConnect(self,button): button.clicked.connect( self.o.stopNWait, type=QtCore.Qt.QueuedConnection ) class Starter(QtCore.QObject): progress = QtCore.pyqtSignal(['int']) """ This object will execute stop and wait in the thread it was created in, provided a QueuedConnection is used """ @QtCore.pyqtSlot() def stopNWait(self): self.progress.emit(QtCore.QThread.currentThreadId()) QtCore.QThread.sleep(1) if __name__=="__main__": app = QtGui.QApplication(sys.argv) wid = DisplayWindow() wid.show() try: sys.exit(app.exec_()) finally: wid.cleanUp()
Things to note:
* I do not connect to the thread, but the object that has been created in the threads event loop. The 'buttonB' starts performs the action in the same id thread that the Thread's 'run' method performs.
* buttonA is executed in the same event loop as buttonC and yet the behaviors are slightly different.
* I have created a similar program to this in c++ and it works the same way you need to create a new object for the slot to be called from the threads event loop.
* There is a race condition if you start the thread and then immediately try to connect to the object that you are creating. It is better to connect to the QThreads .started() signal, or to connect in the run method itself.
* The 'progress' signal is evaluated in the main thread so it is still 'safe'.
class ConnectedThread(QtCore.QThread): """ This is an example that doesn't doe anything, but it shows how to connect a signal to a slot so that the slot will be executed in a separate thread. Note "Starter" cannot be a QWidget as per the documentation. """ def __init__(self, parent=None): QtCore.QThread.__init__(self,pysignal) self.pysignal = pysignal def run(self): self.o = Starter() self.pysignal.connect(o.performAction) self.exec_() def queuedConnect(self,pysignal): pysignal.connect( self.o.performAction, type=QtCore.Qt.QueuedConnection ) class Starter(QtCore.QObject): progress = QtCore.pyqtSignal(['int']) """ Dummy object for connecting in a separate event loop """ @QtCore.pyqtSlot() def performAction(self): pass