2026, Jan 11 23:00

How to Embed seaborn.objects in a PyQt6 GUI: Bind Plot.on to a Matplotlib Figure or Axes for FigureCanvasQTAgg

Learn to embed Seaborn's objects API in a PyQt6 GUI: use Plot.on to target a Matplotlib Figure/Axes and display it via FigureCanvasQTAgg. Step-by-step fix.

Embedding Seaborn’s new objects API into a PyQt6 GUI often stumbles on one detail: FigureCanvasQTAgg accepts only a Matplotlib Figure, while a Plotter object from seaborn.objects doesn’t directly expose a Figure. If you previously routed everything through sns.barplot(ax=...), switching to so.Plot(...).add(so.Bar(...)) can feel like a dead end—until you tell the plot where to draw.

Minimal illustration of the roadblock

The plotting code builds a Plotter with Bar, labels, and data columns, but it doesn’t target any Axes or Figure. Nothing connects it to the Qt canvas you plan to add to a QWidget.

import seaborn.objects as so
chart_obj = (
    so.Plot(data_df, x='XXX', y='XXX')
      .add(so.Bar(color='lightblue'))
      .label(title='XXX', x='XXX', y='XXX')
)

Previously, a Matplotlib-first approach worked because you explicitly passed Axes to an axes-level function and then handed the parent Figure to the Qt canvas:

from matplotlib.figure import Figure
import seaborn as sns
mpl_fig = Figure()
ax_main = mpl_fig.add_subplot(111)
...
sns.barplot(..., ax=ax_main)
...
self.main_layout.addWidget(FigureCanvasQTAgg(mpl_fig))

What’s really happening

The objects API separates the idea of building a plot specification from deciding where it renders. Unlike the older axes-level call style, Bar doesn’t take ax. Instead, the target container is selected at the Plot level. This is exactly what Plot.on provides: you attach your Plot to an existing Axes or Figure. That’s the missing link between Seaborn’s objects API and a Matplotlib Figure owned by your Qt canvas.

The documentation for Plot explicitly offers Plot.on(ax) and Plot.on(fig). In other words, the Bar mark isn’t supposed to receive an Axes; the Plot is.

The fix: bind the Plot to Axes or Figure

Create your Figure and Axes as before, then use Plot.on to direct the rendering. Once the plot draws onto that Figure, pass the Figure to FigureCanvasQTAgg and add it to your layout.

from matplotlib.figure import Figure
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
import seaborn.objects as so
mpl_fig = Figure()
ax_main = mpl_fig.add_subplot(111)
chart_obj = (
    so.Plot(data_df, x='XXX', y='XXX')
      .on(ax_main)  # or .on(mpl_fig)
      .add(so.Bar(color='lightblue'))
      .label(title='XXX', x='XXX', y='XXX')
)
self.main_layout.addWidget(FigureCanvasQTAgg(mpl_fig))

If you prefer to attach the target at the end, that also works:

chart_obj = (
    so.Plot(data_df, x='XXX', y='XXX')
      .add(so.Bar(color='lightblue'))
      .label(title='XXX', x='XXX', y='XXX')
      .on(mpl_fig)  # or .on(ax_main)
)

Why this detail matters

In a GUI pipeline, your Qt widget owns a Matplotlib Figure via FigureCanvasQTAgg. The only way the Seaborn plot becomes visible in that widget is if it renders into the exact Figure you hand to the canvas. Plot.on is the contract point that aligns the Seaborn objects API with Matplotlib’s Figure/Axes, which in turn aligns with the PyQt6 canvas.

This also aligns with the broader distinction in Seaborn between figure-level and axes-level usage. Bar doesn’t accept ax, and that’s intentional. The Plot itself controls the target through .on(ax) or .on(fig).

Wrap-up and practical advice

When using seaborn.objects with PyQt6, always anchor the Plot to a Matplotlib container before embedding. Build the Figure and Axes as usual, call Plot.on(ax) or Plot.on(fig), then pass that same Figure to FigureCanvasQTAgg. If behavior differs across functions, consult the relevant Seaborn documentation to determine whether the target should be set at the Plot level and whether ax is expected. And if integration feels unclear, reduce it to a minimal working snippet that constructs a Figure, binds the Plot via .on, and embeds the canvas—this keeps the GUI and plotting layers cleanly connected.