QML provides a number of ways to dynamically create and manage QML objects. The Loader, Repeater, ListView, GridView and PathView elements all support dynamic object management. Objects can also be created and managed from C++, and this is the preferred method for hybrid QML/C++ applications (see Using QML Bindings in C++ Applications).
QML also supports the dynamic creation of objects from within JavaScript code. This is useful if the existing QML elements do not fit the needs of your application, and there are no C++ components involved.
See the Dynamic Scene example for a demonstration of the concepts discussed on this page.
There are two ways to create objects dynamically from JavaScript. You can either call Qt.createComponent() to dynamically create a Component object, or use Qt.createQmlObject() to create an item from a string of QML. Creating a component is better if you have an existing component defined in a .qml file, and you want to dynamically create instances of that component. Otherwise, creating an item from a string of QML is useful when the item QML itself is generated at runtime.
To dynamically load a component defined in a QML file, call the Qt.createComponent() function on the QML Global Object. This function takes the URL of the QML file as its only argument and creates a Component object from this URL.
Once you have a Component, you can call its createObject() method to create an instance of the component. This function can take one or two arguments:
Here is an example. First there is Sprite.qml, which defines a simple QML component:
import QtQuick 1.0 Rectangle { width: 80; height: 50; color: "red" }
Our main application file, main.qml, imports a componentCreation.js JavaScript file that will create Sprite objects:
import QtQuick 1.0 import "componentCreation.js" as MyScript Rectangle { id: appWindow width: 300; height: 300 Component.onCompleted: MyScript.createSpriteObjects(); }
Here is componentCreation.js. Notice it checks whether the component status is Component.Ready before calling createObject() in case the QML file is loaded over a network and thus is not ready immediately.
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()); } }
If you are certain the QML file to be loaded is a local file, you could omit the finishCreation() function and call createObject() immediately:
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"); } }
Notice in both instances, createObject() is called with appWindow passed as an argument so that the created object will become a child of the appWindow item in main.qml. Otherwise, the new item will not appear in the scene.
When using files with relative paths, the path should be relative to the file where Qt.createComponent() is executed.
To connect signals to (or receive signals from) dynamically created objects, use the signal connect() method. See Connecting Signals to Methods and Signals for more information.
If the QML is not defined until runtime, you can create a QML item from a string of QML using the Qt.createQmlObject() function, as in the following example:
var newObject = Qt.createQmlObject('import QtQuick 1.0; Rectangle {color: "red"; width: 20; height: 20}', parentItem, "dynamicSnippet1");
The first argument is the string of QML to create. Just like in a new file, you will need to import any types you wish to use. The second argument is the parent item for the new item; this should be an existing item in the scene. The third argument is the file path to associate with the new item; this is used for error reporting.
If the string of QML imports files using relative paths, the path should be relative to the file in which the parent item (the second argument to the method) is defined.
When managing dynamically created items, you must ensure the creation context outlives the created item. Otherwise, if the creation context is destroyed first, the bindings in the dynamic item will no longer work.
The actual creation context depends on how an item is created:
Also, note that while dynamically created objects may be used the same as other objects, they do not have an id in QML.
In many user interfaces, it is sufficient to set an item's opacity to 0 or to move the item off the screen instead of deleting the item. If you have lots of dynamically created items, however, you may receive a worthwhile performance benefit if unused items are deleted.
Note that you should never manually delete items that were dynamically created by QML elements (such as Loader and Repeater). Also, you should avoid deleting items that you did not dynamically create yourself.
Items can be deleted using the destroy() method. This method has an optional argument (which defaults to 0) that specifies the approximate delay in milliseconds before the object is to be destroyed.
Here is an example. The application.qml creates five instances of the SelfDestroyingRect.qml component. Each instance runs a NumberAnimation, and when the animation has finished, calls destroy() on its root item to destroy itself:
application.qml | SelfDestroyingRect.qml |
import QtQuick 1.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; } } } | import QtQuick 1.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(); } } } } |
Alternatively, the application.qml could have destroyed the created object by calling object.destroy().
Note that it is safe to call destroy() on an object within that object. Objects are not destroyed the instant destroy() is called, but are cleaned up sometime between the end of that script block and the next frame (unless you specified a non-zero delay).
Note also that if a SelfDestroyingRect instance was created statically like this:
Item { SelfDestroyingRect { // ... } }
This would result in an error, since items can only be dynamically destroyed if they were dynamically created.
Objects created with Qt.createQmlObject() can similarly be destroyed using destroy():
var newObject = Qt.createQmlObject('import QtQuick 1.0; Rectangle {color: "red"; width: 20; height: 20}', parentItem, "dynamicSnippet1"); newObject.destroy(1000);