2626import re
2727import sys
2828import time
29+ try :
30+ import threading
31+ except ImportError :
32+ import dummy_threading as threading
2933
3034from cycler import cycler
3135import matplotlib
@@ -175,6 +179,11 @@ def findobj(o=None, match=None, include_self=True):
175179 return o .findobj (match , include_self = include_self )
176180
177181
182+ def _get_required_interactive_framework (backend_mod ):
183+ return getattr (
184+ backend_mod .FigureCanvas , "required_interactive_framework" , None )
185+
186+
178187def switch_backend (newbackend ):
179188 """
180189 Close all open figures and set the Matplotlib backend.
@@ -188,6 +197,8 @@ def switch_backend(newbackend):
188197 newbackend : str
189198 The name of the backend to use.
190199 """
200+ global _backend_mod
201+
191202 close ("all" )
192203
193204 if newbackend is rcsetup ._auto_backend_sentinel :
@@ -210,15 +221,17 @@ def switch_backend(newbackend):
210221 rcParamsOrig ["backend" ] = "agg"
211222 return
212223
224+ # Backends are implemented as modules, but "inherit" default method
225+ # implementations from backend_bases._Backend. This is achieved by
226+ # creating a "class" that inherits from backend_bases._Backend and whose
227+ # body is filled with the module's globals.
228+
213229 backend_name = cbook ._backend_module_name (newbackend )
214- backend_mod = importlib .import_module (backend_name )
215- Backend = type (
216- "Backend" , (matplotlib .backend_bases ._Backend ,), vars (backend_mod ))
217- _log .debug ("Loaded backend %s version %s." ,
218- newbackend , Backend .backend_version )
219230
220- required_framework = getattr (
221- Backend .FigureCanvas , "required_interactive_framework" , None )
231+ class backend_mod (matplotlib .backend_bases ._Backend ):
232+ locals ().update (vars (importlib .import_module (backend_name )))
233+
234+ required_framework = _get_required_interactive_framework (backend_mod )
222235 if required_framework is not None :
223236 current_framework = cbook ._get_running_interactive_framework ()
224237 if (current_framework and required_framework
@@ -228,23 +241,42 @@ def switch_backend(newbackend):
228241 "framework, as {!r} is currently running" .format (
229242 newbackend , required_framework , current_framework ))
230243
231- # Update both rcParams and rcDefaults so restoring the defaults later with
232- # rcdefaults won't change the backend. A bit of overkill as 'backend' is
233- # already in style.core.STYLE_BLACKLIST, but better to be safe.
234- rcParams ['backend' ] = rcParamsDefault ['backend' ] = newbackend
244+ _log .debug ("Loaded backend %s version %s." ,
245+ newbackend , backend_mod .backend_version )
235246
236- global _backend_mod , new_figure_manager , draw_if_interactive , _show
247+ rcParams [ 'backend' ] = rcParamsDefault [ 'backend' ] = newbackend
237248 _backend_mod = backend_mod
238- new_figure_manager = Backend . new_figure_manager
239- draw_if_interactive = Backend . draw_if_interactive
240- _show = Backend . show
249+ for func_name in [ " new_figure_manager" , "draw_if_interactive" , "show" ]:
250+ globals ()[ func_name ]. __signature__ = inspect . signature (
251+ getattr ( backend_mod , func_name ))
241252
242253 # Need to keep a global reference to the backend for compatibility reasons.
243254 # See https://github.com/matplotlib/matplotlib/issues/6092
244255 matplotlib .backends .backend = newbackend
245256
246257
247- def show (* args , ** kw ):
258+ def _warn_if_gui_out_of_main_thread ():
259+ if (_get_required_interactive_framework (_backend_mod )
260+ and threading .current_thread () is not threading .main_thread ()):
261+ cbook ._warn_external (
262+ "Starting a Matplotlib GUI outside of the main thread will likely "
263+ "fail." )
264+
265+
266+ # This function's signature is rewritten upon backend-load by switch_backend.
267+ def new_figure_manager (* args , ** kwargs ):
268+ """Create a new figure manager instance."""
269+ _warn_if_gui_out_of_main_thread ()
270+ return _backend_mod .new_figure_manager (* args , ** kwargs )
271+
272+
273+ # This function's signature is rewritten upon backend-load by switch_backend.
274+ def draw_if_interactive (* args , ** kwargs ):
275+ return _backend_mod .draw_if_interactive (* args , ** kwargs )
276+
277+
278+ # This function's signature is rewritten upon backend-load by switch_backend.
279+ def show (* args , ** kwargs ):
248280 """
249281 Display all figures.
250282
@@ -263,8 +295,8 @@ def show(*args, **kw):
263295 This is experimental, and may be set to ``True`` or ``False`` to
264296 override the blocking behavior described above.
265297 """
266- global _show
267- return _show (* args , ** kw )
298+ _warn_if_gui_out_of_main_thread ()
299+ return _backend_mod . show (* args , ** kwargs )
268300
269301
270302def isinteractive ():
0 commit comments