1

How do I call a Python method from a Qt Quick WorkerScript?

Preamble:

I have managed to access my Python methods from my QML app by registering a subclass of QObject:

class Foo(QtCore.QObject):
    @QtCore.pyqtSlot()
    def bar(self):
        pass # do stuff here

app = QtGui.QGuiApplication([])
QtQml.qmlRegisterType(Foo, 'Foo', 1, 0, 'Foo')
# more boilerplate Qt/Qt Quick

So from within my QML application, I can successfully invoke Foo.bar().

And from what I understand, I should call any long-running functions from a WorkerScript.

Problem:

How can I run Foo.bar() in a background thread using WorkerThread?

Per the docs, WorkerScripts can't use the import syntax, so I'm not sure how I can access Foo without the import.

Update:

I need the UI to be able to display progress from Foo.bar(), since the function takes a little while and does several things.

1 Answer 1

2

The only method to send information to the WorkerScript is through sendMessage():

sendMessage(jsobject message)

Sends the given message to a worker script handler in another thread. The other worker script handler can receive this message through the onMessage() handler.

The message object may only contain values of the following types:

  • boolean, number, string
  • JavaScript objects and arrays
  • ListModel objects (any other type of QObject* is not allowed)

All objects and arrays are copied to the message. With the exception of ListModel objects, any modifications by the other thread to an object passed in message will not be reflected in the original object.

But since it reads all the elements are copied (except those of type ListModel), so it is not possible to use any object that inherits from the QObject class or its methods.


You can make the progress a pyqtProperty so you can expose it to QML, and use a slot to update its values from another thread through QRunnable with QThreadPool, the update is done through QMetaObject::invokeMethod()

class Runnable(QRunnable):
    def __init__(self, obj):
        QRunnable.__init__(self)
        # main thread
        self.obj = obj

    def run(self):
        # another thread
        self.obj.bar()


class Foo(QObject):
    def __init__(self, *args, **kwags):
        QObject.__init__(self, *args, **kwags)
        self._progress = 0

    @pyqtSlot()
    def run_bar(self):
        self.runnable = Runnable(self)
        QThreadPool.globalInstance().start(self.runnable)

    progressChanged = pyqtSignal(int)

    @pyqtProperty(int, notify=progressChanged)
    def progress(self):
        return self._progress


    @pyqtSlot(int)
    def updateProgress(self, value):
        if self._progress == value:
            return
        self._progress = value
        self.progressChanged.emit(self._progress)

    def bar(self):
        for i in range(100):
            QMetaObject.invokeMethod(self, "updateProgress",
                                     Qt.QueuedConnection,
                                     Q_ARG(int, i))
            QThread.msleep(1000)

Then you can start it through run_bar (), and show it through the progress property:

import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
import QtQuick 2.0
import Foo 1.0

ApplicationWindow{
    width: 300
    height: 300
    visible: true

    Text{
        id:txt
        text: "press me to start"
        anchors.fill: parent
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignHCenter
    }

    Foo{
        id: foo
        onProgressChanged: txt.text= progress
    }

    MouseArea {
        anchors.fill: parent
        onClicked: foo.run_bar()
    }

    statusBar: StatusBar {
        RowLayout {
            anchors.fill: parent
            ProgressBar {
                value: foo.progress
                maximumValue: 100
                anchors.fill: parent
            }
        }
    }
}

The complete example can be found in the following link

Sign up to request clarification or add additional context in comments.

3 Comments

So does that mean that you can run Javascript from a background thread in Qt Quick, but not Python or C++?
I just find that surprising; it seemed logical that you'd be able to run backend code in WorkerThread
If I used a QThread, would I be able to send signals to the UI to, say, update a status bar? Foo.bar() needs to do several things, and I want the interface to show what it's doing

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.