Scatter Example▲
The scatter example shows how to make a simple 3D scatter graph using Q3DScatter and combining the use of widgets for adjusting several adjustable qualities. The example shows how to:
-
Create an application with Q3DScatter and some widgets
-
Use QScatterDataProxy to set data to the graph
-
Adjust some graph properties using widget controls
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 a QApplication, instantiate Q3DScatter, and a window container for it:
QApplication app(argc, argv);
Q3DScatter *
graph =
new
Q3DScatter();
QWidget *
container =
QWidget::
createWindowContainer(graph);
The call to QWidget::createWindowContainer is required, as all data visualization graph classes (Q3DBars, Q3DScatter, and Q3DSurface) inherit QWindow. Any class inheriting QWindow cannot be used as a widget any other way.
Then we'll create horizontal and vertical layouts. We'll add the graph and the vertical layout into the horizontal one:
QWidget *
widget =
new
QWidget;
QHBoxLayout *
hLayout =
new
QHBoxLayout(widget);
QVBoxLayout *
vLayout =
new
QVBoxLayout();
hLayout-&
gt;addWidget(container, 1
);
hLayout-&
gt;addLayout(vLayout);
We're not using the vertical layout for anything yet, but we'll get back to it in Using widgets to control the graph
Next, let's create another class to handle the data addition and other interaction with the graph. Let's call it ScatterDataModifier (See Setting up the graph and Adding data to the graph for details):
ScatterDataModifier *
modifier =
new
ScatterDataModifier(graph);
The application main is done. We can show the graph and start the event loop:
widget-&
gt;show();
return
app.exec();
Setting up the Graph▲
Let's set up some visual qualities for the graph in the constructor of the ScatterDataModifier class we instantiated in the application main:
m_graph-&
gt;activeTheme()-&
gt;setType(Q3DTheme::
ThemeEbony);
QFont font =
m_graph-&
gt;activeTheme()-&
gt;font();
font.setPointSize(m_fontSize);
m_graph-&
gt;activeTheme()-&
gt;setFont(font);
m_graph-&
gt;setShadowQuality(QAbstract3DGraph::
ShadowQualitySoftLow);
m_graph-&
gt;scene()-&
gt;activeCamera()-&
gt;setCameraPreset(Q3DCamera::
CameraPresetFront);
None of these are required, but are used to override graph defaults. You can try how it looks with the preset defaults by commenting the block above out.
Finally we create a QScatterDataProxy and the associated QScatter3DSeries. We set custom label format and mesh smoothing for the series and add it to the graph:
QScatterDataProxy *
proxy =
new
QScatterDataProxy;
QScatter3DSeries *
series =
new
QScatter3DSeries(proxy);
series-&
gt;setItemLabelFormat(QStringLiteral("@xTitle: @xLabel @yTitle: @yLabel @zTitle: @zLabel"
));
series-&
gt;setMeshSmooth(m_smooth);
m_graph-&
gt;addSeries(series);
That concludes setting up the graph.
Adding Data to the Graph▲
The last thing we do in the ScatterDataModifier constructor is to add data to the graph:
addData();
The actual data addition is done in addData() method. First we configure the axes:
m_graph-&
gt;axisX()-&
gt;setTitle("X"
);
m_graph-&
gt;axisY()-&
gt;setTitle("Y"
);
m_graph-&
gt;axisZ()-&
gt;setTitle("Z"
);
This could have been done in the constructor of ScatterDataModifier, but we added it here to keep the constructor simpler and the axes configuration near the data.
Next we create a data array:
QScatterDataArray *
dataArray =
new
QScatterDataArray;
dataArray-&
gt;resize(m_itemCount);
QScatterDataItem *
ptrToDataArray =
&
amp;dataArray-&
gt;first();
and populate it:
float
limit =
qSqrt(m_itemCount) /
2.0
f;
for
(float
i =
-
limit; i &
lt; limit; i++
) {
for
(float
j =
-
limit; j &
lt; limit; j++
) {
ptrToDataArray-&
gt;setPosition(QVector3D(i +
0.5
f,
qCos(qDegreesToRadians((i *
j) /
m_curveDivider)),
j +
0.5
f));
ptrToDataArray++
;
}
}
Finally we tell the proxy to start using the data we gave it:
m_graph-&
gt;seriesList().at(0
)-&
gt;dataProxy()-&
gt;resetArray(dataArray);
Now our graph has the data and is ready to be used. There isn't much interaction yet, though, so let's continue by adding some widgets to play with.
Using Widgets to Control the Graph▲
First, back in the application main, we'll create some widgets:
QComboBox *
themeList =
new
QComboBox(widget);
themeList-&
gt;addItem(QStringLiteral("Qt"
));
themeList-&
gt;addItem(QStringLiteral("Primary Colors"
));
themeList-&
gt;addItem(QStringLiteral("Digia"
));
themeList-&
gt;addItem(QStringLiteral("Stone Moss"
));
themeList-&
gt;addItem(QStringLiteral("Army Blue"
));
themeList-&
gt;addItem(QStringLiteral("Retro"
));
themeList-&
gt;addItem(QStringLiteral("Ebony"
));
themeList-&
gt;addItem(QStringLiteral("Isabelle"
));
themeList-&
gt;setCurrentIndex(6
);
QPushButton *
labelButton =
new
QPushButton(widget);
labelButton-&
gt;setText(QStringLiteral("Change label style"
));
QCheckBox *
smoothCheckBox =
new
QCheckBox(widget);
smoothCheckBox-&
gt;setText(QStringLiteral("Smooth dots"
));
smoothCheckBox-&
gt;setChecked(true
);
QComboBox *
itemStyleList =
new
QComboBox(widget);
itemStyleList-&
gt;addItem(QStringLiteral("Sphere"
), int
(QAbstract3DSeries::
MeshSphere));
itemStyleList-&
gt;addItem(QStringLiteral("Cube"
), int
(QAbstract3DSeries::
MeshCube));
itemStyleList-&
gt;addItem(QStringLiteral("Minimal"
), int
(QAbstract3DSeries::
MeshMinimal));
itemStyleList-&
gt;addItem(QStringLiteral("Point"
), int
(QAbstract3DSeries::
MeshPoint));
itemStyleList-&
gt;setCurrentIndex(0
);
QPushButton *
cameraButton =
new
QPushButton(widget);
cameraButton-&
gt;setText(QStringLiteral("Change camera preset"
));
QPushButton *
itemCountButton =
new
QPushButton(widget);
itemCountButton-&
gt;setText(QStringLiteral("Toggle item count"
));
QCheckBox *
backgroundCheckBox =
new
QCheckBox(widget);
backgroundCheckBox-&
gt;setText(QStringLiteral("Show background"
));
backgroundCheckBox-&
gt;setChecked(true
);
QCheckBox *
gridCheckBox =
new
QCheckBox(widget);
gridCheckBox-&
gt;setText(QStringLiteral("Show grid"
));
gridCheckBox-&
gt;setChecked(true
);
QComboBox *
shadowQuality =
new
QComboBox(widget);
shadowQuality-&
gt;addItem(QStringLiteral("None"
));
shadowQuality-&
gt;addItem(QStringLiteral("Low"
));
shadowQuality-&
gt;addItem(QStringLiteral("Medium"
));
shadowQuality-&
gt;addItem(QStringLiteral("High"
));
shadowQuality-&
gt;addItem(QStringLiteral("Low Soft"
));
shadowQuality-&
gt;addItem(QStringLiteral("Medium Soft"
));
shadowQuality-&
gt;addItem(QStringLiteral("High Soft"
));
shadowQuality-&
gt;setCurrentIndex(4
);
QFontComboBox *
fontList =
new
QFontComboBox(widget);
fontList-&
gt;setCurrentFont(QFont("Arial"
));
And add them to the vertical layout we created earlier:
vLayout-&
gt;addWidget(labelButton, 0
, Qt::
AlignTop);
vLayout-&
gt;addWidget(cameraButton, 0
, Qt::
AlignTop);
vLayout-&
gt;addWidget(itemCountButton, 0
, Qt::
AlignTop);
vLayout-&
gt;addWidget(backgroundCheckBox);
vLayout-&
gt;addWidget(gridCheckBox);
vLayout-&
gt;addWidget(smoothCheckBox, 0
, Qt::
AlignTop);
vLayout-&
gt;addWidget(new
QLabel(QStringLiteral("Change dot style"
)));
vLayout-&
gt;addWidget(itemStyleList);
vLayout-&
gt;addWidget(new
QLabel(QStringLiteral("Change theme"
)));
vLayout-&
gt;addWidget(themeList);
vLayout-&
gt;addWidget(new
QLabel(QStringLiteral("Adjust shadow quality"
)));
vLayout-&
gt;addWidget(shadowQuality);
vLayout-&
gt;addWidget(new
QLabel(QStringLiteral("Change font"
)));
vLayout-&
gt;addWidget(fontList, 1
, Qt::
AlignTop);
Now, let's connect them to methods in ScatterDataModifier:
QObject::
connect(cameraButton, &
amp;QPushButton::
clicked, modifier,
&
amp;ScatterDataModifier::
changePresetCamera);
QObject::
connect(labelButton, &
amp;QPushButton::
clicked, modifier,
&
amp;ScatterDataModifier::
changeLabelStyle);
QObject::
connect(itemCountButton, &
amp;QPushButton::
clicked, modifier,
&
amp;ScatterDataModifier::
toggleItemCount);
QObject::
connect(backgroundCheckBox, &
amp;QCheckBox::
stateChanged, modifier,
&
amp;ScatterDataModifier::
setBackgroundEnabled);
QObject::
connect(gridCheckBox, &
amp;QCheckBox::
stateChanged, modifier,
&
amp;ScatterDataModifier::
setGridEnabled);
QObject::
connect(smoothCheckBox, &
amp;QCheckBox::
stateChanged, modifier,
&
amp;ScatterDataModifier::
setSmoothDots);
QObject::
connect(modifier, &
amp;ScatterDataModifier::
backgroundEnabledChanged,
backgroundCheckBox, &
amp;QCheckBox::
setChecked);
QObject::
connect(modifier, &
amp;ScatterDataModifier::
gridEnabledChanged,
gridCheckBox, &
amp;QCheckBox::
setChecked);
QObject::
connect(itemStyleList, SIGNAL(currentIndexChanged(int
)), modifier,
SLOT(changeStyle(int
)));
QObject::
connect(themeList, SIGNAL(currentIndexChanged(int
)), modifier,
SLOT(changeTheme(int
)));
QObject::
connect(shadowQuality, SIGNAL(currentIndexChanged(int
)), modifier,
SLOT(changeShadowQuality(int
)));
QObject::
connect(modifier, &
amp;ScatterDataModifier::
shadowQualityChanged, shadowQuality,
&
amp;QComboBox::
setCurrentIndex);
QObject::
connect(graph, &
amp;Q3DScatter::
shadowQualityChanged, modifier,
&
amp;ScatterDataModifier::
shadowQualityUpdatedByVisual);
QObject::
connect(fontList, &
amp;QFontComboBox::
currentFontChanged, modifier,
&
amp;ScatterDataModifier::
changeFont);
QObject::
connect(modifier, &
amp;ScatterDataModifier::
fontChanged, fontList,
&
amp;QFontComboBox::
setCurrentFont);
Here are the methods in ScatterDataModifier the signals were connected to:
void
ScatterDataModifier::
changeStyle(int
style)
{
QComboBox *
comboBox =
qobject_cast&
lt;QComboBox *&
gt;(sender());
if
(comboBox) {
m_style =
QAbstract3DSeries::
Mesh(comboBox-&
gt;itemData(style).toInt());
if
(m_graph-&
gt;seriesList().size())
m_graph-&
gt;seriesList().at(0
)-&
gt;setMesh(m_style);
}
}
void
ScatterDataModifier::
setSmoothDots(int
smooth)
{
m_smooth =
bool
(smooth);
QScatter3DSeries *
series =
m_graph-&
gt;seriesList().at(0
);
series-&
gt;setMeshSmooth(m_smooth);
}
void
ScatterDataModifier::
changeTheme(int
theme)
{
Q3DTheme *
currentTheme =
m_graph-&
gt;activeTheme();
currentTheme-&
gt;setType(Q3DTheme::
Theme(theme));
emit backgroundEnabledChanged(currentTheme-&
gt;isBackgroundEnabled());
emit gridEnabledChanged(currentTheme-&
gt;isGridEnabled());
emit fontChanged(currentTheme-&
gt;font());
}
void
ScatterDataModifier::
changePresetCamera()
{
static
int
preset =
Q3DCamera::
CameraPresetFrontLow;
m_graph-&
gt;scene()-&
gt;activeCamera()-&
gt;setCameraPreset((Q3DCamera::
CameraPreset)preset);
if
(++
preset &
gt; Q3DCamera::
CameraPresetDirectlyBelow)
preset =
Q3DCamera::
CameraPresetFrontLow;
}
void
ScatterDataModifier::
changeLabelStyle()
{
m_graph-&
gt;activeTheme()-&
gt;setLabelBackgroundEnabled(!
m_graph-&
gt;activeTheme()-&
gt;isLabelBackgroundEnabled());
}
void
ScatterDataModifier::
changeFont(const
QFont &
amp;font)
{
QFont newFont =
font;
newFont.setPointSizeF(m_fontSize);
m_graph-&
gt;activeTheme()-&
gt;setFont(newFont);
}
void
ScatterDataModifier::
shadowQualityUpdatedByVisual(QAbstract3DGraph::
ShadowQuality sq)
{
int
quality =
int
(sq);
emit shadowQualityChanged(quality); // connected to a checkbox in main.cpp
}
void
ScatterDataModifier::
changeShadowQuality(int
quality)
{
QAbstract3DGraph::
ShadowQuality sq =
QAbstract3DGraph::
ShadowQuality(quality);
m_graph-&
gt;setShadowQuality(sq);
}
void
ScatterDataModifier::
setBackgroundEnabled(int
enabled)
{
m_graph-&
gt;activeTheme()-&
gt;setBackgroundEnabled((bool
)enabled);
}
void
ScatterDataModifier::
setGridEnabled(int
enabled)
{
m_graph-&
gt;activeTheme()-&
gt;setGridEnabled((bool
)enabled);
}
And so we have an application in which we can control:
-
Label style
-
Camera preset
-
Background visibility
-
Grid visibility
-
Dot shading smoothness
-
Dot style
-
Theme
-
Shadow quality
-
Label font