I have a PyQt application that displays six Matplotlib plots after a button click. I've added a save button and I want to save all six plots to a single PDF file, but I'm getting errors. Outside of PyQt I know how to use
PdfPagesandsavefig(), but I'm stuck getting it working inside my application. How can I save multiple Matplotlib plots to a PDF from a PyQt app?
Saving multiple Matplotlib plots to a PDF from within a PyQt6 application is very doable — the approach is essentially the same as doing it in a plain Python script, with just a little extra wiring to connect it to your GUI. In this article, we'll walk through how to display multiple plots in a PyQt6 window and then save them all to a single PDF file using Matplotlib's PdfPages.
The core idea
Matplotlib provides a utility called PdfPages (from matplotlib.backends.backend_pdf) that lets you write multiple figures to a single PDF file, one figure per page. The basic pattern looks like this:
from matplotlib.backends.backend_pdf import PdfPages
with PdfPages("output.pdf") as pdf:
pdf.savefig(figure_1)
pdf.savefig(figure_2)
# ...and so on
Each call to pdf.savefig() adds a new page to the PDF. This works exactly the same way whether you're running a script or inside a PyQt6 application — you just need to have references to the Matplotlib Figure objects you want to save.
Displaying plots in PyQt6 with Matplotlib
To embed Matplotlib plots in a PyQt6 window, you use FigureCanvasQTAgg as a widget. Each canvas wraps a single Matplotlib Figure. If you want to show six plots in your window, you'll create six Figure objects and six corresponding canvases.
Let's start with a simple example that creates six plots and displays them in a grid layout:
from matplotlib.figure import Figure
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
import numpy as np
class MplCanvas(FigureCanvasQTAgg):
"""A Matplotlib canvas that can be used as a PyQt6 widget."""
def __init__(self, parent=None, width=4, height=3, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.fig.add_subplot(111)
super().__init__(self.fig)
This gives us a reusable widget. Each MplCanvas instance holds its own Figure (accessible via self.fig) and a set of axes we can plot on.
Saving all plots to a PDF
When the user clicks the save button, we need to:
- Ask the user where to save the file (using
QFileDialog). - Open a
PdfPagescontext for that file path. - Loop through all our
Figureobjects and callpdf.savefig()on each one.
Here's what the save method looks like:
from matplotlib.backends.backend_pdf import PdfPages
from PyQt6.QtWidgets import QFileDialog
def save_plots_to_pdf(self):
file_path, _ = QFileDialog.getSaveFileName(
self, "Save PDF", "", "PDF Files (*.pdf)"
)
if file_path:
with PdfPages(file_path) as pdf:
for canvas in self.canvases:
pdf.savefig(canvas.fig)
Each figure is saved as a separate page in the PDF. The PdfPages context manager handles opening and closing the file for you.
Saving all plots on a single page
If you'd prefer all six plots on a single PDF page instead of six separate pages, you can create a new figure with subplots, copy the data across, and save that single figure. An even simpler approach: create the single-page layout at save time using Matplotlib's subplots:
def save_plots_single_page(self):
file_path, _ = QFileDialog.getSaveFileName(
self, "Save PDF", "", "PDF Files (*.pdf)"
)
if file_path:
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
for ax, canvas in zip(axes.flat, self.canvases):
# Re-draw each plot's data onto the combined figure
for line in canvas.axes.get_lines():
ax.plot(line.get_xdata(), line.get_ydata())
ax.set_title(canvas.axes.get_title())
fig.tight_layout()
fig.savefig(file_path)
plt.close(fig)
This creates a temporary 2×3 grid of subplots, copies the line data from each displayed plot, and saves everything to a single page. For the common case of saving each plot as its own page, the PdfPages approach above is simpler and more reliable.
Complete working example
Here's a full PyQt6 application that creates six plots with random data, displays them in a grid, and provides a button to save them all to a multi-page PDF:
import sys
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
from matplotlib.backends.backend_pdf import PdfPages
from PyQt6.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QGridLayout,
QPushButton,
QVBoxLayout,
QFileDialog,
)
class MplCanvas(FigureCanvasQTAgg):
"""A matplotlib canvas widget for embedding plots in PyQt6."""
def __init__(self, parent=None, width=4, height=3, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.fig.add_subplot(111)
super().__init__(self.fig)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Multiple Plots to PDF")
# Create six plot canvases.
self.canvases = []
plot_titles = [
"Sine Wave",
"Cosine Wave",
"Random Walk",
"Exponential",
"Quadratic",
"Noise",
]
x = np.linspace(0, 10, 200)
plot_data = [
np.sin(x),
np.cos(x),
np.cumsum(np.random.randn(200)),
np.exp(x / 5),
x ** 2,
np.random.randn(200),
]
for title, y in zip(plot_titles, plot_data):
canvas = MplCanvas(self, width=4, height=3, dpi=100)
canvas.axes.plot(x, y)
canvas.axes.set_title(title)
canvas.fig.tight_layout()
self.canvases.append(canvas)
# Arrange the canvases in a 2x3 grid.
plot_grid = QGridLayout()
for i, canvas in enumerate(self.canvases):
row = i // 3
col = i % 3
plot_grid.addWidget(canvas, row, col)
# Add a save button.
save_button = QPushButton("Save All Plots to PDF")
save_button.clicked.connect(self.save_to_pdf)
# Main layout.
layout = QVBoxLayout()
layout.addLayout(plot_grid)
layout.addWidget(save_button)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
def save_to_pdf(self):
file_path, _ = QFileDialog.getSaveFileName(
self,
"Save PDF",
"",
"PDF Files (*.pdf)",
)
if not file_path:
return
# Ensure the file has a .pdf extension.
if not file_path.lower().endswith(".pdf"):
file_path += ".pdf"
with PdfPages(file_path) as pdf:
for canvas in self.canvases:
pdf.savefig(canvas.fig)
print(f"Saved {len(self.canvases)} plots to {file_path}")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
Run this and you'll see a window with six plots arranged in a grid. Click "Save All Plots to PDF", choose a location, and all six plots will be saved to a multi-page PDF — one plot per page.
A few things to keep in mind
The figures must exist when you save. Make sure you're keeping references to your Figure objects (or the canvases that hold them). If you create figures in a method and don't store them as instance attributes, they may be garbage collected before you try to save.
PdfPages works with Figure objects, not canvas widgets. When calling pdf.savefig(), pass the Matplotlib Figure instance (e.g., canvas.fig), not the FigureCanvasQTAgg widget itself.
You can customize the PDF output. The savefig() method accepts all the usual Matplotlib keyword arguments — dpi, bbox_inches, facecolor, and so on. For example, to get tighter margins:
pdf.savefig(canvas.fig, bbox_inches="tight")
This works the same in PyQt5 and PyQt6. The only differences are the import paths (PyQt5 vs PyQt6) and using app.exec() instead of app.exec_(). The Matplotlib side is identical.
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.