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