Galerie de graphiques de surface
Galerie présentant trois façons différentes d'utiliser un graphique Surface3D.
Lagalerie des graphiques de surface présente trois fonctions personnalisées différentes avec les graphiques Surface3D. Ces fonctions ont leurs propres onglets dans l'application.
Les sections suivantes se concentrent uniquement sur ces fonctionnalités et omettent d'expliquer la fonctionnalité de base - pour une documentation d'exemple QML plus détaillée, voir Simple Scatter Graph (graphique de dispersion simple).

Exécution de l'exemple
Pour exécuter l'exemple à partir de Qt Creatorouvrez le mode Welcome et sélectionnez l'exemple à partir de Examples. Pour plus d'informations, voir Qt Creator: Tutorial : Construire et exécuter.
Carte des hauteurs
Dans l'onglet Height Map, générez un graphique de surface à partir des données d'altitude. Les données utilisées sont une carte d'altitude des monts Ruapehu et 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 fichier Surface3DSeries. À l'intérieur de HeightMapSurfaceDataProxy, la propriété heightMapFile spécifie le fichier image contenant les données d'altitude. Les propriétés de valeur dans le proxy définissent les valeurs minimales et maximales pour la largeur, la profondeur et la hauteur de la surface. Les valeurs z et x sont exprimées en latitude et en longitude, approximativement à la position réelle, et la valeur 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 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 main.qml, configurez l'élément Surface3D pour afficher les données.
Commencez par définir le gradient personnalisé à utiliser pour la surface. Définissez les couleurs de la position 0.0 à 1.0 avec ColorGradient, avec 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" } }
Placez cet élément dans la propriété baseGradients dans la propriété theme utilisée 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 d'autres fonctions de Surface3D.
Le premier bouton permet d'activer et de désactiver la grille de surface. Le mode de dessin ne peut pas être complètement supprimé, donc à moins que la surface elle-même ne soit visible, la grille de surface ne peut pas être cachée :
onClicked: { if (heightSeries.drawMode & Surface3DSeries.DrawWireframe) heightSeries.drawMode &= ~Surface3DSeries.DrawWireframe; else heightSeries.drawMode |= Surface3DSeries.DrawWireframe; }
Le deuxième bouton définit 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 permet d'activer ou de désactiver la surface dans le mode de dessin de la surface. Le mode de dessin ne peut pas être complètement désactivé, de sorte qu'à moins que la grille de la surface ne soit visible, la surface elle-même ne peut pas être caché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.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 la projection orthographique pour les afficher en 2D.
Un spectrogramme est un graphique de surface avec un gradient de gamme utilisé pour mettre en évidence les différentes valeurs. Généralement, les spectrogrammes sont affichés avec des surfaces bidimensionnelles, ce qui est simulé par une vue orthographique du haut vers le 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 2D, définissez un élément Surface3D avec les données fournies dans Surface3DSeries avec 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 pour 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 regardant le graphique directement du dessus :
// Remove the perspective and view the graph from top down to achieve 2D effect orthoProjection: true scene.activeCamera.cameraPreset: Camera3D.CameraPresetDirectlyAbove
Comme ce point de vue fait en sorte 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 sur le dessus du graphique :
flipHorizontalGrid: true
Spectrogramme polaire
En fonction des données, il est parfois plus naturel d'utiliser un graphique polaire qu'un graphique cartésien. Cela est possible grâce à la propriété polar.
Ajouter 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 est converti en axe polaire radial. Les points de la surface sont recalculés en fonction des nouveaux axes.
Par défaut, les étiquettes des axes radiaux sont dessinées à l'extérieur du graphique. Pour les dessiner juste à côté de l'axe angulaire de 0 degré à l'intérieur du graphique, définissez seulement un petit décalage pour eux :
radialLabelOffset: 0.01
Pour 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 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 dans une application et affichez des données qui changent 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 il faut utiliser des proxys de base pour obtenir les meilleures performances lors de l'affichage de données changeant en temps réel. Elles ne sont pas prises en charge par QML, car les éléments de données qu'elles stockent n'héritent pas de QObject et ne peuvent donc pas être manipulées directement à partir du code QML. Pour surmonter cette limitation, mettez en œuvre une simple classe DataSource en C++ pour remplir le proxy de données de la série.
Créez une classe DataSource pour fournir deux méthodes qui peuvent être invoquées à partir de 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é à 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 seconde méthode, update(), copie un ensemble de données mises en cache dans un autre tableau, qui est défini sur le proxy de données de la série en appelant QSurfaceDataProxy::resetArray(). Pour minimiser les frais généraux, il convient de réutiliser 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 sur le proxy, QSurfaceDataProxy::resetArray() doit toujours être appelé après avoir modifié les données qu'il contient afin d'inviter le graphique à rendre les données.
Pour pouvoir accéder aux méthodes DataSource à partir de QML, exposez la source de données en faisant de DataSource un QML_ELEMENT:
class DataSource : public QObject { Q_OBJECT QML_ELEMENT
De plus, déclarez-la comme 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 les 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 *>();
QML Application
Pour utiliser DataSource, importez le module QML et créez une instance de DataSource à utiliser :
import SurfaceGallery ... DataSource { id: dataSource }
Définir un graphe Surface3D et lui donner 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 le Surface3DSeries que vous attachez au graphique. La série utilise ainsi la valeur par défaut QSurfaceDataProxy.
Masquez l'étiquette de l'élément avec itemLabelVisible. Avec des données dynamiques et changeant rapidement, une étiquette de sélection flottante serait gênante et difficile à lire.
itemLabelVisible: false
Vous pouvez afficher les informations sur l'élément sélectionné dans un élément Text au lieu de l'étiquette flottante par défaut au-dessus du pointeur de sélection :
onItemLabelChanged: { if (surfaceSeries.selectedPoint == surfaceSeries.invalidSelectionPosition) selectionText.text = "No selection"; else selectionText.text = surfaceSeries.itemLabel; }
Initialiser le cache 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 une fonction Timer, qui appelle la méthode update() dans DataSource aux intervalles demandés :
Timer { id: refreshTimer interval: 1000 / frequencySlider.value running: true repeat: true onTriggered: dataSource.update(surfaceSeries); }
Activation du rendu direct
Étant donné que cette application traite potentiellement un grand nombre de données changeant rapidement, elle utilise le mode de rendu direct pour des raisons de performance. 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));
Contenu de l'exemple
© 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.