【问题标题】:Call Python method from Qt Quick WorkerScript从 Qt Quick WorkerScript 调用 Python 方法
【发布时间】: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 docsWorkerScripts 不能使用 import 语法,所以我不确定如何在不导入的情况下访问 Foo

更新:

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

【问题讨论】:

    标签: python qml qt5 pyqt5 qtquick2


    【解决方案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() 需要做几件事,我希望界面显示它在做什么
    猜你喜欢
    • 1970-01-01
    • 2017-08-04
    • 1970-01-01
    • 2018-10-14
    • 1970-01-01
    • 2012-01-27
    • 1970-01-01
    • 2014-10-22
    • 1970-01-01
    相关资源
    最近更新 更多