Create Stunning Pie Charts in Qt : A Step-by-Step Tutorial (2024)

Introduction

It’s time to learn new stuff again. This time, I want to implement adata visualization component in my tool: pie charts. I had some experiencewith matplotlib before, but I’m excited to find out that Qthas an add-on module QtCharts that can integrate with Qt application.

Here are my learning results: on the left side, I created a static and simplistic designand on the right side, a slightly more flashy, animated design.

In this blog, I will break down how they are created. They are also availablein my guiUtil with full code.

Create Stunning Pie Charts in Qt : A Step-by-Step Tutorial (1)
Example #1
Create Stunning Pie Charts in Qt : A Step-by-Step Tutorial (2)
Example #2

Installing QtCharts

QtCharts was first introduced as an add-on module in Qt in version 5.7,so the Qt.py for Python Qt binding doesn’tsupport QtCharts as it isn’t available in PyQt and PySide.

  1. check your PySide2 or PyQt5 version:QtCore.qVersion(). (I’m using PyQt5 version 5.15.2)

  2. install the corresponding version of PyQtChart using

    1
    pip install PyQtChart
  3. finally, test import from PyQt5 import QtCharts

The Basics

Classes

QChart:QChart refers to the main diagram, in our case the pie chart is a QChart object.

QPieSeries:To draw a pie chart, we’ll need one or more QPieSeries added to our QChart object,to form the circular shape. To build other types of chart, we’ll want to useSeries such as QBoxPlotSeries, QCandlestickSeries,QXYSeries, QAreaSeries or QAbstractBarSeries.

QPieSlice:a QPieSlice object represents a slice inside a QPieSeries object.

Create Stunning Pie Charts in Qt : A Step-by-Step Tutorial (3)

Components

There are other components that the examples will cover.

Value reflects the size/span of each slice, and is added one by one in run-timeto the QPieSeries. We’re able to retrieve useful information suchas percentage and span angle once all the data is added.

Label is a crucial component within the scope of the QPieSlice.Label can be displayed in different ways (Inside the slice: LabelInsideHorizontal, LabelInsideTangential, LabelInsideNormalor outside the slice: LabelOutside with labelArm).

Legend in default, is attached to QChart, connecting with the labels ofall the QPieSlice. We can separate it by detaching it or setting each individualmarkers (items) of the legend.

We also defined an immutable data structure to represent the data fedinto the QChart, which we’ll take a look shortly.

Example 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

class MySimpleChart(QtChart.QChart):

def __init__(self, datas, parent=None):
super(MySimpleChart, self).__init__(parent)
self._datas = datas

self.outer = QtChart.QPieSeries()
self.set_outer_series()
self.addSeries(self.outer)

def set_outer_series(self):
slices = list()
for data in self._datas:
slice_ = QtChart.QPieSlice(data.name, data.value)
slice_.setLabelVisible()
slice_.setColor(data.primary_color)
slice_.setLabelBrush(data.primary_color)

slices.append(slice_)
self.outer.append(slice_)

# label styling
for slice_ in slices:
label = "<p align='center' style='color:{}'>{}%</p>".format(round(slice_.percentage()*100, 2))
slice_.setLabel(label)

We start off by subclassing QChart, we then would want to add a QPieSeriesas a container for inserting our slices (generated from our data).

A simplified process:

1
2
3
4
5
6
pie_chart = QChart()
series = QPieSeries()
slice_ = QPieSlice(label, value)

series.append(slice_)
pie_chart.addSeries(series)

note: slice is a Python built-in name so I’m against using it as variable name

Labels and Legend

As we can see from the example above, we have some flexibility withlabel formatting (QLabel methods and HTML formatting),we can also do legend formatting to some extent. Such as alignment and marker shapes.

1
2
self.legend().setAlignment(QtCore.Qt.AlignRight)
self.legend().setMarkerShape(QtChart.QLegend.MarkerShapeCircle)

we can even separate legend vs. slice label, so they could display differentcontent(but it may be bad being practice to unlink the two)

1
2
for index, marker in enumerate(self.legend().markers()):
marker.setLabel(self._datas[index].name)

Other

Adding variation can be easily done by shifting angles of all the slices.

1
2
3
offset = 40
self.outer.setPieStartAngle(offset)
self.outer.setPieEndAngle(offset+360)

If the outside labels are cramped together, we can create additional spacingby extending the label arm:

1
slice_.setLabelArmLengthFactor(0.4)

Example 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class MyChart(QtChart.QChart):

def __init__(self, datas, parent=None):
super(MyChart, self).__init__(parent)
self._datas = datas

self.legend().hide()
self.setAnimationOptions(QtChart.QChart.SeriesAnimations)

self.outer = QtChart.QPieSeries()
self.inner = QtChart.QPieSeries()
self.outer.setHoleSize(0.35)
self.inner.setPieSize(0.35)
self.inner.setHoleSize(0.3)

self.set_outer_series()
self.set_inner_series()

self.addSeries(self.outer)
self.addSeries(self.inner)

def set_outer_series(self):
slices = list()
for data in self._datas:
slice_ = QtChart.QPieSlice(data.name, data.value)
slice_.setLabelVisible()
slice_.setColor(data.primary_color)
slice_.setLabelBrush(data.primary_color)

slices.append(slice_)
self.outer.append(slice_)

# label styling
for slice_ in slices:
color = 'black'
if slice_.percentage() > 0.1:
slice_.setLabelPosition(QtChart.QPieSlice.LabelInsideHorizontal)
color = 'white'

label = "<p align='center' style='color:{}'>{}<br>{}%</p>".format(
color,
slice_.label(),
round(slice_.percentage()*100, 2)
)
slice_.setLabel(label)

def set_inner_series(self):
for data in self._datas:
slice_ = self.inner.append(data.name, data.value)
slice_.setColor(data.secondary_color)
slice_.setBorderColor(data.secondary_color)

Inner and Outer Series

To create a circular/loop shape pie chart, we need to create a holewith setHoleSize(). And the inner loop and outer loop are two separate seriesadjacent to each other.

In the example, I used a light color for inner loop (50% blend with white)and used solely the outer loop to display labels and values. Also, in order forlabels to be displayed properly, I added a condition so that label willbe displayed outside when the angle span is less than a threshold.

Exploding Animation

QPieSlice has a built-in hovered signal for us to achieve theexploding effect when mouse hovering over.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def set_outer_series(self):
...
slice_.hovered.connect(partial(self.explode, slice_))

def explode(self, slice_, is_hovered):
if is_hovered:
start = slice_.startAngle()
end = slice_.startAngle()+slice_.angleSpan()
self.inner.setPieStartAngle(end)
self.inner.setPieEndAngle(start+360)
else:
self.inner.setPieStartAngle(0)
self.inner.setPieEndAngle(360)

slice_.setExplodeDistanceFactor(0.1)
slice_.setExploded(is_hovered)

The outer loop explosion can be set using slice_.setExploded(); The inner loop shiftingis done by offsetting the pie start and end angle.

Also, make sure we have set QChart.SeriesAnimations on the QChartobject.

Bonus

Custom Data Class

1
2
3
4
5
6
7
from collections import namedtuple

Data = namedtuple('Data', ['name', 'value', 'primary_color', 'secondary_color'])

node = Data('Node', 333, QtGui.QColor("#82d3e5"), QtGui.QColor("#cfeef5"))
connection = Data('Connection', 105, QtGui.QColor("#fd635c"), QtGui.QColor("#fdc4c1"))
other = Data('Other', 20, QtGui.QColor("#feb543"), QtGui.QColor("#ffe3b8"))

A helper data structure for adding items to our pie chart.

Generally, only the label/name and the value are required.I also assigned specific color values to achieve certain palette,but they can be randomly generated in the QChart class as well.

Putting it Together

The last thing we need to do is add a main body andinstantiate a pie chart with some random data. We do this using a QChartView container.

All the code is available at guiUtil.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import sys


class ChartView(QtWidgets.QMainWindow):

def __init__(self, parent=None):
super(ChartView, self).__init__(parent)
self.setFixedSize(QtCore.QSize(700, 400))

datas = [node, connection, other]
chart = MySimpleChart(datas)

chart_view = QtChart.QChartView(chart)
chart_view.setRenderHint(QtGui.QPainter.Antialiasing)
self.setCentralWidget(chart_view)

if __name__ == '__main__':
global win
app = QtWidgets.QApplication(sys.argv)
win = ChartView()
win.show()
sys.exit(app.exec_())

Reference

Qt Doc - Nested Donut Example

Qt Doc - QLegend

Qt Doc - QChart

Qt Doc - QPieSeries

Qt Doc - QPieSlice

Stack Overflow - Attach colors of my choosing to each slice of QPieSeries

Create Stunning Pie Charts in Qt : A Step-by-Step Tutorial (2024)
Top Articles
Peanut Butter Snowballs Recipe
Toffee Walnuts - An Easy Candied Nuts Recipe!
Spasa Parish
Rentals for rent in Maastricht
159R Bus Schedule Pdf
Sallisaw Bin Store
Black Adam Showtimes Near Maya Cinemas Delano
Www.myschedule.kp.org
Ascension St. Vincent's Lung Institute - Riverside
Understanding British Money: What's a Quid? A Shilling?
Xenia Canary Dragon Age Origins
Momokun Leaked Controversy - Champion Magazine - Online Magazine
Maine Coon Craigslist
‘An affront to the memories of British sailors’: the lies that sank Hollywood’s sub thriller U-571
Tyreek Hill admits some regrets but calls for officer who restrained him to be fired | CNN
Haverhill, MA Obituaries | Driscoll Funeral Home and Cremation Service
Rogers Breece Obituaries
Ems Isd Skyward Family Access
Elektrische Arbeit W (Kilowattstunden kWh Strompreis Berechnen Berechnung)
Omni Id Portal Waconia
Kellifans.com
Banned in NYC: Airbnb One Year Later
Four-Legged Friday: Meet Tuscaloosa's Adoptable All-Stars Cub & Pickle
Model Center Jasmin
Ice Dodo Unblocked 76
Is Slatt Offensive
Labcorp Locations Near Me
Storm Prediction Center Convective Outlook
Experience the Convenience of Po Box 790010 St Louis Mo
Fungal Symbiote Terraria
modelo julia - PLAYBOARD
Abby's Caribbean Cafe
Joanna Gaines Reveals Who Bought the 'Fixer Upper' Lake House and Her Favorite Features of the Milestone Project
Tri-State Dog Racing Results
Navy Qrs Supervisor Answers
Trade Chart Dave Richard
Lincoln Financial Field Section 110
Free Stuff Craigslist Roanoke Va
Stellaris Resolution
Wi Dept Of Regulation & Licensing
Pick N Pull Near Me [Locator Map + Guide + FAQ]
Crystal Westbrooks Nipple
Ice Hockey Dboard
Über 60 Prozent Rabatt auf E-Bikes: Aldi reduziert sämtliche Pedelecs stark im Preis - nur noch für kurze Zeit
Wie blocke ich einen Bot aus Boardman/USA - sellerforum.de
Infinity Pool Showtimes Near Maya Cinemas Bakersfield
Hooda Math—Games, Features, and Benefits — Mashup Math
Dermpathdiagnostics Com Pay Invoice
How To Use Price Chopper Points At Quiktrip
Maria Butina Bikini
Busted Newspaper Zapata Tx
Latest Posts
Article information

Author: Clemencia Bogisich Ret

Last Updated:

Views: 5747

Rating: 5 / 5 (60 voted)

Reviews: 83% of readers found this page helpful

Author information

Name: Clemencia Bogisich Ret

Birthday: 2001-07-17

Address: Suite 794 53887 Geri Spring, West Cristentown, KY 54855

Phone: +5934435460663

Job: Central Hospitality Director

Hobby: Yoga, Electronics, Rafting, Lockpicking, Inline skating, Puzzles, scrapbook

Introduction: My name is Clemencia Bogisich Ret, I am a super, outstanding, graceful, friendly, vast, comfortable, agreeable person who loves writing and wants to share my knowledge and understanding with you.