Galerie de graphiques de surface
Galerie présentant trois façons différentes d'utiliser un graphique « Surface3D ».
La galerie de graphiques de surface présente trois fonctionnalités personnalisées différentes avec les graphiques « Surface3D ». Ces fonctionnalités disposent de leurs propres onglets dans l'application.
Les sections suivantes se concentrent uniquement sur ces fonctionnalités et ne traitent pas des fonctionnalités de base. Pour une documentation plus détaillée avec des exemples en QML, consultez la page « Graphique de dispersion simple ».

Exécution de l'exemple
Vous pouvez exécuter l'exemple à partir de :
- Qt Creator
Ouvrez le mode « Welcome » et sélectionnez l’exemple à l’adresse Examples. Pour plus d’informations, consultez Qt Creator: Tutoriel : Compiler et exécuter.
- Qt Extension for Visual Studio Code
Exécutez la commande « Qt: Open Qt examples » à partir de « Command Palette », puis sélectionnez l’exemple dans la liste. Pour plus d’informations, consultez Qt Extension for Visual Studio Code: Tutoriel : Compiler et exécuter.
Carte d'altitude
Dans l’onglet « Height Map », générez un graphique de surface à partir de données d’altitude. Les données utilisées sont une carte d’altitude du mont Ruapehu et du mont Ngauruhoe en Nouvelle-Zélande.
Ajout de données au graphique
Les données sont définies à l’aide de l’ HeightMapSurfaceDataProxy, qui lit les informations de hauteur à partir d’une image de carte d’altitude. Le proxy lui-même est contenu dans un Surface3DSeries. À l’intérieur de l’ HeightMapSurfaceDataProxy, la propriété heightMapFile spécifie le fichier image contenant les données d’altitude. Les propriétés « value » du proxy définissent les valeurs minimales et maximales de la largeur, de la profondeur et de la hauteur de la surface. Les coordonnées « z » et « x » correspondent à la latitude et à la longitude, approximativement à la position réelle, tandis que la « y » est exprimée en mètres.
Remarque : le rapport d’aspect du graphique n’est pas défini à l’échelle réelle, mais la hauteur est exagérée.
Surface3DSeries {
id: heightSeries
shading: Surface3DSeries.Shading.Smooth
drawMode: Surface3DSeries.DrawSurface
HeightMapSurfaceDataProxy {
heightMapFile: "://qml/surfacegallery/heightmap.png"
// We don't want the default data values set by heightmap proxy, but use
// actual coordinate and height values instead
autoScaleY: true
minYValue: 740
maxYValue: 2787
minZValue: -374 // ~ -39.374411"N
maxZValue: -116 // ~ -39.115971"N
minXValue: 472 // ~ 175.471767"E
maxXValue: 781 // ~ 175.780758"E
}
onDrawModeChanged: heightMapView.checkState()
}Affichage des données
Dans main.qml, configurez l’élément Surface3D pour afficher les données.
Commencez par définir le dégradé personnalisé à utiliser pour la surface. Définissez les couleurs de la position 0,0 à 1,0 à l’aide de Gradient, en ajoutant deux arrêts supplémentaires pour rendre le graphique plus vivant :
Gradient {
id: surfaceGradient
GradientStop { position: 0.0; color: "darkgreen"}
GradientStop { position: 0.15; color: "darkslategray" }
GradientStop { position: 0.7; color: "peru" }
GradientStop { position: 1.0; color: "white" }
}Définissez cet élément dans la propriété « baseGradients » de l'theme utilisé dans Surface3D:
theme: GraphsTheme {
colorScheme: GraphsTheme.ColorScheme.Dark
labelFont.family: "STCaiyun"
labelFont.pointSize: 35
colorStyle: GraphsTheme.ColorStyle.ObjectGradient
baseGradients: [surfaceGradient] // Use the custom gradient
}Utilisez les boutons pour contrôler les autres fonctionnalités d’ Surface3D.
Le premier bouton active ou désactive la grille de surface. Le mode de dessin ne peut pas être effacé complètement ; ainsi, à moins que la surface elle-même ne soit visible, la grille de surface ne peut pas être masquée :
onClicked: {
if (heightSeries.drawMode & Surface3DSeries.DrawWireframe)
heightSeries.drawMode &= ~Surface3DSeries.DrawWireframe;
else
heightSeries.drawMode |= Surface3DSeries.DrawWireframe;
}Le deuxième permet de définir la couleur de la grille de surface :
onClicked: {
if (Qt.colorEqual(heightSeries.wireframeColor, "#000000")) {
heightSeries.wireframeColor = "red";
text = "Black surface\ngrid color";
} else {
heightSeries.wireframeColor = "black";
text = "Red surface\ngrid color";
}
}Le troisième active ou désactive la surface en mode de dessin de surface. Le mode de dessin ne peut pas être entièrement effacé ; ainsi, à moins que la grille de surface ne soit visible, la surface elle-même ne peut pas être masquée :
onClicked: {
if (heightSeries.drawMode & Surface3DSeries.DrawSurface)
heightSeries.drawMode &= ~Surface3DSeries.DrawSurface;
else
heightSeries.drawMode |= Surface3DSeries.DrawSurface;
}Le quatrième définit le mode d’ombrage. Si vous exécutez l’exemple sur un système OpenGL ES, l’ombrage plat n’est pas disponible :
onClicked: {
if (heightSeries.shading === Surface3DSeries.Shading.Flat) {
heightSeries.shading = Surface3DSeries.Shading.Smooth;
text = "Show\nFlat"
} else {
heightSeries.shading = Surface3DSeries.Shading.Flat;
text = "Show\nSmooth"
}
}Les boutons restants contrôlent les caractéristiques de l'arrière-plan du graphique.
Spectrogramme
Dans l'onglet « Spectrogram », affichez des spectrogrammes polaires et cartésiens et utilisez la projection orthographique pour les représenter en 2D.
Un spectrogramme est un graphique de surface présentant un dégradé de valeurs utilisé pour mettre en évidence les différentes valeurs. Généralement, les spectrogrammes sont représentés sous forme de surfaces bidimensionnelles, simulées à l’aide d’une vue orthographique de dessus du graphique. Pour renforcer l’effet 2D, désactivez la rotation du graphique à l’aide de la souris ou du toucher lorsque vous êtes en mode orthographique.
Création d’un spectrogramme
Pour créer un spectrogramme en 2D, définissez un élément « Surface3D » avec les données fournies dans la documentation Surface3DSeries et en spécifiant l’ ItemModelSurfaceDataProxy:
Surface3D {
id: surfaceGraph
anchors.fill: parent
// Don't show specular spotlight as we don't want it to distort the colors
lightStrength: 0.0
ambientLightStrength: 1.0
Surface3DSeries {
id: surfaceSeries
shading: Surface3DSeries.Shading.Smooth
drawMode: Surface3DSeries.DrawSurface
baseGradient: surfaceGradient
colorStyle: GraphsTheme.ColorStyle.RangeGradient
itemLabelFormat: "(@xLabel, @zLabel): @yLabel"
ItemModelSurfaceDataProxy {
itemModel: surfaceData.model
rowRole: "radius"
columnRole: "angle"
yPosRole: "value"
}
}Les propriétés clés permettant d’activer l’effet 2D sont « orthoProjection » et « cameraPreset ». Supprimez la perspective en activant la projection orthographique pour le graphique, et la dimension Y en visualisant le graphique directement depuis le dessus :
// Remove the perspective and view the graph from top down to achieve 2D effect
orthoProjection: true
cameraPreset: Graphs3D.CameraPreset.DirectlyAboveComme ce point de vue fait que la grille de l’axe horizontal est en grande partie masquée par la surface, inversez la grille horizontale pour qu’elle soit dessinée par-dessus le graphique :
flipHorizontalGrid: trueSpectrogramme polaire
Selon les données, il est parfois plus naturel d’utiliser un graphique polaire plutôt qu’un graphique cartésien. Cette fonctionnalité est prise en charge via la propriété polar.
Ajoutez un bouton pour basculer entre les modes polaire et cartésien :
Button {
id: polarToggle
anchors.margins: 5
anchors.left: parent.left
anchors.top: parent.top
width: spectrogramView.buttonWidth // Calculated elsewhere based on screen orientation
text: "Switch to\n" + (surfaceGraph.polar ? "cartesian" : "polar")
onClicked: surfaceGraph.polar = !surfaceGraph.polar;
}En mode polaire, l’axe X est converti en axe polaire angulaire, et l’axe Z en axe polaire radial. Les points de la surface sont recalculés en fonction des nouveaux axes.
Par défaut, les étiquettes de l'axe radial sont dessinées à l'extérieur du graphique. Pour les dessiner juste à côté de l'axe angulaire à 0 degré à l'intérieur du graphique, définissez un décalage minime pour celles-ci :
radialLabelOffset: 0.01Pour renforcer l’effet 2D, désactivez la rotation du graphique en mode orthographique en remplaçant le gestionnaire d’entrée par défaut par un gestionnaire personnalisé, qui active ou désactive automatiquement la propriété ` rotationEnabled ` en fonction du mode de projection :
rotationEnabled: !surfaceGraph.orthoProjectionOscilloscope
Dans l’onglet « Oscilloscope », combinez C++ et QML au sein d’une application et affichez des données qui évoluent dynamiquement.
Source de données en C++
Les proxys basés sur le modèle d’élément conviennent aux graphiques simples ou statiques, mais utilisez des proxys de base pour obtenir les meilleures performances lors de l’affichage de données évoluant en temps réel. Ceux-ci ne sont pas pris en charge dans QML, car les éléments de données qu’ils stockent n’héritent pas de la propriété ` QObject ` et ne peuvent donc pas être manipulés directement à partir du code QML. Pour contourner cette limitation, implémentez une classe ` DataSource ` simple en C++ afin de remplir le proxy de données de la série.
Créez une classe ` DataSource ` fournissant deux méthodes pouvant être appelées depuis QML :
class DataSource : public QObject
{
Q_OBJECT
...
Q_INVOKABLE void generateData(int cacheCount,
int rowCount,
int columnCount,
float xMin,
float xMax,
float yMin,
float yMax,
float zMin,
float zMax);
Q_INVOKABLE void update(QSurface3DSeries *series);La première méthode, ` generateData()`, crée un cache de données d'oscilloscope simulées à afficher. Les données sont mises en cache dans un format accepté par ` QSurfaceDataProxy ` :
// Populate caches
for (int i = 0; i < cacheCount; i++) {
QSurfaceDataArray &cache = m_data[i];
float cacheXAdjustment = cacheStep * i;
float cacheIndexAdjustment = cacheIndexStep * i;
for (int j = 0; j < rowCount; j++) {
QSurfaceDataRow &row = cache[j];
float rowMod = (float(j)) / float(rowCount);
float yRangeMod = yRange * rowMod;
float zRangeMod = zRange * rowMod;
float z = zRangeMod + zMin;
qreal waveAngleMul = M_PI * M_PI * rowMod;
float waveMul = yRangeMod * 0.2f;
for (int k = 0; k < columnCount; k++) {
float colMod = (float(k)) / float(columnCount);
float xRangeMod = xRange * colMod;
float x = xRangeMod + xMin + cacheXAdjustment;
float colWave = float(qSin((2.0 * M_PI * colMod) - (1.0 / 2.0 * M_PI)) + 1.0);
float y = (colWave * ((float(qSin(waveAngleMul * colMod) + 1.0)))) * waveMul
+ QRandomGenerator::global()->bounded(0.15f) * yRangeMod;
int index = k + cacheIndexAdjustment;
if (index >= columnCount) {
// Wrap over
index -= columnCount;
x -= xRange;
}
row[index] = QSurfaceDataItem(x, y, z);
}
}
}La deuxième méthode, ` update()`, copie un ensemble de données mises en cache dans un autre tableau, qui est attribué à la série en appelant ` QSurfaceDataProxy::resetArray()`. Pour minimiser la surcharge, réutilisez le même tableau si ses dimensions n’ont pas changé :
// Each iteration uses data from a different cached array
m_index++;
if (m_index > m_data.count() - 1)
m_index = 0;
QSurfaceDataArray array = m_data.at(m_index);
int newRowCount = array.size();
int newColumnCount = array.at(0).size();
// If the first time or the dimensions of the cache array have changed,
// reconstruct the reset array
if (m_resetArray.isEmpty() || series->dataProxy()->rowCount() != newRowCount
|| series->dataProxy()->columnCount() != newColumnCount) {
m_resetArray.clear();
m_resetArray.reserve(newRowCount);
for (int i = 0; i < newRowCount; i++)
m_resetArray.append(QSurfaceDataRow(newColumnCount));
}
// Copy items from our cache to the reset array
for (int i = 0; i < newRowCount; i++) {
const QSurfaceDataRow &sourceRow = array.at(i);
QSurfaceDataRow &row = m_resetArray[i];
for (int j = 0; j < newColumnCount; j++)
row[j].setPosition(sourceRow.at(j).position());
}
// Notify the proxy that data has changed
series->dataProxy()->resetArray(m_resetArray);Même si nous opérons sur le pointeur de tableau précédemment associé à la série, il faut tout de même appeler QSurfaceDataProxy::resetArray() après avoir modifié les données qu’il contient afin que le graphique affiche les données.
Pour pouvoir accéder aux méthodes d’ DataSource depuis QML, exposez la source de données en définissant DataSource comme QML_ELEMENT:
class DataSource : public QObject
{
Q_OBJECT
QML_ELEMENTDe plus, déclarez-la comme module QML dans le fichier CMakeLists.txt :
qt6_add_qml_module(surfacegallery
URI SurfaceGallery
VERSION 1.0
NO_RESOURCE_TARGET_PATH
SOURCES
datasource.cpp datasource.h
...
)Pour utiliser des pointeurs « QSurface3DSeries » comme paramètres des méthodes de la classe « DataSource » dans tous les environnements et toutes les versions, assurez-vous que le méta-type est bien enregistré :
qRegisterMetaType<QSurface3DSeries *>();Application QML
Pour utiliser l'DataSource, importez le module QML et créez une instance de DataSource à utiliser :
import SurfaceGalleryExample
...
DataSource {
id: dataSource
}Définissez un graphe « Surface3D » et attribuez-lui un « Surface3DSeries » :
Surface3D {
id: surfaceGraph
anchors.fill: parent
Surface3DSeries {
id: surfaceSeries
drawMode: Surface3DSeries.DrawSurfaceAndWireframe
itemLabelFormat: "@xLabel, @zLabel: @yLabel"Ne spécifiez pas de proxy pour l’ Surface3DSeries que vous associez au graphe. Cela permet à la série d’utiliser l’ QSurfaceDataProxy par défaut.
Masquez l’étiquette de l’élément à l’aide de l’ itemLabelVisible. Avec des données dynamiques et évoluant rapidement, une étiquette de sélection flottante serait gênante et difficile à lire.
itemLabelVisible: falseVous pouvez afficher les informations relatives à l’élément sélectionné dans un élément « Text » au lieu de l’étiquette flottante par défaut située au-dessus du pointeur de sélection :
onItemLabelChanged: {
if (surfaceSeries.selectedPoint == surfaceSeries.invalidSelectionPosition)
selectionText.text = "No selection";
else
selectionText.text = surfaceSeries.itemLabel;
}Initialisez le cache de l’ DataSource lorsque le graphique est terminé en appelant une fonction d’aide generateData(), qui appelle la méthode du même nom dans DataSource:
Component.onCompleted: oscilloscopeView.generateData();
...
function generateData() {
dataSource.generateData(oscilloscopeView.sampleCache, oscilloscopeView.sampleRows,
oscilloscopeView.sampleColumns,
surfaceGraph.axisX.min, surfaceGraph.axisX.max,
surfaceGraph.axisY.min, surfaceGraph.axisY.max,
surfaceGraph.axisZ.min, surfaceGraph.axisZ.max);
}Pour déclencher les mises à jour des données, définissez un ` Timer`, qui appelle la méthode ` update() ` dans ` DataSource ` aux intervalles souhaités :
Timer {
id: refreshTimer
interval: 1000 / frequencySlider.value
running: true
repeat: true
onTriggered: dataSource.update(surfaceSeries);
}Activation du rendu direct
Comme cette application est susceptible de traiter un grand nombre de données évoluant rapidement, elle utilise le mode de rendu direct pour optimiser ses performances. Pour activer l’anticrénelage dans ce mode, modifiez le format de surface de la fenêtre de l’application. Le format par défaut utilisé par QQuickView ne prend pas en charge l’anticrénelage. Utilisez la fonction utilitaire fournie pour modifier le format de surface dans main.cpp:
#include <QtGraphs/qutils.h>
...
// Enable antialiasing in direct rendering mode
viewer.setFormat(QQuick3D::idealSurfaceFormat(8));Exemple de contenu
© 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.