I'm embedding a Matplotlib graph with a navigation toolbar in my PySide/PyQt application. The toolbar's controls appear really large compared to the rest of the interface. Is there any way to make them smaller — say, the same height as a default line of text? Ideally, I'd love to see them as a semi-transparent overlay on the graph's canvas itself.
When you embed a Matplotlib plot in a PyQt6 or PySide6 application, the NavigationToolbar2QT toolbar can look oversized compared to the rest of your UI. The icons and spacing feel heavy, especially next to standard widgets like QPushButton or QLabel. Fortunately, because the toolbar is a regular Qt widget, you have full control over its appearance using standard Qt techniques.
In this article, we'll look at several ways to bring the toolbar in line with the rest of your interface — from simple icon resizing, to styling with stylesheets, to building a fully custom floating overlay toolbar.
The default toolbar
Here's a minimal example showing the default Matplotlib toolbar embedded in a PyQt6 window. If you're using PySide6, swap the imports accordingly — the rest of the code is identical.
import sys
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QVBoxLayout, QWidget,
)
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
class MplCanvas(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
super().__init__(fig)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
self.canvas.axes.plot([0, 1, 2, 3, 4], [10, 1, 20, 3, 40])
toolbar = NavigationToolbar(self.canvas, self)
layout = QVBoxLayout()
layout.addWidget(toolbar)
layout.addWidget(self.canvas)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
If you run this, you'll see the toolbar with its default (large) icons. Let's fix that.
Reducing the icon size
The simplest way to make the toolbar smaller is to change its icon size. NavigationToolbar2QT inherits from QToolBar, so you can call setIconSize() directly on it. Qt uses QSize to specify dimensions.
from PyQt6.QtCore import QSize
toolbar = NavigationToolbar(self.canvas, self)
toolbar.setIconSize(QSize(16, 16))
The default icon size is typically 24×24 or larger depending on your platform. Setting it to 16×16 produces a noticeably more compact toolbar. You can experiment with values like 12, 14, or 20 to find what works best for your layout.
Setting a fixed height on the toolbar
If you want even more control over the toolbar's vertical footprint, you can constrain its maximum height:
toolbar = NavigationToolbar(self.canvas, self)
toolbar.setIconSize(QSize(16, 16))
toolbar.setMaximumHeight(24)
This caps the total toolbar height (icons + padding) at 24 pixels. Combined with smaller icons, this produces a much more compact result.
Styling the toolbar with Qt stylesheets
You can go further and use Qt stylesheets to adjust padding, margins, and even the appearance of individual toolbar buttons. For example, to reduce the padding around each tool button:
toolbar.setStyleSheet("""
QToolBar { spacing: 2px; }
QToolButton { padding: 1px; margin: 0px; }
""")
You can also tone down the visual weight of the toolbar to make it feel less dominant. For instance, setting a lighter background:
toolbar.setStyleSheet("""
QToolBar {
spacing: 2px;
background: #f0f0f0;
border: none;
}
QToolButton {
padding: 1px;
margin: 0px;
}
""")
Stylesheets are very flexible — you can adjust colors, borders, hover effects, and more. See the Qt Stylesheet reference for details.
Removing the coordinate display label
The Matplotlib toolbar includes a QLabel at the right side that displays cursor coordinates when you hover over the plot. This label contributes to the toolbar's height because it needs vertical space for text. If you don't need it, you can hide it:
toolbar = NavigationToolbar(self.canvas, self)
# The locLabel is the coordinate display widget
toolbar.locLabel.setVisible(False)
This frees up space and makes the toolbar look cleaner, especially when combined with smaller icons.
Placing the toolbar below the plot
A small layout change can also help visually. Placing the toolbar below the canvas (rather than above) often feels less intrusive, since the user's attention is drawn to the plot first:
layout = QVBoxLayout()
layout.addWidget(self.canvas)
layout.addWidget(toolbar)
This is purely a design preference, but many applications use this arrangement.
Building a floating overlay toolbar
The original question mentioned wanting the toolbar as a semi-transparent overlay on the canvas — similar to how MATLAB handles it. Matplotlib's built-in toolbar doesn't support this directly, but you can achieve it by placing the toolbar on top of the canvas using a QVBoxLayout inside the canvas, or by positioning the toolbar as a child widget of the canvas.
Here's an approach that places the toolbar as an overlay on the canvas itself:
import sys
from PyQt6.QtCore import QSize, Qt
from PyQt6.QtWidgets import (
QApplication, QHBoxLayout, QMainWindow, QVBoxLayout, QWidget,
)
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
class MplCanvas(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
super().__init__(fig)
class OverlayToolbar(NavigationToolbar):
"""A NavigationToolbar styled to sit as a semi-transparent overlay."""
def __init__(self, canvas, parent=None):
super().__init__(canvas, parent)
self.setIconSize(QSize(16, 16))
self.setStyleSheet("""
QToolBar {
background: rgba(255, 255, 255, 180);
border: 1px solid rgba(0, 0, 0, 30);
border-radius: 4px;
spacing: 2px;
padding: 2px;
}
QToolButton {
padding: 2px;
margin: 0px;
}
""")
# Hide the coordinate label to save space
self.locLabel.setVisible(False)
class PlotWidget(QWidget):
"""A widget that displays a Matplotlib canvas with an overlay toolbar."""
def __init__(self):
super().__init__()
self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
self.canvas.axes.plot([0, 1, 2, 3, 4], [10, 1, 20, 3, 40])
self.toolbar = OverlayToolbar(self.canvas, self.canvas)
# Main layout just holds the canvas
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.canvas)
self.setLayout(layout)
def resizeEvent(self, event):
super().resizeEvent(event)
# Position the toolbar in the top-left corner of the canvas
self.toolbar.move(10, 10)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Matplotlib Overlay Toolbar")
plot_widget = PlotWidget()
self.setCentralWidget(plot_widget)
self.resize(700, 500)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
In this example, the OverlayToolbar is created as a child of the canvas widget itself. Because it's a child widget, it renders on top of the canvas. The resizeEvent override repositions the toolbar whenever the window is resized, keeping it anchored to the top-left corner. The semi-transparent background (using rgba) lets the plot show through slightly behind the controls.
You could also position it centered at the top or bottom by calculating the offset from the canvas width:
def resizeEvent(self, event):
super().resizeEvent(event)
# Center the toolbar at the top of the canvas
toolbar_width = self.toolbar.sizeHint().width()
x = (self.canvas.width() - toolbar_width) // 2
self.toolbar.move(x, 10)
Complete working example
Here's a complete example that brings together the most practical improvements: smaller icons, reduced padding, a compact toolbar placed below the canvas, and a hidden coordinate label.
import sys
from PyQt6.QtCore import QSize
from PyQt6.QtWidgets import (
QApplication, QHBoxLayout, QMainWindow, QPushButton,
QVBoxLayout, QWidget,
)
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
class MplCanvas(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
super().__init__(fig)
class PlotterWidget(QWidget):
def __init__(self):
super().__init__()
# Example buttons
btn_sine = QPushButton("Plot Sine")
btn_cosine = QPushButton("Plot Cosine")
btn_clear = QPushButton("Clear")
btn_sine.clicked.connect(self.plot_sine)
btn_cosine.clicked.connect(self.plot_cosine)
btn_clear.clicked.connect(self.clear_plot)
button_layout = QHBoxLayout()
button_layout.addWidget(btn_sine)
button_layout.addWidget(btn_cosine)
button_layout.addWidget(btn_clear)
# Create the Matplotlib canvas
self.canvas = MplCanvas(self, width=5, height=3, dpi=100)
# Create a compact toolbar
toolbar = NavigationToolbar(self.canvas, self)
toolbar.setIconSize(QSize(16, 16))
toolbar.setMaximumHeight(28)
toolbar.locLabel.setVisible(False)
toolbar.setStyleSheet("""
QToolBar {
spacing: 2px;
background: palette(window);
border: none;
}
QToolButton {
padding: 1px;
margin: 0px;
}
""")
# Layout: buttons on top, then canvas, then toolbar at the bottom
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.addLayout(button_layout)
layout.addWidget(self.canvas)
layout.addWidget(toolbar)
self.setLayout(layout)
def plot_sine(self):
import numpy as np
self.canvas.axes.clear()
x = np.linspace(0, 10, 200)
self.canvas.axes.plot(x, np.sin(x))
self.canvas.draw()
def plot_cosine(self):
import numpy as np
self.canvas.axes.clear()
x = np.linspace(0, 10, 200)
self.canvas.axes.plot(x, np.cos(x))
self.canvas.draw()
def clear_plot(self):
self.canvas.axes.clear()
self.canvas.draw()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Compact Matplotlib Toolbar")
plotter = PlotterWidget()
self.setCentralWidget(plotter)
self.resize(700, 500)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
Run this example and you'll see a much more proportional toolbar that blends in with the rest of your interface. The buttons, canvas, and toolbar all feel like they belong together, without the toolbar dominating the layout.
From here you can fine-tune the icon size, height, and stylesheet values to match your application's design. And if you want to go all-in on a modern look, the overlay approach shown earlier gives you a MATLAB-style floating toolbar right on top of your plot.
Create GUI Applications with Python & Qt6 by Martin Fitzpatrick
(PySide6 Edition) The hands-on guide to making apps with Python — Over 15,000 copies sold!