如何从Qt Quick WorkerScript调用Python方法?

前言:

我已经通过注册QObject的子类从QML应用程序访问了我的Python方法:

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


因此,从我的QML应用程序中,我可以成功调用Foo.bar()

从我的理解中,我应该从WorkerScript调用任何长时间运行的函数。

问题:

如何使用Foo.bar()在后台线程中运行WorkerThread

Per the docsWorkerScript不能使用import语法,因此我不确定如何在不导入的情况下访问Foo

更新:

我需要用户界面能够显示Foo.bar()的进度,因为该功能需要一些时间并且可以完成多项操作。

最佳答案

将信息发送到WorkerScript的唯一方法是通过sendMessage()


  sendMessage(jsobject消息)
  
  将给定的消息发送到另一个线程中的工作脚本处理程序。
  其他工作程序脚本处理程序可以通过
  onMessage()处理函数。
  
  消息对象只能包含以下类型的值:
  
  
  布尔值,数字,字符串
  JavaScript对象和数组
  ListModel对象(不允许使用任何其他类型的QObject *)
  
  
  所有对象和数组都复制到消息中。除ListModel对象外,其他线程对传入消息中的对象所做的任何修改都不会反映在原始对象中。


但是,由于它读取了所有复制的元素(ListModel类型的元素除外),因此不可能使用从QObject类或其方法继承的任何对象。



您可以将进度设置为pyqtProperty,以便将其公开给QML,并使用插槽通过QRunnable通过QThreadPool从另一个线程更新其值,该更新通过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)


然后,您可以通过run_bar()启动它,并通过progress属性显示它:

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
            }
        }
    }
}


完整的示例可以在以下link中找到

10-08 14:20