通过 QML,我想:
- 调用 Python 插槽。
- 传递回调。
- 在槽完成后运行该回调。
我已经尝试过这个:
- 注册上下文属性(
服务
) - 调用
Service.request("data", function (response) { console.log(response) }
- 在 Python 中,函数以
QtQml.QJSValue
形式接收 - 经过一些昂贵的操作后,该函数在单独的线程中调用
但是,该函数仅有时有效果,大多数时候根本没有效果,或者会使 Python 解释器崩溃。如果我删除对 time.sleep(1)
的调用,则更有可能产生结果。
有什么想法吗?
这是上述内容的非工作实现
main.qml
import QtQuick 2.3
import "application.js" as App
Rectangle {
id: appWindow
width: 200
height: 200
Component.onCompleted: App.onLoad()
}
main.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
最好,
马库斯
最佳答案
我发现了一种也有效的替代方法。
差异是:
- Python 注册一个新类型,而不是设置上下文属性
- Python 不是从 Python 调用 Javascript,而是发出信号
这对我来说似乎更干净,因为 Javascript 永远不需要进入 Python。
main.qml
import QtQuick 2.0
import "application.js" as App
Rectangle {
id: appWindow
width: 200
height: 200
Component.onCompleted: App.onLoad()
}
main.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/