<div dir="ltr">I have just observed some odd behavior and would like to know if this is normal or a bug.<br><br>I have a non-Qt thread where I create a Qt object, connect a signal, move the object to the main application thread, then use the signal to execute a callback function within the Qt event loop.  Here is some sample code that can be copied directly into a python instance:<br><font face="courier new, monospace"><br></font><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><font face="courier new, monospace"># Create the Qt application in a different thread</font><div><font face="courier new, monospace"># so that testing is easy from the python command prompt</font></div><div><div><font face="courier new, monospace">class QtAppThread(threading.Thread):</font></div></div><div><div><font face="courier new, monospace">    def run(self):</font></div></div><div><div><font face="courier new, monospace">        from PyQt5.QtCore import QThread,QCoreApplication</font></div></div><div><div><font face="courier new, monospace">        self.app = QCoreApplication([])</font></div></div><div><div><font face="courier new, monospace">        print(f"Main App Thread Id: {int(QThread.currentThreadId())}")</font></div></div><div><div><font face="courier new, monospace">        self.app.exec_()</font></div></div><div><div><font face="courier new, monospace">appthread = QtAppThread()</font></div></div><div><div><font face="courier new, monospace">appthread.start()</font></div></div><div><div><font face="courier new, monospace"><br></font></div></div><div><div><font face="courier new, monospace"># Run from a thread that is NOT a QThread:</font></div></div><div><div><font face="courier new, monospace">from PyQt5.QtCore import *</font></div></div><div><div><font face="courier new, monospace">class QSigRunner(QObject):</font></div></div><div><div><font face="courier new, monospace">    sig_run = pyqtSignal(object, tuple)</font></div></div><div><div><font face="courier new, monospace"><br></font></div></div><div><div><font face="courier new, monospace">    def __init__(self, *arg, **kw):</font></div></div><div><div><font face="courier new, monospace">        super().__init__(*arg, **kw)</font></div></div><div><div><span style="font-family:"courier new",monospace">        self.sig_run.connect(self.on_sig_run)</span></div></div><div><div><font face="courier new, monospace"><br></font></div></div><div><div><div><font face="courier new, monospace">    def on_sig_run(self, func, args):</font></div></div></div><div><div><div><font face="courier new, monospace">        func(*args)</font></div></div></div><div><div><div><font face="courier new, monospace"><br></font></div></div></div><div><div><div><font face="courier new, monospace">def my_callback(*args):</font></div></div></div><div><div><font face="courier new, monospace">    print(f"Called with {args}")</font></div></div><div><div><font face="courier new, monospace">    print(f"Thread {int(QThread.currentThreadId())}")</font></div></div><div><div><br></div></div><div><div><font face="courier new, monospace">obj = QSigRunner()</font></div></div><div><div><font face="courier new, monospace">obj.moveToThread(QCoreApplication.instance().thread())</font></div></div><div><div><font face="courier new, monospace">print(f"My Thread Id: </font><span style="font-family:"courier new",monospace">{int(QThread.currentThreadId())}")</span></div></div><div><div><font face="courier new, monospace">obj.sig_run.emit(my_callback, (True,))</font></div></div></blockquote><div><div><br>Based on the description of pyqt/qt signals, I expected the above code to execute in the main application's thread, however examining thread IDs I see that the callback function is executed in the same thread as I emitted the signal.<br><br>However, if I change the above code in a very minor way to declare the method <font face="courier new, monospace">on_sig_run</font> as an explicity pyqtSlot, everything works as expected and the emitted signal is run as part of the main Qt thread via the Qt event loop.  Here is the code:<br><br></div></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><div><span style="font-family:"courier new",monospace"># Run from a thread that is NOT a QThread:</span></div></div><div><div><span style="font-family:"courier new",monospace">from PyQt5.QtCore import *</span></div></div><div><div><font face="courier new, monospace">class QSigRunner(QObject):</font></div></div><div><div><font face="courier new, monospace">    sig_run = pyqtSignal(object, tuple)</font></div></div><div><div><font face="courier new, monospace"><br></font></div></div><div><div><font face="courier new, monospace">    def __init__(self, *arg, **kw):</font></div></div><div><div><font face="courier new, monospace">        super().__init__(*arg, **kw)</font></div></div><div><div><span style="font-family:"courier new",monospace">        self.sig_run.connect(self.on_sig_run)</span></div></div><div><div><font face="courier new, monospace"><br></font></div></div><div><div><div><font face="courier new, monospace">    @pyqtSlot(object, tuple)</font></div></div></div><div><div><font face="courier new, monospace">    def on_sig_run(self, func, args):</font></div></div><div><div><font face="courier new, monospace">        func(*args)</font></div></div><div><div><span style="font-family:"courier new",monospace"><br></span></div></div><div><div><span style="font-family:"courier new",monospace">def my_callback(*args):</span></div></div><div><div><font face="courier new, monospace">    print(f"Called with {args}")</font></div></div><div><div><font face="courier new, monospace">    print(f"Thread {int(QThread.currentThreadId())}")</font></div></div><div><div><font face="courier new, monospace"><br></font></div></div><div><div><font face="courier new, monospace">obj = QSigRunner()</font></div></div><div><div><font face="courier new, monospace">obj.moveToThread(QCoreApplication.instance().thread())</font></div></div><div><div><font face="courier new, monospace">print(f"My Thread Id: </font><span style="font-family:"courier new",monospace">{int(QThread.currentThreadId())}")</span></div></div><div><div><font face="courier new, monospace">obj.sig_run.emit(my_callback, (True,))</font></div></div></blockquote><div><br>Alternatively, I can connect the signal <b>after</b> moving the object to a new thread and again, everything works perfectly with the emitted signal executing in the Qt event loop.  Here is the updated code:<br><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><span style="font-family:"courier new",monospace"># Run from a thread that is NOT a QThread:</span></div><div><span style="font-family:"courier new",monospace">from PyQt5.QtCore import *</span></div><div><font face="courier new, monospace">class QSigRunner(QObject):</font></div><div><font face="courier new, monospace">    sig_run = pyqtSignal(object, tuple)</font></div><div><font face="courier new, monospace"><br></font></div><div><font face="courier new, monospace">    def __init__(self, *arg, **kw):</font></div><div><font face="courier new, monospace">        super().__init__(*arg, **kw)</font></div><div><font face="courier new, monospace"><br></font></div><div><div><font face="courier new, monospace">    def on_sig_run(self, func, args):</font></div></div><div><div><font face="courier new, monospace">        func(*args)</font></div></div><div><div><font face="courier new, monospace"><br></font></div></div><div><div><font face="courier new, monospace">    def moveToThread(self, *arg, **kw):</font></div></div><div><div><font face="courier new, monospace">        super().moveToThread(*arg, **kw)</font></div></div><div><div><font face="courier new, monospace">        self.sig_run.connect(self.on_sig_run)</font></div></div><div><div><font face="courier new, monospace"><br></font></div></div><div><div><font face="courier new, monospace">def my_callback(*args):</font></div></div><div><div><font face="courier new, monospace">    print(f"Called with {args}")</font></div></div><div><div><font face="courier new, monospace">    print(f"Thread {int(QThread.currentThreadId())}")</font></div></div><div><div><font face="courier new, monospace"><br></font></div></div><div><div><font face="courier new, monospace">obj = QSigRunner()</font></div></div><div><div><font face="courier new, monospace">obj.moveToThread(QCoreApplication.instance().thread())</font></div></div><div><div><font face="courier new, monospace">print(f"My Thread Id: </font><span style="font-family:"courier new",monospace">{int(QThread.currentThreadId())}")</span></div></div><div><div><font face="courier new, monospace">obj.sig_run.emit(my_callback, (True,))</font></div></div></blockquote><div><br>Is this acting as expected?  If so, can anyone explain why the first version does not work but the other two both work?  Or is there a bug involved in the first configuration?<br><br>Thank you,<br>David</div></div>