QML-Module schreiben
Sie sollten ein QML-Modul mithilfe der CMake-QML-Modul-API deklarieren, um:
- qmldir- und *.qmltypes-Dateien zu generieren.
- C++-Typen zu registrieren, die mit „ QML_ELEMENT “ annotiert sind.
- QML-Dateien und C++-basierte Typen im selben Modul zu kombinieren.
- „qmlcachegen“ für alle QML-Dateien auszuführen.
- die vorkompilierten Versionen der QML-Dateien innerhalb des Moduls zu verwenden.
- Stellen Sie das Modul sowohl im physischen als auch im Ressourcen-Dateisystem bereit.
- Erstellen Sie eine Backing-Bibliothek und ein optionales Plugin. Binden Sie die Backing-Bibliothek 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-Modul-API.
Mehrere QML-Module in einer Binärdatei
Sie können mehrere QML-Module in dieselbe Binärdatei einbinden. Definieren Sie für jedes Modul ein CMake-Ziel und verknüpfen Sie die Ziele anschließend mit der ausführbaren Datei. Wenn es sich bei den zusätzlichen Zielen ausschließlich um statische Bibliotheken handelt, entsteht eine einzige Binärdatei, die mehrere QML-Module enthält. Kurz gesagt können Sie eine Anwendung wie folgt erstellen:
myProject
| - CMakeLists.txt
| - main.cpp
| - main.qml
| - onething.h
| - onething.cpp
| - ExtraModule
| - CMakeLists.txt
| - Extra.qml
| - extrathing.h
| - extrathing.cppNehmen wir zunächst an, dass „main.qml“ eine Instanziierung von „Extra.qml“ enthält:
import ExtraModule
Extra { ... }Das Zusatzmodul 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 /
)Dadurch werden zwei Ziele generiert: „ extra_module “ für die zugrunde liegende Bibliothek und „ extra_moduleplugin “ für das Plugin. Da es sich ebenfalls um eine statische Bibliothek handelt, kann das Plugin 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 zusätzlichen Moduls korrekt funktioniert, müssen Sie:
- Ein Symbol im Zusatzmodul definieren.
- Erstellen Sie im Hauptprogramm einen Verweis auf das Symbol.
QML-Plugins enthalten ein Symbol, das Sie für diesen Zweck verwenden können. Mit dem Makro „ Q_IMPORT_QML_PLUGIN “ können Sie einen Verweis auf dieses Symbol 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 zusammen, an die „ Plugin “ angehängt ist. Verknüpfen Sie dann in der Datei „CMakeLists.txt“ des Hauptprogramms das Plugin – nicht die zugrunde liegende Bibliothek – in das Hauptprogramm ein:
target_link_libraries(main_program PRIVATE extra_moduleplugin)Versionen
QML verfügt über ein komplexes System zur Zuweisung von Versionen zu Komponenten und Modulen. In den meisten Fällen sollten Sie dies vollständig ignorieren, indem Sie:
- Fügen Sie Ihren Import-Anweisungen niemals eine Version hinzu
- Geben Sie in „qt_add_qml_module“ niemals Versionen an
- Verwenden Sie niemals „ QML_ADDED_IN_VERSION “ oder „QT_QML_SOURCE_VERSIONS“
- Verwenden Sie niemals „ Q_REVISION “ oder das Attribut „
REVISION()“ in Q_PROPERTY - Vermeiden Sie unqualifizierten Zugriff
- Verwenden Sie Import-Namespaces großzügig
Die Versionsverwaltung sollte idealerweise außerhalb der Sprache selbst erfolgen. Sie können beispielsweise separate Importpfade für verschiedene Sätze von QML-Modulen vorhalten. Oder Sie können einen von Ihrem Betriebssystem bereitgestellten Versionsmechanismus nutzen, um Pakete mit QML-Modulen zu installieren oder zu deinstallieren.
In einigen Fällen können die Qt-eigenen Qml-Module je nach importierter Version ein unterschiedliches Verhalten zeigen. Insbesondere wenn einer Qml-Komponente eine Eigenschaft hinzugefügt wird und Ihr Code einen unqualifizierten Zugriff auf eine andere Eigenschaft mit demselben Namen enthält, wird Ihr Code nicht mehr funktionieren. Im folgenden Beispiel verhält sich der Code je nach Qt-Version unterschiedlich, da die Eigenschaft „ topLeftRadius “ erst 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 besteht hier darin, den unqualifizierten Zugriff zu vermeiden. Mit „qmllint“ lassen sich solche Probleme aufspüren. Das folgende Beispiel greift auf die tatsächlich gemeinte Eigenschaft auf 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 sich von verschiedenen Modulen importierte QML-Komponenten gegenseitig überschatten können. Im folgenden Beispiel würde, falls „ MyModule “ in einer neueren Version eine Komponente namens „ Rectangle “ einführen würde, die von diesem Dokument erstellte „ Rectangle “ nicht mehr „ QQuickRectangle “ sein, sondern vielmehr die neue „ Rectangle “, die durch „ MyModule “ eingeführt wurde.
import QtQuick
import MyModule
Rectangle {
// MyModule's Rectangle, not QtQuick's
}Eine gute Möglichkeit, diese Überschreibung zu vermeiden, wäre, „ QtQuick “ und/oder „ MyModule “ wie folgt in Typ-Namespaces zu importieren:
import QtQuick as QQ
import MyModule as MM
QQ.Rectangle {
// QtQuick's Rectangle
}Alternativ lässt sich die Überschreibung auch vermeiden, wenn Sie „ 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:
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. Dabei sind einige 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 aktuellste Version sein, die Ihr Modul bereitstellt. Ältere Nebenversionen derselben Hauptversion 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 der Version „ x.0 “ Ihres Moduls eingeführt wurde, wobei „ x “ die aktuelle Hauptversion ist.
Wenn Sie vergessen, ein Versions-Tag hinzuzufügen, ist die Komponente in allen Versionen verfügbar, wodurch die Versionierung wirkungslos wird.
Es gibt jedoch keine Möglichkeit, Versionen zu Eigenschaften, Methoden und Signalen hinzuzufügen, die in QML definiert sind. Die einzige Möglichkeit, QML-Dokumente zu versionieren, besteht darin, für jede Änderung ein neues Dokument mit separaten QT_QML_SOURCE_VERSIONS 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 Stammelement instanziiert, dann ist die Importversion von B für die in der resultierenden Komponente verfügbaren Eigenschaften maßgeblich, unabhängig davon, welche Version von A von einem Benutzer importiert wird.
Betrachten wir 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 wir nun einen Nutzer von „ TypeFromA “:
import A 2.6
// This is TypeFromB 2.7.
TypeFromA { }Der Benutzer erwartet, die Version „ 2.6 “ zu sehen, erhält jedoch tatsächlich die Version „ 2.7 “ der Basisklasse „ TypeFromB “.
Um auf Nummer sicher zu gehen, müssen Sie daher Ihre QML-Dateien nicht nur duplizieren und ihnen neue Versionen zuweisen, wenn Sie selbst Eigenschaften hinzufügen, sondern auch, wenn Sie die Versionen der von Ihnen importierten Module erhöhen.
Der qualifizierte Zugriff berücksichtigt die Versionsnummerierung nicht
Die Versionierung wirkt sich nur auf den unqualifizierten Zugriff auf Mitglieder eines Typs oder auf den Typ selbst aus. Im Beispiel mit ` topLeftRadius`: Wenn Sie ` this.topLeftRadius` schreiben, wird die Eigenschaft bei Verwendung von Qt 6.7 aufgelöst, selbst wenn Sie ` import QtQuick 6.6` verwenden.
Versionen und Revisionen
Mit ` QML_ADDED_IN_VERSION` sowie den Varianten mit zwei Argumenten von ` Q_REVISION ` und der Funktion ` REVISION()` von ` Q_PROPERTY` können Sie nur Versionen deklarieren, die eng mit der Revision ` metaobject's ` verknüpft sind, wie in ` QMetaMethod::revision ` und ` QMetaProperty::revision` beschrieben. Das bedeutet, dass alle Typen in Ihrer Typenhierarchie demselben Versionsschema folgen müssen. Dies schließt alle von Qt selbst bereitgestellten Typen ein, von denen Sie erben.
Mit „ qmlRegisterType “ und den zugehörigen Funktionen können Sie beliebige Zuordnungen zwischen Metaobjekt-Revisionen und Typversionen registrieren. Dazu müssen Sie die Ein-Argument-Formen von „ Q_REVISION “ sowie das Attribut „ REVISION “ von „ Q_PROPERTY “ verwenden. Dies kann jedoch recht komplex und verwirrend werden und wird daher nicht empfohlen.
Exportieren mehrerer Hauptversionen aus demselben Modul
„qt_add_qml_module“ berücksichtigt standardmäßig die im Argument „VERSION“ angegebene Hauptversion, selbst wenn die einzelnen Typen über „QT_QML_SOURCE_VERSIONS“ oder „ Q_REVISION “ andere Versionen in ihrer spezifischen Version deklarieren. Wenn ein Modul unter mehr als einer Version verfügbar ist, müssen Sie außerdem festlegen, 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 “ in einzelnen 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 höchste 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 Versionsangaben hinzugefügt. „Thing.qml“ ist ab Version 1.4 verfügbar, und „OtherThing.qml“ ist ab Version 2.2 verfügbar. Sie müssen in jeder „ set_source_files_properties() “-Datei auch die späteren Versionen angeben, da Sie beim Erhöhen der Hauptversion möglicherweise QML-Dateien aus einem Modul entfernen. Für „OneMoreThing.qml“ gibt es keine explizite Versionsangabe. Das bedeutet, dass „OneMoreThing.qml“ in allen Hauptversionen ab der Nebenversion 0 verfügbar ist.
Bei dieser Konfiguration registriert der generierte Registrierungscode das Modul „ versions “ für jede Hauptversion mithilfe von ` qmlRegisterModule()`. Auf diese Weise können alle Versionen importiert werden.
Benutzerdefinierte Verzeichnisstrukturen
Die einfachste Art, QML-Module zu strukturieren, besteht darin, sie in Verzeichnissen abzulegen, die nach ihren URIs benannt sind. Ein Modul namens „My.Extra.Module“ würde beispielsweise in einem Verzeichnis „My/Extra/Module“ relativ zu der Anwendung liegen, die es verwendet. Auf diese Weise können sie zur Laufzeit und von beliebigen Tools leicht gefunden werden.
In komplexeren Projekten kann diese Konvention jedoch zu einschränkend sein. Möglicherweise möchten Sie beispielsweise alle QML-Module an einem Ort gruppieren, um eine Überfrachtung des Stammverzeichnisses des Projekts zu vermeiden. Oder Sie möchten ein einzelnes Modul in mehreren Anwendungen wiederverwenden. Für diese Fälle können „ QT_QML_OUTPUT_DIRECTORY “ in Kombination mit „ RESOURCE_PREFIX “ und „IMPORT_PATH“ verwendet werden.
Um QML-Module in einem bestimmten Ausgabeverzeichnis zu sammeln, beispielsweise in einem Unterverzeichnis „qml“ im Build-Verzeichnis QT_QML_OUTPUT_DIRECTORY, legen Sie Folgendes in der obersten Ebene der Datei „CMakeLists.txt“ fest:
set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)Die Ausgabeverzeichnisse der QML-Module werden an den neuen Speicherort verschoben. Ebenso werden die Aufrufe von ` qmllint ` und ` qmlcachegen ` automatisch angepasst, sodass das neue Ausgabeverzeichnis als Importpfad verwendet wird. 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.
Nachdem nun das physische Dateisystem geregelt ist, möchten Sie die QML-Module möglicherweise dennoch an einen anderen Ort im Ressourcen-Dateisystem verschieben. Hierfür ist die Option `RESOURCE_PREFIX` vorgesehen. Sie müssen sie in jedem `qt_add_qml_module` separat angeben. Das QML-Modul wird dann unter dem angegebenen Präfix abgelegt, wobei ein aus der URI generierter Zielpfad angehängt wird. Betrachten Sie beispielsweise 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
)Dadurch wird ein Verzeichnis „ example.com/qml/My/Great/Module “ zum Ressourcen-Dateisystem hinzugefügt und das oben definierte QML-Modul darin abgelegt. Es ist streng genommen nicht erforderlich, das Ressourcenpräfix zum QML-Importpfad hinzuzufügen, da das Modul auch im physischen Dateisystem gefunden werden kann. Im Allgemeinen ist es jedoch empfehlenswert, das Ressourcenpräfix zum QML-Importpfad hinzuzufügen, da das Laden aus dem Ressourcen-Dateisystem 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 Tools wie „ qmllint “ keinen Zugriff darauf und finden möglicherweise die richtigen Abhängigkeiten nicht. Verwenden Sie „ IMPORT_PATH “, um den Tools mitzuteilen, welche zusätzlichen Pfade sie berücksichtigen müssen. Beispiel:
qt_add_qml_module(
URI My.Dependent.Module
VERSION 1.0
QML_FILES
C.qml
IMPORT_PATH "/some/where/else"
)Vermeidung von Dateisystemzugriffen zur Laufzeit
Wenn alle QML-Module stets aus dem Ressourcen-Dateisystem geladen werden, können Sie die Anwendung als einzelne Binärdatei bereitstellen.
Wenn die Richtlinie „QTP0001“ auf „ NEW “ gesetzt ist, wird das Argument „ RESOURCE_PREFIX “ für „ qt_add_qml_module() “ standardmäßig auf „ /qt/qml/ “ gesetzt; daher werden Ihre Module im Dateisystem unter „ :/qt/qml/ “ abgelegt. Dies ist Teil des standardmäßigen QML-Importpfads, wird jedoch von Qt selbst nicht verwendet. Für Module, die innerhalb Ihrer Anwendung verwendet werden sollen, ist dies der richtige Speicherort.
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 bei der Verwendung von Bibliotheken von Drittanbietern erforderlich sein, um Konflikte bei den Modulnamen zu vermeiden. In allen anderen Fällen wird von der Verwendung eines benutzerdefinierten Ressourcenpräfixes abgeraten.
Der Pfad „ :/qt-project.org/imports/ “ ist ebenfalls Teil des standardmäßigen QML-Importpfads. Für Module, die in verschiedenen Projekten oder Qt-Versionen häufig wiederverwendet werden, ist „ :/qt-project.org/imports/ “ als Ressourcenpräfix akzeptabel. Dort befinden sich jedoch die Qt-eigenen QML-Module. 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.
Integration benutzerdefinierter QML-Plugins
Wenn Sie ein „ image provider “ im QML-Modul bündeln, müssen Sie die Methode ` QQmlEngineExtensionPlugin::initializeEngine()` implementieren. Dies macht es wiederum erforderlich, ein eigenes Plugin zu schreiben. Um diesen Anwendungsfall zu unterstützen, kann ` NO_GENERATE_PLUGIN_SOURCE ` verwendet werden.
Betrachten wir ein Modul, das eine 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 Bild-Provider in „myimageprovider.h“ wie folgt deklarieren:
class MyImageProvider : public QQuickImageProvider
{
[...]
};In `plugin.cpp` können Sie dann den ` 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 zugrunde liegende Bibliothek befinden sich beide im selben CMake-Ziel „imageproviderplugin“. Dies geschieht, damit der Linker in verschiedenen Szenarien keine Teile des Moduls weglässt.
Da das Plugin einen Bildanbieter erstellt, verfügt es nicht mehr über eine triviale ` initializeEngine `-Funktion. Daher ist das Plugin nicht mehr optional.
Siehe auch „Änderungen an Qt Qml “, „Modernisierung von QML-Modulen“ und „Portierung von QML-Modulen nach CMake“.
© 2026 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.