Creating Grouped and Stacked Bar Charts with PyQtGraph

Build professional grouped and stacked bar charts in your PyQt6 applications using pyqtgraph's BarGraphItem
Heads up! You've already completed this tutorial.

PyQtGraph makes it straightforward to create bar charts using BarGraphItem, and once you understand the basics, you can extend the same approach to build grouped and stacked bar charts. In this tutorial, we'll walk through creating a simple bar chart, then progress to grouped bars (multiple series side by side) and stacked bars (series on top of each other).

A Simple Bar Chart

Before getting into grouped and stacked charts, let's start with a single bar chart so we have a foundation to build on.

BarGraphItem takes a few key parameters:

  • x — the x positions of the bars
  • height — the height of each bar
  • width — how wide each bar should be
  • brush — the fill color

Here's a minimal example:

python
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets
import sys

app = QtWidgets.QApplication(sys.argv)

win = pg.plot()
win.setWindowTitle("Simple Bar Chart")

x = [1, 2, 3, 4, 5]
heights = [10, 25, 15, 30, 20]

bar = pg.BarGraphItem(x=x, height=heights, width=0.6, brush="steelblue")
win.addItem(bar)

sys.exit(app.exec())

Running this gives you five blue bars. The x values control where the bars sit along the horizontal axis, height sets how tall each bar is, and width controls the bar thickness.

Creating a Grouped Bar Chart

A grouped bar chart places multiple series of bars side by side at each position on the x-axis. Think of comparing sales figures for different products across several months — each month has a cluster of bars, one per product.

The trick is to offset each series slightly along the x-axis so the bars sit next to each other rather than on top of one another.

Let's say we have three categories and two data series. We'll use a bar width of 0.3 and shift each series by that amount:

python
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets
import sys

app = QtWidgets.QApplication(sys.argv)

win = pg.plot()
win.setWindowTitle("Grouped Bar Chart")

# Data
categories = [1, 2, 3, 4, 5]
series_a = [20, 35, 30, 35, 27]
series_b = [25, 32, 34, 20, 25]

x = np.array(categories, dtype=float)
width = 0.3

# Offset each series so they sit side by side
bar_a = pg.BarGraphItem(x=x - width / 2, height=series_a, width=width, brush="#4a86c8", name="Series A")
bar_b = pg.BarGraphItem(x=x + width / 2, height=series_b, width=width, brush="#e85d04", name="Series B")

win.addItem(bar_a)
win.addItem(bar_b)

# Add a legend
win.addLegend()

sys.exit(app.exec())

Each BarGraphItem gets its own set of x positions, shifted left or right by half the bar width. This keeps the group centered on the original x position.

For two series, the offsets are -width/2 and +width/2. If you had three series, you'd use -width, 0, and +width:

python
bar_a = pg.BarGraphItem(x=x - width, height=series_a, width=width, brush="#4a86c8")
bar_b = pg.BarGraphItem(x=x,         height=series_b, width=width, brush="#e85d04")
bar_c = pg.BarGraphItem(x=x + width, height=series_c, width=width, brush="#2a9d8f")

The general pattern: center the group of bars around each x tick by spreading them evenly.

Creating a Stacked Bar Chart

A stacked bar chart places one series on top of another at the same x position. This is useful when you want to show how individual parts contribute to a total.

PyQtGraph's BarGraphItem has a y0 parameter that sets the baseline (bottom edge) of each bar. By setting the y0 of the second series to be the height of the first series, the bars stack up.

python
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets
import sys

app = QtWidgets.QApplication(sys.argv)

win = pg.plot()
win.setWindowTitle("Stacked Bar Chart")

# Data
categories = [1, 2, 3, 4, 5]
series_a = np.array([20, 35, 30, 35, 27])
series_b = np.array([25, 32, 34, 20, 25])

x = np.array(categories, dtype=float)
width = 0.6

# First series sits on the baseline (y0 defaults to 0)
bar_a = pg.BarGraphItem(x=x, height=series_a, width=width, brush="#4a86c8", name="Series A")

# Second series sits on top of the first
bar_b = pg.BarGraphItem(x=x, height=series_b, width=width, y0=series_a, brush="#e85d04", name="Series B")

win.addItem(bar_a)
win.addItem(bar_b)

win.addLegend()

sys.exit(app.exec())

The first series uses the default baseline of 0. The second series uses y0=series_a, which places each bar's bottom edge at the top of the corresponding bar in series A. The height of each bar in series B is still relative — it represents how much that segment adds to the stack.

To add a third series, set its y0 to the sum of the first two:

python
series_c = np.array([15, 10, 20, 15, 18])

bar_c = pg.BarGraphItem(
    x=x, height=series_c, width=width,
    y0=series_a + series_b,
    brush="#2a9d8f", name="Series C"
)
win.addItem(bar_c)

Adding Custom Axis Labels

Numeric x-axis labels (1, 2, 3...) aren't always what you need. You'll often want text labels like month names or product categories. You can achieve this with a custom axis using a dictionary that maps tick positions to label strings.

Here's how to set custom x-axis tick labels:

python
# Define tick labels
ticks = {1: "Jan", 2: "Feb", 3: "Mar", 4: "Apr", 5: "May"}

# Get the bottom axis and set ticks
ax = win.getAxis("bottom")
ax.setTicks([list(ticks.items())])

The setTicks method expects a list of tick levels. Each level is a list of (position, label) tuples. Passing a single level like this gives you one row of labels.

Complete Working Example

Let's put everything together into a complete PyQt6 application that shows a grouped bar chart with custom labels, a legend, and a title. If you're new to building PyQt6 applications, you may want to start with creating your first window before diving in.

python
import sys
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets, QtCore


class BarChartWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Grouped Bar Chart — PyQtGraph")
        self.resize(800, 500)

        # Create the plot widget
        self.plot_widget = pg.PlotWidget()
        self.setCentralWidget(self.plot_widget)

        # Enable the legend
        self.plot_widget.addLegend()

        # Title and labels
        self.plot_widget.setTitle("Quarterly Sales by Product", size="14pt")
        self.plot_widget.setLabel("left", "Revenue ($k)")
        self.plot_widget.setLabel("bottom", "Quarter")

        # Data
        quarters = np.array([1, 2, 3, 4], dtype=float)
        product_a = [120, 150, 180, 200]
        product_b = [90, 130, 160, 170]
        product_c = [60, 80, 110, 140]

        width = 0.25

        # Create bars, offset for grouping
        bar_a = pg.BarGraphItem(
            x=quarters - width, height=product_a,
            width=width, brush="#4a86c8", name="Product A"
        )
        bar_b = pg.BarGraphItem(
            x=quarters, height=product_b,
            width=width, brush="#e85d04", name="Product B"
        )
        bar_c = pg.BarGraphItem(
            x=quarters + width, height=product_c,
            width=width, brush="#2a9d8f", name="Product C"
        )

        self.plot_widget.addItem(bar_a)
        self.plot_widget.addItem(bar_b)
        self.plot_widget.addItem(bar_c)

        # Custom x-axis tick labels
        ticks = {1: "Q1", 2: "Q2", 3: "Q3", 4: "Q4"}
        ax = self.plot_widget.getAxis("bottom")
        ax.setTicks([list(ticks.items())])

        # Start the y-axis at 0
        self.plot_widget.setYRange(0, 220)


app = QtWidgets.QApplication(sys.argv)
window = BarChartWindow()
window.show()
sys.exit(app.exec())

This gives you a clean, professional-looking grouped bar chart with three products compared across four quarters.

Grouped bar chart showing quarterly sales by product

Complete Stacked Bar Example

And here's the same data displayed as a stacked bar chart instead:

python
import sys
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets


class StackedBarChartWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Stacked Bar Chart — PyQtGraph")
        self.resize(800, 500)

        self.plot_widget = pg.PlotWidget()
        self.setCentralWidget(self.plot_widget)

        self.plot_widget.addLegend()
        self.plot_widget.setTitle("Quarterly Sales by Product (Stacked)", size="14pt")
        self.plot_widget.setLabel("left", "Revenue ($k)")
        self.plot_widget.setLabel("bottom", "Quarter")

        # Data
        quarters = np.array([1, 2, 3, 4], dtype=float)
        product_a = np.array([120, 150, 180, 200])
        product_b = np.array([90, 130, 160, 170])
        product_c = np.array([60, 80, 110, 140])

        width = 0.6

        # Stack the bars
        bar_a = pg.BarGraphItem(
            x=quarters, height=product_a,
            width=width, brush="#4a86c8", name="Product A"
        )
        bar_b = pg.BarGraphItem(
            x=quarters, height=product_b,
            width=width, y0=product_a, brush="#e85d04", name="Product B"
        )
        bar_c = pg.BarGraphItem(
            x=quarters, height=product_c,
            width=width, y0=product_a + product_b, brush="#2a9d8f", name="Product C"
        )

        self.plot_widget.addItem(bar_a)
        self.plot_widget.addItem(bar_b)
        self.plot_widget.addItem(bar_c)

        # Custom x-axis tick labels
        ticks = {1: "Q1", 2: "Q2", 3: "Q3", 4: "Q4"}
        ax = self.plot_widget.getAxis("bottom")
        ax.setTicks([list(ticks.items())])

        # Adjust y-axis to fit stacked totals
        max_height = max(product_a + product_b + product_c)
        self.plot_widget.setYRange(0, max_height + 20)


app = QtWidgets.QApplication(sys.argv)
window = StackedBarChartWindow()
window.show()
sys.exit(app.exec())

Summary

Both grouped and stacked bar charts in pyqtgraph use the same BarGraphItem — the difference is just how you position each series:

Chart Type Technique
Grouped Offset each series along the x-axis so bars sit side by side
Stacked Use the y0 parameter to place each series on top of the previous one

Once you're comfortable with these patterns, you can combine them with pyqtgraph's other features — tooltips, interactive zooming, real-time updates — to build rich, dynamic data visualizations right inside your PyQt6 applications. For more on plotting with pyqtgraph, see our PyQtGraph plotting tutorial. You can also embed pyqtgraph plots as custom widgets within larger applications, or explore plotting with Matplotlib as an alternative charting approach.

Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

Packaging Python Applications with PyInstaller by Martin Fitzpatrick

This step-by-step guide walks you through packaging your own Python applications from simple examples to complete installers and signed executables.

More info Get the book

Martin Fitzpatrick

Creating Grouped and Stacked Bar Charts with PyQtGraph was written by Martin Fitzpatrick.

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt. Martin founded PythonGUIs to provide easy to follow GUI programming tutorials to the Python community. He has written a number of popular Python books on the subject.