'Python Qt - do signals need to be created in the worker thread?

I am using Python Qt (PySide2) which has a GUI in the main thread and a library that does IO, data-crunching, etc in a second thread.

I use signals & slots to update the GUI. In the examples I have seen on SO, the signal is always created in the worker (non-GUI) thread. Is it necessary to do this?

Reason: my library can be used with a GUI or could be used in another Python script. So, it might output data to the GUI or maybe to console/log file. To make the code in the library generic, I thought that whatever calls the library can register a callback. That callback can be to emit to Qt or output to file, etc.

Here's an example where the Signal is created in the GUI thread. It works, but could it cause thread-related issues?

import threading
import time
import sys
from PySide2.QtWidgets import QWidget, QVBoxLayout, QTextEdit, QPushButton, QApplication
from PySide2 import QtCore

class aio_connection(QtCore.QObject):
    data_recvd = QtCore.Signal(object) 

class TextEditDemo(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.setWindowTitle("TEST")
        self.resize(600,540)

        self.textEdit = QTextEdit()
        self.btnPress1 = QPushButton("Run")

        layout = QVBoxLayout()
        layout.addWidget(self.textEdit)
        layout.addWidget(self.btnPress1)
        self.setLayout(layout)

        self.btnPress1.clicked.connect(self.btnPress1_Clicked)

        self.aio_conn = aio_connection()    # Signal is created in main (GUI) thread

        # Connect signals (data_recvd) and slots (on_data_ready)
        self.aio_conn.data_recvd.connect(self.on_data_ready)

    def btnPress1_Clicked(self):
        threading.Thread(target=bg_task, args=(self.cb,)).start() 

    @QtCore.Slot()
    def on_data_ready(self, msg):
        self.textEdit.append(msg)

    def cb(self, info):
        self.aio_conn.data_recvd.emit(info)

# Simulate the library that runs in a second thread
def bg_task(callback):
    for i in range(100):
        callback(str(i))
        time.sleep(0.1)

def main():
    app = QApplication(sys.argv)
    win = TextEditDemo()
    win.show()

    sys.exit(app.exec_())

if __name__ == "__main__":
    main()


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source