Dynamische QML-Objekterstellung aus JavaScript
QML unterstützt die dynamische Erstellung von Objekten aus JavaScript heraus. Dies ist nützlich, um die Instanziierung von Objekten bis zur Notwendigkeit zu verzögern und so die Startzeit der Anwendung zu verbessern. Außerdem können visuelle Objekte dynamisch erstellt und der Szene als Reaktion auf Benutzereingaben oder andere Ereignisse hinzugefügt werden.
Dynamische Erstellung von Objekten
Es gibt zwei Möglichkeiten, Objekte dynamisch von JavaScript aus zu erstellen. Sie können entweder Qt.createComponent() aufrufen, um dynamisch ein Component Objekt zu erstellen, oder Qt.createQmlObject() verwenden, um ein Objekt aus einer QML-Zeichenkette zu erstellen. Das Erstellen einer Komponente ist besser, wenn Sie eine bestehende Komponente in einem QML-Dokument definiert haben und dynamisch Instanzen dieser Komponente erstellen möchten. Ansonsten ist das Erstellen eines Objekts aus einer QML-Zeichenkette nützlich, wenn das Objekt QML selbst zur Laufzeit generiert wird.
Dynamische Erstellung einer Komponente
Um eine in einer QML-Datei definierte Komponente dynamisch zu laden, rufen Sie die Funktion Qt.createComponent() in der Qt object auf. Diese Funktion nimmt die URL der QML-Datei als einziges Argument und erstellt ein Component Objekt aus dieser URL.
Sobald Sie eine Component haben, können Sie deren Methode createObject() aufrufen, um eine Instanz der Komponente zu erstellen. Diese Funktion kann ein oder zwei Argumente annehmen:
- Das erste ist das Elternteil für das neue Objekt. Das Elternteil kann ein grafisches Objekt (d.h. vom Typ Item ) oder ein nicht-grafisches Objekt (d.h. vom Typ QtObject oder C++ QObject ) sein. Nur grafische Objekte mit grafischen Elternobjekten werden auf der Qt Quick visuellen Leinwand gerendert. Wenn Sie das übergeordnete Objekt später festlegen möchten, können Sie
null
an diese Funktion übergeben. - Das zweite Argument ist optional und besteht aus einer Zuordnung von Eigenschafts-Werte-Paaren, die die anfänglichen Eigenschaftswerte für das Objekt definieren. Die durch dieses Argument angegebenen Eigenschaftswerte werden auf das Objekt angewandt, bevor seine Erstellung abgeschlossen ist, wodurch Bindungsfehler vermieden werden, die auftreten können, wenn bestimmte Eigenschaften initialisiert werden müssen, um andere Eigenschaftsbindungen zu ermöglichen. Außerdem gibt es kleine Leistungsvorteile im Vergleich zur Definition von Eigenschaftswerten und Bindungen nach der Erstellung des Objekts.
Hier ist ein Beispiel. Zunächst ist da Sprite.qml
, das eine einfache QML-Komponente definiert:
import QtQuick Rectangle { width: 80; height: 50; color: "red" }
Unsere Hauptanwendungsdatei, main.qml
, importiert eine componentCreation.js
JavaScript-Datei, die Sprite
Objekte erstellt:
import QtQuick import "componentCreation.js" as MyScript Rectangle { id: appWindow width: 300; height: 300 Component.onCompleted: MyScript.createSpriteObjects(); }
Hier ist componentCreation.js
. Beachten Sie, dass vor dem Aufruf von createObject() geprüft wird, ob es sich bei der Komponente status um Component.Ready
handelt, falls die QML-Datei über ein Netzwerk geladen wird und daher nicht sofort verfügbar ist.
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()); } }
Wenn Sie sicher sind, dass es sich bei der zu ladenden QML-Datei um eine lokale Datei handelt, können Sie die Funktion finishCreation()
weglassen und sofort createObject() aufrufen:
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"); } }
Beachten Sie, dass in beiden Fällen createObject() mit appWindow
als übergeordnetem Argument aufgerufen wird, da das dynamisch erstellte Objekt ein visuelles Objekt (Qt Quick) ist. Das erstellte Objekt wird ein untergeordnetes Objekt des appWindow
Objekts in main.qml
und erscheint in der Szene.
Wenn Sie Dateien mit relativen Pfaden verwenden, sollte der Pfad relativ zu der Datei sein, in der Qt.createComponent() ausgeführt wird.
Um Signale mit dynamisch erstellten Objekten zu verbinden (oder Signale von ihnen zu empfangen), verwenden Sie die Methode signal connect()
. Weitere Informationen finden Sie unter Verbinden von Signalen mit Methoden und Signalen.
Es ist auch möglich, Komponenten ohne Blockierung über die Funktion incubateObject() zu instanziieren.
Erstellen eines Objekts aus einem String von QML
Warnung: Das Erzeugen von Objekten aus einer QML-Zeichenkette ist extrem langsam, da die Engine die übergebene QML-Zeichenkette jedes Mal kompilieren muss, wenn Sie dies tun. Außerdem ist es sehr einfach, ungültige QML zu erzeugen, wenn man QML-Code programmatisch konstruiert. Es ist viel besser, Ihre QML-Komponenten als separate Dateien zu speichern und Eigenschaften und Methoden hinzuzufügen, um ihr Verhalten anzupassen, als neue Komponenten durch Stringmanipulation zu erzeugen.
Wenn QML erst zur Laufzeit definiert wird, können Sie mit der Funktion Qt.createQmlObject() ein QML-Objekt aus einer QML-Zeichenkette erstellen, wie im folgenden Beispiel:
const newObject = Qt.createQmlObject(` import QtQuick Rectangle { color: "red" width: 20 height: 20 } `, parentItem, "myDynamicSnippet" );
Das erste Argument ist die zu erstellende QML-Zeichenkette. Genau wie bei einer neuen Datei müssen Sie alle Typen, die Sie verwenden möchten, importieren. Das zweite Argument ist das übergeordnete Objekt für das neue Objekt, und die Semantik des übergeordneten Arguments, die für Komponenten gilt, gilt auch für createQmlObject()
. Das dritte Argument ist der Dateipfad, der mit dem neuen Objekt verknüpft werden soll; dieser wird für die Fehlerberichterstattung verwendet.
Wenn der String von QML Dateien mit relativen Pfaden importiert, sollte der Pfad relativ zu der Datei sein, in der das Elternobjekt (das zweite Argument der Methode) definiert ist.
Wichtig: Bei der Erstellung statischer QML-Anwendungen werden die QML-Dateien gescannt, um Importabhängigkeiten zu erkennen. Auf diese Weise werden alle notwendigen Plugins und Ressourcen zur Kompilierzeit aufgelöst. Es werden jedoch nur explizite Importanweisungen berücksichtigt (die am Anfang einer QML-Datei stehen), nicht jedoch Importanweisungen, die in String-Literalen eingeschlossen sind. Um statische Builds zu unterstützen, müssen Sie daher sicherstellen, dass QML-Dateien, die Qt.createQmlObject() verwenden, alle notwendigen Importe explizit am Anfang der Datei und zusätzlich innerhalb der String-Literale enthalten.
Pflege von dynamisch erzeugten Objekten
Bei der Verwaltung von dynamisch erstellten Objekten müssen Sie sicherstellen, dass der Erstellungskontext das erstellte Objekt überdauert. Wenn der Erstellungskontext zuerst zerstört wird, funktionieren die Bindungen und Signalhandler im dynamischen Objekt nicht mehr.
Der tatsächliche Erstellungskontext hängt davon ab, wie ein Objekt erstellt wird:
- Wenn Qt.createComponent() verwendet wird, ist der Erstellungskontext der QQmlContext, in dem diese Methode aufgerufen wird.
- Wenn Qt.createQmlObject() aufgerufen wird, ist der Erstellungskontext der Kontext des übergeordneten Objekts, das an diese Methode übergeben wird.
- Wenn ein
Component{}
Objekt definiert ist und createObject() oder incubateObject() für dieses Objekt aufgerufen wird, ist der Erstellungskontext der Kontext, in demComponent
definiert ist.
Beachten Sie auch, dass dynamisch erzeugte Objekte zwar wie andere Objekte verwendet werden können, aber in QML keine ID haben.
Dynamisches Löschen von Objekten
In vielen Benutzeroberflächen reicht es aus, die Deckkraft eines visuellen Objekts auf 0 zu setzen oder das visuelle Objekt vom Bildschirm zu entfernen, anstatt es zu löschen. Wenn Sie jedoch viele dynamisch erstellte Objekte haben, kann es sich für Sie lohnen, nicht verwendete Objekte zu löschen.
Beachten Sie, dass Sie niemals Objekte manuell löschen sollten, die dynamisch durch komfortable QML-Objektfabriken (wie Loader und Repeater) erstellt wurden. Auch sollten Sie es vermeiden, Objekte zu löschen, die Sie nicht selbst dynamisch erstellt haben.
Objekte können mit der Methode destroy()
gelöscht werden. Diese Methode hat ein optionales Argument (Standardwert 0), das die ungefähre Verzögerung in Millisekunden angibt, bevor das Objekt zerstört werden soll.
Hier ist ein Beispiel. Das Programm application.qml
erstellt fünf Instanzen der Komponente SelfDestroyingRect.qml
. Jede Instanz führt eine NumberAnimation aus, und wenn die Animation beendet ist, ruft sie destroy()
auf ihrem Stammobjekt auf, um sich selbst zu zerstören:
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(); } } } } |
Alternativ dazu hätte application.qml
das erstellte Objekt durch den Aufruf von object.destroy()
zerstören können.
Beachten Sie, dass es sicher ist, destroy() für ein Objekt innerhalb dieses Objekts aufzurufen. Objekte werden nicht in dem Moment zerstört, in dem destroy() aufgerufen wird, sondern werden irgendwann zwischen dem Ende dieses Skriptblocks und dem nächsten Frame aufgeräumt (es sei denn, Sie haben eine Verzögerung ungleich Null angegeben).
Beachten Sie auch, dass wenn eine SelfDestroyingRect
Instanz statisch auf diese Weise erstellt wurde:
Item { SelfDestroyingRect { // ... } }
Dies würde zu einem Fehler führen, da Objekte nur dynamisch zerstört werden können, wenn sie dynamisch erstellt wurden.
Objekte, die mit Qt.createQmlObject() erstellt wurden, können ebenfalls mit destroy()
zerstört werden:
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.