如何为 Qt 5.5 + QtQuick 2.5 定义 Qml 组件文件解析器?

Posted

技术标签:

【中文标题】如何为 Qt 5.5 + QtQuick 2.5 定义 Qml 组件文件解析器?【英文标题】:How to define Qml component file resolver for Qt 5.5 + QtQuick 2.5? 【发布时间】:2016-01-06 16:52:36 【问题描述】:

有什么方法可以为 QmlEngine 中缺少的 Qml 组件实现动态文件解析器?如何实现外部资源动态加载到QmlEngine?

我可以使用以下 sn-p 从任何数据流中加载 qml 组件:

QUrl uri(...)
QByteArray data(...);
QQmlComponent::setData(data,uri);

但是当传递的组件引用另一个组件(尚未加载)时,QmlEngine 由于缺少资源而停止。

是否有任何事件/回调可以处理此类丢失的资源?

添加的用例场景:

我们正在开发一些 QML 可视化组件。一些组件实现为.qml files,一些实现为QQuickItem

例如想象以下情况(非常简化):

QmlItem“DiagramPoint”,在point.qml中实现 QmlItem line.qml 中实现的“Line”,类使用“DiagramPoint”项 QQuickItem (c++) "ConnectedLine" 内部使用 "Line" 对象 QmlProject 使用“ConnectedLine”组件。

如果 point.qml 和 line.qml 位于硬盘上或存储在 Qt 资源中,一切都会自动运行。但是我们想要实现的是将这些文件以加密形式存储在我们内部的.dat file 中,并且仅在需要时对其进行解码。

我们能够从“ConnectedLine”实现中解码和加载“Line”对象。但是如果“Line”(line.qml)依赖于另一个加密文件“DiagramPoint”(point.qml),这个解决方案就不起作用了。

另一种可能的解决方案

另一种解决方案是在应用程序启动时注册所有解密的 .qml 文件,然后使用它。类似于qmlRegisterType 的东西,它允许将c++ QQuickItems 注册到QmlEngine

不幸的是,这些方法都不允许从字符串缓冲区注册 Qml sn-p。

【问题讨论】:

QQmlAbstractUrlInterceptor 有没有成功?我也在寻找一种方法来保护我的 QML 代码免受抄袭,我考虑了拦截器,但我认为它只处理 url,而不是你在 QML 声明性代码中使用的 QML 类型的解析。我考虑了一个解决方案,其中QQmlComponents 是从内存中的字符串创建的,存储在加密的二进制文件中,但似乎没有办法做到这一点 - 就像 QtQuick API 是故意设计的,没有隐藏的方法您的代码,除非您为商业许可和 QML 编译器付费... 我的意思是虽然可以从数据创建QQmlComponent,但似乎无法将其注册为可在 QML 源中使用的 QML 类型。在我的情况下,不同的组件也相互依赖。 不幸的是,当您尝试用这种方式实现它时,会遇到很多困难。显然,Qt 开发人员只为单一场景准备了所有库 - 全部在打开的 Qml 中开发并从 c++ 注入数据。出于我们的目的,我们决定将所有基本 Qml 对象实现为 QQuickItems,将其注册到 QmlEngine,而不是将 Qml 仅用作这些 QQuickItems 的渲染引擎和装饰器。这似乎是唯一有效的方法。所有其他可能性都基于使用 QQmlComponent::setData 从字符串创建对象,但这对我来说绝对是错误的。 如果你能找到其他方法,你能给我寄一些样品吗?在寻找合理的解决方案时,我在互联网上花费了数十个小时,但这是唯一的一个。不幸的是,关于这种高级 Qml 用法的信息很少。 我将在这里发布任何进展***.com/questions/34814738/… 【参考方案1】:

我仍然有点不确定你会如何做到这一点,但你可能会发现 QQmlAbstractUrlInterceptor 很有用:

QQmlAbstractUrlInterceptor 是一个接口,可用于在 QML 引擎使用 URL 之前更改它们。这主要用于将文件 url 更改为其他文件 url,例如为当前平台选择不同的图形资源。

相对 URL 在根据当前 QML 上下文的文件路径解析后被拦截。在为加载的 QML 文件设置基本路径后,也会发生 URL 拦截。这意味着为该 QML 文件加载的内容使用截获的 URL,但在文件内部,预截获的 URL 用于解析相对路径。这允许拦截 .qml 文件加载,而无需拦截内容中的所有路径(或本地类型)插入不同的相对路径。

与setNetworkAccessManagerFactory相比,QQmlAbstractUrlInterceptor影响所有的URL和路径,包括本地文件和嵌入的资源文件。 QQmlAbstractUrlInterceptor 是同步的,对于异步文件必须返回一个带有异步方案的 url(例如 http 或由您自己的自定义 QNetworkAccessManager 处理的自定义方案)。您可以使用 QQmlAbstractUrlInterceptor 将文件 URL 更改为网络由您自己的自定义 QNetworkAccessManager 处理的 URL。

要实现对自定义网络方案的支持,请参阅 setNetworkAccessManagerFactory。

它说它是同步的,所以也许你可以在截获 URL 时解码 QML 文件以确保它们存在?

【讨论】:

谢谢,我会试试的。我不知道。 看来 QQmlAbstractUrlInterceptor 和自定义 QNetworkAccessManager 是解决这个问题的正确方法。谢谢回复。 哈!凉爽的!那么解码对 QML 的加载时间没有任何明显的影响呢? 老实说,我们只做了一个小的测试用例,尽管我们最终没有使用它。 Qml 项目的整个层次结构非常复杂,以至于我们决定尝试另一种方法。现在我们将所有基础对象实现为 c++ QQuickItems,一切似乎都变得容易得多。 绝对;-)。这是我们第一个使用 Qml 的项目,要找到正确的方法来设计整个应用程序架构是非常棘手的。但似乎使用 Qml 引擎以 c++ 对象为核心,而不是仅使用 Qml 视觉元素来装饰对象是一个不错的方法。

以上是关于如何为 Qt 5.5 + QtQuick 2.5 定义 Qml 组件文件解析器?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 QtQuick 应用程序选择 OpenGL 上下文

QT开发编译问题备忘

如何在 Ubuntu 上安装 QtQuick.Controls 1.4?

[Qt Quick入门] 基本元素初体验

在 qt creator 中从 ..qml 文件生成 .cpp 和 .h 文件

QT开发(五十一)——QtQuick基础