Surface Example¶
Using
Q3DSurface
in a widget application.The surface example shows how to make a simple 3D surface graph using
Q3DSurface
and combining the use of widgets for adjusting several adjustable qualities. This example demonstrates the following features:
How to set up a basic
QSurfaceDataProxy
and set data for it.How to use
QHeightMapSurfaceDataProxy
for showing 3D height maps.Three different selection modes for studying the graph.
Axis range usage for displaying selected portions of the graph.
Changing theme.
How to set a custom surface gradient.
For instructions about how to interact with the graph, see this page .
Running the Example¶
To run the example from Qt Creator , open the Welcome mode and select the example from Examples . For more information, visit Building and Running an Example.
Creating the Application¶
First, in
main.cpp
, we create aQApplication
, instantiateQ3DSurface
, and a window container for it:QApplication app(argc, argv); Q3DSurface *graph = new Q3DSurface(); QWidget *container = QWidget::createWindowContainer(graph);The call to
createWindowContainer
is required, as all data visualization graph classes (Q3DBars
,Q3DScatter
, andQ3DSurface
) inheritQWindow
. Any class inheritingQWindow
cannot be used as a widget any other way.Then we’ll create horizontal and vertical layouts. We’ll add the graph with the container and the vertical layout into the horizontal one:
QWidget *widget = new QWidget; QHBoxLayout *hLayout = new QHBoxLayout(widget); QVBoxLayout *vLayout = new QVBoxLayout(); hLayout->addWidget(container, 1); hLayout->addLayout(vLayout); vLayout->setAlignment(Qt::AlignTop);The rest of the code in
main.cpp
is creating control widgets for features inQ3DSurface
. We have separated code for changing these features intosurfacegraph.cpp
and only connect signals from widgets into methods insurfacegraph.cpp
. Next chapter explains more about usingQ3DSurface
.
Setting up Proxies and Data¶
First we instantiate a new
QSurfaceDataProxy
and attach it to a newQSurface3DSeries
:m_sqrtSinProxy = new QSurfaceDataProxy(); m_sqrtSinSeries = new QSurface3DSeries(m_sqrtSinProxy);Then we fill the proxy with a simple square root and sine wave data. This is done by creating a new
QSurfaceDataArray
instance and addingQSurfaceDataRow
elements to it. The createdQSurfaceDataArray
is set to be the data array for theQSurfaceDataProxy
.void SurfaceGraph::fillSqrtSinProxy() { float stepX = (sampleMax - sampleMin) / float(sampleCountX - 1); float stepZ = (sampleMax - sampleMin) / float(sampleCountZ - 1); QSurfaceDataArray *dataArray = new QSurfaceDataArray; dataArray->reserve(sampleCountZ); for (int i = 0 ; i < sampleCountZ ; i++) { QSurfaceDataRow *newRow = new QSurfaceDataRow(sampleCountX); // Keep values within range bounds, since just adding step can cause minor drift due // to the rounding errors. float z = qMin(sampleMax, (i * stepZ + sampleMin)); int index = 0; for (int j = 0; j < sampleCountX; j++) { float x = qMin(sampleMax, (j * stepX + sampleMin)); float R = qSqrt(z * z + x * x) + 0.01f; float y = (qSin(R) / R + 0.24f) * 1.61f; (*newRow)[index++].setPosition(QVector3D(x, y, z)); } *dataArray << newRow; } m_sqrtSinProxy->resetArray(dataArray); }The height map is created by instantiating a
QHeightMapSurfaceDataProxy
with aQImage
containing the height data. The methodsetValueRanges()
is used to define the value range of the map. In our example the map is from imaginary position of 34.0° N - 40.0° N and 18.0° E - 24.0° E. These values are used to show and position the map to the axis.QImage heightMapImage(":/maps/mountain"); m_heightMapProxy = new QHeightMapSurfaceDataProxy(heightMapImage); m_heightMapSeries = new QSurface3DSeries(m_heightMapProxy); m_heightMapSeries->setItemLabelFormat(QStringLiteral("(@xLabel, @zLabel): @yLabel")); m_heightMapProxy->setValueRanges(34.0f, 40.0f, 18.0f, 24.0f);For demonstrating different proxies this example has two radio buttons which the user can use to switch between the series. When the user selects the
Sqrt & Sin
radio button, the selected series is activated with the following code. First we set the decorative issues like enable the grid for the surface and select the flat shading mode. Next lines define the axis label format and value ranges. Automatic label rotation is set to improve label readability at low camera angles. Finally we make sure the correct series is added to the graph:m_sqrtSinSeries->setDrawMode(QSurface3DSeries::DrawSurfaceAndWireframe); m_sqrtSinSeries->setFlatShadingEnabled(true); m_graph->axisX()->setLabelFormat("%.2f"); m_graph->axisZ()->setLabelFormat("%.2f"); m_graph->axisX()->setRange(sampleMin, sampleMax); m_graph->axisY()->setRange(0.0f, 2.0f); m_graph->axisZ()->setRange(sampleMin, sampleMax); m_graph->axisX()->setLabelAutoRotation(30); m_graph->axisY()->setLabelAutoRotation(90); m_graph->axisZ()->setLabelAutoRotation(30); m_graph->removeSeries(m_heightMapSeries); m_graph->addSeries(m_sqrtSinSeries);When the
Height Map
radio button is activated, the following code sets the correct series active. The axis label format is set to show N and E letters and ranges are set to the imaginary coordinates. Auto adjusting Y-axis range is fine for our height map surface, so we ensure it is set.m_heightMapSeries->setDrawMode(QSurface3DSeries::DrawSurface); m_heightMapSeries->setFlatShadingEnabled(false); m_graph->axisX()->setLabelFormat("%.1f N"); m_graph->axisZ()->setLabelFormat("%.1f E"); m_graph->axisX()->setRange(34.0f, 40.0f); m_graph->axisY()->setAutoAdjustRange(true); m_graph->axisZ()->setRange(18.0f, 24.0f); m_graph->axisX()->setTitle(QStringLiteral("Latitude")); m_graph->axisY()->setTitle(QStringLiteral("Height")); m_graph->axisZ()->setTitle(QStringLiteral("Longitude")); m_graph->removeSeries(m_sqrtSinSeries); m_graph->addSeries(m_heightMapSeries);
Selection Modes¶
Q3Dsurface supports three different selection modes. These are demonstrated in the example with radio buttons, which the user can use to activate a suitable selection mode. The following inline methods are connected to radio buttons to activate the selected mode.
void toggleModeNone() { m_graph->setSelectionMode(QAbstract3DGraph::SelectionNone); } void toggleModeItem() { m_graph->setSelectionMode(QAbstract3DGraph::SelectionItem); } void toggleModeSliceRow() { m_graph->setSelectionMode(QAbstract3DGraph::SelectionItemAndRow | QAbstract3DGraph::SelectionSlice); } void toggleModeSliceColumn() { m_graph->setSelectionMode(QAbstract3DGraph::SelectionItemAndColumn | QAbstract3DGraph::SelectionSlice); }
Axis Ranges for Studying the Graph¶
The example has four slider controls for adjusting the min and max values for X and Z axis. When selecting the proxy these sliders are adjusted so that one step on the slider moves the range by one segment step:
// Reset range sliders for Sqrt&Sin m_rangeMinX = sampleMin; m_rangeMinZ = sampleMin; m_stepX = (sampleMax - sampleMin) / float(sampleCountX - 1); m_stepZ = (sampleMax - sampleMin) / float(sampleCountZ - 1); m_axisMinSliderX->setMaximum(sampleCountX - 2); m_axisMinSliderX->setValue(0); m_axisMaxSliderX->setMaximum(sampleCountX - 1); m_axisMaxSliderX->setValue(sampleCountX - 1); m_axisMinSliderZ->setMaximum(sampleCountZ - 2); m_axisMinSliderZ->setValue(0); m_axisMaxSliderZ->setMaximum(sampleCountZ - 1); m_axisMaxSliderZ->setValue(sampleCountZ - 1);The ranges are set for the axes like this:
void SurfaceGraph::setAxisXRange(float min, float max) { m_graph->axisX()->setRange(min, max); } void SurfaceGraph::setAxisZRange(float min, float max) { m_graph->axisZ()->setRange(min, max); }
Themes¶
Q3DSurface
supports all the themes Qt Data Visualization has. The example has a pull down menu for selecting the theme. The following method is connected to the menu to activate the selected theme. The theme type is changed to another predefined theme, which overwrites all theme properties to predefined values:void SurfaceGraph::changeTheme(int theme) { m_graph->activeTheme()->setType(Q3DTheme::Theme(theme)); }
Custom Surface Gradients¶
The example demonstrates the custom surface gradients with two push buttons. The gradient can be defined with
QLinearGradient
where the desired colors are set to positions. The following code shows how to create an example gradient and set it to the series. Note that you also need to change the color style toColorStyleRangeGradient
to actually use the gradient.QLinearGradient gr; gr.setColorAt(0.0, Qt::black); gr.setColorAt(0.33, Qt::blue); gr.setColorAt(0.67, Qt::red); gr.setColorAt(1.0, Qt::yellow); m_graph->seriesList().at(0)->setBaseGradient(gr); m_graph->seriesList().at(0)->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
© 2022 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.