2026, Jan 09 05:00

How to make Matplotlib windows appear in a Python 3.10 uv virtualenv on Ubuntu 24.04

Fix Matplotlib on Ubuntu 24.04 with Python 3.10 (uv): install PyQt6, python3.10-tk, libxcb-cursor0; use QtAgg/TkAgg, avoid Qt6Agg. Includes a tested setup.

Getting a Matplotlib window to appear from a Python 3.10 virtual environment on Ubuntu 24.04 can unexpectedly fail, especially when the environment is created with uv. The symptoms range from a non-interactive backend warning to Qt platform plugin errors and missing tkinter. Below is a concise walkthrough of what happens and how to get to a working setup without guesswork.

Reproducing the issue

The minimal plotting script is straightforward. Yet on a fresh Python 3.10 environment created with uv, calling show() may not display anything interactively and instead warn that the canvas is non-interactive.

import numpy as np
import matplotlib.pyplot as plot
xs = np.linspace(0, 10, 100)
ys = np.sin(xs)
plot.plot(xs, ys, label='sin(x)', color='blue', linestyle='--')
plot.show()

One of the observed messages is:

... UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown

Attempts to switch to Qt5 can surface:

qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized.

And selecting TkAgg can fail due to missing tkinter:

ImportError: failed to load tkinter functions

Choosing "Qt6Agg" is not valid either:

ValueError: 'Qt6Agg' is not a valid value for backend; supported values are [..., 'qtagg', 'qt5agg', ..., 'tkagg', ...]

What’s actually going on

Matplotlib needs an interactive GUI backend to render windows. In this environment it defaulted to a non-interactive Agg canvas, hence nothing pops up. Switching to Qt5 failed because the Qt platform plugin "xcb" wasn’t usable. Switching to TkAgg failed because on Ubuntu tkinter is packaged separately from Python and wasn’t present. Finally, "Qt6Agg" isn’t a valid backend name; the correct option in this family is QtAgg, and support depends on installed Qt bindings.

Using PyQt6 works in this setup, while PyQt5 does not. Explicitly setting the backend isn’t required after installing PyQt6, although QtAgg or Qt5Agg will also work with PyQt6 installed. On Ubuntu, tkinter comes via the python3.10-tk package. On Ubuntu Server (without X11), tkinter may not be present at all. If you install the full system Python 3.10 via apt and then create a venv from it, tkinter is available when you install python3.10-tk.

Working setup on Ubuntu 24.04 with uv and Python 3.10

The following sequence has been verified to work. It uses uv to create the Python 3.10 environment, installs Matplotlib and PyQt6, and adds the missing system packages required by TkAgg and Qt on this Ubuntu release.

$ uv init test_py310 --python 3.10 
$ cd test_py310/
$ uv add numpy matplotlib pyqt6
$ sudo apt install python3.10-tk libxcb-cursor0
$ uv run main.py

There is no need to force the backend in code after installing PyQt6; Matplotlib can use the Qt-based backend automatically. If you want to switch backends at runtime to test combinations, you can pass the backend name as a command-line argument.

Fixed and instrumented example

The example below prints the interpreter path and allows passing a backend name, for instance uv run main.py qtagg or uv run main.py tkagg. It preserves the plotting logic while making it easy to validate which backend you’re actually using.

import sys
print('>>> Executable:', sys.executable)
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as chart
if len(sys.argv) > 1:
    print('>>> Using:', sys.argv[1])
    mpl.use(sys.argv[1])
x_vals = np.linspace(0, 10, 100)
y_vals = np.sin(x_vals)
chart.plot(x_vals, y_vals, label='sin(x)', color='blue', linestyle='--')
chart.show()

With PyQt6 installed, you can run it without specifying a backend. If you do pass one, use supported names such as QtAgg, Qt5Agg, or TkAgg. Avoid Qt6Agg, as it is not a valid backend string.

Alternative route if you depend on tkinter

On Ubuntu, tkinter ships as a separate package. If your workflow is built around TkAgg and you prefer a system-managed interpreter, install system Python 3.10 along with python3.10-tk and then create a venv from that interpreter.

$ sudo apt install python3.10-full
# or
$ sudo apt install python3.10 python3.10-tk
$ python3.10 -m venv .venv

This approach avoids the missing tkinter issue you may encounter with uv’s Python 3.10.

Why this matters

Interactive plotting hinges on the GUI backend and the OS-level libraries behind it. When a venv is created from a different distribution of Python, expected pieces like tkinter may not be present, and Qt-based backends can fail if their platform plugins aren’t found. Understanding which backend is active, which bindings are installed, and which system packages are needed saves time and avoids trial-and-error. It’s also worth noting that Python 3.10 is close to End of Life, which may reduce the likelihood of fixes for edge cases; use it only when a project strictly requires it.

Conclusion

If you need Matplotlib windows from a uv-managed Python 3.10 on Ubuntu 24.04, installing PyQt6 is the straightforward fix, and you typically don’t need to force a backend. When you do choose one explicitly, use QtAgg or Qt5Agg rather than an unsupported name like Qt6Agg. If you rely on TkAgg, add python3.10-tk from apt, and on this Ubuntu release installing libxcb-cursor0 can be necessary for Qt. When tkinter is a hard requirement across the stack, consider creating your venv from the system Python 3.10 after installing the appropriate apt packages. This keeps the plotting pipeline simple and predictable while meeting the constraints of a Python 3.10 codebase.