QML-Module schreiben
Sie sollten ein QML-Modul mit Hilfe der CMake QML Module API deklarieren, um:
- qmldir und *.qmltypes Dateien zu generieren.
- C++-Typen zu registrieren, die mit QML_ELEMENT annotiert sind.
- Kombinieren von QML-Dateien und C++-basierten Typen im selben Modul.
- qmlcachegen für alle QML-Dateien aufzurufen.
- Verwenden Sie die vorkompilierten Versionen von QML-Dateien innerhalb des Moduls.
- Stellen Sie das Modul sowohl im physischen als auch im Ressourcendateisystem bereit.
- Erstellen Sie eine Backing-Bibliothek und ein optionales Plugin. Binden Sie die Backing Library in die Anwendung ein, um das Laden des Plugins zur Laufzeit zu vermeiden.
Alle oben genannten Aktionen können auch separat konfiguriert werden. Weitere Informationen finden Sie unter CMake QML Module API.
Mehrere QML-Module in einer Binärdatei
Sie können mehrere QML-Module in ein und dasselbe Binary einbinden. Definieren Sie für jedes Modul ein CMake-Target und binden Sie dann die Targets mit der ausführbaren Datei. Wenn die zusätzlichen Targets alle statische Bibliotheken sind, ist das Ergebnis eine Binärdatei, die mehrere QML-Module enthält. Kurzum, Sie können eine Anwendung wie diese erstellen:
myProject | - CMakeLists.txt | - main.cpp | - main.qml | - onething.h | - onething.cpp | - ExtraModule | - CMakeLists.txt | - Extra.qml | - extrathing.h | - extrathing.cpp
Nehmen wir an, main.qml enthält eine Instanziierung von Extra.qml:
import ExtraModule Extra { ... }
Das Extra-Modul muss eine statische Bibliothek sein, damit Sie es in das Hauptprogramm einbinden können. Geben Sie dies daher in ExtraModule/CMakeLists.txt an:
# Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause qt_add_library(extra_module STATIC) qt_add_qml_module(extra_module URI "ExtraModule" VERSION 1.0 QML_FILES Extra.qml SOURCES extrathing.cpp extrathing.h RESOURCE_PREFIX / )
Dies erzeugt zwei Ziele: extra_module
für die unterstützende Bibliothek und extra_moduleplugin
für das Plugin. Da das Plugin ebenfalls eine statische Bibliothek ist, kann es nicht zur Laufzeit geladen werden.
In myProject/CMakeLists.txt müssen Sie das QML-Modul angeben, zu dem main.qml und alle in onething.h deklarierten Typen gehören:
qt_add_executable(main_program main.cpp) qt_add_qml_module(main_program VERSION 1.0 URI myProject QML_FILES main.qml SOURCES onething.cpp onething.h )
Von dort aus fügen Sie das Unterverzeichnis für das zusätzliche Modul hinzu:
add_subdirectory(ExtraModule)
Um sicherzustellen, dass die Verknüpfung des Zusatzmoduls korrekt funktioniert, müssen Sie:
- Definieren Sie ein Symbol im Zusatzmodul.
- Einen Verweis auf das Symbol im Hauptprogramm erstellen.
QML-Plugins enthalten ein Symbol, das Sie für diesen Zweck verwenden können. Sie können das Makro Q_IMPORT_QML_PLUGIN verwenden, um einen Verweis auf dieses Symbol zu erstellen. Fügen Sie den folgenden Code in die Datei main.cpp ein:
#include <QtQml/QQmlExtensionPlugin> Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)
ExtraModulePlugin
ist der Name der generierten Plugin-Klasse. Er setzt sich aus der Modul-URI und dem angehängten Plugin
zusammen. Binden Sie dann in der CMakeLists.txt des Hauptprogramms das Plugin, nicht die unterstützende Bibliothek, in das Hauptprogramm ein:
target_link_libraries(main_program PRIVATE extra_moduleplugin)
Versionen
QML hat ein komplexes System, um den Komponenten und Modulen Versionen zuzuweisen. In den meisten Fällen sollten Sie das Ganze ignorieren:
- Niemals eine Version zu Ihren Import-Anweisungen hinzufügen
- Niemals eine Version in qt_add_qml_module angeben
- Niemals QML_ADDED_IN_VERSION oder QT_QML_SOURCE_VERSIONS verwenden
- Verwenden Sie niemals Q_REVISION oder das
REVISION()
Attribut in Q_PROPERTY - Vermeiden von unqualifiziertem Zugriff
- Großzügige Verwendung von Import-Namespaces
Die Versionierung wird idealerweise außerhalb der Sprache selbst gehandhabt. Sie können z.B. getrennte Importpfade für verschiedene Gruppen von QML-Modulen verwenden. Oder Sie verwenden einen von Ihrem Betriebssystem bereitgestellten Versionskontrollmechanismus, um Pakete mit QML-Modulen zu installieren oder zu deinstallieren.
In einigen Fällen können die Qt-eigenen QML-Module ein unterschiedliches Verhalten zeigen, je nachdem, welche Version importiert wird. Insbesondere, wenn eine Eigenschaft zu einer QML-Komponente hinzugefügt wird und Ihr Code einen unqualifizierten Zugriff auf eine andere Eigenschaft mit demselben Namen enthält, wird Ihr Code abbrechen. Im folgenden Beispiel wird sich der Code je nach Qt-Version unterschiedlich verhalten, da die Eigenschaft topLeftRadius in Qt 6.7 hinzugefügt wurde:
import QtQuick Item { // property you want to use property real topLeftRadius: 24 Rectangle { // correct for Qt version < 6.7 but uses Rectangle's topLeftRadius in 6.7 objectName: "top left radius:" + topLeftRadius } }
Die Lösung ist hier, den unqualifizierten Zugriff zu vermeiden. qmllint kann verwendet werden, um solche Probleme zu finden. Das folgende Beispiel greift auf die eigentlich gemeinte Eigenschaft auf eine sichere, qualifizierte Weise zu:
import QtQuick Item { id: root // property you want to use property real topLeftRadius: 24 Rectangle { // never mixes up topLeftRadius with unrelated Rectangle's topLeftRadius objectName: "top left radius:" + root.topLeftRadius } }
Sie können die Inkompatibilität auch vermeiden, indem Sie eine bestimmte Version von QtQuick importieren:
// make sure Rectangle has no topLeftRadius property import QtQuick 6.6 Item { property real topLeftRadius: 24 Rectangle { objectName: "top left radius:" + topLeftRadius } }
Ein weiteres Problem, das durch die Versionierung gelöst wird, ist die Tatsache, dass QML-Komponenten, die von verschiedenen Modulen importiert werden, sich gegenseitig überschatten können. Wenn im folgenden Beispiel MyModule
eine Komponente mit dem Namen Rectangle
in einer neueren Version einführt, wäre das von diesem Dokument erstellte Rectangle
kein QQuickRectangle
mehr, sondern das neue Rectangle
, das von MyModule
eingeführt wurde.
import QtQuick import MyModule Rectangle { // MyModule's Rectangle, not QtQuick's }
Eine gute Möglichkeit, die Schattenbildung zu vermeiden, wäre, QtQuick
und/oder MyModule
wie folgt in Typ-Namensräume zu importieren:
import QtQuick as QQ import MyModule as MM QQ.Rectangle { // QtQuick's Rectangle }
Wenn Sie alternativ MyModule
mit einer festen Version importieren und die neue Komponente über QML_ADDED_IN_VERSION oder QT_QML_SOURCE_VERSIONS ein korrektes Versions-Tag erhält, wird die Abschattung ebenfalls vermieden:
import QtQuick 6.6 // Types introduced after 1.0 are not available, like Rectangle for example import MyModule 1.0 Rectangle { // QtQuick's Rectangle }
Damit dies funktioniert, müssen Sie Versionen in MyModule
verwenden. Es gibt ein paar Dinge zu beachten.
Wenn Sie Versionen hinzufügen, fügen Sie sie überall hinzu
Sie müssen ein VERSION
Attribut zu qt_add_qml_module hinzufügen. Die Version sollte die neueste Version sein, die von Ihrem Modul bereitgestellt wird. Ältere Minor-Versionen der gleichen Major-Version werden automatisch registriert. Für ältere Hauptversionen, siehe unten.
Sie sollten QML_ADDED_IN_VERSION oder QT_QML_SOURCE_VERSIONS zu jedem Typ hinzufügen, der nicht in Version x.0
Ihres Moduls eingeführt wurde, wobei x
die aktuelle Hauptversion ist.
Wenn Sie vergessen, ein Versions-Tag hinzuzufügen, wird die Komponente in allen Versionen verfügbar sein, was die Versionierung unwirksam macht.
Es gibt jedoch keine Möglichkeit, den in QML definierten Eigenschaften, Methoden und Signalen Versionen hinzuzufügen. Die einzige Möglichkeit, QML-Dokumente zu versionieren, besteht darin, ein neues Dokument mit separaten QT_QML_SOURCE_VERSIONS für jede Änderung hinzuzufügen.
Versionen sind nicht transitiv
Wenn eine Komponente aus Ihrem Modul A
ein anderes Modul B
importiert und einen Typ aus diesem Modul als Wurzelelement instanziiert, dann ist die Importversion von B
für die verfügbaren Eigenschaften der resultierenden Komponente relevant, unabhängig davon, welche Version von A
von einem Benutzer importiert wird.
Betrachten Sie eine Datei TypeFromA.qml
mit der Version 2.6
im Modul A
:
import B 2.7 // Exposes TypeFromB 2.7, no matter what version of A is imported TypeFromB { }
Betrachten Sie nun einen Benutzer von TypeFromA
:
import A 2.6 // This is TypeFromB 2.7. TypeFromA { }
Der Benutzer hofft, die Version 2.6
zu sehen, erhält aber tatsächlich die Version 2.7
der Basisklasse TypeFromB
.
Um sicher zu sein, müssen Sie also nicht nur Ihre QML-Dateien duplizieren und ihnen neue Versionen geben, wenn Sie selbst Eigenschaften hinzufügen, sondern auch, wenn Sie die Versionen von Modulen, die Sie importieren, ändern.
Qualifizierter Zugriff kennt keine Versionierung
Die Versionierung wirkt sich nur auf den unqualifizierten Zugriff auf Mitglieder eines Typs oder den Typ selbst aus. Wenn Sie im Beispiel mit topLeftRadius
this.topLeftRadius
schreiben, wird die Eigenschaft aufgelöst, wenn Sie Qt 6.7 verwenden, auch wenn Sie import QtQuick 6.6
schreiben.
Versionen und Revisionen
Mit QML_ADDED_IN_VERSION und den Zwei-Argument-Varianten von Q_REVISION und Q_PROPERTY's REVISION()
können Sie nur Versionen deklarieren, die eng mit der metaobject's Revision gekoppelt sind, wie sie in QMetaMethod::revision und QMetaProperty::revision dargestellt sind. Das bedeutet, dass alle Typen in Ihrer Typenhierarchie dem gleichen Versionsschema folgen müssen. Dies schließt alle Typen ein, die von Qt selbst bereitgestellt werden und von denen Sie erben.
Mit qmlRegisterType und verwandten Funktionen können Sie jedes Mapping zwischen Metaobjekt-Revisionen und Typversionen registrieren. Sie müssen dann die Ein-Argument-Formen von Q_REVISION und das REVISION
-Attribut von Q_PROPERTY verwenden. Dies kann jedoch ziemlich komplex und verwirrend werden und wird nicht empfohlen.
Exportieren mehrerer Hauptversionen aus demselben Modul
qt_add_qml_module berücksichtigt standardmäßig die in seinem VERSION-Argument angegebene Hauptversion, auch wenn die einzelnen Typen in ihrer hinzugefügten spezifischen Version über QT_QML_SOURCE_VERSIONS oder Q_REVISION andere Versionen deklarieren. Wenn ein Modul unter mehr als einer Version verfügbar ist, müssen Sie auch entscheiden, unter welchen Versionen die einzelnen QML-Dateien verfügbar sind. Um weitere Hauptversionen zu deklarieren, können Sie die Option PAST_MAJOR_VERSIONS
für qt_add_qml_module
sowie die Eigenschaft QT_QML_SOURCE_VERSIONS
für einzelne QML-Dateien verwenden.
set_source_files_properties(Thing.qml PROPERTIES QT_QML_SOURCE_VERSIONS "1.4;2.0;3.0" ) set_source_files_properties(OtherThing.qml PROPERTIES QT_QML_SOURCE_VERSIONS "2.2;3.0" ) qt_add_qml_module(my_module URI MyModule VERSION 3.2 PAST_MAJOR_VERSIONS 1 2 QML_FILES Thing.qml OtherThing.qml OneMoreThing.qml SOURCES everything.cpp everything.h )
MyModule
ist in den Hauptversionen 1, 2 und 3 verfügbar. Die maximal verfügbare Version ist 3.2. Sie können jede Version 1.x oder 2.x mit einem positiven x importieren. Für Thing.qml und OtherThing.qml haben wir explizite Versionsinformationen hinzugefügt. Thing.qml ist ab Version 1.4 verfügbar, und OtherThing.qml ist ab Version 2.2 verfügbar. Sie müssen auch die späteren Versionen in jedem set_source_files_properties()
angeben, weil Sie QML-Dateien aus einem Modul entfernen können, wenn Sie die Hauptversion erhöhen. Für OneMoreThing.qml gibt es keine explizite Versionsangabe. Dies bedeutet, dass OneMoreThing.qml in allen Hauptversionen ab der Nebenversion 0 verfügbar ist.
Mit dieser Einstellung wird der generierte Registrierungscode das Modul versions
mit qmlRegisterModule() für jede der Hauptversionen registrieren. Auf diese Weise können alle Versionen importiert werden.
Benutzerdefinierte Verzeichnislayouts
Die einfachste Art, QML-Module zu strukturieren, besteht darin, sie in Verzeichnissen zu speichern, die nach ihren URIs benannt sind. Zum Beispiel würde ein Modul My.Extra.Module in einem Verzeichnis My/Extra/Module relativ zu der Anwendung, die es verwendet, liegen. Auf diese Weise können sie zur Laufzeit und von allen Werkzeugen leicht gefunden werden.
In komplexeren Projekten kann diese Konvention zu einschränkend sein. Sie könnten zum Beispiel alle QML-Module an einem Ort gruppieren wollen, um das Stammverzeichnis des Projekts nicht zu verschmutzen. Oder Sie möchten ein einzelnes Modul in mehreren Anwendungen wiederverwenden. Für diese Fälle kann QT_QML_OUTPUT_DIRECTORY
in Kombination mit RESOURCE_PREFIX
und IMPORT_PATH verwendet werden.
Um QML-Module in einem bestimmten Ausgabeverzeichnis zu sammeln, zum Beispiel einem Unterverzeichnis "qml" im Build-Verzeichnis QT_QML_OUTPUT_DIRECTORY, setzen Sie Folgendes in der CMakeLists.txt auf oberster Ebene:
set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)
Die Ausgabeverzeichnisse der QML-Module werden an den neuen Ort verschoben. Ebenso werden die Aufrufe qmllint
und qmlcachegen
automatisch angepasst, um das neue Ausgabeverzeichnis als Importpfad zu verwenden. Da das neue Ausgabeverzeichnis nicht Teil des standardmäßigen QML-Importpfads ist, müssen Sie es zur Laufzeit explizit hinzufügen, damit die QML-Module gefunden werden können.
Nun, da das physische Dateisystem erledigt ist, möchten Sie die QML-Module vielleicht noch an einen anderen Ort im Ressourcendateisystem verschieben. Hierfür ist die Option RESOURCE_PREFIX gedacht. Sie müssen sie in jedem qt_add_qml_module separat angeben. Das QML-Modul wird dann unter dem angegebenen Präfix platziert, wobei ein aus der URI generierter Zielpfad angehängt wird. Betrachten Sie zum Beispiel das folgende Modul:
qt_add_qml_module( URI My.Great.Module VERSION 1.0 RESOURCE_PREFIX /example.com/qml QML_FILES A.qml B.qml )
Damit wird ein Verzeichnis example.com/qml/My/Great/Module
zum Ressourcendateisystem hinzugefügt und das oben definierte QML-Modul darin abgelegt. Es ist nicht unbedingt erforderlich, das Ressourcenpräfix zum QML-Importpfad hinzuzufügen, da das Modul weiterhin im physischen Dateisystem gefunden werden kann. Im Allgemeinen ist es jedoch eine gute Idee, das Ressourcenpräfix zum QML-Importpfad hinzuzufügen, da das Laden aus dem Ressourcendateisystem bei den meisten Modulen schneller ist als das Laden aus dem physischen Dateisystem.
Wenn die QML-Module in einem größeren Projekt mit mehreren Importpfaden verwendet werden sollen, müssen Sie einen zusätzlichen Schritt durchführen: Selbst wenn Sie die Importpfade zur Laufzeit hinzufügen, haben Werkzeuge wie qmllint
keinen Zugriff darauf und finden möglicherweise nicht die richtigen Abhängigkeiten. Verwenden Sie IMPORT_PATH
, um dem Tooling die zusätzlichen Pfade mitzuteilen, die es berücksichtigen muss. Ein Beispiel:
qt_add_qml_module( URI My.Dependent.Module VERSION 1.0 QML_FILES C.qml IMPORT_PATH "/some/where/else" )
Eliminierung des Dateisystemzugriffs zur Laufzeit
Wenn alle QML-Module immer aus dem Ressourcendateisystem geladen werden, können Sie die Anwendung als eine einzige Binärdatei bereitstellen.
Wenn die QTP0001-Richtlinie auf NEW
gesetzt ist, wird das RESOURCE_PREFIX
-Argument für qt_add_qml_module()
standardmäßig auf /qt/qml/
gesetzt, so dass Ihre Module in :/qt/qml/
im Ressourcendateisystem abgelegt werden. Dies ist Teil des standardmäßigen QML-Importpfades, wird aber nicht von Qt selbst verwendet. Für Module, die innerhalb Ihrer Anwendung verwendet werden sollen, ist dies der richtige Ort.
Wenn Sie stattdessen ein benutzerdefiniertes RESOURCE_PREFIX
angegeben haben, müssen Sie das benutzerdefinierte Ressourcenpräfix zum QML-Importpfad hinzufügen. Sie können auch mehrere Ressourcenpräfixe hinzufügen:
QQmlEngine qmlEngine; qmlEngine.addImportPath(QStringLiteral(":/my/resource/prefix")); qmlEngine.addImportPath(QStringLiteral(":/other/resource/prefix")); // Use qmlEngine to load the main.qml file.
Dies kann notwendig sein, wenn Sie Bibliotheken von Drittanbietern verwenden, um Modulnamenskonflikte zu vermeiden. In allen anderen Fällen wird von der Verwendung eines benutzerdefinierten Ressource-Präfixes abgeraten.
Der Pfad :/qt-project.org/imports/
ist auch Teil des standardmäßigen QML-Importpfads. Für Module, die über verschiedene Projekte oder Qt-Versionen hinweg häufig wiederverwendet werden, ist :/qt-project.org/imports/
als Ressourcenpräfix akzeptabel. Qt-eigene QML-Module werden jedoch dort platziert. Sie müssen darauf achten, diese nicht zu überschreiben.
Fügen Sie keine unnötigen Importpfade hinzu. Die QML-Engine könnte Ihre Module dann an der falschen Stelle finden. Dies kann zu Problemen führen, die sich nur in bestimmten Umgebungen reproduzieren lassen.
Einbinden eigener QML-Plugins
Wenn Sie ein image provider im QML-Modul bündeln, müssen Sie die Methode QQmlEngineExtensionPlugin::initializeEngine() implementieren. Dies wiederum macht es notwendig, ein eigenes Plugin zu schreiben. Um diesen Anwendungsfall zu unterstützen, kann NO_GENERATE_PLUGIN_SOURCE verwendet werden.
Betrachten wir ein Modul, das seine eigene Plugin-Quelle bereitstellt:
qt_add_qml_module(imageproviderplugin VERSION 1.0 URI "ImageProvider" PLUGIN_TARGET imageproviderplugin NO_PLUGIN_OPTIONAL NO_GENERATE_PLUGIN_SOURCE CLASS_NAME ImageProviderExtensionPlugin QML_FILES AAA.qml BBB.qml SOURCES moretypes.cpp moretypes.h myimageprovider.cpp myimageprovider.h plugin.cpp )
Sie können einen Bildanbieter in myimageprovider.h deklarieren, etwa so:
class MyImageProvider : public QQuickImageProvider { [...] };
In plugin.cpp können Sie dann die QQmlEngineExtensionPlugin definieren:
#include <myimageprovider.h> #include <QtQml/qqmlextensionplugin.h> class ImageProviderExtensionPlugin : public QQmlEngineExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid) public: void initializeEngine(QQmlEngine *engine, const char *uri) final { Q_UNUSED(uri); engine->addImageProvider("myimg", new MyImageProvider); } };
Dadurch wird der Bildanbieter verfügbar gemacht. Das Plugin und die unterstützende Bibliothek befinden sich beide in demselben CMake-Ziel imageproviderplugin. Dies geschieht, damit der Linker nicht in verschiedenen Szenarien Teile des Moduls fallen lässt.
Da das Plugin einen Image-Provider erstellt, verfügt es nicht mehr über eine triviale initializeEngine
Funktion. Daher ist das Plugin nicht mehr optional.
© 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.