Simultaneous scaling ViewBox elements

How to synchronize axis scaling across multiple PyQtGraph plots
Heads up! You've already completed this tutorial.

I have several signal plots (ViewBox elements) and I'd like them to scale at the same time — so when I zoom or pan one, they all follow — without merging them into a single shared plot area. How can I do this with PyQtGraph?

If you're displaying multiple plots stacked vertically (or side by side) and want their axes to stay in sync when you zoom or scroll, PyQtGraph has built-in support for this through axis linking. You can link the X axis, the Y axis, or both across any number of plots, so interacting with one plot automatically updates the others.

In this tutorial we'll walk through how to set this up using setXLink() and setYLink() on PyQtGraph plot items.

What axis linking does

When you link the axis of one plot to another, the two plots share the same visible range on that axis. If you zoom into one plot along the linked axis, the other plot zooms to match. If you pan one, the other follows. Each plot still has its own data and its own drawing area — they remain visually separate — but their coordinate systems stay synchronized.

This is especially useful when you have multiple signals recorded over the same time range and want to compare them while scrolling through the data.

Setting up multiple plots

Let's start by creating a simple window with three plots stacked vertically using GraphicsLayoutWidget. Each plot will show a different signal.

python
import sys
import numpy as np
import pyqtgraph as pg
from PyQt6.QtWidgets import QApplication

app = QApplication(sys.argv)

# Create a graphics layout widget with multiple plots
win = pg.GraphicsLayoutWidget(show=True, title="Multiple Plots")
win.resize(800, 600)

# Generate some sample data
x = np.linspace(0, 10, 500)
y1 = np.sin(x)
y2 = np.sin(x * 2) * 0.5
y3 = np.cos(x) * 0.8

# Add three plots, each on its own row
p1 = win.addPlot(row=0, col=0, title="Signal 1")
p1.plot(x, y1, pen="r")

p2 = win.addPlot(row=1, col=0, title="Signal 2")
p2.plot(x, y2, pen="g")

p3 = win.addPlot(row=2, col=0, title="Signal 3")
p3.plot(x, y3, pen="b")

sys.exit(app.exec())

Run this and you'll see three separate plots. You can zoom and pan each one independently — but that's not what we want. We want them to move together.

Linking the X axis

To synchronize horizontal scrolling and zooming, use setXLink(). You pass in a reference to the plot (or ViewBox) you want to link to. All linked plots will then share the same X-axis range.

Add these two lines before the sys.exit() call:

python
p2.setXLink(p1)
p3.setXLink(p1)

Now p2 and p3 are both linked to p1 on the X axis. When you zoom or pan horizontally on any of the three plots, the others will follow. Each plot still scales its Y axis independently, so you can see each signal at its own vertical scale.

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.

Get the book

Linking the Y axis

If you also want the vertical axis to stay in sync, use setYLink() in the same way:

python
p2.setYLink(p1)
p3.setYLink(p1)

With both axes linked, zooming or panning on any plot will update all three plots identically. This is useful when your signals share the same units and magnitude, and you want a direct visual comparison.

You can mix and match — for example, link only the X axis (common for time-series data) while leaving the Y axis independent for each signal.

Working with ViewBox directly

The setXLink() and setYLink() methods are available on both PlotItem (what addPlot() returns) and ViewBox objects. If you're building a more custom layout using ViewBox instances directly, the same approach works:

python
vb1 = pg.ViewBox()
vb2 = pg.ViewBox()

vb2.setXLink(vb1)

You can also pass a string name instead of an object reference. Each ViewBox can be given a name, and you can link by that name. Using direct object references as shown above is usually simpler.

Applying a uniform Y-axis range manually

Sometimes you don't want the Y axes linked (where zooming one zooms all), but you do want them all to start with the same visible range. In that case, calculate the range yourself and apply it to each plot:

python
all_data = [y1, y2, y3]
y_min = min(d.min() for d in all_data)
y_max = max(d.max() for d in all_data)

# Add a small margin
margin = (y_max - y_min) * 0.05

for p in [p1, p2, p3]:
    p.setYRange(y_min - margin, y_max + margin)

This gives each plot the same initial scale without locking them together — users can still zoom each plot independently afterward.

Complete working example

Here's the full example with X-axis linking enabled, so all three plots scroll and zoom horizontally together while keeping independent vertical scaling:

python
import sys
import numpy as np
import pyqtgraph as pg
from PyQt6.QtWidgets import QApplication

app = QApplication(sys.argv)

# Create a graphics layout widget with multiple plots
win = pg.GraphicsLayoutWidget(show=True, title="Linked Plots Example")
win.resize(800, 600)

# Generate some sample data
x = np.linspace(0, 10, 500)
y1 = np.sin(x)
y2 = np.sin(x * 2) * 0.5
y3 = np.cos(x) * 0.8

# Add three plots, each on its own row
p1 = win.addPlot(row=0, col=0, title="Signal 1")
p1.plot(x, y1, pen="r")

p2 = win.addPlot(row=1, col=0, title="Signal 2")
p2.plot(x, y2, pen="g")

p3 = win.addPlot(row=2, col=0, title="Signal 3")
p3.plot(x, y3, pen="b")

# Link X axes so horizontal zoom/pan is synchronized
p2.setXLink(p1)
p3.setXLink(p1)

sys.exit(app.exec())

Try zooming in horizontally on any of the three plots — all three will zoom together. Vertical zooming remains independent, which is often exactly what you want for comparing different signals that share a time axis but have different amplitudes.

If you want full synchronization on both axes, add setYLink() calls as shown earlier. And if you just need a consistent starting scale without ongoing synchronization, use setYRange() to apply the same range to each plot at startup.

For more on embedding PyQtGraph into your PyQt6 applications as custom widgets, see our tutorial on embedding PyQtGraph custom widgets. You might also want to explore plotting time-series data with PyQtGraph or learn about plotting with two Y axes for more advanced chart configurations.

Over 15,000 developers have bought Create GUI Applications with Python & Qt!
Create GUI Applications with Python & Qt6
Get the book

Downloadable ebook (PDF, ePub) & Complete Source code

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

Simultaneous scaling ViewBox elements 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.