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