Sur cette page

Écrire des extensions QML en C++

Le Qt Qml module fournit un ensemble d'API permettant d'étendre QML via des extensions C++. Vous pouvez écrire des extensions pour ajouter vos propres types QML, étendre des types Qt existants ou appeler des fonctions C/C++ qui ne sont pas accessibles depuis du code QML standard.

Ce tutoriel montre comment écrire une extension QML en C++ qui inclut les fonctionnalités principales de QML, notamment les propriétés, les signaux et les liaisons. Il explique également comment déployer ces extensions via des plugins.

De nombreux sujets abordés dans ce tutoriel sont documentés plus en détail dans la section « Présentation - Intégration de QML et C++ » et ses sous-sections. Vous serez notamment intéressé par les sous-sections « Exposition des attributs des classes C++ à QML » et « Définition de types QML à partir de C++ ».

Ouverture des sources du tutoriel

Le code de ce tutoriel est disponible dans les sources de Qt. Si vous avez installé Qt avec l'option « Qt Online Installer », vous trouverez les sources dans le répertoire d'installation de Qt, sous Examples/Qt-6.12.0/qml/tutorials/extending-qml/.

Création d’un projet à partir de zéro

Vous pouvez également suivre le tutoriel en créant les sources à partir de zéro : pour chaque chapitre, créez un nouveau projet à l’aide du modèle d’application «Qt Quick »:

Suivez ensuite les étapes en adaptant et en étendant le code squelette généré.

Chapitre 1 : Création d’un nouveau type

extending-qml/chapter1-basics

Une tâche courante lors de l’extension de QML consiste à fournir un nouveau type QML prenant en charge certaines fonctionnalités personnalisées allant au-delà de celles fournies par les éléments intégrés Qt Quick types. Par exemple, cela peut être nécessaire pour implémenter des modèles de données spécifiques, fournir des types dotés de capacités de peinture et de dessin personnalisées, ou accéder à des fonctionnalités système telles que la programmation réseau, qui ne sont pas accessibles via les fonctionnalités QML intégrées.

Dans ce tutoriel, nous allons montrer comment utiliser les classes C++ du module Qt Quick pour étendre QML. Le résultat final sera l’affichage d’un simple diagramme circulaire, mis en œuvre par plusieurs types QML personnalisés reliés entre eux via des fonctionnalités QML telles que les liaisons et les signaux, et mis à la disposition du QML Runtime via un plugin.

Pour commencer, créons un nouveau type QML appelé « PieChart » qui possède deux propriétés : un nom et une couleur. Nous le rendrons disponible dans un espace de noms de types importable appelé « Charts », avec une version 1.0.

Nous souhaitons que ce type PieChart soit utilisable depuis QML comme suit :

import Charts

PieChart {
    width: 100; height: 100
    name: "A simple pie chart"
    color: "red"
}

Remarque : vous devrez peut-être activer l’ QML Language Server e et sa mise en évidence sémantique (dans Qt Creator ou dans Qt Extension for Visual Studio Code) pour que Qt Creator mette correctement en évidence PieChart.

Pour ce faire, nous avons besoin d'une classe C++ qui encapsule ce type PieChart et ses propriétés. Étant donné que QML utilise largement le système de méta-objets de Qt, cette nouvelle classe doit :

  • Hériter de QObject
  • Déclarer ses propriétés à l’aide de la macro ` Q_PROPERTY `

Déclaration de la classe

Voici notre classe ` PieChart `, définie dans ` piechart.h` :

#include <QtQuick/QQuickPaintedItem>
#include <QColor>

class PieChart : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE setName FINAL)
    Q_PROPERTY(QColor color READ color WRITE setColor FINAL)
    QML_ELEMENT

public:
    PieChart(QQuickItem *parent = nullptr);

    QString name() const;
    void setName(const QString &name);

    QColor color() const;
    void setColor(const QColor &color);

    void paint(QPainter *painter) override;

private:
    QString m_name;
    QColor m_color;
};

La classe hérite de ` QQuickPaintedItem ` car nous souhaitons redéfinir la méthode ` QQuickPaintedItem::paint()` afin d’effectuer des opérations de dessin à l’aide de l’API ` QPainter `. Si la classe ne représentait qu’un type de données et n’était pas un élément devant être affiché, elle pourrait simplement hériter de ` QObject`. Ou bien, si nous souhaitons étendre les fonctionnalités d’une classe existante basée sur ` QObject`, elle pourrait hériter de cette classe à la place. Par ailleurs, si nous souhaitons créer un élément visuel qui n’a pas besoin d’effectuer d’opérations de dessin avec l’API QPainter, nous pouvons simplement créer une sous-classe de QQuickItem.

La classe ` PieChart ` définit les deux propriétés ` name ` et ` color` à l’aide de la macro ` Q_PROPERTY `, et redéfinit la méthode ` QQuickPaintedItem::paint()`. La classe ` PieChart ` est enregistrée à l’aide de la macro ` QML_ELEMENT `, afin de permettre son utilisation depuis QML. Si vous n’enregistrez pas la classe, ` App.qml ` ne pourra pas créer d’instance de ` PieChart`.

Configuration qmake

Pour que l’enregistrement prenne effet, l’option ` qmltypes ` est ajoutée à ` CONFIG ` dans le fichier de projet, et les valeurs ` QML_IMPORT_NAME ` et ` QML_IMPORT_MAJOR_VERSION ` sont indiquées :

CONFIG += qmltypes
QML_IMPORT_NAME = Charts
QML_IMPORT_MAJOR_VERSION = 1

De plus, un fichier qmldir doit être ajouté manuellement pour créer un module QML.

module Charts
typeinfo chapter1-basics.qmltypes
depends QtQuick
prefer :/qt/qml/Charts/
App 254.0 App.qml

Configuration de CMake

Pour que l'enregistrement prenne effet lors de l'utilisation de CMake, utilisez la commande qt_add_qml_module():

qt_add_qml_module(chapter1-basics
    URI Charts
    QML_FILES App.qml
    DEPENDENCIES QtQuick
)

L'API qt_add_qml_module() génère automatiquement un fichier « qmldir » pour le module QML.

Implémentation de la classe

L’implémentation de la classe dans piechart.cpp se contente de définir et de renvoyer les valeurs m_name et m_color comme il se doit, et implémente paint() pour dessiner un simple diagramme circulaire :

PieChart::PieChart(QQuickItem *parent)
    : QQuickPaintedItem(parent)
{
}
...
void PieChart::paint(QPainter *painter)
{
    QPen pen(m_color, 2);
    painter->setPen(pen);
    painter->setRenderHints(QPainter::Antialiasing, true);
    painter->drawPie(boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16);
}

Utilisation en QML

Maintenant que nous avons défini le type ` PieChart `, nous allons l’utiliser depuis QML. Le fichier ` App.qml ` crée un élément ` PieChart ` et affiche les détails du diagramme circulaire à l’aide d’un élément QML standard ` Text ` :

import Charts
import QtQuick

Item {
    width: 300; height: 200

    PieChart {
        id: aPieChart
        anchors.centerIn: parent
        width: 100; height: 100
        name: "A simple pie chart"
        color: "red"
    }

    Text {
        anchors { bottom: parent.bottom; horizontalCenter: parent.horizontalCenter; bottomMargin: 20 }
        text: aPieChart.name
    }
}

Notez que bien que la couleur soit spécifiée sous forme de chaîne de caractères dans QML, elle est automatiquement convertie en objet QColor pour la propriété color de PieChart. Des conversions automatiques sont prévues pour divers autres types de valeurs. Par exemple, une chaîne de caractères telle que « 640x480 » peut être automatiquement convertie en valeur QSize.

Nous allons également créer une application C++ qui utilise un objet ` QQuickView ` pour exécuter et afficher des graphiques circulaires ( App.qml).

Voici l’ main.cpp de l’application :

#include "piechart.h"
#include <QtQuick/QQuickView>
#include <QGuiApplication>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQuickView view;
    view.setResizeMode(QQuickView::SizeRootObjectToView);
    view.loadFromModule("Charts", "App");
    view.show();
    return QGuiApplication::exec();
}

Compilation du projet

Pour compiler le projet, nous incluons les fichiers, nous effectuons la liaison avec les bibliothèques et nous définissons un espace de noms de types appelé « Charts » avec la version 1.0 pour tous les types exposés à QML.

Utilisation de qmake :

QT += qml quick

CONFIG += qmltypes
QML_IMPORT_NAME = Charts
QML_IMPORT_MAJOR_VERSION = 1

HEADERS += piechart.h
SOURCES += piechart.cpp \
           main.cpp

RESOURCES += chapter1-basics.qrc

DESTPATH = $$[QT_INSTALL_EXAMPLES]/qml/tutorials/extending-qml/chapter1-basics
target.path = $$DESTPATH
INSTALLS += target

Avec CMake :

# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

cmake_minimum_required(VERSION 3.16)
project(chapter1-basics LANGUAGES CXX)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)

qt_standard_project_setup(REQUIRES 6.8)

qt_add_executable(chapter1-basics
    main.cpp
    piechart.cpp piechart.h
)

set_target_properties(chapter1-basics PROPERTIES
    WIN32_EXECUTABLE TRUE
    MACOSX_BUNDLE TRUE
)

target_link_libraries(chapter1-basics PUBLIC
    Qt6::Core
    Qt6::Gui
    Qt6::Qml
    Qt6::Quick
)
qt_add_qml_module(chapter1-basics
    URI Charts
    QML_FILES App.qml
    DEPENDENCIES QtQuick
)
install(TARGETS chapter1-basics
    BUNDLE  DESTINATION .
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

qt_generate_deploy_qml_app_script(
    TARGET chapter1-basics
    OUTPUT_SCRIPT deploy_script
    NO_UNSUPPORTED_PLATFORM_ERROR
    DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
install(SCRIPT ${deploy_script})

Nous pouvons désormais compiler et exécuter l'application :

L'application crée le diagramme circulaire selon les propriétés définies par le type de diagramme circulaire

Remarque : vous verrez peut-être s’afficher l’avertissement « Expression … dépend de propriétés non liables : PieChart::name ». Cela est dû au fait que nous avons ajouté un lien à la propriété modifiable « name », mais que nous n’avons pas encore défini de signal de notification pour celle-ci. Le moteur QML ne peut donc pas mettre à jour le lien si la valeur de « name » change. Ce problème sera résolu dans les chapitres suivants.

Chapitre 2 : Connexion aux méthodes et signaux C++

extending-qml/chapter2-methods

Supposons que nous souhaitions que ` PieChart ` dispose d’une méthode « `clearChart()`` » qui efface le graphique, puis émette un signal « `chartCleared` ». Notre ` App.qml ` serait alors capable d’appeler ` clearChart() ` et de recevoir des signaux ` chartCleared() ` comme suit :

import Charts
import QtQuick

Item {
    width: 300; height: 200

    PieChart {
        id: aPieChart
        anchors.centerIn: parent
        width: 100; height: 100
        color: "red"

        onChartCleared: console.log("The chart has been cleared")
    }

    MouseArea {
        anchors.fill: parent
        onClicked: aPieChart.clearChart()
    }

    Text {
        anchors { bottom: parent.bottom; horizontalCenter: parent.horizontalCenter; bottomMargin: 20 }
        text: "Click anywhere to clear the chart"
    }
}

L'utilisateur peut cliquer n'importe où dans la fenêtre de l'application pour effacer le graphique. Cela déclenche la méthode « Clear Chart ».

Pour ce faire, nous ajoutons une méthode ` clearChart() ` et un signal ` chartCleared() ` à notre classe C++ :

class PieChart : public QQuickPaintedItem
{
    ...
public:
    ...
    Q_INVOKABLE void clearChart();

signals:
    void chartCleared();
    ...
};

L’utilisation de ` Q_INVOKABLE ` rend la méthode ` clearChart() ` accessible au système de méta-objets Qt, et par conséquent, au QML.

Remarque : vous pouvez également déclarer la méthode en tant que slot Qt au lieu d'utiliser ` Q_INVOKABLE`, car les slots publics et protégés sont également appelables depuis QML (vous ne pouvez pas appeler les slots privés).

La méthode ` clearChart() ` change la couleur en ` Qt::transparent`, rafraîchit le graphique, puis émet le signal ` chartCleared() ` :

void PieChart::clearChart()
{
    setColor(QColor(Qt::transparent));
    update();

    emit chartCleared();
}

Désormais, lorsque nous lançons l’application et que nous cliquons sur la fenêtre, le diagramme circulaire disparaît et l’application affiche :

qml: The chart has been cleared

Chapitre 3 : Ajout de liaisons de propriétés

extending-qml/chapter3-bindings

Le liaison de propriétés est une fonctionnalité puissante de QML qui permet de synchroniser automatiquement des valeurs de types différents. Elle utilise des signaux pour notifier et mettre à jour les valeurs d’autres types lorsque les valeurs des propriétés sont modifiées.

Activons les liaisons de propriétés pour la propriété ` color `. Cela signifie que si nous avons un code comme celui-ci :

import Charts
import QtQuick

Item {
    width: 300; height: 200

    Row {
        anchors.centerIn: parent
        spacing: 20

        PieChart {
            id: chartA
            width: 100; height: 100
            color: "red"
        }

        PieChart {
            id: chartB
            width: 100; height: 100
            color: chartA.color
        }
    }

    MouseArea {
        anchors.fill: parent
        onClicked: { chartA.color = "blue" }
    }

    Text {
        anchors { bottom: parent.bottom; horizontalCenter: parent.horizontalCenter; bottomMargin: 20 }
        text: "Click anywhere to change the chart color"
    }
}

La propriété « couleur » du diagramme circulaire B est liée à celle du diagramme circulaire A.

L’instruction « color: chartA.color » lie la valeur « color » de chartB à la propriété « color » de chartA. Chaque fois que la valeur « color » de chartA change, la valeur « color » de chartB est mise à jour avec la même valeur. Lorsque l’on clique sur la fenêtre, le gestionnaire « onClicked » dans MouseArea modifie la couleur de chartA, ce qui fait passer les deux graphiques à la couleur bleue.

Il est facile d’activer la liaison de propriété pour la propriété color. Nous ajoutons une fonctionnalité NOTIFY à sa déclaration Q_PROPERTY() afin d’indiquer qu’un signal « colorChanged » est émis chaque fois que la valeur change.

class PieChart : public QQuickPaintedItem
{
    ...
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
public:
    ...
signals:
    void colorChanged();
    ...
};

Ensuite, nous émettons ce signal dans ` setColor()` :

void PieChart::setColor(const QColor &color)
{
    if (color != m_color) {
        m_color = color;
        update();   // repaint with the new color
        emit colorChanged();
    }
}

Il est important que ` setColor() ` vérifie que la valeur de la couleur a bien changé avant d’émettre ` colorChanged()`. Cela garantit que le signal n’est pas émis inutilement et évite également les boucles lorsque d’autres éléments réagissent au changement de valeur.

L’utilisation des liaisons est essentielle en QML. Vous devez toujours ajouter des signaux NOTIFY aux propriétés si celles-ci peuvent être implémentées, afin que vos propriétés puissent être utilisées dans des liaisons. Les propriétés qui ne peuvent pas être liées ne peuvent pas être mises à jour automatiquement et ne peuvent pas être utilisées de manière aussi flexible dans QML. De plus, comme les liaisons sont très fréquemment invoquées et constituent un élément essentiel de l’utilisation de QML, les utilisateurs de vos types QML personnalisés risquent de rencontrer des comportements inattendus si les liaisons ne sont pas implémentées.

Chapitre 4 : Utilisation des types de propriétés personnalisés

extending-qml/chapter4-customPropertyTypes

Le type ` PieChart ` dispose actuellement d’une propriété de type chaîne de caractères et d’une propriété de type couleur. Il pourrait comporter de nombreux autres types de propriétés. Par exemple, il pourrait disposer d’une propriété de type entier pour stocker un identifiant pour chaque graphique :

// C++
class PieChart : public QQuickPaintedItem
{
    Q_PROPERTY(int chartId READ chartId WRITE setChartId NOTIFY chartIdChanged)
    ...

public:
    void setChartId(int chartId);
    int chartId() const;
    ...

signals:
    void chartIdChanged();
};

// QML
PieChart {
    ...
    chartId: 100
}

Outre ` int`, nous pourrions utiliser divers autres types de propriétés. De nombreux types de données Qt, tels que ` QColor`, ` QSize ` et ` QRect `, sont automatiquement pris en charge par QML. (Voir la documentation « Conversion des types de données entre QML et C++ » pour une liste complète.)

Si nous souhaitons créer une propriété dont le type n’est pas pris en charge par défaut par QML, nous devons enregistrer ce type auprès du moteur QML.

Par exemple, remplaçons l’utilisation de l’ property par un type appelé « PieSlice » qui possède une propriété color. Au lieu d’attribuer une couleur, nous attribuons une valeur PieSlice qui contient elle-même une color:

import Charts
import QtQuick

Item {
    width: 300; height: 200

    PieChart {
        id: chart
        anchors.centerIn: parent
        width: 100; height: 100

        pieSlice: PieSlice {
            anchors.fill: parent
            color: "red"
        }
    }

    Component.onCompleted: console.log("The pie is colored " + chart.pieSlice.color)
}

Tout comme PieChart, ce nouveau type PieSlice hérite de QQuickPaintedItem et déclare ses propriétés à l'aide de Q_PROPERTY() :

class PieSlice : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(QColor color READ color WRITE setColor FINAL)
    QML_ELEMENT

public:
    PieSlice(QQuickItem *parent = nullptr);

    QColor color() const;
    void setColor(const QColor &color);

    void paint(QPainter *painter) override;

private:
    QColor m_color;
};

Pour l’utiliser dans PieChart, nous modifions la déclaration de la propriété color et les signatures des méthodes associées :

class PieChart : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(PieSlice* pieSlice READ pieSlice WRITE setPieSlice FINAL)
    ...
public:
    ...
    PieSlice *pieSlice() const;
    void setPieSlice(PieSlice *pieSlice);
    ...
};

Il y a un point à prendre en compte lors de l’implémentation de ` setPieSlice()`. L’élément ` PieSlice ` est un élément visuel ; il doit donc être défini comme enfant de ` PieChart ` à l’aide de ` QQuickItem::setParentItem()` afin que ` PieChart ` sache qu’il doit dessiner cet élément enfant lors du rendu de son contenu :

void PieChart::setPieSlice(PieSlice *pieSlice)
{
    m_pieSlice = pieSlice;
    pieSlice->setParentItem(this);
}

Tout comme le type PieChart, le type PieSlice doit être exposé à QML à l’aide de QML_ELEMENT.

class PieSlice : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(QColor color READ color WRITE setColor FINAL)
    QML_ELEMENT

public:
    PieSlice(QQuickItem *parent = nullptr);

    QColor color() const;
    void setColor(const QColor &color);

    void paint(QPainter *painter) override;

private:
    QColor m_color;
};
    ...

Comme pour PieChart, nous ajoutons l'espace de noms de type « Charts », version 1.0, à notre fichier de construction :

Avec qmake :

QT += qml quick

CONFIG += qmltypes
QML_IMPORT_NAME = Charts
QML_IMPORT_MAJOR_VERSION = 1

HEADERS += piechart.h \
           pieslice.h
SOURCES += piechart.cpp \
           pieslice.cpp \
           main.cpp

RESOURCES += chapter4-customPropertyTypes.qrc

DESTPATH = $$[QT_INSTALL_EXAMPLES]/qml/tutorials/extending-qml/chapter4-customPropertyTypes
target.path = $$DESTPATH
INSTALLS += target

Avec CMake :

    ...
qt_add_executable(chapter4-customPropertyTypes
    main.cpp
    piechart.cpp piechart.h
    pieslice.cpp pieslice.h
)
qt_add_qml_module(chapter4-customPropertyTypes
    URI Charts
    QML_FILES App.qml
    DEPENDENCIES QtQuick
)
    ...

Chapitre 5 : Utilisation des types de propriétés de liste

extending-qml/chapter5-listproperties

À l’heure actuelle, un PieChart ne peut avoir qu’un seul PieSlice. Idéalement, un graphique devrait comporter plusieurs segments, avec des couleurs et des tailles différentes. Pour ce faire, nous pourrions disposer d’une propriété slices acceptant une liste d’éléments PieSlice:

pragma ComponentBehavior: Bound
import Charts
import QtQuick

Item {
    width: 300; height: 200

    PieChart {
        id: chart
        anchors.centerIn: parent
        width: 100; height: 100

        component Slice: PieSlice {
            parent: chart
            anchors.fill: parent
        }

        slices: [
            Slice {
                color: "red"
                fromAngle: 0
                angleSpan: 110
            },
            Slice {
                color: "black"
                fromAngle: 110
                angleSpan: 50
            },
            Slice {
                color: "blue"
                fromAngle: 160
                angleSpan: 100
            }
        ]
    }
}

La propriété `slices` accepte une liste d'éléments de type « pieslice ». L'élément `pieslice` définit l'angle et la couleur de chaque part de camembert.

Pour ce faire, nous remplaçons la propriété ` pieSlice ` de ` PieChart ` par une propriété ` slices `, déclarée de type ` QQmlListProperty `. La classe ` QQmlListProperty ` permet de créer des propriétés de type liste dans les types exposés à QML. Nous remplaçons la fonction ` pieSlice() ` par une fonction ` slices() ` qui renvoie une liste de tranches. Nous utilisons également un ` QList ` pour stocker la liste interne des tranches sous la forme ` m_slices` :

class PieChart : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<PieSlice> slices READ slices FINAL)
    ...
public:
    ...
    QQmlListProperty<PieSlice> slices();

private:
    QString m_name;
    QList<PieSlice *> m_slices;
};

Bien que la propriété slices ne dispose pas d’une fonction WRITE associée, elle reste modifiable en raison du fonctionnement de QQmlListProperty. Dans l’implémentation de PieChart, nous implémentons PieChart::slices() pour renvoyer une valeur QQmlListProperty:

QQmlListProperty<PieSlice> PieChart::slices()
{
    return QQmlListProperty<PieSlice>(this, &m_slices);
}

Cela synthétise les fonctions nécessaires pour interagir avec la liste depuis QML. L’objet ` QQmlListProperty ` ainsi obtenu offre une vue sur la liste. Vous pouvez également fournir manuellement les fonctions d’accès individuelles à la liste. Cela s’avère nécessaire si votre liste n’est pas de type ` QList ` ou si vous souhaitez restreindre ou personnaliser d’une autre manière l’accès de QML à votre liste. Dans la plupart des cas, cependant, le constructeur prenant un pointeur de type ` QList ` constitue l’option la plus sûre et la plus simple.

La classe ` PieSlice ` a également été modifiée pour inclure les propriétés ` fromAngle ` et ` angleSpan ` et pour tracer la tranche en fonction de ces valeurs. Il s'agit d'une modification simple si vous avez lu les pages précédentes de ce tutoriel ; le code n'est donc pas présenté ici.

Chapitre 6 : Écrire un plugin d’extension

extending-qml/chapter6-plugins

Actuellement, les types « PieChart » et « PieSlice » sont utilisés par « App.qml », qui s’affiche à l’aide d’un « QQuickView » dans une application C++. Une autre façon d’utiliser notre extension QML consiste à créer une bibliothèque de plugins afin de la rendre disponible au moteur QML en tant que nouveau module d’importation QML. Cela permet d’enregistrer les types ` PieChart ` et ` PieSlice ` dans un espace de noms de types pouvant être importé par n’importe quelle application QML, au lieu de limiter l’utilisation de ces types à une seule application.

Les étapes de création d’un plugin sont décrites dans la section Création de plugins C++ pour QML. Pour commencer, nous créons une classe de plugin nommée ` ChartsPlugin`. Elle hérite de ` QQmlEngineExtensionPlugin ` et utilise la macro ` Q_PLUGIN_METADATA()` pour enregistrer le plugin auprès du système de méta-objets Qt.

Voici la définition de ` ChartsPlugin ` dans ` chartsplugin.h` :

#include <QQmlEngineExtensionPlugin>

class ChartsPlugin : public QQmlEngineExtensionPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
};

Ensuite, nous configurons le fichier de compilation pour définir le projet en tant que bibliothèque de plug-ins.

Avec qmake :

TEMPLATE = lib
CONFIG += plugin qmltypes
QT += qml quick

QML_IMPORT_NAME = Charts
QML_IMPORT_MAJOR_VERSION = 1

TARGET = $$qtLibraryTarget(chartsplugin)

HEADERS += piechart.h \
           pieslice.h \
           chartsplugin.h

SOURCES += piechart.cpp \
           pieslice.cpp

DESTPATH=$$[QT_INSTALL_EXAMPLES]/qml/tutorials/extending-qml/chapter6-plugins/$$QML_IMPORT_NAME

target.path=$$DESTPATH
qmldir.files=$$PWD/qmldir
qmldir.path=$$DESTPATH
INSTALLS += target qmldir

CONFIG += install_ok  # Do not cargo-cult this!

OTHER_FILES += qmldir

# Copy the qmldir file to the same folder as the plugin binary
cpqmldir.files = qmldir
cpqmldir.path = .
COPIES += cpqmldir

Avec CMake :

# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

qt6_policy(SET QTP0001 NEW)
qt6_add_qml_module(chartsplugin
    URI "Charts"
    PLUGIN_TARGET chartsplugin
    DEPENDENCIES QtQuick
)

target_sources(chartsplugin PRIVATE
    piechart.cpp piechart.h
    pieslice.cpp pieslice.h
)

target_link_libraries(chartsplugin PRIVATE
    Qt6::Core
    Qt6::Gui
    Qt6::Qml
    Qt6::Quick
)

install(TARGETS chartsplugin
    RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/Charts"
    LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}/Charts"
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
    DESTINATION "${CMAKE_INSTALL_BINDIR}/Charts"
)

Lors de la compilation de cet exemple sous Windows ou Linux, le répertoire ` Charts ` se trouvera au même niveau que l’application qui utilise notre nouveau module d’importation. Ainsi, le moteur QML trouvera notre module, car le chemin de recherche par défaut pour les importations QML inclut le répertoire de l’exécutable de l’application. Sous macOS, le binaire du plugin est copié dans ` Contents/PlugIns ` au sein du bundle de l’application. Avec qmake, ce chemin est défini dans ` chapter6-plugins/app.pro` :

macos:!qtConfig(static) {
    charts.files = $$OUT_PWD/Charts
    charts.path = Contents/PlugIns
    QMAKE_BUNDLE_DATA += charts
}

Pour tenir compte de cela, nous devons également ajouter cet emplacement en tant que chemin d’importation QML dans ` main.cpp` :

    QQuickView view;
    ...

La définition de chemins d’importation personnalisés est également utile lorsque plusieurs applications utilisent les mêmes importations QML.

Le fichier .pro contient également une fonctionnalité supplémentaire garantissant que le fichier qmldir de définition du module est toujours copié au même emplacement que le binaire du plugin.

Le fichier ` qmldir ` déclare le nom du module et le plugin mis à disposition par ce dernier :

module Charts
optional plugin chartsplugin
typeinfo plugins.qmltypes
depends QtQuick
prefer :/qt/qml/Charts/

Nous disposons désormais d’un module QML pouvant être importé dans n’importe quelle application, à condition que le moteur QML sache où le trouver. L’exemple contient un exécutable qui charge le fichier ` App.qml`, lequel utilise l’instruction ` import Charts 1.0 `. Vous pouvez également charger le fichier QML à l’aide de l’outil `qml`, en définissant le chemin d’importation sur le répertoire courant afin qu’il trouve le fichier ` qmldir ` :

qml -I . App.qml

Le module « Charts » sera chargé par le moteur QML, et les types fournis par ce module seront disponibles pour être utilisés dans tout document QML qui l'importe.

Chapitre 7 : Résumé

Dans ce tutoriel, nous avons présenté les étapes de base pour créer une extension QML :

  • Définir de nouveaux types QML en créant des sous-classes de ` QObject ` et en les enregistrant avec ` QML_ELEMENT ` ou ` QML_NAMED_ELEMENT()`
  • Ajouter des méthodes appelables à l'aide de ` Q_INVOKABLE ` ou de slots Qt, et les connecter à des signaux Qt avec la syntaxe ` onSignal `
  • Ajoutez des liaisons de propriétés en définissant des signaux NOTIFY
  • Définissez des types de propriétés personnalisés si les types intégrés ne suffisent pas
  • Définissez des types de propriétés de liste à l’aide de QQmlListProperty
  • Créez une bibliothèque de plugins en définissant un plugin Qt et en rédigeant un fichier qmldir

La documentation présentant la vue d’ensemble de l’intégration QML et C++ présente d’autres fonctionnalités utiles pouvant être ajoutées aux extensions QML. Par exemple, nous pourrions utiliser des propriétés par défaut pour permettre l’ajout de tranches sans utiliser la propriété ` slices ` :

PieChart {
    PieSlice { ... }
    PieSlice { ... }
    PieSlice { ... }
}

Ou ajouter et supprimer aléatoirement des tranches de temps à autre à l’aide de sources de valeurs de propriété:

PieChart {
    PieSliceRandomizer on slices {}
}

Remarque : pour en savoir plus sur les extensions QML et leurs fonctionnalités, suivez le tutoriel « Écrire des extensions QML avancées avec C++ ».

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