Source code for mpl_gui._manage_backend

import importlib
import sys
import logging
import types

from matplotlib import cbook, rcsetup
from matplotlib import rcParams, rcParamsDefault
import matplotlib.backend_bases


_backend_mod = None

_log = logging.getLogger(__name__)


def current_backend_module():
    """
    Get the currently active backend module, selecting one if needed.

    Returns
    -------
    matplotlib.backend_bases._Backend
    """
    if _backend_mod is None:
        select_gui_toolkit()
    return _backend_mod


[docs]def select_gui_toolkit(newbackend=None): """ Select the GUI toolkit to use. The argument is case-insensitive. Switching between GUI toolkits is possible only if no event loop for another interactive backend has started. Switching to and from non-interactive backends is always possible. Parameters ---------- newbackend : Union[str, _Backend] The name of the backend to use or a _Backend class to use. Returns ------- _Backend The backend selected. """ global _backend_mod # work-around the sentinel resolution in Matplotlib 😱 if newbackend is None: newbackend = dict.__getitem__(rcParams, "backend") if newbackend is rcsetup._auto_backend_sentinel: current_framework = cbook._get_running_interactive_framework() mapping = { "qt": "qtagg", "gtk3": "gtk3agg", "gtk4": "gtk4agg", "wx": "wxagg", "tk": "tkagg", "macosx": "macosx", "headless": "agg", } best_guess = mapping.get(current_framework, None) if best_guess is not None: candidates = [best_guess] else: candidates = [] candidates += ["macosx", "qt5agg", "gtk3agg", "tkagg", "wxagg"] # Don't try to fallback on the cairo-based backends as they each have # an additional dependency (pycairo) over the agg-based backend, and # are of worse quality. for candidate in candidates: try: return select_gui_toolkit(candidate) except ImportError: continue else: # Switching to Agg should always succeed; if it doesn't, let the # exception propagate out. return select_gui_toolkit("agg") if isinstance(newbackend, str): # Backends are implemented as modules, but "inherit" default method # implementations from backend_bases._Backend. This is achieved by # creating a "class" that inherits from backend_bases._Backend and whose # body is filled with the module's globals. if newbackend.lower() == "tkagg": backend_name = f"mpl_gui._patched_backends.{newbackend.lower()}" else: backend_name = cbook._backend_module_name(newbackend) mod = importlib.import_module(backend_name) if hasattr(mod, "Backend"): orig_class = mod.Backend else: class orig_class(matplotlib.backend_bases._Backend): locals().update(vars(mod)) @classmethod def mainloop(cls): return mod.Show().mainloop() class BackendClass(orig_class): @classmethod def show_managers(cls, *, managers, block): if not managers: return for manager in managers: manager.show() # Emits a warning for non-interactive backend manager.canvas.draw_idle() if cls.mainloop is None: return if block: try: cls.FigureManager._active_managers = managers cls.mainloop() finally: cls.FigureManager._active_managers = None if not hasattr(BackendClass.FigureManager, "_active_managers"): BackendClass.FigureManager._active_managers = None rc_params_string = newbackend else: BackendClass = newbackend mod_name = f"_backend_mod_{id(BackendClass)}" rc_params_string = f"module://{mod_name}" mod = types.ModuleType(mod_name) mod.Backend = BackendClass sys.modules[mod_name] = mod required_framework = getattr( BackendClass.FigureCanvas, "required_interactive_framework", None ) if required_framework is not None: current_framework = cbook._get_running_interactive_framework() if ( current_framework and required_framework and current_framework != required_framework ): raise ImportError( "Cannot load backend {!r} which requires the {!r} interactive " "framework, as {!r} is currently running".format( newbackend, required_framework, current_framework ) ) _log.debug( "Loaded backend %s version %s.", newbackend, BackendClass.backend_version ) rcParams["backend"] = rcParamsDefault["backend"] = rc_params_string # is IPython imported? mod_ipython = sys.modules.get("IPython") if mod_ipython: # if so are we in an IPython session ip = mod_ipython.get_ipython() if ip: # macosx -> osx mapping for the osx backend in ipython if required_framework == "macosx": required_framework = "osx" ip.enable_gui(required_framework) # remember to set the global variable _backend_mod = BackendClass return BackendClass