当前位置:  开发笔记 > 编程语言 > 正文

从内存而不是URL动态加载QML

如何解决《从内存而不是URL动态加载QML》经验,为你挑选了1个好方法。

是否可以从内存中动态加载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类型的方法.所以我的问题仍然存在.



1> Kevin Kramme..:

一种选择是为QNetworkAccessManager您的QML引擎提供自定义,并通过实现自定义URL方案的处理createRequest().

例如,你的实现createRequest()将检查传递的URL是否有你的方案,让我们说"内存",如果是,它需要URL的其余部分来决定调用哪个函数或传递哪些数据.

对于其他任何东西,它只是调用基础实现.

然后可以像这样加载您的主QML文件

QQuickView view;
view.setSource(QUrl("memory://main.qml"));

QML中的所有URL都是相对于当前文件的,如果没有指定,则查找Simple应该查找memory://Simple.qml

推荐阅读
谢谢巷议
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有