##// END OF EJS Templates
Linting
Ian Thomas -
Show More
@@ -1,498 +1,499 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Pylab (matplotlib) support utilities."""
2 """Pylab (matplotlib) support utilities."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 from io import BytesIO
7 from io import BytesIO
8 from binascii import b2a_base64
8 from binascii import b2a_base64
9 from functools import partial
9 from functools import partial
10 import warnings
10 import warnings
11
11
12 from IPython.core.display import _pngxy
12 from IPython.core.display import _pngxy
13 from IPython.utils.decorators import flag_calls
13 from IPython.utils.decorators import flag_calls
14
14
15
15
16 # Matplotlib backend resolution functionality moved from IPython to Matplotlib
16 # Matplotlib backend resolution functionality moved from IPython to Matplotlib
17 # in IPython 8.24 and Matplotlib 3.9.1. Need to keep `backends` and `backend2gui`
17 # in IPython 8.24 and Matplotlib 3.9.1. Need to keep `backends` and `backend2gui`
18 # here for earlier Matplotlib and for external backend libraries such as
18 # here for earlier Matplotlib and for external backend libraries such as
19 # mplcairo that might rely upon it.
19 # mplcairo that might rely upon it.
20 _deprecated_backends = {
20 _deprecated_backends = {
21 "tk": "TkAgg",
21 "tk": "TkAgg",
22 "gtk": "GTKAgg",
22 "gtk": "GTKAgg",
23 "gtk3": "GTK3Agg",
23 "gtk3": "GTK3Agg",
24 "gtk4": "GTK4Agg",
24 "gtk4": "GTK4Agg",
25 "wx": "WXAgg",
25 "wx": "WXAgg",
26 "qt4": "Qt4Agg",
26 "qt4": "Qt4Agg",
27 "qt5": "Qt5Agg",
27 "qt5": "Qt5Agg",
28 "qt6": "QtAgg",
28 "qt6": "QtAgg",
29 "qt": "QtAgg",
29 "qt": "QtAgg",
30 "osx": "MacOSX",
30 "osx": "MacOSX",
31 "nbagg": "nbAgg",
31 "nbagg": "nbAgg",
32 "webagg": "WebAgg",
32 "webagg": "WebAgg",
33 "notebook": "nbAgg",
33 "notebook": "nbAgg",
34 "agg": "agg",
34 "agg": "agg",
35 "svg": "svg",
35 "svg": "svg",
36 "pdf": "pdf",
36 "pdf": "pdf",
37 "ps": "ps",
37 "ps": "ps",
38 "inline": "module://matplotlib_inline.backend_inline",
38 "inline": "module://matplotlib_inline.backend_inline",
39 "ipympl": "module://ipympl.backend_nbagg",
39 "ipympl": "module://ipympl.backend_nbagg",
40 "widget": "module://ipympl.backend_nbagg",
40 "widget": "module://ipympl.backend_nbagg",
41 }
41 }
42
42
43 # We also need a reverse backends2guis mapping that will properly choose which
43 # We also need a reverse backends2guis mapping that will properly choose which
44 # GUI support to activate based on the desired matplotlib backend. For the
44 # GUI support to activate based on the desired matplotlib backend. For the
45 # most part it's just a reverse of the above dict, but we also need to add a
45 # most part it's just a reverse of the above dict, but we also need to add a
46 # few others that map to the same GUI manually:
46 # few others that map to the same GUI manually:
47 _deprecated_backend2gui = dict(
47 _deprecated_backend2gui = dict(
48 zip(_deprecated_backends.values(), _deprecated_backends.keys())
48 zip(_deprecated_backends.values(), _deprecated_backends.keys())
49 )
49 )
50 # In the reverse mapping, there are a few extra valid matplotlib backends that
50 # In the reverse mapping, there are a few extra valid matplotlib backends that
51 # map to the same GUI support
51 # map to the same GUI support
52 _deprecated_backend2gui["GTK"] = _deprecated_backend2gui["GTKCairo"] = "gtk"
52 _deprecated_backend2gui["GTK"] = _deprecated_backend2gui["GTKCairo"] = "gtk"
53 _deprecated_backend2gui["GTK3Cairo"] = "gtk3"
53 _deprecated_backend2gui["GTK3Cairo"] = "gtk3"
54 _deprecated_backend2gui["GTK4Cairo"] = "gtk4"
54 _deprecated_backend2gui["GTK4Cairo"] = "gtk4"
55 _deprecated_backend2gui["WX"] = "wx"
55 _deprecated_backend2gui["WX"] = "wx"
56 _deprecated_backend2gui["CocoaAgg"] = "osx"
56 _deprecated_backend2gui["CocoaAgg"] = "osx"
57 # There needs to be a hysteresis here as the new QtAgg Matplotlib backend
57 # There needs to be a hysteresis here as the new QtAgg Matplotlib backend
58 # supports either Qt5 or Qt6 and the IPython qt event loop support Qt4, Qt5,
58 # supports either Qt5 or Qt6 and the IPython qt event loop support Qt4, Qt5,
59 # and Qt6.
59 # and Qt6.
60 _deprecated_backend2gui["QtAgg"] = "qt"
60 _deprecated_backend2gui["QtAgg"] = "qt"
61 _deprecated_backend2gui["Qt4Agg"] = "qt4"
61 _deprecated_backend2gui["Qt4Agg"] = "qt4"
62 _deprecated_backend2gui["Qt5Agg"] = "qt5"
62 _deprecated_backend2gui["Qt5Agg"] = "qt5"
63
63
64 # And some backends that don't need GUI integration
64 # And some backends that don't need GUI integration
65 del _deprecated_backend2gui["nbAgg"]
65 del _deprecated_backend2gui["nbAgg"]
66 del _deprecated_backend2gui["agg"]
66 del _deprecated_backend2gui["agg"]
67 del _deprecated_backend2gui["svg"]
67 del _deprecated_backend2gui["svg"]
68 del _deprecated_backend2gui["pdf"]
68 del _deprecated_backend2gui["pdf"]
69 del _deprecated_backend2gui["ps"]
69 del _deprecated_backend2gui["ps"]
70 del _deprecated_backend2gui["module://matplotlib_inline.backend_inline"]
70 del _deprecated_backend2gui["module://matplotlib_inline.backend_inline"]
71 del _deprecated_backend2gui["module://ipympl.backend_nbagg"]
71 del _deprecated_backend2gui["module://ipympl.backend_nbagg"]
72
72
73
73
74 # Deprecated attributes backends and backend2gui mostly following PEP 562.
74 # Deprecated attributes backends and backend2gui mostly following PEP 562.
75 def __getattr__(name):
75 def __getattr__(name):
76 if name in ("backends", "backend2gui"):
76 if name in ("backends", "backend2gui"):
77 warnings.warn(
77 warnings.warn(
78 f"{name} is deprecated since IPython 8.24, backends are managed "
78 f"{name} is deprecated since IPython 8.24, backends are managed "
79 "in matplotlib and can be externally registered.",
79 "in matplotlib and can be externally registered.",
80 DeprecationWarning)
80 DeprecationWarning,
81 )
81 return globals()[f"_deprecated_{name}"]
82 return globals()[f"_deprecated_{name}"]
82 raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
83 raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
83
84
84
85
85 #-----------------------------------------------------------------------------
86 #-----------------------------------------------------------------------------
86 # Matplotlib utilities
87 # Matplotlib utilities
87 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
88
89
89
90
90 def getfigs(*fig_nums):
91 def getfigs(*fig_nums):
91 """Get a list of matplotlib figures by figure numbers.
92 """Get a list of matplotlib figures by figure numbers.
92
93
93 If no arguments are given, all available figures are returned. If the
94 If no arguments are given, all available figures are returned. If the
94 argument list contains references to invalid figures, a warning is printed
95 argument list contains references to invalid figures, a warning is printed
95 but the function continues pasting further figures.
96 but the function continues pasting further figures.
96
97
97 Parameters
98 Parameters
98 ----------
99 ----------
99 figs : tuple
100 figs : tuple
100 A tuple of ints giving the figure numbers of the figures to return.
101 A tuple of ints giving the figure numbers of the figures to return.
101 """
102 """
102 from matplotlib._pylab_helpers import Gcf
103 from matplotlib._pylab_helpers import Gcf
103 if not fig_nums:
104 if not fig_nums:
104 fig_managers = Gcf.get_all_fig_managers()
105 fig_managers = Gcf.get_all_fig_managers()
105 return [fm.canvas.figure for fm in fig_managers]
106 return [fm.canvas.figure for fm in fig_managers]
106 else:
107 else:
107 figs = []
108 figs = []
108 for num in fig_nums:
109 for num in fig_nums:
109 f = Gcf.figs.get(num)
110 f = Gcf.figs.get(num)
110 if f is None:
111 if f is None:
111 print('Warning: figure %s not available.' % num)
112 print('Warning: figure %s not available.' % num)
112 else:
113 else:
113 figs.append(f.canvas.figure)
114 figs.append(f.canvas.figure)
114 return figs
115 return figs
115
116
116
117
117 def figsize(sizex, sizey):
118 def figsize(sizex, sizey):
118 """Set the default figure size to be [sizex, sizey].
119 """Set the default figure size to be [sizex, sizey].
119
120
120 This is just an easy to remember, convenience wrapper that sets::
121 This is just an easy to remember, convenience wrapper that sets::
121
122
122 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
123 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
123 """
124 """
124 import matplotlib
125 import matplotlib
125 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
126 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
126
127
127
128
128 def print_figure(fig, fmt="png", bbox_inches="tight", base64=False, **kwargs):
129 def print_figure(fig, fmt="png", bbox_inches="tight", base64=False, **kwargs):
129 """Print a figure to an image, and return the resulting file data
130 """Print a figure to an image, and return the resulting file data
130
131
131 Returned data will be bytes unless ``fmt='svg'``,
132 Returned data will be bytes unless ``fmt='svg'``,
132 in which case it will be unicode.
133 in which case it will be unicode.
133
134
134 Any keyword args are passed to fig.canvas.print_figure,
135 Any keyword args are passed to fig.canvas.print_figure,
135 such as ``quality`` or ``bbox_inches``.
136 such as ``quality`` or ``bbox_inches``.
136
137
137 If `base64` is True, return base64-encoded str instead of raw bytes
138 If `base64` is True, return base64-encoded str instead of raw bytes
138 for binary-encoded image formats
139 for binary-encoded image formats
139
140
140 .. versionadded:: 7.29
141 .. versionadded:: 7.29
141 base64 argument
142 base64 argument
142 """
143 """
143 # When there's an empty figure, we shouldn't return anything, otherwise we
144 # When there's an empty figure, we shouldn't return anything, otherwise we
144 # get big blank areas in the qt console.
145 # get big blank areas in the qt console.
145 if not fig.axes and not fig.lines:
146 if not fig.axes and not fig.lines:
146 return
147 return
147
148
148 dpi = fig.dpi
149 dpi = fig.dpi
149 if fmt == 'retina':
150 if fmt == 'retina':
150 dpi = dpi * 2
151 dpi = dpi * 2
151 fmt = 'png'
152 fmt = 'png'
152
153
153 # build keyword args
154 # build keyword args
154 kw = {
155 kw = {
155 "format":fmt,
156 "format":fmt,
156 "facecolor":fig.get_facecolor(),
157 "facecolor":fig.get_facecolor(),
157 "edgecolor":fig.get_edgecolor(),
158 "edgecolor":fig.get_edgecolor(),
158 "dpi":dpi,
159 "dpi":dpi,
159 "bbox_inches":bbox_inches,
160 "bbox_inches":bbox_inches,
160 }
161 }
161 # **kwargs get higher priority
162 # **kwargs get higher priority
162 kw.update(kwargs)
163 kw.update(kwargs)
163
164
164 bytes_io = BytesIO()
165 bytes_io = BytesIO()
165 if fig.canvas is None:
166 if fig.canvas is None:
166 from matplotlib.backend_bases import FigureCanvasBase
167 from matplotlib.backend_bases import FigureCanvasBase
167 FigureCanvasBase(fig)
168 FigureCanvasBase(fig)
168
169
169 fig.canvas.print_figure(bytes_io, **kw)
170 fig.canvas.print_figure(bytes_io, **kw)
170 data = bytes_io.getvalue()
171 data = bytes_io.getvalue()
171 if fmt == 'svg':
172 if fmt == 'svg':
172 data = data.decode('utf-8')
173 data = data.decode('utf-8')
173 elif base64:
174 elif base64:
174 data = b2a_base64(data, newline=False).decode("ascii")
175 data = b2a_base64(data, newline=False).decode("ascii")
175 return data
176 return data
176
177
177 def retina_figure(fig, base64=False, **kwargs):
178 def retina_figure(fig, base64=False, **kwargs):
178 """format a figure as a pixel-doubled (retina) PNG
179 """format a figure as a pixel-doubled (retina) PNG
179
180
180 If `base64` is True, return base64-encoded str instead of raw bytes
181 If `base64` is True, return base64-encoded str instead of raw bytes
181 for binary-encoded image formats
182 for binary-encoded image formats
182
183
183 .. versionadded:: 7.29
184 .. versionadded:: 7.29
184 base64 argument
185 base64 argument
185 """
186 """
186 pngdata = print_figure(fig, fmt="retina", base64=False, **kwargs)
187 pngdata = print_figure(fig, fmt="retina", base64=False, **kwargs)
187 # Make sure that retina_figure acts just like print_figure and returns
188 # Make sure that retina_figure acts just like print_figure and returns
188 # None when the figure is empty.
189 # None when the figure is empty.
189 if pngdata is None:
190 if pngdata is None:
190 return
191 return
191 w, h = _pngxy(pngdata)
192 w, h = _pngxy(pngdata)
192 metadata = {"width": w//2, "height":h//2}
193 metadata = {"width": w//2, "height":h//2}
193 if base64:
194 if base64:
194 pngdata = b2a_base64(pngdata, newline=False).decode("ascii")
195 pngdata = b2a_base64(pngdata, newline=False).decode("ascii")
195 return pngdata, metadata
196 return pngdata, metadata
196
197
197
198
198 # We need a little factory function here to create the closure where
199 # We need a little factory function here to create the closure where
199 # safe_execfile can live.
200 # safe_execfile can live.
200 def mpl_runner(safe_execfile):
201 def mpl_runner(safe_execfile):
201 """Factory to return a matplotlib-enabled runner for %run.
202 """Factory to return a matplotlib-enabled runner for %run.
202
203
203 Parameters
204 Parameters
204 ----------
205 ----------
205 safe_execfile : function
206 safe_execfile : function
206 This must be a function with the same interface as the
207 This must be a function with the same interface as the
207 :meth:`safe_execfile` method of IPython.
208 :meth:`safe_execfile` method of IPython.
208
209
209 Returns
210 Returns
210 -------
211 -------
211 A function suitable for use as the ``runner`` argument of the %run magic
212 A function suitable for use as the ``runner`` argument of the %run magic
212 function.
213 function.
213 """
214 """
214
215
215 def mpl_execfile(fname,*where,**kw):
216 def mpl_execfile(fname,*where,**kw):
216 """matplotlib-aware wrapper around safe_execfile.
217 """matplotlib-aware wrapper around safe_execfile.
217
218
218 Its interface is identical to that of the :func:`execfile` builtin.
219 Its interface is identical to that of the :func:`execfile` builtin.
219
220
220 This is ultimately a call to execfile(), but wrapped in safeties to
221 This is ultimately a call to execfile(), but wrapped in safeties to
221 properly handle interactive rendering."""
222 properly handle interactive rendering."""
222
223
223 import matplotlib
224 import matplotlib
224 import matplotlib.pyplot as plt
225 import matplotlib.pyplot as plt
225
226
226 #print '*** Matplotlib runner ***' # dbg
227 #print '*** Matplotlib runner ***' # dbg
227 # turn off rendering until end of script
228 # turn off rendering until end of script
228 with matplotlib.rc_context({"interactive": False}):
229 with matplotlib.rc_context({"interactive": False}):
229 safe_execfile(fname, *where, **kw)
230 safe_execfile(fname, *where, **kw)
230
231
231 if matplotlib.is_interactive():
232 if matplotlib.is_interactive():
232 plt.show()
233 plt.show()
233
234
234 # make rendering call now, if the user tried to do it
235 # make rendering call now, if the user tried to do it
235 if plt.draw_if_interactive.called:
236 if plt.draw_if_interactive.called:
236 plt.draw()
237 plt.draw()
237 plt.draw_if_interactive.called = False
238 plt.draw_if_interactive.called = False
238
239
239 # re-draw everything that is stale
240 # re-draw everything that is stale
240 try:
241 try:
241 da = plt.draw_all
242 da = plt.draw_all
242 except AttributeError:
243 except AttributeError:
243 pass
244 pass
244 else:
245 else:
245 da()
246 da()
246
247
247 return mpl_execfile
248 return mpl_execfile
248
249
249
250
250 def _reshow_nbagg_figure(fig):
251 def _reshow_nbagg_figure(fig):
251 """reshow an nbagg figure"""
252 """reshow an nbagg figure"""
252 try:
253 try:
253 reshow = fig.canvas.manager.reshow
254 reshow = fig.canvas.manager.reshow
254 except AttributeError as e:
255 except AttributeError as e:
255 raise NotImplementedError() from e
256 raise NotImplementedError() from e
256 else:
257 else:
257 reshow()
258 reshow()
258
259
259
260
260 def select_figure_formats(shell, formats, **kwargs):
261 def select_figure_formats(shell, formats, **kwargs):
261 """Select figure formats for the inline backend.
262 """Select figure formats for the inline backend.
262
263
263 Parameters
264 Parameters
264 ----------
265 ----------
265 shell : InteractiveShell
266 shell : InteractiveShell
266 The main IPython instance.
267 The main IPython instance.
267 formats : str or set
268 formats : str or set
268 One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
269 One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
269 **kwargs : any
270 **kwargs : any
270 Extra keyword arguments to be passed to fig.canvas.print_figure.
271 Extra keyword arguments to be passed to fig.canvas.print_figure.
271 """
272 """
272 import matplotlib
273 import matplotlib
273 from matplotlib.figure import Figure
274 from matplotlib.figure import Figure
274
275
275 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
276 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
276 png_formatter = shell.display_formatter.formatters['image/png']
277 png_formatter = shell.display_formatter.formatters['image/png']
277 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
278 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
278 pdf_formatter = shell.display_formatter.formatters['application/pdf']
279 pdf_formatter = shell.display_formatter.formatters['application/pdf']
279
280
280 if isinstance(formats, str):
281 if isinstance(formats, str):
281 formats = {formats}
282 formats = {formats}
282 # cast in case of list / tuple
283 # cast in case of list / tuple
283 formats = set(formats)
284 formats = set(formats)
284
285
285 [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]
286 [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]
286 mplbackend = matplotlib.get_backend().lower()
287 mplbackend = matplotlib.get_backend().lower()
287 if mplbackend in ("nbagg", "ipympl", "widget", "module://ipympl.backend_nbagg"):
288 if mplbackend in ("nbagg", "ipympl", "widget", "module://ipympl.backend_nbagg"):
288 formatter = shell.display_formatter.ipython_display_formatter
289 formatter = shell.display_formatter.ipython_display_formatter
289 formatter.for_type(Figure, _reshow_nbagg_figure)
290 formatter.for_type(Figure, _reshow_nbagg_figure)
290
291
291 supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'}
292 supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'}
292 bad = formats.difference(supported)
293 bad = formats.difference(supported)
293 if bad:
294 if bad:
294 bs = "%s" % ','.join([repr(f) for f in bad])
295 bs = "%s" % ','.join([repr(f) for f in bad])
295 gs = "%s" % ','.join([repr(f) for f in supported])
296 gs = "%s" % ','.join([repr(f) for f in supported])
296 raise ValueError("supported formats are: %s not %s" % (gs, bs))
297 raise ValueError("supported formats are: %s not %s" % (gs, bs))
297
298
298 if "png" in formats:
299 if "png" in formats:
299 png_formatter.for_type(
300 png_formatter.for_type(
300 Figure, partial(print_figure, fmt="png", base64=True, **kwargs)
301 Figure, partial(print_figure, fmt="png", base64=True, **kwargs)
301 )
302 )
302 if "retina" in formats or "png2x" in formats:
303 if "retina" in formats or "png2x" in formats:
303 png_formatter.for_type(Figure, partial(retina_figure, base64=True, **kwargs))
304 png_formatter.for_type(Figure, partial(retina_figure, base64=True, **kwargs))
304 if "jpg" in formats or "jpeg" in formats:
305 if "jpg" in formats or "jpeg" in formats:
305 jpg_formatter.for_type(
306 jpg_formatter.for_type(
306 Figure, partial(print_figure, fmt="jpg", base64=True, **kwargs)
307 Figure, partial(print_figure, fmt="jpg", base64=True, **kwargs)
307 )
308 )
308 if "svg" in formats:
309 if "svg" in formats:
309 svg_formatter.for_type(Figure, partial(print_figure, fmt="svg", **kwargs))
310 svg_formatter.for_type(Figure, partial(print_figure, fmt="svg", **kwargs))
310 if "pdf" in formats:
311 if "pdf" in formats:
311 pdf_formatter.for_type(
312 pdf_formatter.for_type(
312 Figure, partial(print_figure, fmt="pdf", base64=True, **kwargs)
313 Figure, partial(print_figure, fmt="pdf", base64=True, **kwargs)
313 )
314 )
314
315
315 #-----------------------------------------------------------------------------
316 #-----------------------------------------------------------------------------
316 # Code for initializing matplotlib and importing pylab
317 # Code for initializing matplotlib and importing pylab
317 #-----------------------------------------------------------------------------
318 #-----------------------------------------------------------------------------
318
319
319
320
320 def find_gui_and_backend(gui=None, gui_select=None):
321 def find_gui_and_backend(gui=None, gui_select=None):
321 """Given a gui string return the gui and mpl backend.
322 """Given a gui string return the gui and mpl backend.
322
323
323 Parameters
324 Parameters
324 ----------
325 ----------
325 gui : str
326 gui : str
326 Can be one of ('tk','gtk','wx','qt','qt4','inline','agg').
327 Can be one of ('tk','gtk','wx','qt','qt4','inline','agg').
327 gui_select : str
328 gui_select : str
328 Can be one of ('tk','gtk','wx','qt','qt4','inline').
329 Can be one of ('tk','gtk','wx','qt','qt4','inline').
329 This is any gui already selected by the shell.
330 This is any gui already selected by the shell.
330
331
331 Returns
332 Returns
332 -------
333 -------
333 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
334 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
334 'WXAgg','Qt4Agg','module://matplotlib_inline.backend_inline','agg').
335 'WXAgg','Qt4Agg','module://matplotlib_inline.backend_inline','agg').
335 """
336 """
336
337
337 import matplotlib
338 import matplotlib
338
339
339 if _matplotlib_manages_backends():
340 if _matplotlib_manages_backends():
340 backend_registry = matplotlib.backends.registry.backend_registry
341 backend_registry = matplotlib.backends.registry.backend_registry
341
342
342 # gui argument may be a gui event loop or may be a backend name.
343 # gui argument may be a gui event loop or may be a backend name.
343 if gui in ("auto", None):
344 if gui in ("auto", None):
344 backend = matplotlib.rcParamsOrig["backend"]
345 backend = matplotlib.rcParamsOrig["backend"]
345 backend, gui = backend_registry.resolve_backend(backend)
346 backend, gui = backend_registry.resolve_backend(backend)
346 else:
347 else:
347 backend, gui = backend_registry.resolve_gui_or_backend(gui)
348 backend, gui = backend_registry.resolve_gui_or_backend(gui)
348
349
349 return gui, backend
350 return gui, backend
350
351
351 # Fallback to previous behaviour (Matplotlib < 3.9)
352 # Fallback to previous behaviour (Matplotlib < 3.9)
352 mpl_version_info = getattr(matplotlib, "__version_info__", (0, 0))
353 mpl_version_info = getattr(matplotlib, "__version_info__", (0, 0))
353 has_unified_qt_backend = mpl_version_info >= (3, 5)
354 has_unified_qt_backend = mpl_version_info >= (3, 5)
354
355
355 from IPython.core.pylabtools import backends
356 from IPython.core.pylabtools import backends
356
357
357 backends_ = dict(backends)
358 backends_ = dict(backends)
358 if not has_unified_qt_backend:
359 if not has_unified_qt_backend:
359 backends_["qt"] = "qt5agg"
360 backends_["qt"] = "qt5agg"
360
361
361 if gui and gui != 'auto':
362 if gui and gui != 'auto':
362 # select backend based on requested gui
363 # select backend based on requested gui
363 backend = backends_[gui]
364 backend = backends_[gui]
364 if gui == 'agg':
365 if gui == 'agg':
365 gui = None
366 gui = None
366 else:
367 else:
367 # We need to read the backend from the original data structure, *not*
368 # We need to read the backend from the original data structure, *not*
368 # from mpl.rcParams, since a prior invocation of %matplotlib may have
369 # from mpl.rcParams, since a prior invocation of %matplotlib may have
369 # overwritten that.
370 # overwritten that.
370 # WARNING: this assumes matplotlib 1.1 or newer!!
371 # WARNING: this assumes matplotlib 1.1 or newer!!
371 backend = matplotlib.rcParamsOrig['backend']
372 backend = matplotlib.rcParamsOrig['backend']
372 # In this case, we need to find what the appropriate gui selection call
373 # In this case, we need to find what the appropriate gui selection call
373 # should be for IPython, so we can activate inputhook accordingly
374 # should be for IPython, so we can activate inputhook accordingly
374 from IPython.core.pylabtools import backend2gui
375 from IPython.core.pylabtools import backend2gui
375 gui = backend2gui.get(backend, None)
376 gui = backend2gui.get(backend, None)
376
377
377 # If we have already had a gui active, we need it and inline are the
378 # If we have already had a gui active, we need it and inline are the
378 # ones allowed.
379 # ones allowed.
379 if gui_select and gui != gui_select:
380 if gui_select and gui != gui_select:
380 gui = gui_select
381 gui = gui_select
381 backend = backends_[gui]
382 backend = backends_[gui]
382
383
383 # Matplotlib before _matplotlib_manages_backends() can return "inline" for
384 # Matplotlib before _matplotlib_manages_backends() can return "inline" for
384 # no gui event loop rather than the None that IPython >= 8.24.0 expects.
385 # no gui event loop rather than the None that IPython >= 8.24.0 expects.
385 if gui == "inline":
386 if gui == "inline":
386 gui = None
387 gui = None
387
388
388 return gui, backend
389 return gui, backend
389
390
390
391
391 def activate_matplotlib(backend):
392 def activate_matplotlib(backend):
392 """Activate the given backend and set interactive to True."""
393 """Activate the given backend and set interactive to True."""
393
394
394 import matplotlib
395 import matplotlib
395 matplotlib.interactive(True)
396 matplotlib.interactive(True)
396
397
397 # Matplotlib had a bug where even switch_backend could not force
398 # Matplotlib had a bug where even switch_backend could not force
398 # the rcParam to update. This needs to be set *before* the module
399 # the rcParam to update. This needs to be set *before* the module
399 # magic of switch_backend().
400 # magic of switch_backend().
400 matplotlib.rcParams['backend'] = backend
401 matplotlib.rcParams['backend'] = backend
401
402
402 # Due to circular imports, pyplot may be only partially initialised
403 # Due to circular imports, pyplot may be only partially initialised
403 # when this function runs.
404 # when this function runs.
404 # So avoid needing matplotlib attribute-lookup to access pyplot.
405 # So avoid needing matplotlib attribute-lookup to access pyplot.
405 from matplotlib import pyplot as plt
406 from matplotlib import pyplot as plt
406
407
407 plt.switch_backend(backend)
408 plt.switch_backend(backend)
408
409
409 plt.show._needmain = False
410 plt.show._needmain = False
410 # We need to detect at runtime whether show() is called by the user.
411 # We need to detect at runtime whether show() is called by the user.
411 # For this, we wrap it into a decorator which adds a 'called' flag.
412 # For this, we wrap it into a decorator which adds a 'called' flag.
412 plt.draw_if_interactive = flag_calls(plt.draw_if_interactive)
413 plt.draw_if_interactive = flag_calls(plt.draw_if_interactive)
413
414
414
415
415 def import_pylab(user_ns, import_all=True):
416 def import_pylab(user_ns, import_all=True):
416 """Populate the namespace with pylab-related values.
417 """Populate the namespace with pylab-related values.
417
418
418 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
419 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
419
420
420 Also imports a few names from IPython (figsize, display, getfigs)
421 Also imports a few names from IPython (figsize, display, getfigs)
421
422
422 """
423 """
423
424
424 # Import numpy as np/pyplot as plt are conventions we're trying to
425 # Import numpy as np/pyplot as plt are conventions we're trying to
425 # somewhat standardize on. Making them available to users by default
426 # somewhat standardize on. Making them available to users by default
426 # will greatly help this.
427 # will greatly help this.
427 s = ("import numpy\n"
428 s = ("import numpy\n"
428 "import matplotlib\n"
429 "import matplotlib\n"
429 "from matplotlib import pylab, mlab, pyplot\n"
430 "from matplotlib import pylab, mlab, pyplot\n"
430 "np = numpy\n"
431 "np = numpy\n"
431 "plt = pyplot\n"
432 "plt = pyplot\n"
432 )
433 )
433 exec(s, user_ns)
434 exec(s, user_ns)
434
435
435 if import_all:
436 if import_all:
436 s = ("from matplotlib.pylab import *\n"
437 s = ("from matplotlib.pylab import *\n"
437 "from numpy import *\n")
438 "from numpy import *\n")
438 exec(s, user_ns)
439 exec(s, user_ns)
439
440
440 # IPython symbols to add
441 # IPython symbols to add
441 user_ns['figsize'] = figsize
442 user_ns['figsize'] = figsize
442 from IPython.display import display
443 from IPython.display import display
443 # Add display and getfigs to the user's namespace
444 # Add display and getfigs to the user's namespace
444 user_ns['display'] = display
445 user_ns['display'] = display
445 user_ns['getfigs'] = getfigs
446 user_ns['getfigs'] = getfigs
446
447
447
448
448 def configure_inline_support(shell, backend):
449 def configure_inline_support(shell, backend):
449 """
450 """
450 .. deprecated:: 7.23
451 .. deprecated:: 7.23
451
452
452 use `matplotlib_inline.backend_inline.configure_inline_support()`
453 use `matplotlib_inline.backend_inline.configure_inline_support()`
453
454
454 Configure an IPython shell object for matplotlib use.
455 Configure an IPython shell object for matplotlib use.
455
456
456 Parameters
457 Parameters
457 ----------
458 ----------
458 shell : InteractiveShell instance
459 shell : InteractiveShell instance
459 backend : matplotlib backend
460 backend : matplotlib backend
460 """
461 """
461 warnings.warn(
462 warnings.warn(
462 "`configure_inline_support` is deprecated since IPython 7.23, directly "
463 "`configure_inline_support` is deprecated since IPython 7.23, directly "
463 "use `matplotlib_inline.backend_inline.configure_inline_support()`",
464 "use `matplotlib_inline.backend_inline.configure_inline_support()`",
464 DeprecationWarning,
465 DeprecationWarning,
465 stacklevel=2,
466 stacklevel=2,
466 )
467 )
467
468
468 from matplotlib_inline.backend_inline import (
469 from matplotlib_inline.backend_inline import (
469 configure_inline_support as configure_inline_support_orig,
470 configure_inline_support as configure_inline_support_orig,
470 )
471 )
471
472
472 configure_inline_support_orig(shell, backend)
473 configure_inline_support_orig(shell, backend)
473
474
474
475
475 # Determine if Matplotlib manages backends only if needed, and cache result.
476 # Determine if Matplotlib manages backends only if needed, and cache result.
476 # Do not read this directly, instead use _matplotlib_manages_backends().
477 # Do not read this directly, instead use _matplotlib_manages_backends().
477 _matplotlib_manages_backends_value: bool | None = None
478 _matplotlib_manages_backends_value: bool | None = None
478
479
479
480
480 def _matplotlib_manages_backends() -> bool:
481 def _matplotlib_manages_backends() -> bool:
481 """Return True if Matplotlib manages backends, False otherwise.
482 """Return True if Matplotlib manages backends, False otherwise.
482
483
483 If it returns True, the caller can be sure that
484 If it returns True, the caller can be sure that
484 matplotlib.backends.registry.backend_registry is available along with
485 matplotlib.backends.registry.backend_registry is available along with
485 member functions resolve_gui_or_backend, resolve_backend and list_all.
486 member functions resolve_gui_or_backend, resolve_backend and list_all.
486 """
487 """
487 global _matplotlib_manages_backends_value
488 global _matplotlib_manages_backends_value
488 if _matplotlib_manages_backends_value is None:
489 if _matplotlib_manages_backends_value is None:
489 try:
490 try:
490 from matplotlib.backends.registry import backend_registry
491 from matplotlib.backends.registry import backend_registry
491
492
492 _matplotlib_manages_backends_value = hasattr(
493 _matplotlib_manages_backends_value = hasattr(
493 backend_registry, "resolve_gui_or_backend"
494 backend_registry, "resolve_gui_or_backend"
494 )
495 )
495 except ImportError:
496 except ImportError:
496 _matplotlib_manages_backends_value = False
497 _matplotlib_manages_backends_value = False
497
498
498 return _matplotlib_manages_backends_value
499 return _matplotlib_manages_backends_value
General Comments 0
You need to be logged in to leave comments. Login now