Donut Chart Breakdown Example

This example shows how to use create a donut breakdown chart using QPieSeries API.

../_images/examples_donutbreakdown.png

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
series1 = QPieSeries()
series1.setName("Fossil fuels")
series1.append("Oil", 353295)
series1.append("Coal", 188500)
series1.append("Natural gas", 148680)
series1.append("Peat", 94545)
series2 = QPieSeries()
series2.setName("Renewables")
series2.append("Wood fuels", 319663)
series2.append("Hydro power", 45875)
series2.append("Wind power", 1060)
series3 = QPieSeries()
series3.setName("Others")
series3.append("Nuclear energy", 238789)
series3.append("Import energy", 37802)
series3.append("Other", 32441)

Then we create a chart where we add the data. Note that this is our own chart derived from QChart .

donutBreakdown = DonutBreakdownChart()
donutBreakdown.setAnimationOptions(QChart.AllAnimations)
donutBreakdown.setTitle("Total consumption of energy in Finland 2010")
donutBreakdown.legend().setAlignment(Qt.AlignRight)
donutBreakdown.addBreakdownSeries(series1, Qt.red)
donutBreakdown.addBreakdownSeries(series2, Qt.darkGreen)
donutBreakdown.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.__init__(self, .ChartTypeCartesian, parent, wFlags)

    # create the series for main center pie
    m_mainSeries = QPieSeries()
    m_mainSeries.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.

def addBreakdownSeries(self, breakdownSeries, color):

    font = QFont("Arial", 8)
    # add breakdown series as a slice to center pie
    mainSlice = MainSlice(breakdownSeries)
    mainSlice.setName(breakdownSeries.name())
    mainSlice.setValue(breakdownSeries.sum())
    m_mainSeries.append(mainSlice)
    # customize the slice
    mainSlice.setBrush(color)
    mainSlice.setLabelVisible()
    mainSlice.setLabelColor(Qt.white)
    mainSlice.setLabelPosition(QPieSlice.LabelInsideHorizontal)
    mainSlice.setLabelFont(font)
    # position and customize the breakdown series
    breakdownSeries.setPieSize(0.8)
    breakdownSeries.setHoleSize(0.7)
    breakdownSeries.setLabelsVisible()
    slices = breakdownSeries.slices()
    for slice in slices:
        color = color.lighter(115)
        slice.setBrush(color)
        slice.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.

def recalculateAngles(self):

    angle = 0
    slices = m_mainSeries.slices()
    for slice in slices:
        breakdownSeries = MainSlice (slice).breakdownSeries()
        breakdownSeries.setPieStartAngle(angle)
        angle += slice.percentage() * 360.0 # full pie is 360.0
        breakdownSeries.setPieEndAngle(angle)

The legend markers are customized to show the breakdown percentage. The markers for the main level slices are hidden.

def updateLegendMarkers(self):

    # go through all markers
    allseries = series()
    for series in allseries:
        markers = legend().markers(series)
        for marker in markers:
            pieMarker = QPieLegendMarker (marker)
            if (series == m_mainSeries) {
                # hide markers from main series
                pieMarker.setVisible(False)
            else:
                # modify markers from breakdown series
                pieMarker.setLabel(QString("%1 %2%")
                                    .arg(pieMarker.slice().label())
                                    .arg(pieMarker.slice().percentage() * 100, 0, 'f', 2))
                pieMarker.setFont(QFont("Arial", 8))

Instead the main level slices show the percentage on the label.

def __init__(self, breakdownSeries, parent):
    QPieSlice.__init__(self, parent)
      m_breakdownSeries(breakdownSeries)

    connect(self, MainSlice::percentageChanged, self, MainSlice::updateLabel)        def updateLabel(self):

    self.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.

window = QMainWindow()
chartView = QChartView(donutBreakdown)
chartView.setRenderHint(QPainter.Antialiasing)
window.setCentralWidget(chartView)
window.resize(800, 500)
window.show()

Example project @ code.qt.io