python - 带回调的从 QML 到 Python 的异步调用

标签 python pyqt qml qt5 pyqt5

通过 QML,我想:

  1. 调用 Python 插槽。
  2. 传递回调。
  3. 在槽完成后运行该回调。

我已经尝试过这个:

  1. 注册上下文属性(服务)
  2. 调用 Service.request("data", function (response) { console.log(response) }
  3. 在 Python 中,函数以 QtQml.QJSValue 形式接收
  4. 经过一些昂贵的操作后,该函数在单独的线程中调用

但是,该函数仅有时有效果,大多数时候根本没有效果,或者会使 Python 解释器崩溃。如果我删除对 time.sleep(1) 的调用,则更有可能产生结果。

有什么想法吗?

这是上述内容的非工作实现

ma​​in.qml

import QtQuick 2.3

import "application.js" as App


Rectangle {
    id: appWindow
    width: 200
    height: 200
    Component.onCompleted: App.onLoad()
}

ma​​in.py

import sys
import time

import threading

from PyQt5 import QtCore, QtGui, QtQml, QtQuick


class Service(QtCore.QObject):
    def __init__(self, parent=None):
        super(Service, self).__init__(parent)

    @QtCore.pyqtSlot(str, str, QtCore.QVariant, QtQml.QJSValue)
    def request(self, verb, endpoint, data, cb):
        """Expensive call"""
        print verb, endpoint, data

        self.cb = cb

        def thread():
            time.sleep(1)
            event = QtCore.QEvent(1000)
            event.return_value = "expensive result"
            QtGui.QGuiApplication.postEvent(self, event)

        worker = threading.Thread(target=thread)
        worker.daemon = False
        worker.start()

        self.worker = worker

    def event(self, event):
        if event.type() == 1000:
            self.cb.call([event.return_value])

        return super(Service, self).event(event)


app = QtGui.QGuiApplication(sys.argv)
view = QtQuick.QQuickView()
context = view.rootContext()

service = Service()
context.setContextProperty("Service", service)

view.setSource(QtCore.QUrl("main.qml"))
view.show()
app.exec_()

application.js

"use strict";
/*global print, Service*/


function onLoad() {
    Service.request("POST", "/endpoint", {"data": "value"}, function (reply) {
        print(reply);
        print(reply);
        print(reply);
    });

    print("request() was made");
}

该实现改编自此处
https://github.com/ben-github/PyQt5-QML-CallbackFunction

最好,
马库斯

最佳答案

我发现了一种也有效的替代方法。

差异是:

  1. Python 注册一个新类型,而不是设置上下文属性
  2. Python 不是从 Python 调用 Javascript,而是发出信号

这对我来说似乎更干净,因为 Javascript 永远不需要进入 Python。

ma​​in.qml

import QtQuick 2.0

import "application.js" as App


Rectangle {
    id: appWindow
    width: 200
    height: 200
    Component.onCompleted: App.onLoad()
}

ma​​in.py

import sys
import time
import threading

from PyQt5 import QtCore, QtGui, QtQml, QtQuick


class MockHTTPRequest(QtCore.QObject):
    requested = QtCore.pyqtSignal(QtCore.QVariant)

    @QtCore.pyqtSlot(str, str, QtCore.QVariant)
    def request(self, verb, endpoint, data):
        """Expensive call"""
        print verb, endpoint, data

        def thread():
            time.sleep(1)
            self.requested.emit("expensive result")

        threading.Thread(target=thread).start()

app = QtGui.QGuiApplication(sys.argv)
view = QtQuick.QQuickView()
context = view.rootContext()

QtQml.qmlRegisterType(MockHTTPRequest, 'Service', 1, 0, 'MockHTTPRequest')

view.setSource(QtCore.QUrl("main.qml"))
view.show()
app.exec_()

application.js

"use strict";
/*global print, Service, Qt, appWindow*/


function MockHTTPRequest() {
    return Qt.createQmlObject("import Service 1.0; MockHTTPRequest {}",
                              appWindow, "MockHTTPRequest");
}

function onLoad() {
    var xhr = new MockHTTPRequest();
    xhr.requested.connect(function (reply) {
        print(reply);
    });

    xhr.request("POST", "/endpoint", {"data": "value"});

    print("request() was made");
}

关于python - 带回调的从 QML 到 Python 的异步调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27081376/

相关文章:

qml - Qt Quick 2 场景图是否同步到 EGLFS 上的垂直同步?

javascript - 如何在 QWebEngineView 中将值从 QML 传递给 JavaScript

qt - 如何访问 Loader 的 sourceComponent 中的 QML 对象?

python - 在 DigitalOcean 中运行 python 脚本

python - 如何在python中替换回车并将unix返回转换为windows返回?

python - PyQt:翻译标准按钮

python - 带链接轴的 PyQtGraph 网格

python - Postgres 咨询锁不起作用

python - 如何在 Python 3 中异或两个十六进制字符串?

python - 如何使用 Qthread 通过 PyQt 更新 Matplotlib 图?