Skip to content

Commit ac93ecf

Browse files
committed
Tell IPython the correct GUI event loop to use for all backends.
IPython currently uses a hard-coded table (IPython.core.pylabtools.backend2gui) to know which event loop to use for which backend. This approach fails for both the new builtin cairo-based backends (which do not appear in the table), and for third-party backends (e.g. mplcairo). mplcairo has used some custom code to patch that table for a while; reuse it to more generally use the new "required_interactive_framework" attribute. Note that this PR suggests that there should be a better way to go back from the canvas class to the backend module (rather than looking for `sys.modules[cls.__module__].required_interactive_framework`, which is a bit hacky and could in theory fail if the canvas class is actually defined in another submodule).
1 parent 921d4f4 commit ac93ecf

File tree

1 file changed

+32
-0
lines changed

1 file changed

+32
-0
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"""
3434

3535
from contextlib import contextmanager
36+
import functools
3637
import importlib
3738
import io
3839
import os
@@ -43,6 +44,7 @@
4344

4445
import numpy as np
4546

47+
import matplotlib as mpl
4648
from matplotlib import (
4749
backend_tools as tools, cbook, colors, textpath, tight_bbox, transforms,
4850
widgets, get_backend, is_interactive, rcParams)
@@ -1613,6 +1615,7 @@ class FigureCanvasBase(object):
16131615
'Tagged Image File Format')
16141616

16151617
def __init__(self, figure):
1618+
self._fix_ipython_backend2gui()
16161619
self._is_idle_drawing = True
16171620
self._is_saving = False
16181621
figure.set_canvas(self)
@@ -1629,6 +1632,35 @@ def __init__(self, figure):
16291632
self.toolbar = None # NavigationToolbar2 will set me
16301633
self._is_idle_drawing = False
16311634

1635+
@classmethod
1636+
@functools.lru_cache()
1637+
def _fix_ipython_backend2gui(cls):
1638+
# Fix hard-coded module -> toolkit mapping in IPython (used for
1639+
# `ipython --auto`). This cannot be done at import time due to
1640+
# ordering issues, so we do it when creating a canvas, and should only
1641+
# be done once per class (hence the `lru_cache(1)`).
1642+
if "IPython" not in sys.modules:
1643+
return
1644+
import IPython
1645+
ip = IPython.get_ipython()
1646+
if not ip:
1647+
return
1648+
from IPython.core import pylabtools as pt
1649+
backend_mod = sys.modules[cls.__module__]
1650+
rif = getattr(backend_mod, "required_interactive_framework", None)
1651+
backend2gui_rif = {"qt5": "qt", "qt4": "qt", "gtk3": "gtk3",
1652+
"wx": "wx", "macosx": "osx"}.get(rif)
1653+
if backend2gui_rif:
1654+
pt.backend2gui[get_backend()] = backend2gui_rif
1655+
# Work around pylabtools.find_gui_and_backend always reading from
1656+
# rcParamsOrig.
1657+
orig_origbackend = mpl.rcParamsOrig["backend"]
1658+
try:
1659+
mpl.rcParamsOrig["backend"] = mpl.rcParams["backend"]
1660+
ip.enable_matplotlib()
1661+
finally:
1662+
mpl.rcParamsOrig["backend"] = orig_origbackend
1663+
16321664
@contextmanager
16331665
def _idle_draw_cntx(self):
16341666
self._is_idle_drawing = True

0 commit comments

Comments
 (0)