Écrire des modules QML
Vous devez déclarer un module QML en utilisant l'API de module QML de CMake pour :
- Générer des fichiers qmldir et *.qmltypes.
- Enregistrer les types C++ annotés avec QML_ELEMENT.
- Combiner des fichiers QML et des types C++ dans le même module.
- Invoquer qmlcachegen sur tous les fichiers QML.
- Utiliser les versions précompilées des fichiers QML dans le module.
- Fournir le module à la fois dans le système de fichiers physique et dans le système de fichiers de ressources.
- Créer une bibliothèque d'appui et un plugin optionnel. Lier la bibliothèque d'appui à l'application pour éviter de charger le plugin au moment de l'exécution.
Toutes les actions ci-dessus peuvent également être configurées séparément. Pour plus d'informations, voir CMake QML Module API.
Plusieurs modules QML dans un binaire
Vous pouvez ajouter plusieurs modules QML dans le même binaire. Définissez une cible CMake pour chaque module, puis liez les cibles à l'exécutable. Si les cibles supplémentaires sont toutes des bibliothèques statiques, le résultat sera un binaire contenant plusieurs modules QML. En bref, vous pouvez créer une application de cette manière :
myProject
| - CMakeLists.txt
| - main.cpp
| - main.qml
| - onething.h
| - onething.cpp
| - ExtraModule
| - CMakeLists.txt
| - Extra.qml
| - extrathing.h
| - extrathing.cppPour commencer, supposons que main.qml contienne une instanciation de Extra.qml :
import ExtraModule
Extra { ... }Le module supplémentaire doit être une bibliothèque statique afin que vous puissiez le lier au programme principal. Il faut donc l'indiquer dans ExtraModule/CMakeLists.txt :
# 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 /
)Ceci génère deux cibles : extra_module pour la bibliothèque d'accompagnement, et extra_moduleplugin pour le plugin. Etant également une bibliothèque statique, le plugin ne peut pas être chargé à l'exécution.
Dans myProject/CMakeLists.txt, vous devez spécifier le module QML dont main.qml et tous les types déclarés dans onething.h font partie :
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
)À partir de là, vous ajoutez le sous-répertoire du module supplémentaire :
add_subdirectory(ExtraModule)
Pour s'assurer que la liaison du module supplémentaire fonctionne correctement, vous devez :
- Définir un symbole dans le module supplémentaire.
- Créer une référence au symbole à partir du programme principal.
Les plugins QML contiennent un symbole que vous pouvez utiliser à cette fin. Vous pouvez utiliser la macro Q_IMPORT_QML_PLUGIN pour créer une référence à ce symbole. Ajoutez le code suivant au fichier main.cpp :
#include <QtQml/QQmlExtensionPlugin> Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)
ExtraModulePlugin est le nom de la classe de plugin générée. Il est composé de l'URI du module et de Plugin. Ensuite, dans le fichier CMakeLists.txt du programme principal, liez le plugin, et non la bibliothèque d'appui, au programme principal :
target_link_libraries(main_program PRIVATE extra_moduleplugin)
Versions
QML dispose d'un système complexe pour attribuer des versions aux composants et aux modules. Dans la plupart des cas, vous devriez l'ignorer complètement :
- N'ajoutez jamais de version à vos déclarations d'importation
- Ne jamais spécifier de versions dans qt_add_qml_module
- Ne jamais utiliser QML_ADDED_IN_VERSION ou QT_QML_SOURCE_VERSIONS
- Ne jamais utiliser Q_REVISION ou l'attribut
REVISION()dans QT_QML_SOURCE_VERSIONS. Q_PROPERTY - Éviter les accès non qualifiés
- Utiliser généreusement les espaces de noms d'importation
La gestion des versions se fait idéalement en dehors du langage lui-même. Vous pouvez, par exemple, conserver des chemins d'importation distincts pour différents ensembles de modules QML. Vous pouvez également utiliser un mécanisme de versionnement fourni par votre système d'exploitation pour installer ou désinstaller des paquets contenant des modules QML.
Dans certains cas, les modules Qt QML propres à Qtml peuvent avoir un comportement différent, en fonction de la version importée. En particulier, si une propriété est ajoutée à un composant QML et que votre code contient un accès non qualifié à une autre propriété du même nom, votre code sera cassé. Dans l'exemple suivant, le code se comportera différemment selon la version de Qt, car la propriété topLeftRadius a été ajoutée dans Qt 6.7 :
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 } }
La solution ici est d'éviter l'accès non qualifié. qmllint peut être utilisé pour trouver de tels problèmes. L'exemple suivant permet d'accéder de manière sûre et qualifiée à la propriété en question :
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 } }
Vous pouvez également éviter l'incompatibilité en important une version spécifique de QtQuick:
// make sure Rectangle has no topLeftRadius property import QtQuick 6.6 Item { property real topLeftRadius: 24 Rectangle { objectName: "top left radius:" + topLeftRadius } }
Un autre problème résolu par le versioning est le fait que les composants QML importés par différents modules peuvent se faire de l'ombre l'un à l'autre. Dans l'exemple suivant, si MyModule devait introduire un composant nommé Rectangle dans une version plus récente, le Rectangle créé par ce document ne serait plus un QQuickRectangle, mais plutôt le nouveau Rectangle introduit par MyModule.
import QtQuick import MyModule Rectangle { // MyModule's Rectangle, not QtQuick's }
Une bonne façon d'éviter l'ombre serait d'importer QtQuick et/ou MyModule dans des espaces de noms de types comme suit :
import QtQuick as QQ import MyModule as MM QQ.Rectangle { // QtQuick's Rectangle }
Alternativement, si vous importez MyModule avec une version fixe, et que le nouveau composant reçoit une balise de version correcte via QML_ADDED_IN_VERSION ou QT_QML_SOURCE_VERSIONS, le shadowing est également évité :
import QtQuick 6.6 // Types introduced after 1.0 are not available, like Rectangle for example import MyModule 1.0 Rectangle { // QtQuick's Rectangle }
Pour que cela fonctionne, vous devez utiliser les versions dans MyModule. Il y a quelques points à prendre en compte.
Si vous ajoutez des versions, ajoutez-les partout
Vous devez ajouter un attribut VERSION à qt_add_qml_module. La version doit être la version la plus récente fournie par votre module. Les versions mineures plus anciennes de la même version majeure seront automatiquement enregistrées. Pour les versions majeures plus anciennes, voir ci-dessous.
Vous devez ajouter QML_ADDED_IN_VERSION ou QT_QML_SOURCE_VERSIONS à chaque type qui n' a pas été introduit dans la version x.0 de votre module, où x est la version majeure actuelle.
Si vous oubliez d'ajouter une balise de version, le composant sera disponible dans toutes les versions, ce qui rendra le versionnement inefficace.
Cependant, il n'existe aucun moyen d'ajouter des versions aux propriétés, méthodes et signaux définis en QML. La seule façon de versionner des documents QML est d'ajouter un nouveau document avec des QT_QML_SOURCE_VERSIONS distinctes pour chaque modification.
Les versions ne sont pas transitives
Si un composant de votre module A importe un autre module B et instancie un type de ce module en tant qu'élément racine, alors la version d'importation de B est pertinente pour les propriétés disponibles du composant résultant, quelle que soit la version de A importée par un utilisateur.
Considérons un fichier TypeFromA.qml avec la version 2.6 dans le module A:
import B 2.7 // Exposes TypeFromB 2.7, no matter what version of A is imported TypeFromB { }
Considérons maintenant un utilisateur de TypeFromA:
import A 2.6 // This is TypeFromB 2.7. TypeFromA { }
L'utilisateur espère voir la version 2.6 mais obtient en fait la version 2.7 de la classe de base TypeFromB.
Par conséquent, pour être sûr, vous devez non seulement dupliquer vos fichiers QML et leur donner de nouvelles versions lorsque vous ajoutez des propriétés vous-même, mais aussi lorsque vous modifiez les versions des modules que vous importez.
L'accès qualifié ne respecte pas le versionnage
Le versionnage n'affecte que l'accès non qualifié aux membres d'un type ou au type lui-même. Dans l'exemple de topLeftRadius, si vous écrivez this.topLeftRadius, la propriété sera résolue si vous utilisez Qt 6.7, même si vous écrivez import QtQuick 6.6.
Versions et révisions
Avec QML_ADDED_IN_VERSION, et les variantes à deux arguments de Q_REVISION et Q_PROPERTY's REVISION(), vous ne pouvez déclarer que des versions qui sont étroitement couplées à la révision metaobject's telle qu'exposée dans QMetaMethod::revision et QMetaProperty::revision. Cela signifie que tous les types de votre hiérarchie de types doivent suivre le même schéma de versionnement. Cela inclut tous les types fournis par Qt lui-même et dont vous héritez.
Avec qmlRegisterType et les fonctions associées, vous pouvez enregistrer n'importe quelle correspondance entre les révisions de métaobjets et les versions de types. Vous devez alors utiliser les formes à un seul argument de Q_REVISION et l'attribut REVISION de Q_PROPERTY. Cependant, cela peut devenir assez complexe et confus et n'est pas recommandé.
Exporter plusieurs versions majeures d'un même module
qt_add_qml_module considère par défaut la version majeure indiquée dans son argument VERSION, même si les types individuels déclarent d'autres versions dans leur version spécifique ajoutée via QT_QML_SOURCE_VERSIONS ou Q_REVISION. Si un module est disponible sous plus d'une version, vous devez également décider sous quelles versions les fichiers QML individuels sont disponibles. Pour déclarer d'autres versions majeures, vous pouvez utiliser l'option PAST_MAJOR_VERSIONS de qt_add_qml_module ainsi que la propriété QT_QML_SOURCE_VERSIONS sur les fichiers QML individuels.
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 est disponible dans les versions majeures 1, 2 et 3. La version maximale disponible est la 3.2. Vous pouvez importer n'importe quelle version 1.x ou 2.x avec un x positif. Pour Thing.qml et OtherThing.qml, nous avons ajouté des informations explicites sur la version. Thing.qml est disponible à partir de la version 1.4, et OtherThing.qml est disponible à partir de la version 2.2. Vous devez également spécifier les versions ultérieures dans chaque site set_source_files_properties(), car vous pouvez supprimer des fichiers QML d'un module lorsque vous changez la version majeure. Il n'y a pas d'information explicite sur la version de OneMoreThing.qml. Cela signifie que OneMoreThing.qml est disponible dans toutes les versions majeures, à partir de la version mineure 0.
Avec cette configuration, le code d'enregistrement généré enregistrera le module versions en utilisant qmlRegisterModule() pour chacune des versions majeures. De cette manière, toutes les versions peuvent être importées.
Structures de répertoire personnalisées
La manière la plus simple de structurer les modules QML est de les conserver dans des répertoires nommés par leur URI. Par exemple, un module My.Extra.Module se trouverait dans un répertoire My/Extra/Module relatif à l'application qui l'utilise. De cette manière, ils peuvent être facilement trouvés au moment de l'exécution et par n'importe quel outil.
Dans les projets plus complexes, cette convention peut s'avérer trop restrictive. Vous pouvez par exemple vouloir regrouper tous les modules QML en un seul endroit pour éviter de polluer le répertoire racine du projet. Vous pouvez aussi vouloir réutiliser un même module dans plusieurs applications. Dans ce cas, vous pouvez utiliser QT_QML_OUTPUT_DIRECTORY en combinaison avec RESOURCE_PREFIX et IMPORT_PATH.
Pour rassembler les modules QML dans un répertoire de sortie spécifique, par exemple un sous-répertoire "qml" dans le répertoire de construction QT_QML_OUTPUT_DIRECTORY, définissez ce qui suit dans le fichier CMakeLists.txt de premier niveau :
set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)Les répertoires de sortie des modules QML sont déplacés vers le nouvel emplacement. De même, les invocations qmllint et qmlcachegen sont automatiquement adaptées pour utiliser le nouveau répertoire de sortie comme chemin d'importation. Comme le nouveau répertoire de sortie ne fait pas partie du chemin d'importation par défaut de QML, vous devez l'ajouter explicitement au moment de l'exécution, afin que les modules QML puissent être trouvés.
Maintenant que le système de fichiers physique est pris en charge, il se peut que vous souhaitiez déplacer les modules QML à un autre endroit du système de fichiers de ressources. C'est à cela que sert l'option RESOURCE_PREFIX. Vous devez la spécifier séparément dans chaque qt_add_qml_module. Le module QML sera alors placé sous le préfixe spécifié, avec un chemin cible généré à partir de l'URI. Par exemple, considérons le module suivant :
qt_add_qml_module(
URI My.Great.Module
VERSION 1.0
RESOURCE_PREFIX /example.com/qml
QML_FILES
A.qml
B.qml
)Il ajoutera un répertoire example.com/qml/My/Great/Module au système de fichiers de ressources et y placera le module QML défini ci-dessus. Il n'est pas strictement nécessaire d'ajouter le préfixe "resource" au chemin d'importation QML, car le module peut toujours être trouvé dans le système de fichiers physique. Cependant, c'est généralement une bonne idée d'ajouter le préfixe resource au chemin d'importation QML car le chargement à partir du système de fichiers de ressources est plus rapide que le chargement à partir du système de fichiers physique pour la plupart des modules.
Si les modules QML sont destinés à être utilisés dans un projet plus important avec plusieurs chemins d'importation, vous devrez effectuer une étape supplémentaire : Même si vous ajoutez les chemins d'importation au moment de l'exécution, les outils tels que qmllint n'y ont pas accès et risquent de ne pas trouver les dépendances correctes. Utilisez IMPORT_PATH pour indiquer à l'outil les chemins supplémentaires qu'il doit prendre en compte. Par exemple :
qt_add_qml_module(
URI My.Dependent.Module
VERSION 1.0
QML_FILES
C.qml
IMPORT_PATH "/some/where/else"
)Élimination de l'accès au système de fichiers au moment de l'exécution
Si tous les modules QML sont toujours chargés à partir du système de fichiers de ressources, vous pouvez déployer l'application en tant que binaire unique.
Si la stratégie QTP0001 est définie sur NEW, l'argument RESOURCE_PREFIX de qt_add_qml_module() est par défaut /qt/qml/, ce qui signifie que vos modules sont placés dans :/qt/qml/ dans le système de fichiers de ressources. Cela fait partie du chemin d'importation QML par défaut, mais n'est pas utilisé par Qtml lui-même. Pour les modules à utiliser dans votre application, c'est le bon endroit.
Si vous avez spécifié un RESOURCE_PREFIX personnalisé, vous devez ajouter le préfixe de la ressource personnalisée au chemin d'importation QML. Vous pouvez également ajouter plusieurs préfixes de ressources :
QQmlEngine qmlEngine;
qmlEngine.addImportPath(QStringLiteral(":/my/resource/prefix"));
qmlEngine.addImportPath(QStringLiteral(":/other/resource/prefix"));
// Use qmlEngine to load the main.qml file.Cela peut s'avérer nécessaire lors de l'utilisation de bibliothèques tierces afin d'éviter les conflits de noms de modules. L'utilisation d'un préfixe de ressource personnalisé est déconseillée dans tous les autres cas.
Le chemin :/qt-project.org/imports/ fait également partie du chemin d'importation QML par défaut. Pour les modules qui sont fortement réutilisés dans différents projets ou versions de Qt, :/qt-project.org/imports/ est acceptable comme préfixe de ressource. Les modules Qt Qml propres à Qt sont cependant placés à cet endroit. Vous devez faire attention à ne pas les écraser.
N'ajoutez pas de chemins d'importation inutiles. Le moteur QML pourrait alors trouver vos modules au mauvais endroit. Cela peut entraîner des problèmes qui ne peuvent être reproduits que dans des environnements spécifiques.
Intégration de plugins QML personnalisés
Si vous intégrez un image provider dans le module QML, vous devez mettre en œuvre la méthode QQmlEngineExtensionPlugin::initializeEngine(). Il est donc nécessaire d'écrire votre propre plugin. Pour prendre en charge ce cas d'utilisation, NO_GENERATE_PLUGIN_SOURCE peut être utilisé.
Considérons un module qui fournit sa propre source de plugin :
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
)Vous pouvez déclarer un fournisseur d'images dans myimageprovider.h, comme ceci :
class MyImageProvider : public QQuickImageProvider
{
[...]
};Dans plugin.cpp, vous pouvez ensuite définir l'adresse QQmlEngineExtensionPlugin:
#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);
}
};Cela rendra le fournisseur d'images disponible. Le plugin et la bibliothèque d'appui se trouvent tous deux dans la même cible CMake imageproviderplugin. Ceci est fait pour que l'éditeur de liens ne laisse pas tomber des parties du module dans différents scénarios.
Comme le plugin crée un fournisseur d'images, il n'a plus la fonction triviale initializeEngine. Par conséquent, le plugin n'est plus optionnel.
Voir aussi Changements dans Qt QML, Moderniser les modules QML, et Porter les modules QML à 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.