Galerie de graphiques de surface
Galerie présentant trois façons différentes d'utiliser un graphique Surface3D.
La galerie de graphiques en 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 dans « 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 `HeightMapSurfaceDataProxy`, qui lit les informations d’altitude à partir d’une image de carte d’altitude. Le proxy lui-même est contenu dans un `Surface3DSeries`. Au sein du HeightMapSurfaceDataProxy, la propriété heightMapFile spécifie le fichier image contenant les données d’altitude. Les propriétés de valeur du proxy définissent les valeurs minimales et maximales de la largeur, de la profondeur et de la hauteur de la surface. Les valeurs z et x correspondent à la latitude et à la longitude, approximativement à la position réelle, et la y est exprimée en mètres.
Remarque : le rapport d’aspect du graphique n’est pas défini à l’échelle réelle ; c’est la hauteur qui est exagérée.
Surface3DSeries {
id: heightSeries
flatShadingEnabled: false
drawMode: Surface3DSeries.DrawSurface
HeightMapSurfaceDataProxy {
heightMapFile: "://qml/qmlsurfacegallery/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 le fichier « 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 ColorGradient, en ajoutant deux arrêts supplémentaires pour rendre le graphique plus vivant :
ColorGradient {
id: surfaceGradient
ColorGradientStop { position: 0.0; color: "darkgreen"}
ColorGradientStop { position: 0.15; color: "darkslategray" }
ColorGradientStop { position: 0.7; color: "peru" }
ColorGradientStop { position: 1.0; color: "white" }
}Définissez cet élément dans la propriété « baseGradients » de l’ theme e utilisé dans Surface3D :
theme: Theme3D {
type: Theme3D.ThemeStoneMoss
font.family: "STCaiyun"
font.pointSize: 35
colorStyle: Theme3D.ColorStyleRangeGradient
baseGradients: [surfaceGradient] // Use the custom gradient
}Utilisez les boutons pour contrôler les autres fonctionnalités de Surface3D.
Le premier bouton active ou désactive la grille de la surface. Le mode de dessin ne peut pas être entièrement effacé ; par conséquent, à moins que la surface elle-même ne soit visible, la grille de la 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 pouvant pas être entièrement effacé, la surface elle-même ne peut pas être masquée tant que la grille de surface est visible :
onClicked: {
if (heightSeries.drawMode & Surface3DSeries.DrawSurface)
heightSeries.drawMode &= ~Surface3DSeries.DrawSurface;
else
heightSeries.drawMode |= Surface3DSeries.DrawSurface;
}Le quatrième bouton permet de définir 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.flatShadingEnabled) {
heightSeries.flatShadingEnabled = false;
text = "Show\nFlat"
} else {
heightSeries.flatShadingEnabled = true;
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 une 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, ce qui est simulé par une vue orthographique de haut en bas 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 Surface3DSeries à l’aide d’un ItemModelSurfaceDataProxy :
Surface3D {
id: surfaceGraph
anchors.fill: parent
Surface3DSeries {
id: surfaceSeries
flatShadingEnabled: false
drawMode: Surface3DSeries.DrawSurface
baseGradient: surfaceGradient
colorStyle: Theme3D.ColorStyleRangeGradient
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 scene.activeCamera.cameraPreset. Supprimez la perspective en activant la projection orthographique pour le graphique, et la dimension Y en visualisant le graphique directement depuis le haut :
// Remove the perspective and view the graph from top down to achieve 2D effect
orthoProjection: true
scene.activeCamera.cameraPreset: Camera3D.CameraPresetDirectlyAboveComme ce point de vue masque en grande partie la grille de l’axe horizontal derrière 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 permettant de 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-leur un décalage minime :
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 :
inputHandler: TouchInputHandler3D {
rotationEnabled: !surfaceGraph.orthoProjection
}Oscilloscope
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éments 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 en 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
auto *generator = QRandomGenerator::global();
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 rowColWaveAngleMul = M_PI * M_PI * rowMod;
float rowColWaveMul = 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(rowColWaveAngleMul * colMod) + 1.0))))
* rowColWaveMul
+ generator->bounded(0.15f) * yRangeMod;
int index = k + cacheIndexAdjustment;
if (index >= columnCount) {
// Wrap over
index -= columnCount;
x -= xRange;
}
row[index] = QVector3D(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é au proxy de données de 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
if (++m_index >= m_data.size())
m_index = 0;
const 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 || series->dataProxy()->rowCount() != newRowCount
|| series->dataProxy()->columnCount() != newColumnCount) {
m_resetArray = new QSurfaceDataArray();
m_resetArray->reserve(newRowCount);
for (int i = 0; i < newRowCount; ++i)
m_resetArray->append(new 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];
std::copy(sourceRow.cbegin(), sourceRow.cend(), row.begin());
}
// 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 défini comme proxy, la méthode ` QSurfaceDataProxy::resetArray()` doit tout de même être appelée après avoir modifié les données qu’il contient afin de déclencher le rendu des données par le graphique.
Pour pouvoir accéder aux méthodes d’ DataSource depuis QML, exposez la source de données en définissant DataSource comme un QML_ELEMENT:
class DataSource : public QObject
{
Q_OBJECT
QML_ELEMENTDe plus, déclarez-la en tant que module QML dans le fichier CMakeLists.txt :
qt6_add_qml_module(qmlsurfacegallery
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 enregistré :
qRegisterMetaType<QSurface3DSeries *>();Application QML
Pour utiliser ` DataSource`, importez le module QML et créez une instance de ` DataSource ` à utiliser :
import SurfaceGallery
...
DataSource {
id: dataSource
}Définissez un graphe Surface3D et associez-lui une Surface3DSeries :
Surface3D {
id: surfaceGraph
anchors.fill: parent
Surface3DSeries {
id: surfaceSeries
drawMode: Surface3DSeries.DrawSurfaceAndWireframe
itemLabelFormat: "@xLabel, @zLabel: @yLabel"Ne spécifiez pas de proxy pour la Surface3DSeries que vous associez au graphique. La série utilise ainsi le QSurfaceDataProxy par défaut.
Masquez l'étiquette de l'élément à l'aide de ` 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 une fois le graphique terminé en appelant la 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 <QtDataVisualization/qutils.h>
...
// Enable antialiasing in direct rendering mode
viewer.setFormat(qDefaultSurfaceFormat(true));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.