0
votes

I want to run a long task in background and send signals to main thread to update the gui. The Applicatione becomes unresponisive at 1500th to 2000th iteration.

  1. First i call the start_the_thread function from qml to emit the start-signal. This starts the long task i want to run.
  2. On every iteration i emit the send_value signal. This signal is connected to the send_back_to_qml function in main thread.
  3. The send_back_to_qml function emits a Signal sendWordBack.
  4. The sendWordBack-signal is catched in QML with the help of a Connection {...}.
  5. In QML onSendWordBack: {textArea.text += word} append value to textarea field on every iteration.

Did i do something wrong with the implementation?

Are there ways to optimize this kind of operation?

main.py

import sys
import os
from PySide2.QtCore import QObject, QThread, Slot, Signal
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
import time



class BackgroundThread(QObject):
    send_value = Signal(str)
    start = Signal()

    def __init__(self):
        QObject.__init__(self)
        # connect the start signal to heavy task function
        self.start.connect(self.do_background_work)


    def do_background_work(self):
        for i in range(10000):
            print(i)
            self.send_value.emit(str(i))
            time.sleep(0.1)




class BackendController(QObject):
    sendWordBack = Signal(str, arguments=["word"])

    def __init__(self):
        QObject.__init__(self)

        thread = QThread(self)
        self.worker = BackgroundThread()
        self.worker.moveToThread(thread)
        self.worker.send_value.connect(self.send_back_to_qml)
        thread.start()


    @Slot()
    def start_the_thread(self):
        # emit the start signal from BackgroundThread() to start the background task
        self.worker.start.emit()



    @Slot(str)
    def send_back_to_qml(self, value):
        # emit the signal sendWordBack to send the value back to qml gui
        self.sendWordBack.emit(value + '\n')



if __name__ == "__main__":    
    backend = BackendController()
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()
    context = engine.rootContext()
    context.setContextProperty('backend_call', backend)
    engine.load(os.path.join(os.path.dirname(__file__), "main.qml"))

    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

main.qml

import QtQuick 2.13
import QtQuick.Window 2.13
import QtQuick.Controls 2.15

Window {
    id: window
    width: 640
    height: 480
    minimumWidth: 640
    minimumHeight: 480
    visible: true
    color: "#dad8d8"
    title: qsTr("Hello World")

    Rectangle {
        id: rectangle
        width: parent.width
        height: parent.height / 8
        color: "#807272"

        Text {
            id: element
            text: qsTr("PySide Example")
            anchors.fill: parent
            font.pixelSize: 20
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
        }
    }

    Rectangle {
        id: rectangle1
        width: parent.width
        color: "#ffffff"
        anchors.top: rectangle.bottom
        anchors.bottom: bootom_row.top

        ScrollView {
            id: scrollView
            anchors.fill: parent

        TextArea {
            id: textArea
            width: parent.width
            text: ""
            anchors.left: parent.left
            anchors.top: parent.top
            anchors.bottom: parent.bottom
            font.pixelSize: 20
            font.bold: true
            placeholderText: qsTr("Text Area")

    }
  }
}

    Row {
        id: bootom_row
        width: parent.width
        height: parent.height / 8
        anchors.bottom: parent.bottom
        layoutDirection: Qt.LeftToRight

        Button {
            id: button
            height: parent.height
            text: qsTr("Button")
            background: Rectangle {color: "#f3d27f"}
            onClicked: {
                backend_call.start_the_thread()
            }
    }
  }

Connections {
    target: backend_call
    onSendWordBack: {        
        textArea.text += word
        }
    }
}
does the application hang if you don't sent the signal? I mean if you don't touch the GUI thread from the worker thread.folibis
@folibis Sorry. GUI is responsive if i comment out this line: # self.worker.send_value.connect(self.send_back_to_qml)mthecreator
Instead of sending back 20000 signals one word at a time, could you somehow combine multiple words into a single string (say every 10 words) from python then send that string to QML, where QML will split the string back down into words and append it to the textarea? This would cut way down on the overhead required by eliminating 90% of the signals being sentmike510a