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.
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:
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.
Linking the Y axis
If you also want the vertical axis to stay in sync, use setYLink() in the same way:
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:
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:
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:
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.