How can I send a list object from a QThread thread

2020-04-26 06:23发布

问题:

I have written this example code to try and figure out how to communicate between a background thread, and the main thread. As I understand it, a thread cannot simply interact with the UI or variables existing in a different thread.

I would like to grab the list 'data' in the background thread, then display it on 'lbl2' in the UI. If this program is run as is, it will print the result I want on the command line.

Data received in background thread = [1, 2, 3, 4, 5]

Q1: What is the correct way to send data such as a list or string, from the background thread to the main thread?

Q2: How would I begin to implement that in the example code?

#!/usr/bin/env python3.4
from PySide.QtGui import QPushButton, QApplication, QWidget, QLabel
from PySide.QtCore import QThread, QCoreApplication
import queue
import sys


class Gui(QWidget):
    def __init__(self):
        super(Gui, self).__init__()
        self.initUI()

    def initUI(self):
        lbl1 = QLabel('Data Recieved =', self)
        lbl2 = QLabel('None', self)
        lbl2.move(85, 0)
        lbl2.resize(100, 15)
        qbtn = QPushButton('Quit', self)
        qbtn.clicked.connect(QCoreApplication.instance().quit)
        qbtn.move(0, 20)
        btn = QPushButton('Get Summary', self)
        btn.move(100, 20)
        btn.clicked.connect(lambda: bgThread.summary())
        self.setGeometry(300, 300, 200, 50)
        self.setWindowTitle('Thread Communication Example')
        self.show()


class BackgroundThread(QThread):
    def __init__(self, q, loop_time=1.0/60):
        self.q = q
        self.timeout = loop_time
        super(BackgroundThread, self).__init__()

    def onThread(self, function, *args, **kwargs):
        self.q.put((function, args, kwargs))

    def run(self):
        while True:
            try:
               function, args, kwargs =     self.q.get(timeout=self.timeout)
               function(*args, **kwargs)
            except queue.Empty:
                self.idle()

    def idle(self):
        pass

    def _summary(self):
        # Run function which will return a list object
        # data = externalclass.summary()
        # Need to send list:'data' to the main thread.
        data = [1, 2, 3, 4, 5]
        print('Data received in background thread =', data)

    def summary(self):
        self.onThread(self._summary)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    # Setup background thread
    request_queue = queue.Queue()
    bgThread = BackgroundThread(request_queue)
    bgThread.start()
    # Setup Gui
    ui = Gui()
    sys.exit(app.exec_())

回答1:

You can define a custom signal, which can be safely emitted across threads:

from PySide.QtCore import Signal

class Gui(QWidget):
    def initUI(self):
        ...
        bgThread.dataReceived.connect(lambda data: lbl2.setText(str(data)))

class BackgroundThread(QThread):
    dataReceived = Signal(list)
    ...

    def _summary(self):
        ...
        self.dataReceived.emit(data)