从 Qt Quick WorkerScript 调用 Python 方法

Posted

技术标签:

【中文标题】从 Qt Quick WorkerScript 调用 Python 方法【英文标题】:Call Python method from Qt Quick WorkerScript 【发布时间】:2018-01-01 20:34:01 【问题描述】:

如何从 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 调用任何长时间运行的函数。

问题:

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

Per the docs、WorkerScripts 不能使用 import 语法,所以我不确定如何在不导入的情况下访问 Foo

更新:

我需要 UI 能够显示来自 Foo.bar() 的进度,因为该函数需要一些时间并且会执行几项操作。

【问题讨论】:

【参考方案1】:

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

sendMessage(jsobject 消息)

将给定消息发送到另一个线程中的工作脚本处理程序。 另一个工作脚本处理程序可以通过 onMessage() 处理程序。

消息对象只能包含以下类型的值:

布尔值、数字、字符串 javascript 对象和数组 ListModel 对象(不允许使用任何其他类型的 QObject*)

所有对象和数组都被复制到消息中。除 ListModel 对象外,其他线程对传入消息的对象所做的任何修改都不会反映在原始对象中。

但由于它读取的所有元素都是复制的(除了那些类型为 ListModel 的元素),所以不可能使用任何继承自 QObject 类或其方法的对象。


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

【讨论】:

这是否意味着您可以在 Qt Quick 中从后台线程运行 Javascript,但不能从 Python 或 C++ 运行? 我只是觉得这很令人惊讶;您可以在 WorkerThread 中运行后端代码似乎是合乎逻辑的 如果我使用QThread,我是否能够向 UI 发送信号以更新状态栏? Foo.bar() 需要做几件事,我希望界面显示它在做什么

以上是关于从 Qt Quick WorkerScript 调用 Python 方法的主要内容,如果未能解决你的问题,请参考以下文章

通过消息信号从 QML WorkerScript.sendMessage 调用传递数据

Qt Quick 从角色返回对象

Qt Quick:如何从ComboBox获取当前文本

Qt Quick:如何从 ComboBox 获取当前文本

qt-quick(qml) 应用程序无法订阅 ros 主题

从 qml 中的 .txt 或 .csv 文件中读取一行(Qt Quick)