是否可以从内存中动态加载QML而不是使用文件?我见过很多总是需要URL的代码,如下所示:
QGuiApplication app(argc, argv); QQmlEngine* engine = new QQmlEngine; QQmlComponent component(engine, QUrl(QStringLiteral("qrc:/main.qml"))); // ignoring error checking (make sure component is ready, compiles w/o errors, etc) QObject* object = component.create(); app.exec();
在上面的例子中,main.qml
将加载,QML的神奇之处将确保任何类型得到解决.特别是,Qt认为文件的名称是QML类型,它可用于同一目录中的任何其他QML文件.因此,如果main.qml
使用该类型Simple
,它将查找该文件Simple.qml
并将其用作类型定义(http://doc.qt.io/qt-5/qtqml-documents-definetypes.html).
现在我将变得危险:我想使用内存中的QML而不是使用文件.这是它可能工作的方式(请忽略糟糕的设计,内存泄漏等):
QByteArray SimpleQml() { return QByteArray("import QtQuick 2.7; Text {") + "text: \"Hello, World!\"}"; } QByteArray TextExampleQml() { return QByteArray("import QtQuick 2.7; Simple{") + "color: \"Red\"}"; } QObject* createObjectFromMemory(QByteArray qmlData, QQmlEngine* engine, QQmlComponent* componentOut) { componentOut = new QQmlComponent(engine); // note this line: load data from memory (no URL specified) componentOut->setData(qmlData, QUrl()); if (componentOut->status() != QQmlComponent::Status::Ready) { qDebug() << "Component status is not ready"; foreach(QQmlError err, componentOut->errors()) { qDebug() << "Description: " << err.description(); qDebug() << err.toString(); } qDebug() << componentOut->errorString(); return nullptr; } else { return component.create(); } } int main(int argc, char* argv[]) { QGuiApplication app(argc, argv); QQuickView view; QQmlComponent* component = nullptr; // works great: shows a small window with the text "Hello, World!" QObject* object = createObjectFromMemory(SimpleQml(), view.engine(), component); // problem: Simple is not a type // QObject* object = createObjectFromMemory(TextExampleQml(), view.engine(), component); // setContent() is marked as an internal Qt method (but it's public) // see source for qmlscene for why I'm using it here view.setContent(QUrl(), component, object); view.show(); app.exec(); // don't forget to clean up (if you're not doing a demo snippet of code) //delete object; //delete component; }
所以SimpleQml()
简单的是一个带有"Hello,World!"的Text对象.作为文本.当作为数据源createObjectFromMemory()
调用时SimpleQml()
,会出现一个带有文本"Hello,World!"的小窗口.
我正在使用QQuickView :: setContent(),这是一个在Qt源中标记为内部的函数,但它在qmlscene中使用,我试图在这里实现类似的效果(https://code.woboq .org/qt5/qtdeclarative/tools/qmlscene/main.cpp.html #main).
显然,问题是当我尝试作为数据源调用createObjectFromMemory()
时TextExampleQml()
.QML引擎不知道Simple
应该是一个类型 - 我怎么能告诉QML这个类型?我认为qmlRegisterType
(http://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterType)可能是一个很好的候选者,但我找不到一种方法来注册来自QML坐的大块的类型在记忆中.我想说,"嘿,我有这块QML代码,它的类型是'简单'."
假设我可以轻松预测所有类型的依赖关系,并且能够知道加载QML内存块的正确顺序.我可以将SimpleQml()返回的QML代码注册为类型Simple
(将其保存在内存中,而不是将其保存到文件中)吗?如果有的话,我想了解QML引擎如何做到这一点.
我意识到我正在打破一条黄金法则:不要从C++进入QML(而是从QML进入C++).因此,如果任何人都有一个更清洁的JavaScript解决方案,我愿意接受建议,但请记住我正在尝试从内存加载,而不是从文件加载.
更新:
如评论中所述,有一个名为createQmlObject()的JavaScript函数,它从字符串创建一个QML对象,这与我在上面的C++中所做的非常相似.然而,这也遇到了同样的问题 - createQmlObject
创建的类型是......好吧,它不是一个类型,它是一个对象.我想要的是一种将内存中QML字符串注册为QML类型的方法.所以我的问题仍然存在.
一种选择是为QNetworkAccessManager
您的QML引擎提供自定义,并通过实现自定义URL方案的处理createRequest()
.
例如,你的实现createRequest()
将检查传递的URL是否有你的方案,让我们说"内存",如果是,它需要URL的其余部分来决定调用哪个函数或传递哪些数据.
对于其他任何东西,它只是调用基础实现.
然后可以像这样加载您的主QML文件
QQuickView view; view.setSource(QUrl("memory://main.qml"));
QML中的所有URL都是相对于当前文件的,如果没有指定,则查找Simple
应该查找memory://Simple.qml