从 JavaScript 动态创建 QML 对象
QML 支持在 JavaScript 中动态创建对象。这有助于将对象的实例化延迟到必要时,从而缩短应用程序的启动时间。它还允许动态创建可视对象,并根据用户输入或其他事件添加到场景中。
动态创建对象
通过 JavaScript 动态创建对象有两种方法。您可以调用Qt.createComponent() 动态创建Component 对象,或使用Qt.createQmlObject() 从 QML 字符串创建对象。如果你在 QML 文档中定义了一个现有组件,并想动态创建该组件的实例,那么创建组件的方法会更好。否则,如果对象 QML 本身是在运行时生成的,那么从 QML 字符串创建对象就很有用。
动态创建组件
要动态加载 QML 文件中定义的组件,可调用Qt object 中的Qt.createComponent() 函数。该函数将 QML 文件的 URL 作为唯一参数,并从该 URL 创建Component 对象。
有了Component 后,就可以调用createObject() 方法来创建组件实例。该函数可以接受一个或两个参数:
- 第一个参数是新对象的父对象。父对象可以是图形对象(即Item 类型)或非图形对象(即QtObject 或 C++QObject 类型)。只有具有图形父对象的图形对象才会被渲染到可视化画布上。 Qt Quick可视画布。如果您希望稍后设置父对象,可以放心地将
null
传递给此函数。 - 第二个参数是可选参数,是定义对象初始属性值的属性值对映射。该参数指定的属性值将在对象创建完成前应用到对象中,从而避免了在必须初始化特定属性才能启用其他属性绑定时可能出现的绑定错误。此外,与在创建对象后定义属性值和绑定相比,该方法在性能上也有微小的优势。
下面是一个例子。首先是Sprite.qml
,它定义了一个简单的 QML 组件:
import QtQuick Rectangle { width: 80; height: 50; color: "red" }
我们的主应用程序文件main.qml
导入了一个componentCreation.js
JavaScript 文件,该文件将创建Sprite
对象:
import QtQuick 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()
方法。更多信息,请参阅将信号连接到方法和信号。
还可以通过incubateObject() 函数在不阻塞的情况下实例化组件。
从 QML 字符串创建对象
警告 从 QML 字符串创建对象的速度非常慢,因为每次创建时,引擎都要编译传递的 QML 字符串。此外,在以编程方式构造 QML 代码时,很容易产生无效的 QML。与通过字符串操作生成新的组件相比,将 QML 组件保存为单独的文件并添加属性和方法来定制它们的行为要好得多。
如果 QML 在运行前没有定义,您可以使用Qt.createQmlObject() 函数从 QML 字符串创建 QML 对象,如下例所示:
const newObject = Qt.createQmlObject(` import QtQuick Rectangle { color: "red" width: 20 height: 20 } `, parentItem, "myDynamicSnippet" );
第一个参数是要创建的 QML 字符串。就像在新文件中一样,你需要导入任何你想使用的类型。第二个参数是新对象的父对象,适用于组件的父参数语义同样适用于createQmlObject()
。第三个参数是与新对象关联的文件路径,用于错误报告。
如果 QML 字符串使用相对路径导入文件,路径应相对于定义父对象(方法的第二个参数)的文件。
重要: 构建静态 QML 应用程序时,会扫描 QML 文件以检测导入依赖关系。这样,所有必要的插件和资源都能在 Resource Compiler 编译时解决。不过,只有明确的导入语句(在 QML 文件顶部找到的语句)才会被考虑,而不包括包含在字符串字面意义中的导入语句。因此,要支持静态编译,就必须确保使用Qt.createQmlObject() 的 QML 文件除了在字符串字面内,还在文件顶部明确包含所有必要的导入语句。
维护动态创建的对象
在管理动态创建的对象时,必须确保创建上下文(creation context)在创建对象之后仍然存在。否则,如果创建上下文首先被破坏,动态对象中的绑定和信号处理器将不再工作。
实际的创建上下文取决于创建对象的方式:
- 如果使用Qt.createComponent() 方法,创建上下文就是调用该方法的QQmlContext 。
- 如果调用Qt.createQmlObject() 方法,则创建上下文是传递给该方法的父对象的上下文。
- 如果定义了
Component{}
对象,并且在该对象上调用了createObject() 或incubateObject() 方法,则创建上下文是定义Component
的上下文。
此外,请注意,虽然动态创建的对象与其他对象一样可以使用,但它们在 QML 中没有 id。
动态删除对象
在许多用户界面中,只需将可视对象的不透明度设为 0 或将可视对象移出屏幕,而不用删除它。不过,如果您有大量动态创建的对象,删除未使用的对象可能会为您带来性能上的好处。
请注意,千万不要手动删除方便的 QML 对象工厂(如Loader 和Repeater )动态创建的对象。此外,也应避免删除不是自己动态创建的对象。
可以使用destroy()
方法删除项目。该方法有一个可选参数(默认为 0),用于指定对象被销毁前的大约延迟时间(以毫秒为单位)。
下面是一个示例。application.qml
创建了五个SelfDestroyingRect.qml
组件实例。每个实例运行一个NumberAnimation ,当动画结束后,在其根对象上调用destroy()
来销毁自己:
application.qml | import QtQuick 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 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() 的瞬间被销毁,而是会在脚本块结束到下一帧之间的某个时间被清理(除非您指定了非零的延迟)。
还需注意的是,如果像这样静态创建SelfDestroyingRect
实例:
Item { SelfDestroyingRect { // ... } }
这将导致错误,因为只有动态创建的对象才能被动态销毁。
使用Qt.createQmlObject() 创建的对象同样可以使用destroy()
销毁:
const newObject = Qt.createQmlObject(` import QtQuick Rectangle { color: "red" width: 20 height: 20 } `, parentItem, "myDynamicSnippet" ); newObject.destroy(1000);
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.