Dynamic QML Object Creation from JavaScript
QML支持JavaScript的动态创建对象. 对象仅在必要时创建实例, 可以缩短应用程序的启动时间. 它还允许根据用户输入或其他事件动态创建视觉对象, 并将其添加到场景.
示例参见 Dynamic Scene example.
创建动态对象
QML提供两种方式创建JavaScript动态对象. 你可以调用Qt.createComponent()动态创建Component对象, 也可以调用Qt.createQmlObject(), 使用QML字符串创建对象. 如果已在QML文档中定义组件, 并且希望动态创建这个组件的实例, 则创建组件更好. 否则, 在运行时, 从QML的字符串创建对象是有用的.
动态创建组件
在QML文件中, 调用Qt对象的Qt.createComponent()函数创建动态组件. 这个函数将QML文件的URL作为唯一参数, 并根据URL创建一个Component对象.
一旦有了Component, 就可以调用createObject()方法创建组件的实例. 这个函数可以接受一个或两个参数:
- 第一参数是新对象的父对象. 父对象可以是图形对象(即. Item类型)或非图形对象(即QtObject或C++ QObject类型). 只有具有图形父对象的图形对象才会渲染到Qt Quick视觉画布. 如果你希望稍后设置父级, 则可以将
null
传递给函数. - 第二个参数是可选的, 属性对映射, 定义对象初始属性值. 属性值将在对象创建完成之前应用于对象, 从而避免在必须初始化特定属性以启用其他属性绑定时可能出现的绑定错误. 此外, 与在创建对象后定义属性值和绑定相比, 还有一些小的性能优势.
如下所代码位于Sprite.qml
, 定义一个简单的QML组件:
import QtQuick 2.0 Rectangle { width: 80; height: 50; color: "red" }
主程序文件main.qml
, 导入JavaScript文件componentCreation.js
, 并创建Sprite
对象:
import QtQuick 2.0 import "componentCreation.js" as MyScript Rectangle { id: appWindow width: 300; height: 300 Component.onCompleted: MyScript.createSpriteObjects(); }
下面是componentCreation.js
. 调用createObject()之前, 先检测组件的status是否Component.Ready
, 防止QML文件是从网络加载, 不能立即读取.
var component; var sprite; function createSpriteObjects() { component = Qt.createComponent("Sprite.qml"); if (component.status == Component.Ready) finishCreation(); else component.statusChanged.connect(finishCreation); } function finishCreation() { if (component.status == Component.Ready) { sprite = component.createObject(appWindow, {"x": 100, "y": 100}); if (sprite == null) { // Error Handling console.log("Error creating object"); } } else if (component.status == Component.Error) { // Error Handling console.log("Error loading component:", component.errorString()); } }
如果你确定QML文件是从本地加载, 你可以省略finishCreation()
函数, 立即调用createObject():
function createSpriteObjects() { component = Qt.createComponent("Sprite.qml"); sprite = component.createObject(appWindow, {"x": 100, "y": 100}); if (sprite == null) { // Error Handling console.log("Error creating object"); } }
注意: 在这两个示例中, createObject()将appWindow
作为参数传递, 这是因为动态创建的对象是一个可视(Qt Quick)对象. 创建的对象将成为 main.qml
文件中appWindow
对象的子对象, 并显示在场景中.
使用相对路径的文件时, 路径应该相对Qt.createComponent()的执行路径.
要将信号连接到动态创建的对象(或从中接收信号), 使用信号的connect()
方法. 详见 Connecting Signals to Methods and Signals.
调用 incubateObject() 函数异步实例化组件.
使用QML字符串创建对象
如果QML直到运行时才定义, 你可以调用Qt.createQmlObject()函数, 使用QML字符串创建对象, 如下所示:
var newObject = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "red"; width: 20; height: 20}', parentItem, "dynamicSnippet1");
第一个参数是要创建的QML字符串. 就像在新文件中一样,您需要导入任何想要使用的类型. 第二个参数是新对象的父对象, 应用于组件的父参数语义同样适用于createQmlObject()
. 第三个参数是要与新对象关联的文件路径; 这用于错误报告.
如果QML字符串使用相对路径导入文件, 则该路径是相对于定义父对象(第二个参数)的文件.
重要: 构建静态QML应用程序时, QML引擎扫描QML文件, 检测导入依赖关系. 这样, 所有必要的插件和资源都会在编译时解决. 但是, 只会考虑显式导入语句(位于QML文件顶部), 而不考虑包含在字符串中的导入语句. 因此, 为支持静态构建, 你必需确保使用Qt.createQmlObject()的QML文件, 除了字符串内部外, 还在文件顶部显式包含所有必要导入.
维护动态创建的对象
管理动态创建的对象时, 你必须确保创建上下文环境比创建对象的存在时间长. 否则, 上下文环境销毁, 动态对象的属性绑定和信号处理程序将不再工作.
实际的创建上下文环境取决于对象的创建方式:
- 调用Qt.createComponent(), 创建上下文环境就是调用方法的QQmlContext
- 调用Qt.createQmlObject(), 创建上下文环境就是传入函数的父对象的上下文环境
- 如果定义
Component{}
对象, 在对象里调用createObject()或incubateObject(), 上下文环境就是Component
的上下文环境
此外, 注意, 虽然动态创建的对象可以与其他对象一样使用, 但它们在QML中没有id.
动态删除对象
在许多用户界面中, 将界面的透明度设为0或移出屏幕可以满足要求. 但是, 如果有许多动态创建的对象, 删除未使用的对象可以提高性能.
注意: 你永远不应该手动删除由QML对象工厂(如Loader或Repeater)动态创建的对象. 此外, 你应该避免删除不是你动态创建的对象.
调用destroy()
删除item. 这个方法有一个可选参数(默认为0), 指定销毁对象的时延(单位为ms).
在下面的示例中, application.qml
创建5个实例组件SelfDestroyingRect.qml
. 每个实例运行一个NumberAnimation, 动画完成后, 在根对象上调用destroy()
销毁自身:
application.qml | import QtQuick 2.0 Item { id: container width: 500; height: 100 Component.onCompleted: { var component = Qt.createComponent("SelfDestroyingRect.qml"); for (var i=0; i<5; i++) { var object = component.createObject(container); object.x = (object.width + 10) * i; } } } |
SelfDestroyingRect.qml | import QtQuick 2.0 Rectangle { id: rect width: 80; height: 80 color: "red" NumberAnimation on opacity { to: 0 duration: 1000 onRunningChanged: { if (!running) { console.log("Destroying...") rect.destroy(); } } } } |
或者, application.qml
可以调用object.destroy()
销毁创建的对象.
注意: 在对象里调用destroy()
删除对象是安全的. 调用destroy()
不会立即删除对象, 而是在代码块结尾和下一帧之间的某一时刻清除对象(除非设置非0时延).
注意: 如果SelfDestroyingRect
实例以如下静态方式创建:
Item { SelfDestroyingRect { // ... } }
这将导致错误, 因为只有动态创建的对象才能动态销毁.
调用Qt.createQmlObject()创建的对象, 同样可以调用destroy()
销毁:
var newObject = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "red"; width: 20; height: 20}', parentItem, "dynamicSnippet1"); newObject.destroy(1000);