As Développeurs Qtnous produisons chaque jour de nombreuses lignes de code QML. Bien sûr, nous sommes tous conscients de l'importance de disposer de composants maintenables et bien organisés. Nous connaissons les bonnes pratiques et nous nous efforçons de les respecter chaque fois que nous ajoutons du nouveau code à la base existante. Cependant, à mesure que notre projet gagne en complexité, il peut être difficile de garder une vue d'ensemble de toutes les bonnes pratiques et de garantir que le code respecte les normes requises. C'est l'une des raisons pour lesquelles vous devriez envisager qmllint comme candidat pour devenir votre nouvel ami.

Dans cet article, je souhaite vous montrer comment je travaille avec qmllint et comment vous pouvez intégrer les vérifications qmllint dans vos tâches quotidiennes.

Qu'est-ce que qmllint ?

En bref, qmllint est un puissant outil de linting conçu spécifiquement pour QML. Son objectif principal est de vérifier la validité syntaxique des fichiers QML et d'avertir de certains anti-patterns QML. Il est facilement configurable, ce qui nous permet d'activer la vérification et l'affichage uniquement des avertissements qui nous intéressent. J'active toujours tous les avertissements afin de m'assurer que le code QML est écrit sans aucune erreur.

Par exemple, cet outil peut vous alerter sur :

  • Accès non qualifié aux propriétés
  • Problèmes liés à la compilation du code QML
  • Code obsolète
  • Beaucoup d'autres…

L'utilisation de l'outil facilitera certainement l'application des meilleures pratiques pour un code propre et le maintien du code dans la meilleure forme possible.

Par où commencer

J'ai préparé un court extrait de code pour cet article. Bien entendu, les erreurs dans le code sont intentionnelles afin de démontrer l'utilisation de cet outil.

Voici le code de démarrage :

import QtQuick import QtQuick.Window import QtQuick.Controls Window { width: 640 height: 480 visible: true title: qsTr("Hello World") readonly property int delegateWidth: 640 readonly property int delegateHeight: 30 ListModel { id: items ListElement { name: "red" textColor: "red" } ListElement { name: "blue" textColor: "blue" } ListElement { name: "green" textColor: "green" } } ListView { id: listView anchors.fill: parent model: items delegate: Label { text: model.name color: model.textColor width: delegateWidth height: delegateHeight horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter } } }

Désormais, nous pouvons vérifier le code avec cet outil :

1. Assurez-vous que le chemin vers le répertoire où se trouve qmllint.exe est ajouté aux variables d'environnement.

2. Exécutez simplement la commande.

3. qmllint <pathToFile>

4. Vérifiez la sortie de la console.

qmllint output in the terminal showing unqualified access warnings in a QML file.

Comme vous pouvez le constater, qmllint émet certains avertissements même sur ce court extrait de code. Je sais que vous ne commettrez jamais ce type d'erreur, mais croyez-moi, vos collègues pourraient le faire, ou même vous pourriez passer une mauvaise journée et oublier de suivre les bonnes pratiques.

Examinons de plus près la sortie. Nous avons quelques avertissements d'accès non qualifié et l'information selon laquelle le modèle a été implicitement injecté dans le délégué. Non seulement il affiche les avertissements, mais il nous donne également des informations sur la façon de s'en débarrasser. Suivons donc les suggestions.

5. Corrigez les avertissements affichés en suivant la sortie.

6. import QtQuick 7. import QtQuick.Window 8. import QtQuick.Controls 9. 10. Window { 11. id: root //Ajout d'un id au composant racine 12. width: 640 13. height: 480 14. visible: true 15. title: qsTr("Hello World") 16. 17. readonly property int delegateWidth: 640 18. readonly property int delegateHeight: 30 19. 20. ListModel { 21. id: items 22. 23. ListElement { 24. name: "red" 25. textColor: "red" 26. } 27. ListElement { 28. name: "blue" 29. textColor: "blue" 30. } 31. ListElement { 32. name: "green" 33. textColor: "green" 34. } 35. } 36. 37. ListView { 38. id: listView 39. anchors.fill: parent 40. model: items 41. 42. delegate: Label { 43. //Ajout de la propriété requise au délégué 44. required property string name 45. required property string textColor 46. 47. text: name 48. color: textColor 49. 50. //Ajout d'un id pour accéder directement aux propriétés depuis la racine. 51. width: root.delegateWidth 52. height: root.delegateHeight 53. 54. horizontalAlignment: Qt.AlignHCenter 55. verticalAlignment: Qt.AlignVCenter 56. } 57. } 58. }

6. Relancez qmllint.

qmllint warnings with suggestion to use ComponentBehavior: Bound in QML.

Il a trouvé un autre avertissement et nous a fourni des informations sur la façon dont il pouvait être corrigé. Faisons la modification.

//Ajout du pragma nécessaire en haut du fichier
pragma ComponentBehavior: Bound

import QtQuick
import QtQuick.Window
import QtQuick.Controls

Window {
    id: root
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    readonly property int delegateWidth: 640
    readonly property int delegateHeight: 30

    ListModel {
        id: items
        ListElement { name: "red"; textColor: "red" }
        ListElement { name: "blue"; textColor: "blue" }
        ListElement { name: "green"; textColor: "green" }
    }

    ListView {
        id: listView
        anchors.fill: parent
        model: items
        delegate: Label {
            required property string name
            required property string textColor
            text: name
            color: textColor
            width: root.delegateWidth
            height: root.delegateHeight
            horizontalAlignment: Qt.AlignHCenter
            verticalAlignment: Qt.AlignVCenter
        }
    }
}

La prochaine exécution de qmllint n'affichera rien. Nous avons corrigé l'intégralité du fichier.

Ok, nous avons donc terminé notre première vérification qmllint. Et maintenant ?

Il serait formidable de configurer les paramètres de qmllint pour pouvoir vérifier tous les fichiers avec une seule commande, faire prendre conscience à qmllint des modules externes, stocker la sortie quelque part, et peut-être lui donner plus de puissance afin qu'il puisse résoudre nos avertissements automatiquement.

Configuration de qmllint

qmllint peut être configuré séparément pour chaque projet. Cela peut être utile si vous souhaitez désactiver certains avertissements ou fournir un chemin vers des modules supplémentaires.

Il est possible d'obtenir le même résultat en exécutant la commande qmllint avec des options supplémentaires, mais il est bien préférable de configurer qmllint une fois dans un fichier séparé. Il peut générer le fichier de configuration par défaut pour nous.

qmllint –write-defaults

Le fichier .qmllint.ini est créé dans le répertoire à partir duquel nous avons exécuté la commande. qmllint tient compte de ces paramètres s'il analyse le fichier situé dans le même répertoire que les paramètres ou dans les sous-répertoires. Vous pouvez ignorer ces paramètres si vous le souhaitez ou remplacer certaines options en ligne de commande.

//ignore settings file qmllint --ignore-settings //overwrite CompilerWarnings option qmllint --compiler warning

Nous pouvons maintenant spécifier quelle catégorie doit être désactivée ou traitée comme un message d'information. Dans notre cas, je recommande d'activer CompilerWarnings. Cela vous avertira des parties du code qml qui ne peuvent pas être compilées par qmlsc.

[Warnings]
ImportFailure=warning
ReadOnlyProperty=warning
BadSignalHandlerParameters=warning
UnusedImports=info
DuplicatedName=warning
PrefixedImportType=warning
AccessSingletonViaObject=warning
Deprecated=warning
ControlsSanity=disable
UnresolvedType=warning
LintPluginWarnings=disable
MultilineStrings=info
RestrictedType=warning
PropertyAliasCycles=warning
VarUsedBeforeDeclaration=warning
AttachedPropertyReuse=disable
RequiredProperty=warning
WithStatement=warning
InheritanceCycle=warning
UnqualifiedAccess=warning
UncreatableType=warning
MissingProperty=warning
InvalidLintDirective=warning
**CompilerWarnings=warning**
UseProperFunction=warning
NonListProperty=warning
IncompatibleType=warning
TopLevelComponent=warning
MissingType=warning
DuplicatePropertyBinding=warning

[General]
DisableDefaultImports=false
AdditionalQmlImportPaths=
OverwriteImportTypes=
DisablePlugins=
ResourcePath=

Assurer la connaissance des modules

Étendons un peu notre application afin qu'elle utilise certains modules. Par exemple, nous voulons créer un bouton personnalisé qui sera utilisé comme délégué dans notre ListView. Le bouton peut également être utilisé dans différentes parties de l'application, il est donc judicieux de le placer dans un module séparé.

Structure des fichiers :

QML project structure with qmllint configuration and custom imports module.
//Main.qml
pragma ComponentBehavior: Bound

import QtQuick
import QtQuick.Window
import QtQuick.Controls
import CustomControls

Window {
    id: root
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    readonly property int delegateWidth: 640
    readonly property int delegateHeight: 30

    ListModel {
        id: items
        ListElement { name: "red"; textColor: "red" }
        ListElement { name: "blue"; textColor: "blue" }
        ListElement { name: "green"; textColor: "green" }
        ListElement { name: "black"; textColor: "black" }
    }

    ListView {
        id: listView
        anchors.fill: parent
        model: items
        delegate: CustomButton {
            required property string name
            required property string textColor
            text: name
            color: textColor
        }
    }
}

//CustomButton.qml
import QtQuick
import QtQuick.Controls.Fusion as Fusion

Fusion.Button {
    id: control
    property alias color: content.color
    implicitWidth: 640
    implicitHeight: 30

    contentItem: Fusion.Label {
        id: content
        text: control.text
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignHCenter
    }
}

Lintons Main.qml.

qmllint output showing import errors and unresolved types in a QML project.

Pourquoi y a-t-il autant d'avertissements ? Nous n'avons apporté que des modifications mineures au fichier, comme l'ajout d'un module et la modification du libellé en CustomButton. La raison est que l'outil n'a pas connaissance des modules que nous avons créés. Alors, comment pouvons-nous lui faire prendre en compte ces modules ?

Il existe deux méthodes :

1. Nous pouvons exécuter qmllint avec une option supplémentaire.

qmllint -I imports Main.qml

2. Ajoutez le chemin vers le répertoire parent du module dans le fichier de paramètres.

AdditionalQmlImportPaths=imports

Relançons qmllint.

Running qmllint from the command line to analyse QML files.

Le module est désormais lu correctement, et tous les avertissements ont disparu sans aucune modification du code. Il ne reste qu'un seul message indiquant que le fichier contient un import inutilisé. C'est un vestige de la version précédente du code lorsque nous utilisions Label. Il est facile d'oublier de supprimer les imports qui ne sont plus nécessaires. Ainsi, chaque fois qu'un nouveau module est créé, nous devons nous assurer que le chemin est correctement transmis aux paramètres de qmllint.

Stockage de la sortie dans un fichier JSON

Dans certaines situations, il n'est pas suffisant de visualiser la sortie directement dans la console. Par exemple, la sortie peut être si longue que la lecture des avertissements n'est plus claire, ou le rapport doit être stocké quelque part pour une analyse ultérieure. Cela nous permet de stocker facilement la sortie au format JSON.

qmllint –json report.json Main.qml

La commande ci-dessus crée un fichier report.json. Les résultats de l'analyse du fichier Main.qml sont stockés dans ce fichier.

//report.json { "files": [ { "filename": "C:/Users/kaj/Documents/qmllint_test/Main.qml", "success": true, "warnings": [ { "charOffset": 74, "column": 1, "id": "unused-imports", "length": 6, "line": 5, "message": "Unused import", "suggestions": [], "type": "info" } ] } ], "revision": 3 }

Vérification de tous les fichiers QML de l'application

Bien, nous savons comment vérifier un seul fichier. Cependant, pour des projets plus importants, il sera bien plus judicieux de pouvoir vérifier tous les fichiers du projet avec une seule commande qmllint. Il est possible de passer plusieurs fichiers en arguments. QMLllint les vérifiera tous. Toutefois, ce n'est toujours pas la meilleure solution.

qmllint Main.qml imports/CustomControl/CustomButton.qml

Je recommande de masquer l'exécution de la commande dans un script. Dans mon cas, j'écris un simple script Python qui recherche tous les fichiers qml dans le répertoire et les sous-répertoires, puis exécute qmllint sur ceux-ci. Le résultat est stocké dans le fichier report.json.

import os import subprocess import sys command = ["qmllint"] json_file = "report.json" directory = os.getcwd() qml_files = [] command += ["--json", json_file] for root, dirs, files in os.walk(directory): for file in files: if file.endswith(".qml"): path = os.path.join(root, file) qml_files.append(path) if not qml_files: print(f"No .qml files found in {directory}") sys.exit(1) command += qml_files print(f"Linting {len(qml_files)} QML files in {directory}") with open(json_file, "w") as f: subprocess.run(command, stdout=f, stderr=subprocess.PIPE)

qmllint autonome

Dans un grand projet, nous pouvons avoir une très longue liste d'avertissements répartis dans plusieurs fichiers. La plupart d'entre eux peuvent être liés à un « accès non qualifié ». La correction est triviale, mais elle prendra certainement un certain temps à appliquer. Dans ce cas, nous pouvons déléguer cette tâche à qmllint. Essayons-le sur la version précédente du code avec Label comme délégué pour notre ListView :

delegate: Label { required property string name required property string textColor text: name color: textColor width: delegateWidth height: delegateHeight }

sortie de qmllint :

Corrected QML code after resolving issues reported by qmllint.

Nous sommes désormais certains que les avertissements sont faciles à corriger et que qmllint sait exactement comment les corriger. Nous pouvons donc lui permettre de les corriger automatiquement. La seule chose à faire est d'exécuter la même commande avec une option supplémentaire « -f »

qmllint -f Main.qml

Le correctif est appliqué à Main.qml et la version précédente du fichier est stockée dans le fichier Main.qml.bak, afin que nous puissions la sauvegarder en cas de problème. Main.qml après le correctif :

delegate: Label { required property string name required property string textColor text: name color: textColor width: root.delegateWidth height: root.delegateHeight }

Comme nous pouvons le constater, il fait exactement la même chose que nous, mais nous pouvons gagner du temps en déléguant le travail à qmllint.

Résumé

qmllint est un outil très utile pour les développeurs Qt/QML. S'il est utilisé souvent et correctement, il peut garantir que vos fichiers qml sont écrits sans erreurs.

J'espère vous avoir montré pourquoi vous devriez utiliser qmllint dans votre projet et comment l'utiliser. Bien sûr, il existe de nombreuses autres façons de l'utiliser, mais celles que j'ai décrites dans l'article sont, à mon avis, les plus importantes. La prochaine étape pour vous est d'intégrer les vérifications qmllint dans votre pipeline CI et de voir combien d'erreurs vous avez manquées. Rappelez-vous que qmllint est votre ami et vous dira toujours quand vous avez fait une erreur.

Nos experts vous aideront à créer des applications de haute qualité, riches en fonctionnalités et conviviales avec Qt. Visitez notre Page des offres Qt pour en savoir plus.

FAQ

qmllint est un outil d'analyse statique pour QML qui aide à détecter les erreurs, les avertissements et les problèmes potentiels dans les applications Qt. Il permet aux développeurs d'identifier les problèmes en amont, avant d'exécuter l'application.

L'utilisation de qmllint contribue à améliorer la qualité du code et à réduire les erreurs d'exécution. Il met en évidence l'utilisation incorrecte des propriétés, les incompatibilités de types et d'autres problèmes qui peuvent ne pas être immédiatement visibles lors du développement.

qmllint peut être exécuté depuis la ligne de commande en le pointant vers un fichier QML ou un répertoire de projet. Il analyse le code et renvoie une liste d'avertissements et d'erreurs nécessitant une attention particulière.

qmllint peut détecter les erreurs de syntaxe, les liaisons de propriétés invalides, les incohérences de types et d'autres problèmes courants dans le code QML. Il est particulièrement utile pour détecter les erreurs en amont du processus de développement.