Search code examples
python-3.xpyqt5qthread

Return lists from QThread to main thread


How would I return multiple lists generated in QThread to the mainthread without blocking the GUI?

I am looking to do time intensive stuff with xlwings in a thread and then when thread is finished I want to be able to use those lists in the mainthread. Is there any easy way of doing this?

I could think of doing something like adding the values from the thread to a global list and access them in the mainthread.

from PyQt5 import QtCore, QtWidgets
import sys


class Ui_Calculations(object):
    def setupUi(self, Calculations):
        Calculations.setObjectName("Calculations")
        Calculations.resize(850, 363)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(Calculations.sizePolicy().hasHeightForWidth())
        Calculations.setSizePolicy(sizePolicy)
        Calculations.setMinimumSize(QtCore.QSize(850, 350))
        Calculations.setMaximumSize(QtCore.QSize(860, 363))
        self.centralwidget = QtWidgets.QWidget(Calculations)
        self.centralwidget.setObjectName("centralwidget")
        Calculations.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(Calculations)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 850, 21))
        self.menubar.setObjectName("menubar")
        self.menuScreenshot = QtWidgets.QMenu(self.menubar)
        self.menuScreenshot.setObjectName("menuScreenshot")
        Calculations.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(Calculations)
        self.statusbar.setObjectName("statusbar")
        Calculations.setStatusBar(self.statusbar)
        self.actionScreenshot = QtWidgets.QAction(Calculations)
        self.actionScreenshot.setObjectName("actionScreenshot")
        self.menuScreenshot.addAction(self.actionScreenshot)
        self.menubar.addAction(self.menuScreenshot.menuAction())

        self.retranslateUi(Calculations)
        QtCore.QMetaObject.connectSlotsByName(Calculations)

    def retranslateUi(self, Calculations):
        _translate = QtCore.QCoreApplication.translate
        Calculations.setWindowTitle(_translate("Calculations", "Calculations"))
        self.menuScreenshot.setTitle(_translate("Calculations", "File"))
        self.actionScreenshot.setText(_translate("Calculations", "Screenshot"))

class mainClass(QtWidgets.QMainWindow, Ui_Calculations):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.thread = exampleThread()
        self.thread.finished.connect(self.run_return)
        self.thread.start()

    def run_return(self):
       #do something with return values
        print(exampleList[-1])
        print(example1List[-1])

class exampleThread(QtCore.QThread):
    """Example thread"""
    finished = QtCore.pyqtSignal()
    def __init__(self,parent = None):
        super().__init__(parent)

    def run(self):
        exampleList = [5,4,3]
        example1List = [5,4,3]
        self.finished.emit()
        #return exampleList,example1List 

def main():
    app = QtWidgets.QApplication(sys.argv)
    exampleWindow = mainClass()
    app.processEvents()
    exampleWindow.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()


Solution

  • You can pass known Python types as parameters when you emit a signal.

    In your example, change the declaration of the "signal" to include the data types you want to pass, both of which are of type list:

    finished = QtCore.pyqtSignal(list, list)
    

    Then have the corresponding "slot" accept the two parameters:

    def run_return(self, list1, list2):
        print(list1[-1])
        print(list2[-1])