Donut Chart Breakdown Example▲
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 Donut Breakdown Charts▲
Let's start by defining some data for the chart.
// Graph is based on data of 'Total consumption of energy increased by 10 per cent in 2010'
// Statistics Finland, 13 December 2011
// http://www.stat.fi/til/ekul/2010/ekul_2010_2011-12-13_tie_001_en.html
QPieSeries *
series1 =
new
QPieSeries();
series1-&
gt;setName("Fossil fuels"
);
series1-&
gt;append("Oil"
, 353295
);
series1-&
gt;append("Coal"
, 188500
);
series1-&
gt;append("Natural gas"
, 148680
);
series1-&
gt;append("Peat"
, 94545
);
QPieSeries *
series2 =
new
QPieSeries();
series2-&
gt;setName("Renewables"
);
series2-&
gt;append("Wood fuels"
, 319663
);
series2-&
gt;append("Hydro power"
, 45875
);
series2-&
gt;append("Wind power"
, 1060
);
QPieSeries *
series3 =
new
QPieSeries();
series3-&
gt;setName("Others"
);
series3-&
gt;append("Nuclear energy"
, 238789
);
series3-&
gt;append("Import energy"
, 37802
);
series3-&
gt;append("Other"
, 32441
);
Then we create a chart where we add the data. Note that this is our own chart derived from QChart.
DonutBreakdownChart *
donutBreakdown =
new
DonutBreakdownChart();
donutBreakdown-&
gt;setAnimationOptions(QChart::
AllAnimations);
donutBreakdown-&
gt;setTitle("Total consumption of energy in Finland 2010"
);
donutBreakdown-&
gt;legend()-&
gt;setAlignment(Qt::
AlignRight);
donutBreakdown-&
gt;addBreakdownSeries(series1, Qt::
red);
donutBreakdown-&
gt;addBreakdownSeries(series2, Qt::
darkGreen);
donutBreakdown-&
gt;addBreakdownSeries(series3, Qt::
darkBlue);
Our own chart works in such a way that we create a main series in the constructor we create a main series, which aggregates the data provided by the breakdown series. This is the piechart in the center.
DonutBreakdownChart::
DonutBreakdownChart(QGraphicsItem *
parent, Qt::
WindowFlags wFlags)
:
QChart(QChart::
ChartTypeCartesian, parent, wFlags)
{
// create the series for main center pie
m_mainSeries =
new
QPieSeries();
m_mainSeries-&
gt;setPieSize(0.7
);
QChart::
addSeries(m_mainSeries);
}
When a breakdown series is added the data is used to create a slice in the main series and the breakdown series itself is used to create a segment of a donut positioned so that it is aligned with the corresponding slice in the main series.
void
DonutBreakdownChart::
addBreakdownSeries(QPieSeries *
breakdownSeries, QColor color)
{
QFont font("Arial"
, 8
);
// add breakdown series as a slice to center pie
MainSlice *
mainSlice =
new
MainSlice(breakdownSeries);
mainSlice-&
gt;setName(breakdownSeries-&
gt;name());
mainSlice-&
gt;setValue(breakdownSeries-&
gt;sum());
m_mainSeries-&
gt;append(mainSlice);
// customize the slice
mainSlice-&
gt;setBrush(color);
mainSlice-&
gt;setLabelVisible();
mainSlice-&
gt;setLabelColor(Qt::
white);
mainSlice-&
gt;setLabelPosition(QPieSlice::
LabelInsideHorizontal);
mainSlice-&
gt;setLabelFont(font);
// position and customize the breakdown series
breakdownSeries-&
gt;setPieSize(0.8
);
breakdownSeries-&
gt;setHoleSize(0.7
);
breakdownSeries-&
gt;setLabelsVisible();
const
auto
slices =
breakdownSeries-&
gt;slices();
for
(QPieSlice *
slice : slices) {
color =
color.lighter(115
);
slice-&
gt;setBrush(color);
slice-&
gt;setLabelFont(font);
}
// add the series to the chart
QChart::
addSeries(breakdownSeries);
// recalculate breakdown donut segments
recalculateAngles();
// update customize legend markers
updateLegendMarkers();
}
Here's how the start and end angles for the donut segments are calculated.
void
DonutBreakdownChart::
recalculateAngles()
{
qreal angle =
0
;
const
auto
slices =
m_mainSeries-&
gt;slices();
for
(QPieSlice *
slice : slices) {
QPieSeries *
breakdownSeries =
qobject_cast&
lt;MainSlice *&
gt;(slice)-&
gt;breakdownSeries();
breakdownSeries-&
gt;setPieStartAngle(angle);
angle +=
slice-&
gt;percentage() *
360.0
; // full pie is 360.0
breakdownSeries-&
gt;setPieEndAngle(angle);
}
}
The legend markers are customized to show the breakdown percentage. The markers for the main level slices are hidden.
void
DonutBreakdownChart::
updateLegendMarkers()
{
// go through all markers
const
auto
allseries =
series();
for
(QAbstractSeries *
series : allseries) {
const
auto
markers =
legend()-&
gt;markers(series);
for
(QLegendMarker *
marker : markers) {
QPieLegendMarker *
pieMarker =
qobject_cast&
lt;QPieLegendMarker *&
gt;(marker);
if
(series ==
m_mainSeries) {
// hide markers from main series
pieMarker-&
gt;setVisible(false
);
}
else
{
// modify markers from breakdown series
pieMarker-&
gt;setLabel(QString("%1 %2%"
)
.arg(pieMarker-&
gt;slice()-&
gt;label())
.arg(pieMarker-&
gt;slice()-&
gt;percentage() *
100
, 0
, 'f'
, 2
));
pieMarker-&
gt;setFont(QFont("Arial"
, 8
));
}
}
}
}
Instead the main level slices show the percentage on the label.
MainSlice::
MainSlice(QPieSeries *
breakdownSeries, QObject *
parent)
:
QPieSlice(parent),
m_breakdownSeries(breakdownSeries)
{
connect(this
, &
amp;MainSlice::
percentageChanged, this
, &
amp;MainSlice::
updateLabel);
}
void
MainSlice::
updateLabel()
{
this
-&
gt;setLabel(QString("%1 %2%"
).arg(m_name).arg(percentage() *
100
, 0
, 'f'
, 2
));
}
Now that we have our chart defined, we can finally create a QChartView and show the chart.
QMainWindow window;
QChartView *
chartView =
new
QChartView(donutBreakdown);
chartView-&
gt;setRenderHint(QPainter::
Antialiasing);
window.setCentralWidget(chartView);
window.resize(800
, 500
);
window.show();