异步调用与回调
问题描述:
从QML到Python,我想:异步调用与回调
- 呼叫一个Python插槽。
- 传递一个回调。
- 插槽完成后是否运行该回调。
我已经试过这样:
- 注册上下文属性(
Service
) - 呼叫
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
最佳改编,
马库斯
答
没有从文档表明QJSValue
是线程安全的。 This page表示可重入或线程安全的类在文档中标记为这样。但是,QJSValue
页面上没有提及该字的线程。
因此,我建议你确保你的回调只从主线程调用。显然,你仍然希望将一个长时间运行的任务放在一个线程中,所以我建议你使用类似QCoreApplication.postEvent()
的东西来从Python线程向主线程发送一个事件,然后主线程会调用你的回调函数。
注:我已将PyQt4 here的呼叫打包为QCoreApplication.postEvent
。如果您需要帮助了解如何使用QCoreApplication.postEvent
方法,则可以调整它以使用PyQt5。
答
我发现了一种可行的替代方法。
的区别是:
- 的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");
}
这是最有可能的是,甚至没有考虑这个! – 2014-11-23 09:03:40
我给出了这个例子,并且更新了这个例子,你会介意再看一次吗?它还没有完全切断它,在使用信号而不是事件时它也不起作用,并且我刚刚从创意中脱颖而出。 – 2014-11-23 09:45:14
我目前没有PyQt5。事件()被调用吗? (你可以插入一个print语句来检查) – 2014-11-23 10:14:59