Galería de gráficos de superficie
Galería con tres formas diferentes de utilizar un gráfico Surface3D.
LaGalería de Gráficos deSuperficie muestra tres funciones personalizadas diferentes con los gráficos de Surface3D. Las funciones tienen sus propias pestañas en la aplicación.
Las siguientes secciones se concentran sólo en esas características y omiten la explicación de la funcionalidad básica - para una documentación más detallada del ejemplo QML, ver Simple Scatter Graph.

Ejecutar el ejemplo
Para ejecutar el ejemplo desde Qt Creatorabra el modo Welcome y seleccione el ejemplo de Examples. Para más información, consulte Qt Creator: Tutorial: Construir y ejecutar.
Mapa de alturas
En la pestaña Height Map, genere un gráfico de superficie a partir de los datos de altura. Los datos utilizados son un mapa de alturas del Monte Ruapehu y del Monte Ngauruhoe en Nueva Zelanda.
Añadir datos al gráfico
Los datos se configuran utilizando HeightMapSurfaceDataProxy, que lee la información de altura de una imagen de mapa de alturas. El proxy en sí está contenido en Surface3DSeries. Dentro de HeightMapSurfaceDataProxy, la propiedad heightMapFile especifica el archivo de imagen que contiene los datos de altura. Las propiedades de valor en el proxy definen los valores mínimo y máximo para la anchura, profundidad y altura de la superficie. Los valores z y x están en latitud y longitud, aproximadamente en la posición del mundo real, y el y está en metros.
Nota: la relación de aspecto del gráfico no se ajusta a la escala del mundo real, sino que se exagera la altura.
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() }
Visualización de los datos
En main.qml, configure el elemento Surface3D para mostrar los datos.
En primer lugar, define el gradiente personalizado que se utilizará para la superficie. Establece los colores de la posición 0.0 a 1.0 con ColorGradient, con dos paradas extra para hacer el gráfico más vivo:
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" } }
Establezca este elemento en la propiedad baseGradients en el theme utilizado en Surface3D:
theme: Theme3D { type: Theme3D.ThemeStoneMoss font.family: "STCaiyun" font.pointSize: 35 colorStyle: Theme3D.ColorStyleRangeGradient baseGradients: [surfaceGradient] // Use the custom gradient }
Utilice los botones para controlar otras funciones de Surface3D.
El primer botón activa y desactiva la cuadrícula de superficie. El modo de dibujo no se puede borrar completamente, por lo que a menos que la propia superficie sea visible, la rejilla de superficie no se puede ocultar:
onClicked: { if (heightSeries.drawMode & Surface3DSeries.DrawWireframe) heightSeries.drawMode &= ~Surface3DSeries.DrawWireframe; else heightSeries.drawMode |= Surface3DSeries.DrawWireframe; }
El segundo establece el color de la rejilla de superficie:
onClicked: { if (Qt.colorEqual(heightSeries.wireframeColor, "#000000")) { heightSeries.wireframeColor = "red"; text = "Black surface\ngrid color"; } else { heightSeries.wireframeColor = "black"; text = "Red surface\ngrid color"; } }
El tercero activa o desactiva la superficie en el modo de dibujo. El modo de dibujo no se puede desactivar completamente, así que a menos que la rejilla de la superficie sea visible, la propia superficie no se puede ocultar:
onClicked: { if (heightSeries.drawMode & Surface3DSeries.DrawSurface) heightSeries.drawMode &= ~Surface3DSeries.DrawSurface; else heightSeries.drawMode |= Surface3DSeries.DrawSurface; }
El cuarto establece el modo de sombreado. Si está ejecutando el ejemplo en un sistema OpenGL ES, el sombreado plano no está disponible:
onClicked: { if (heightSeries.flatShadingEnabled) { heightSeries.flatShadingEnabled = false; text = "Show\nFlat" } else { heightSeries.flatShadingEnabled = true; text = "Show\nSmooth" } }
Los botones restantes controlan las características del fondo del gráfico.
Espectrograma
En la pestaña Spectrogram, muestre espectrogramas polares y cartesianos y utilice la proyección ortográfica para mostrarlos en 2D.
Un espectrograma es un gráfico de superficie con un gradiente de rango utilizado para resaltar los diferentes valores. Normalmente, los espectrogramas se muestran con superficies bidimensionales, lo que se simula con una vista ortográfica descendente del gráfico. Para reforzar el efecto 2D, desactive la rotación del gráfico mediante el ratón o el tacto cuando se encuentre en el modo ortográfico.
Creación de un espectrograma
Para crear un espectrograma 2D, defina un elemento Surface3D con los datos indicados en Surface3DSeries con 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" } }
Las propiedades clave para activar el efecto 2D son orthoProjection y scene.activeCamera.cameraPreset. Elimine la perspectiva activando la proyección ortográfica para el gráfico, y la dimensión Y visualizando el gráfico directamente desde arriba:
// Remove the perspective and view the graph from top down to achieve 2D effect orthoProjection: true scene.activeCamera.cameraPreset: Camera3D.CameraPresetDirectlyAbove
Dado que este punto de vista hace que la rejilla del eje horizontal quede oculta en su mayor parte por la superficie, voltee la rejilla horizontal para que se dibuje encima del gráfico:
flipHorizontalGrid: true
Espectrograma polar
Dependiendo de los datos, a veces es más natural utilizar un gráfico polar en lugar de uno cartesiano. Esto es posible gracias a la propiedad polar.
Añade un botón para cambiar entre los modos polar y cartesiano:
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 el modo polar, el eje X se convierte en el eje polar angular, y el eje Z se convierte en un eje polar radial. Los puntos de la superficie se recalculan según los nuevos ejes.
Las etiquetas de los ejes radiales se dibujan fuera del gráfico por defecto. Para dibujarlas justo al lado del eje angular de 0 grados dentro del gráfico, defina sólo un pequeño desplazamiento para ellas:
radialLabelOffset: 0.01
Para reforzar el efecto 2D, desactive la rotación de la gráfica en modo ortográfico sustituyendo el manejador de entrada por defecto por uno personalizado, que active automáticamente la propiedad rotationEnabled en función del modo de proyección:
inputHandler: TouchInputHandler3D { rotationEnabled: !surfaceGraph.orthoProjection }
Osciloscopio
En la pestaña Oscilloscope, combina C++ y QML en una aplicación, y muestra datos que cambian dinámicamente.
Fuente de datos en C
Los proxies basados en modelos de elementos son buenos para gráficos simples o estáticos, pero utiliza proxies básicos para conseguir el mejor rendimiento cuando muestres datos que cambian en tiempo real. No son compatibles con QML, ya que los elementos de datos que almacenan no heredan QObject y, por tanto, no pueden manipularse directamente desde código QML. Para superar esta limitación, implemente una clase DataSource sencilla en C++ para rellenar el proxy de datos de la serie.
Cree una clase DataSource para proporcionar dos métodos que puedan invocarse desde 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);
El primer método, generateData(), crea una caché de datos de osciloscopio simulados para mostrar. Los datos se almacenan en caché en un formato que QSurfaceDataProxy acepta:
// 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); } } }
El segundo método, update(), copia un conjunto de los datos almacenados en caché en otra matriz, que se establece en el proxy de datos de la serie llamando a QSurfaceDataProxy::resetArray(). Para minimizar la sobrecarga, reutiliza el mismo array si las dimensiones del array no han cambiado:
// 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);
Aunque estemos operando sobre el puntero del array previamente establecido al proxy, QSurfaceDataProxy::resetArray() todavía necesita ser llamado después de cambiar los datos en él para que el gráfico renderice los datos.
Para poder acceder a los métodos de DataSource desde QML, exponga la fuente de datos convirtiendo el DataSource en QML_ELEMENT:
class DataSource : public QObject { Q_OBJECT QML_ELEMENT
Además, declararlo como un módulo QML en el CMakeLists.txt:
qt6_add_qml_module(qmlsurfacegallery
URI SurfaceGallery
VERSION 1.0
NO_RESOURCE_TARGET_PATH
SOURCES
datasource.cpp datasource.h
...
)Para utilizar punteros QSurface3DSeries como parámetros de los métodos de la clase DataSource en todos los entornos y versiones, asegúrese de que el metatipo está registrado:
qRegisterMetaType<QSurface3DSeries *>();
Aplicación QML
Para utilizar DataSource, importa el módulo QML y crea una instancia de DataSource para ser utilizada:
import SurfaceGallery ... DataSource { id: dataSource }
Define un gráfico Surface3D y dale un Surface3DSeries:
Surface3D { id: surfaceGraph anchors.fill: parent Surface3DSeries { id: surfaceSeries drawMode: Surface3DSeries.DrawSurfaceAndWireframe itemLabelFormat: "@xLabel, @zLabel: @yLabel"
No especifique un proxy para el Surface3DSeries que adjunta al gráfico. Esto hace que la serie utilice el valor por defecto QSurfaceDataProxy.
Oculte la etiqueta de selección con itemLabelVisible. Con datos dinámicos que cambian rápidamente, una etiqueta de selección flotante distraería y sería difícil de leer.
itemLabelVisible: false
Puede mostrar la información del elemento seleccionado en un elemento Text en lugar de la etiqueta flotante por defecto sobre el puntero de selección:
onItemLabelChanged: { if (surfaceSeries.selectedPoint == surfaceSeries.invalidSelectionPosition) selectionText.text = "No selection"; else selectionText.text = surfaceSeries.itemLabel; }
Inicialice la caché DataSource cuando el gráfico esté completo llamando a una función de ayuda generateData(), que llama al método con el mismo nombre en 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); }
Para activar las actualizaciones de datos, defina una función Timer, que llame al método update() en DataSource a intervalos solicitados:
Timer { id: refreshTimer interval: 1000 / frequencySlider.value running: true repeat: true onTriggered: dataSource.update(surfaceSeries); }
Activación de la renderización directa
Dado que esta aplicación maneja potencialmente una gran cantidad de datos que cambian rápidamente, utiliza el modo de renderizado directo para mejorar el rendimiento. Para habilitar el antialiasing en este modo, cambie el formato de superficie de la ventana de la aplicación. El formato por defecto utilizado por QQuickView no soporta antialiasing. Utilice la función de utilidad proporcionada para cambiar el formato de superficie en main.cpp:
#include <QtDataVisualization/qutils.h> ... // Enable antialiasing in direct rendering mode viewer.setFormat(qDefaultSurfaceFormat(true));
Contenido del ejemplo
© 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.