Aufbau eines QML-Dokuments

Ein QML-Dokument ist ein eigenständiges Stück QML-Quellcode, das aus drei Teilen besteht:

  • Eine optionale Liste von Pragmas
  • Seine Import-Anweisungen
  • einer einzelnen Root-Objekt-Deklaration

Konventionell trennt eine einzelne Leerzeile die Importe von der Definition der Objekthierarchie.

QML-Dokumente sind immer im UTF-8-Format kodiert.

Pragmas

Pragmas sind Anweisungen an die QML-Engine selbst, die verwendet werden können, um bestimmte Eigenschaften von Objekten in der aktuellen Datei zu spezifizieren oder um zu ändern, wie die Engine Code interpretiert. Die folgenden Pragmas werden im Folgenden näher erläutert.

  • Singleton
  • ListPropertyAssignBehavior
  • ComponentBehavior
  • FunctionSignatureBehavior
  • NativeMethodBehavior
  • ValueTypeBehavior
  • Translator

Singleton

pragma Singleton deklariert die im QML-Dokument definierte Komponente als Singleton. Singletons werden nur einmal pro QML-Engine erstellt. Um ein in QML deklariertes Singleton zu verwenden, müssen Sie es auch bei seinem Modul registrieren. Siehe qt_target_qml_sources, wie man dies mit CMake macht.

ListPropertyAssignBehavior

Mit diesem Pragma können Sie festlegen, wie Zuweisungen zu Listeneigenschaften in Komponenten, die im QML-Dokument definiert sind, behandelt werden sollen. Standardmäßig wird die Zuweisung an eine Listeneigenschaft an die Liste angehängt. Sie können dieses Verhalten mit dem Wert Append explizit anfordern. Alternativ können Sie mit Replace verlangen, dass der Inhalt von Listeneigenschaften immer ersetzt wird, oder mit ReplaceIfNotDefault, dass er ersetzt wird, wenn die Eigenschaft nicht die Standardeigenschaft ist. Ein Beispiel:

pragma ListPropertyAssignBehavior: ReplaceIfNotDefault

Hinweis: Die gleiche Deklaration kann auch für C++-definierte Typen gegeben werden, indem die Makros QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_APPEND, QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE und QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT zur Klassendeklaration hinzugefügt werden.

ComponentBehavior

Sie können mehrere Komponenten in derselben QML-Datei definieren. Der Wurzelbereich der QML-Datei ist eine Komponente, und Sie können zusätzlich Elemente des Typs QQmlComponent, explizit oder implizit als Eigenschaften erstellt, oder Inline-Komponenten haben. Diese Komponenten sind verschachtelt. Jede der inneren Komponenten befindet sich innerhalb einer bestimmten äußeren Komponente. In den meisten Fällen sind die in einer äußeren Komponente definierten IDs in allen geschachtelten inneren Komponenten zugänglich. Sie können jedoch Elemente aus einer Komponente in einem beliebigen anderen Kontext erstellen, in dem andere IDs verfügbar sind. Dadurch wird die Annahme, dass die äußeren IDs verfügbar sind, aufgehoben. Daher können die Engine und das QML-Tooling im Allgemeinen nicht im Voraus wissen, welcher Typ, wenn überhaupt, solche IDs zur Laufzeit auflösen wird.

Mit dem ComponentBehavior-Pragma können Sie alle in einer Datei definierten inneren Komponenten so einschränken, dass sie nur Objekte innerhalb ihres ursprünglichen Kontexts erzeugen. Wenn eine Komponente an ihren Kontext gebunden ist, können Sie sicher IDs von äußeren Komponenten in derselben Datei innerhalb der Komponente verwenden. Das QML-Tooling geht dann davon aus, dass die äußeren IDs mit ihren spezifischen Typen verfügbar sind.

Um die Komponenten an ihren Kontext zu binden, geben Sie das Argument Bound an:

pragma ComponentBehavior: Bound

Dies impliziert, dass im Falle von Namenskonflikten IDs, die außerhalb einer gebundenen Komponente definiert sind, lokale Eigenschaften von Objekten überschreiben, die aus der Komponente erzeugt wurden. Andernfalls wäre es nicht wirklich sicher, die IDs zu verwenden, da spätere Versionen eines Moduls der Komponente weitere Eigenschaften hinzufügen könnten. Wenn die Komponente nicht gebunden ist, haben lokale Eigenschaften Vorrang vor IDs, die außerhalb der Komponente definiert sind, aber nicht vor IDs, die innerhalb der Komponente definiert sind.

Im folgenden Beispiel wird die Eigenschaft r des Objekts ListView mit der ID color ausgegeben, nicht die Eigenschaft r der Farbe des Rechtecks.

pragma ComponentBehavior: Bound
import QtQuick

ListView {
 id: color
 property int r: 12
 model: 1

 delegate: Rectangle {
  Component.onCompleted: console.log(color.r)
 }
}

Der Standardwert von ComponentBehavior ist Unbound. Sie können ihn auch explizit angeben. In einer zukünftigen Version von Qt wird der Standardwert auf Bound geändert.

Delegate-Komponenten, die an ihren Kontext gebunden sind, erhalten bei der Instanziierung keine eigenen privaten Kontexte. Das bedeutet, dass Modelldaten in diesem Fall nur über erforderliche Eigenschaften übergeben werden können. Die Übergabe von Modelldaten über Kontexteigenschaften wird nicht funktionieren. Dies betrifft z.B. Delegates zu Instantiator, Repeater, ListView, TableView, GridView, TreeView und generell alles, was DelegateModel intern verwendet.

Zum Beispiel wird das Folgende nicht funktionieren:

pragma ComponentBehavior: Bound
import QtQuick

ListView {
 delegate: Rectangle {
     color: model.myColor
 }
}

Die Eigenschaft delegate von ListView ist eine Komponente. Daher wird hier implizit eine Component um die Rectangle erstellt. Diese Komponente ist an ihren Kontext gebunden. Sie erhält nicht die Context-Eigenschaft model, die von ListView bereitgestellt wird. Damit es funktioniert, müssten Sie es so schreiben:

pragma ComponentBehavior: Bound
import QtQuick

ListView {
 delegate: Rectangle {
     required property color myColor
     color: myColor
 }
}

Sie können Komponenten in einer QML-Datei verschachteln. Das Pragma gilt für alle Komponenten in der Datei, unabhängig davon, wie tief sie verschachtelt sind.

FunctionSignatureBehavior

Mit diesem Pragma können Sie die Art und Weise ändern, wie Typ-Annotationen auf Funktionen behandelt werden. Seit Qt 6.7 werden Typ-Annotationen beim Aufruf von Funktionen erzwungen. Vorher hat nur der QML-Skript-Compiler die Typ-Annotationen durchgesetzt. Der Interpreter und der JIT-Compiler ignorierten sie. Das ständige Erzwingen der Typ-Annotationen ist eine Verhaltensänderung im Vergleich zu früheren Versionen, da man vorher Funktionen mit nicht übereinstimmenden Argumenten aufrufen konnte.

Die Angabe von Ignored als Wert führt dazu, dass die QML-Engine und der QML-Skript-Compiler alle Typ-Annotationen ignorieren und somit das Verhalten von Interpreter und JIT vor Version 6.7 wiederherstellen. Infolgedessen wird weniger Code im Voraus nach C++ kompiliert, und mehr Code muss interpretiert oder JIT-kompiliert werden.

Die Angabe von Enforced als Wert gibt explizit den Standardwert an: Typ-Annotationen werden immer erzwungen.

NativeMethodBehavior

Der Aufruf von C++-Methoden mit this -Objekten, die sich von dem Objekt unterscheiden, von dem sie abgerufen wurden, ist aus historischen Gründen nicht mehr möglich. Das ursprüngliche Objekt wird als this Objekt verwendet. Sie können die Verwendung des angegebenen this -Objekts zulassen, indem Sie pragma NativeMethodBehavior: AcceptThisObject setzen. Die Angabe von RejectThisObject behält das historische Verhalten bei.

Ein Beispiel hierfür finden Sie unter C++-Methoden und dem 'this'-Objekt.

ValueTypeBehavior

Mit diesem Pragma können Sie die Art und Weise ändern, wie Wertetypen und Sequenzen behandelt werden.

Normalerweise können klein geschriebene Namen keine Typnamen in JavaScript-Code sein. Dies ist ein Problem, da Wertetypen-Namen klein geschrieben werden. Sie können Addressable als Wert für dieses Pragma angeben, um dies zu ändern. Wenn Addressable angegeben wird, kann ein JavaScript-Wert explizit zu einem bestimmten, benannten Wertetyp gezwungen werden. Dies geschieht mit dem Operator as, wie bei Objekttypen. Darüber hinaus können Sie auch mit dem Operator instanceof nach Werttypen suchen:

pragma ValueTypeBehavior: Addressable
import QtQml

QtObject {
 property var a
 property real b: (a as rect).x
 property bool c: a instanceof rect

 property var rect // inaccessible. "rect" is a type name.
}

Da rect im obigen Beispiel nun ein Typname ist, wird er alle Eigenschaften mit dem Namen rect überschatten.

Das explizite Casting auf den gewünschten Typ hilft beim Tooling. Es kann es dem Qt Quick Compiler effizienten Code zu generieren, wo dies sonst nicht möglich wäre. Sie können qmllint verwenden, um solche Vorkommnisse zu finden.

Es gibt auch einen Inaddressable Wert, den Sie verwenden können, um das Standardverhalten explizit festzulegen.

Ein weiteres Attribut zum ValueTypeBehavior pragma ist Assertable, eingeführt in Qt 6.8. Aufgrund eines Fehlers in Qt 6.6 und 6.7 prüft das obige a as rect nicht nur, ob a ein rect ist, sondern konstruiert auch ein rect, wenn a von einem kompatiblen Typ ist. Dies ist offensichtlich nicht das, was eine Typ-Assertion tun sollte. Die Angabe von Assertable verhindert dieses Verhalten und schränkt Typ-Assertions für Werttypen darauf ein, nur den Typ zu prüfen. Sie sollten dies immer angeben, wenn Sie Wertetypen mit as verwenden. Wenn die Typzusicherung für einen Wertetyp fehlschlägt, ist das Ergebnis undefined.

instanceof hat dieses Problem nicht, da es nur auf Vererbung prüft, nicht auf alle möglichen Typenzwänge.

Hinweis: Die Verwendung von as mit den Typen int und double ist nicht ratsam, da nach den JavaScript-Regeln das Ergebnis jeder Berechnung eine Fließkommazahl ist, auch wenn es zufällig denselben Wert wie sein ganzzahliges Äquivalent hat. Umgekehrt ist jede Integer-Konstante, die Sie in JavaScript deklarieren, nach den QML-Typzuordnungsregeln kein Double. Außerdem sind int und double reservierte Wörter. Sie können diese Typen nur über Typ-Namensräume ansprechen.

Wertetypen und Sequenzen werden generell als Referenzen behandelt. Das heißt, wenn Sie eine Werttypinstanz aus einer Eigenschaft in einen lokalen Wert abrufen und dann den lokalen Wert ändern, wird auch die ursprüngliche Eigenschaft geändert. Wenn Sie außerdem die ursprüngliche Eigenschaft explizit schreiben, wird auch der lokale Wert aktualisiert. Dieses Verhalten ist an vielen Stellen eher unintuitiv, und Sie sollten sich nicht darauf verlassen. Die Werte Copy und Reference für das Pragma ValueTypeBehavior sind experimentelle Optionen zur Änderung dieses Verhaltens. Sie sollten sie nicht verwenden. Die Angabe von Copy bewirkt, dass alle Wertetypen als tatsächliche Kopien behandelt werden. Durch die Angabe von Reference wird das Standardverhalten explizit festgelegt.

Anstatt Copy zu verwenden, sollten Sie Verweise auf Werttypen und Sequenzen immer dann explizit neu laden, wenn sie von Seiteneffekten betroffen sein könnten. Seiteneffekte können immer dann auftreten, wenn Sie eine Funktion aufrufen oder eine Eigenschaft zwingend setzen müssen. qmllint bietet hierzu eine Anleitung. Im folgenden Code ist zum Beispiel die Variable f nach dem Schreiben von width von Seiteneffekten betroffen. Das liegt daran, dass es möglicherweise eine Bindung in einem abgeleiteten Typ oder in einem Element Binding gibt, die font aktualisiert, wenn width geändert wird.

import QtQuick
Text {
 function a() : real {
     var f = font;
     width = f.pixelSize;
     return f.pointSize;
 }
}

Um dies zu vermeiden, können Sie f während des Schreibvorgangs auf width nicht halten:

import QtQuick
Text {
 function a() : real {
     var f = font;
     width = f.pixelSize;
     f = font;
     return f.pointSize;
 }
}

Dies kann wiederum auf verkürzt werden:

import QtQuick
Text {
 function a() : real {
     width = font.pixelSize;
     return font.pointSize;
 }
}

Man könnte annehmen, dass das erneute Abrufen der Eigenschaft font kostspielig ist, aber tatsächlich aktualisiert die QML-Engine Werttypreferenzen jedes Mal automatisch, wenn man aus ihnen liest. Dies ist also nicht teurer als die erste Version, sondern eine klarere Art, dieselben Operationen auszudrücken.

Übersetzer

Mit diesem Pragma können Sie den Kontext für die Übersetzungen in der Datei festlegen.

pragma Translator: myTranslationContext
pragma Translator: "myTranslationContext"

Weitere Informationen zur Internationalisierung mit QML finden Sie unter Verwendung von qsTr.

Importe

Ein Dokument muss die notwendigen Module oder Typ-Namensräume importieren, damit die Engine die im Dokument referenzierten QML-Objekttypen laden kann. Standardmäßig kann ein Dokument auf alle QML-Objekttypen zugreifen, die über .qml -Dateien im selben Verzeichnis definiert wurden; wenn ein Dokument auf andere Objekttypen verweisen muss, muss es den Typ-Namensraum importieren, in dem diese Typen registriert wurden.

QML verfügt im Gegensatz zu C oder C++ nicht über einen Präprozessor, der das Dokument vor der Präsentation auf QML engine modifiziert. Die Anweisungen von import kopieren nicht den Code im Dokument und fügen ihn ein, sondern weisen die QML-Engine an, wie sie die im Dokument gefundenen Typreferenzen auflösen soll. Jede Typreferenz in einem QML-Dokument - wie Rectangle und ListView - einschließlich derjenigen innerhalb eines JavaScript-Blocks oder Property-Bindings, wird ausschließlich auf der Grundlage der Import-Anweisungen aufgelöst. Es muss mindestens eine import -Anweisung vorhanden sein, wie z. B. import QtQuick 2.0.

Ausführliche Informationen zu QML-Importen finden Sie in der Dokumentation QML-Syntax - Import-Anweisungen.

Die Root-Objekt-Deklaration

Ein QML-Dokument beschreibt eine Hierarchie von Objekten, die instanziiert werden können. Jede Objektdefinition hat eine bestimmte Struktur; sie hat einen Typ, sie kann eine ID und einen Objektnamen haben, sie kann Eigenschaften haben, sie kann Methoden haben, sie kann Signale haben und sie kann Signalhandler haben.

Eine QML-Datei darf nur eine einzige Stammobjektdefinition enthalten. Das Folgende ist ungültig und führt zu einem Fehler:

// MyQmlFile.qml
import QtQuick 2.0

Rectangle { width: 200; height: 200; color: "red" }
Rectangle { width: 200; height: 200; color: "blue" }    // invalid!

Das liegt daran, dass eine .qml-Datei automatisch einen QML-Typ definiert, der eine einzelne QML-Objektdefinition kapselt. Dies wird in Dokumente als QML-Objekttyp-Definitionen näher erläutert.

Siehe auch Type annotations and assertions und Type annotations and assertions.

© 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.