Fixed, I think. We just need to patch a dlopen call.
Now TkAgg works out of the box, without requiring changes to user Python code. I hope that also means we can change the default backend from Agg to TkAgg.
Submitted this PR (and got lucky number 1000!)
with the fix and change of backend.
Cheers
Jake
Continuing:
In `lib/matplotlib/cbook.py`, the function
`_get_running_interactive_framework` has:
```text
if not _c_internal_utils.display_is_valid():
return "headless"
```
In `src/_c_internal_utils.c` we have this docstring for `display_is_valid`:
```text
"Check whether the current X11 or Wayland display is valid.\n\n"
"On Linux, returns True if either $DISPLAY is set and XOpenDisplay(NULL)\n"
"succeeds, or $WAYLAND_DISPLAY is set and wl_display_connect(NULL)\n"
"succeeds.\n\n"
"On other platforms, always returns True."},
```
$DISPLAY is set, so it must be XOpenDisplay(NULL) failing...
I found a post [1] from a Nix user:
> For anyone else facing similar issues - I resolved this by adding the X11 library to the LD_LIBRARY_PATH of my development shell.
But I haven't made any more progress.
Cheers
Jake
[1] https://discourse.nixos.org/t/python-matplotlib-tkinter-matplotlib-reverts-to-agg-backend-because-it-cant-find-a-valid-display/45064
On Thu, Jul 3, 2025 at 1:19 PM Jake <jforst.mailman@gmail.com> wrote:
>
> Hi Lily
>
> It looks like this bug isn't going away anytime soon.
>
> Here is the offending code in `lib/matplotlib/pyplot.py`, with an
> added print statement:
>
> ```text
> # If rcParams['backend_fallback'] is true, and an interactive backend is
> # requested, ignore rcParams['backend'] and force selection of a backend that
> # is compatible with the current running interactive framework.
> if (rcParams["backend_fallback"]
> and rcParams._get_backend_or_none() in ( # type: ignore
> set(rcsetup.interactive_bk) - {'WebAgg', 'nbAgg'})
> and cbook._get_running_interactive_framework()): # type: ignore
> print(f"running interactive framework is:
> {cbook._get_running_interactive_framework()}")
> rcParams._set("backend", rcsetup._auto_backend_sentinel) # type: ignore
> ```
>
> Running this
>
> ```text
> MPLBACKEND=tkagg guix shell
> --with-source=python-matplotlib=$HOME/matplotlib
> --without-tests=python-matplotlib python python-matplotlib -- python3
> -c "import matplotlib; print(matplotlib.get_backend()); import
> matplotlib.pyplot as plt; print(matplotlib.get_backend())"
> ```
>
> gives
>
> ```text
> TkAgg
> running interactive framework is: headless
> agg
> ```
>
> So I guess the next question is: why does it think I'm headless? To
> be continued...
>
> Thanks
> Jake
>
>
> On Sat, Apr 5, 2025 at 11:00 PM Liliana Marie Prikler
> <liliana.prikler@gmail.com> wrote:
> >
> > Hi Jake,
> >
> > Am Donnerstag, dem 03.04.2025 um 11:36 +0000 schrieb Jake:
> > > It appears that importing matplotlib.pyplot resets the Matplotlib
> > > backend to Agg.
> > >
> > > $ guix shell python python-matplotlib -- bash -c 'MPLBACKEND=tkagg
> > > python3 -c "import matplotlib; print(matplotlib.get_backend());
> > > import matplotlib.pyplot; print(matplotlib.get_backend())"'
> > >
> > > TkAgg
> > > agg
> > I recently encountered the same issue. The issue is that matplotlib
> > internally sources a configuration file that sets the backend *after*
> > reading the environment variable. To circumvent this, you use
> > something along the lines of the following code until the issue is
> > fixed:
> >
> > from matplotlib import set_backend
> > from os import environ
> >
> > […]
> >
> > if __name__ == '__main__':
> > if 'MPLBACKEND' in environ: set_backend(environ['MPLBACKEND'])
> > […]
> >
> > Cheers