Sur cette page

Graphique de dispersion simple

Utilisation d'Scatter3D dans une application QML.

Le graphique de dispersion simple montre comment créer un graphique de dispersion simple à l'aide d'Scatter3D et de QML.

Pour savoir comment interagir avec le graphique, consultez cette page.

Pour savoir comment créer votre propre application Qt Quick, consultez l'aide d'Qt Creator.

Nuage de points en 3D avec des sphères colorées et des commandes de visualisation

Exécution de l'exemple

Vous pouvez exécuter l’exemple à partir de :

Configuration de la mise en page

Avant de se plonger dans le code QML, main.cpp configure l’application. Le fichier main.qml est lu à partir de la ressource (qrc:)

viewer.setSource(QUrl("qrc:/qml/scatter/main.qml"));

Ce fichier ` main.qml ` constitue le point de départ du code QML de l’application. Commencez par importer tous les modules QML requis :

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtGraphs

Ensuite, créez un élément « Data » qui contiendra les données du graphique.

Data {
    id: seriesData
}

Ensuite, créez deux éléments ` GraphsTheme ` qui définissent deux thèmes différents pour le graphique : un thème « Qt Green » et un thème « Qt Neon Green ».

GraphsTheme {
    id: themeQt
    theme: GraphsTheme.Theme.QtGreen
    labelFont.pointSize: 40
}

GraphsTheme {
    id: themeQtNeonGreen
    theme: GraphsTheme.Theme.QtGreenNeon
    colorScheme: GraphsTheme.ColorScheme.Dark
}

La mise en page de cette application comprendra cinq boutons permettant d’activer ou de désactiver différentes options visuelles du graphique, ainsi que le graphique lui-même, bien sûr. Il existe de nombreuses façons de les organiser ; l’une d’entre elles consiste à utiliser un élément « GridLayout » contenant quatre boutons, l’élément « Graph » et le cinquième bouton, tous affichés dans un élément « ColumnLayout ».

ColumnLayout {
    id: mainLayout
    anchors.fill: parent
    anchors.margins: margin
    spacing: spacing

La GridLayout est responsive. Cela signifie que le nombre de colonnes utilisées dépend de la largeur de la fenêtre de l’application. Pour ce faire, on attribue à la propriété columns un opérateur ternaire, qui prend la valeur 1, 2 ou 4 en fonction de la largeur de la fenêtre.

GridLayout {
    Layout.fillWidth: true
    rowSpacing: spacing
    columnSpacing: spacing

    columns: mainView.width < mainView.buttonMinWidth * 2 + mainView.spacing + mainView.margin * 2 // width of 2 buttons
             ? 1
             : (mainView.width < mainView.buttonMinWidth * 4 + mainView.spacing * 3 + mainView.margin * 2 // width of 4 buttons
                ? 2
                : 4)

Dans cet « GridLayout », quatre instances de ` RoundButton` sont créées. L’une pour activer ou désactiver les ombres, une autre pour le lissage du maillage, une troisième pour la position de la caméra et enfin, une dernière pour modifier l’arrière-plan du graphique. Tous les boutons suivent la même structure. Plutôt que de redéfinir cette structure pour chaque bouton, il est possible de créer un composant personnalisé.

component CustomButton : RoundButton {
    id: buttonRoot

CustomButtom Ce composant étend le type ` RoundButton `, mais y ajoute quelques éléments supplémentaires. Des propriétés telles que ` Layout.minimumWidth`, ` Layout.FillWidth`, ` radius ` et ` background ` sont définies. Celles-ci déterminent le style et la mise en page du bouton.

property alias source: iconImage.source

Layout.minimumWidth: buttonMinWidth
Layout.fillWidth: true

radius: mainView.radius

background: Rectangle {
    radius: mainView.radius
    color: "white"
    border.color: "black"
}

Quant à la propriété contentItem, elle définit le contenu du bouton, en l’occurrence un élément de type Row contenant une icône et du texte. L’icône est importée à l’aide d’un élément de type « Image », en définissant la propriété « source ». Le texte est créé à l’aide d’un élément de type « Label » et défini via sa propriété « text ». Deux alias sont créés pour ces deux propriétés en haut du composant personnalisé ; cela permettra de les définir lors de la création d’une instance de « CustomButton ».

contentItem: Row {
    id: content
    Image {
        id: iconImage
    }
    Label {
        text: buttonRoot.text
        horizontalAlignment: Text.AlignLeft
        anchors.verticalCenter: parent.verticalCenter
    }
}

Dans le fichier « GridLayout », quatre objets « CustomButton» sont créés. Trois propriétés doivent être définies : les deux alias pour l’image de l’icône et le texte, ainsi que le signal « onClicked ».

CustomButton {
    id: shadowButton
    text: graph.shadowQuality === Graphs3D.ShadowQuality.None ?
              qsTr("Show Shadows") : qsTr("Hide Shadows")
    source: graph.shadowQuality === Graphs3D.ShadowQuality.None ?
                "qrc:/images/shadow.svg" : "qrc:/images/shadow_hide.svg"
    onClicked: {
        graph.shadowQuality = graph.shadowQuality === Graphs3D.ShadowQuality.None ?
                    Graphs3D.ShadowQuality.High :
                    Graphs3D.ShadowQuality.None
    }
}

Par exemple, le bouton « shadow » permet d’activer ou de désactiver l’ombre à l’aide de l’ onClicked.

Configuration du graphique

Vient ensuite dans la mise en page l’ Graph, qui est défini dans son propre fichier QML Graph.qml et instancié comme suit :

Graph {
    id: graph
    Layout.fillWidth: true
    Layout.fillHeight: true
}

Si l’on examine son implémentation dans ` Graph.qml`, le graphique est de type ` Scatter3D`, imbriqué dans un élément de type ` Item `.

Scatter3D {
    id: scatterGraph

Avant de définir les ensembles de données, certaines propriétés des axes sont configurées.

axisX.segmentCount: 3
axisX.subSegmentCount: 2
axisX.labelFormat: "%.2f"
axisZ.segmentCount: 2
axisZ.subSegmentCount: 2
axisZ.labelFormat: "%.2f"
axisY.segmentCount: 2
axisY.subSegmentCount: 2
axisY.labelFormat: "%.2f"

Configuration des données

Le graphique présente trois ensembles de données, représentant les données fournies dans Data.qml. Les ensembles de données sont stockés dans des objets de type ListModel

ListModel {
    id: dataModel
    ListElement{ xPos: -10.0; yPos: 5.0; zPos: -5.0 }
    ...
    ListElement{ xPos: -7.54 ; yPos: 2.8 ; zPos: -3.68 }
}

ListModel {
    id: dataModelTwo
    ListElement{ xPos: 2.25 ; yPos: 1.36 ; zPos: -1.3 }
    ...
    ListElement{ xPos: -3.4 ; yPos: 0.6 ; zPos: 0.9 }
}

ListModel {
    id: dataModelThree
    ListElement{ xPos: 8.0; yPos: -2.0; zPos: 4.0 }
    ...
    ListElement{ xPos: 5.66 ; yPos: -4.98 ; zPos: 3.72 }
}

et exposés aux autres fichiers à l’aide d’ property aliass

property alias model: dataModel
property alias modelTwo: dataModelTwo
property alias modelThree: dataModelThree

Avant d’utiliser ces alias, créez un type « Scatter3DSeries » contenant un élément « ItemModelScatterDataProxy ».

Scatter3DSeries {
    id: scatterSeries
    itemLabelFormat: "Series 1: X:@xLabel Y:@yLabel Z:@zLabel"

    ItemModelScatterDataProxy {
        itemModel: seriesData.model
        xPosRole: "xPos"
        yPosRole: "yPos"
        zPosRole: "zPos"
    }
}

Ce proxy de données fait référence aux ensembles de données dans Data.qml à l’aide de sa propriété itemModel. Les alias mentionnés ci-dessus sont accessibles via la méthode id du type Data instancié dans main.qml, seriesData.

itemModel: seriesData.model

Répétez cette opération pour les deux autres jeux de données.

Scatter3DSeries {
    id: scatterSeriesTwo
    ...
ItemModelScatterDataProxy {
    itemModel: seriesData.modelTwo
    ...
    }
}
Scatter3DSeries {
    id: scatterSeriesThree
    ...
ItemModelScatterDataProxy {
    itemModel: seriesData.modelThree
    ...
    }
}

Parfait, le graphique de dispersion pointe désormais vers les trois ensembles de données.

Il ne reste plus qu’à ajouter le bouton restant, theme button, à notre ColumnLayout. La seule différence avec ce bouton est qu’il est positionné sur le côté gauche de la mise en page. Pour cela, il suffit de définir la propriété Layout.alignment sur Qt.AlignLeft et Layout.fillWidth sur false.

CustomButton {
    id: themeButton
    Layout.alignment: Qt.AlignLeft
    Layout.fillWidth: false

Exemple de contenu

Exemple de projet @ code.qt.io

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