##// END OF EJS Templates
fix linting missed while darker was broken
Paul Ivanov -
Show More
@@ -1,388 +1,390 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Pylab (matplotlib) support utilities."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 from io import BytesIO
8 8 import warnings
9 9
10 10 from IPython.core.display import _pngxy
11 11 from IPython.utils.decorators import flag_calls
12 12
13 13 # If user specifies a GUI, that dictates the backend, otherwise we read the
14 14 # user's mpl default from the mpl rc structure
15 15 backends = {
16 16 "tk": "TkAgg",
17 17 "gtk": "GTKAgg",
18 18 "gtk3": "GTK3Agg",
19 19 "wx": "WXAgg",
20 20 "qt4": "Qt4Agg",
21 21 "qt5": "Qt5Agg",
22 22 "qt": "Qt5Agg",
23 23 "osx": "MacOSX",
24 24 "nbagg": "nbAgg",
25 25 "notebook": "nbAgg",
26 26 "agg": "agg",
27 27 "svg": "svg",
28 28 "pdf": "pdf",
29 29 "ps": "ps",
30 30 "inline": "module://matplotlib_inline.backend_inline",
31 31 "ipympl": "module://ipympl.backend_nbagg",
32 32 "widget": "module://ipympl.backend_nbagg",
33 33 }
34 34
35 35 # We also need a reverse backends2guis mapping that will properly choose which
36 36 # GUI support to activate based on the desired matplotlib backend. For the
37 37 # most part it's just a reverse of the above dict, but we also need to add a
38 38 # few others that map to the same GUI manually:
39 39 backend2gui = dict(zip(backends.values(), backends.keys()))
40 40 # Our tests expect backend2gui to just return 'qt'
41 41 backend2gui['Qt4Agg'] = 'qt'
42 42 # In the reverse mapping, there are a few extra valid matplotlib backends that
43 43 # map to the same GUI support
44 44 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
45 45 backend2gui['GTK3Cairo'] = 'gtk3'
46 46 backend2gui['WX'] = 'wx'
47 47 backend2gui['CocoaAgg'] = 'osx'
48 48 # And some backends that don't need GUI integration
49 49 del backend2gui["nbAgg"]
50 50 del backend2gui["agg"]
51 51 del backend2gui["svg"]
52 52 del backend2gui["pdf"]
53 53 del backend2gui["ps"]
54 54 del backend2gui["module://matplotlib_inline.backend_inline"]
55 55
56 56 #-----------------------------------------------------------------------------
57 57 # Matplotlib utilities
58 58 #-----------------------------------------------------------------------------
59 59
60 60
61 61 def getfigs(*fig_nums):
62 62 """Get a list of matplotlib figures by figure numbers.
63 63
64 64 If no arguments are given, all available figures are returned. If the
65 65 argument list contains references to invalid figures, a warning is printed
66 66 but the function continues pasting further figures.
67 67
68 68 Parameters
69 69 ----------
70 70 figs : tuple
71 71 A tuple of ints giving the figure numbers of the figures to return.
72 72 """
73 73 from matplotlib._pylab_helpers import Gcf
74 74 if not fig_nums:
75 75 fig_managers = Gcf.get_all_fig_managers()
76 76 return [fm.canvas.figure for fm in fig_managers]
77 77 else:
78 78 figs = []
79 79 for num in fig_nums:
80 80 f = Gcf.figs.get(num)
81 81 if f is None:
82 82 print('Warning: figure %s not available.' % num)
83 83 else:
84 84 figs.append(f.canvas.figure)
85 85 return figs
86 86
87 87
88 88 def figsize(sizex, sizey):
89 89 """Set the default figure size to be [sizex, sizey].
90 90
91 91 This is just an easy to remember, convenience wrapper that sets::
92 92
93 93 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
94 94 """
95 95 import matplotlib
96 96 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
97 97
98 98
99 99 def print_figure(fig, fmt='png', bbox_inches='tight', **kwargs):
100 100 """Print a figure to an image, and return the resulting file data
101 101
102 102 Returned data will be bytes unless ``fmt='svg'``,
103 103 in which case it will be unicode.
104 104
105 105 Any keyword args are passed to fig.canvas.print_figure,
106 106 such as ``quality`` or ``bbox_inches``.
107 107 """
108 108 # When there's an empty figure, we shouldn't return anything, otherwise we
109 109 # get big blank areas in the qt console.
110 110 if not fig.axes and not fig.lines:
111 111 return
112 112
113 113 dpi = fig.dpi
114 114 if fmt == 'retina':
115 115 dpi = dpi * 2
116 116 fmt = 'png'
117 117
118 118 # build keyword args
119 119 kw = {
120 120 "format":fmt,
121 121 "facecolor":fig.get_facecolor(),
122 122 "edgecolor":fig.get_edgecolor(),
123 123 "dpi":dpi,
124 124 "bbox_inches":bbox_inches,
125 125 }
126 126 # **kwargs get higher priority
127 127 kw.update(kwargs)
128 128
129 129 bytes_io = BytesIO()
130 130 if fig.canvas is None:
131 131 from matplotlib.backend_bases import FigureCanvasBase
132 132 FigureCanvasBase(fig)
133 133
134 134 fig.canvas.print_figure(bytes_io, **kw)
135 135 data = bytes_io.getvalue()
136 136 if fmt == 'svg':
137 137 data = data.decode('utf-8')
138 138 return data
139 139
140 140 def retina_figure(fig, **kwargs):
141 141 """format a figure as a pixel-doubled (retina) PNG"""
142 142 pngdata = print_figure(fig, fmt='retina', **kwargs)
143 143 # Make sure that retina_figure acts just like print_figure and returns
144 144 # None when the figure is empty.
145 145 if pngdata is None:
146 146 return
147 147 w, h = _pngxy(pngdata)
148 148 metadata = {"width": w//2, "height":h//2}
149 149 return pngdata, metadata
150 150
151 151 # We need a little factory function here to create the closure where
152 152 # safe_execfile can live.
153 153 def mpl_runner(safe_execfile):
154 154 """Factory to return a matplotlib-enabled runner for %run.
155 155
156 156 Parameters
157 157 ----------
158 158 safe_execfile : function
159 159 This must be a function with the same interface as the
160 160 :meth:`safe_execfile` method of IPython.
161 161
162 162 Returns
163 163 -------
164 164 A function suitable for use as the ``runner`` argument of the %run magic
165 165 function.
166 166 """
167 167
168 168 def mpl_execfile(fname,*where,**kw):
169 169 """matplotlib-aware wrapper around safe_execfile.
170 170
171 171 Its interface is identical to that of the :func:`execfile` builtin.
172 172
173 173 This is ultimately a call to execfile(), but wrapped in safeties to
174 174 properly handle interactive rendering."""
175 175
176 176 import matplotlib
177 177 import matplotlib.pyplot as plt
178 178
179 179 #print '*** Matplotlib runner ***' # dbg
180 180 # turn off rendering until end of script
181 181 is_interactive = matplotlib.rcParams['interactive']
182 182 matplotlib.interactive(False)
183 183 safe_execfile(fname,*where,**kw)
184 184 matplotlib.interactive(is_interactive)
185 185 # make rendering call now, if the user tried to do it
186 186 if plt.draw_if_interactive.called:
187 187 plt.draw()
188 188 plt.draw_if_interactive.called = False
189 189
190 190 # re-draw everything that is stale
191 191 try:
192 192 da = plt.draw_all
193 193 except AttributeError:
194 194 pass
195 195 else:
196 196 da()
197 197
198 198 return mpl_execfile
199 199
200 200
201 201 def _reshow_nbagg_figure(fig):
202 202 """reshow an nbagg figure"""
203 203 try:
204 204 reshow = fig.canvas.manager.reshow
205 205 except AttributeError as e:
206 206 raise NotImplementedError() from e
207 207 else:
208 208 reshow()
209 209
210 210
211 211 def select_figure_formats(shell, formats, **kwargs):
212 212 """Select figure formats for the inline backend.
213 213
214 214 Parameters
215 215 ==========
216 216 shell : InteractiveShell
217 217 The main IPython instance.
218 218 formats : str or set
219 219 One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
220 220 **kwargs : any
221 221 Extra keyword arguments to be passed to fig.canvas.print_figure.
222 222 """
223 223 import matplotlib
224 224 from matplotlib.figure import Figure
225 225
226 226 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
227 227 png_formatter = shell.display_formatter.formatters['image/png']
228 228 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
229 229 pdf_formatter = shell.display_formatter.formatters['application/pdf']
230 230
231 231 if isinstance(formats, str):
232 232 formats = {formats}
233 233 # cast in case of list / tuple
234 234 formats = set(formats)
235 235
236 236 [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]
237 237 mplbackend = matplotlib.get_backend().lower()
238 238 if mplbackend == 'nbagg' or mplbackend == 'module://ipympl.backend_nbagg':
239 239 formatter = shell.display_formatter.ipython_display_formatter
240 240 formatter.for_type(Figure, _reshow_nbagg_figure)
241 241
242 242 supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'}
243 243 bad = formats.difference(supported)
244 244 if bad:
245 245 bs = "%s" % ','.join([repr(f) for f in bad])
246 246 gs = "%s" % ','.join([repr(f) for f in supported])
247 247 raise ValueError("supported formats are: %s not %s" % (gs, bs))
248 248
249 249 if 'png' in formats:
250 250 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
251 251 if 'retina' in formats or 'png2x' in formats:
252 252 png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))
253 253 if 'jpg' in formats or 'jpeg' in formats:
254 254 jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', **kwargs))
255 255 if 'svg' in formats:
256 256 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg', **kwargs))
257 257 if 'pdf' in formats:
258 258 pdf_formatter.for_type(Figure, lambda fig: print_figure(fig, 'pdf', **kwargs))
259 259
260 260 #-----------------------------------------------------------------------------
261 261 # Code for initializing matplotlib and importing pylab
262 262 #-----------------------------------------------------------------------------
263 263
264 264
265 265 def find_gui_and_backend(gui=None, gui_select=None):
266 266 """Given a gui string return the gui and mpl backend.
267 267
268 268 Parameters
269 269 ----------
270 270 gui : str
271 271 Can be one of ('tk','gtk','wx','qt','qt4','inline','agg').
272 272 gui_select : str
273 273 Can be one of ('tk','gtk','wx','qt','qt4','inline').
274 274 This is any gui already selected by the shell.
275 275
276 276 Returns
277 277 -------
278 278 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
279 279 'WXAgg','Qt4Agg','module://matplotlib_inline.backend_inline','agg').
280 280 """
281 281
282 282 import matplotlib
283 283
284 284 if gui and gui != 'auto':
285 285 # select backend based on requested gui
286 286 backend = backends[gui]
287 287 if gui == 'agg':
288 288 gui = None
289 289 else:
290 290 # We need to read the backend from the original data structure, *not*
291 291 # from mpl.rcParams, since a prior invocation of %matplotlib may have
292 292 # overwritten that.
293 293 # WARNING: this assumes matplotlib 1.1 or newer!!
294 294 backend = matplotlib.rcParamsOrig['backend']
295 295 # In this case, we need to find what the appropriate gui selection call
296 296 # should be for IPython, so we can activate inputhook accordingly
297 297 gui = backend2gui.get(backend, None)
298 298
299 299 # If we have already had a gui active, we need it and inline are the
300 300 # ones allowed.
301 301 if gui_select and gui != gui_select:
302 302 gui = gui_select
303 303 backend = backends[gui]
304 304
305 305 return gui, backend
306 306
307 307
308 308 def activate_matplotlib(backend):
309 309 """Activate the given backend and set interactive to True."""
310 310
311 311 import matplotlib
312 312 matplotlib.interactive(True)
313 313
314 314 # Matplotlib had a bug where even switch_backend could not force
315 315 # the rcParam to update. This needs to be set *before* the module
316 316 # magic of switch_backend().
317 317 matplotlib.rcParams['backend'] = backend
318 318
319 319 # Due to circular imports, pyplot may be only partially initialised
320 320 # when this function runs.
321 321 # So avoid needing matplotlib attribute-lookup to access pyplot.
322 322 from matplotlib import pyplot as plt
323 323
324 324 plt.switch_backend(backend)
325 325
326 326 plt.show._needmain = False
327 327 # We need to detect at runtime whether show() is called by the user.
328 328 # For this, we wrap it into a decorator which adds a 'called' flag.
329 329 plt.draw_if_interactive = flag_calls(plt.draw_if_interactive)
330 330
331 331
332 332 def import_pylab(user_ns, import_all=True):
333 333 """Populate the namespace with pylab-related values.
334 334
335 335 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
336 336
337 337 Also imports a few names from IPython (figsize, display, getfigs)
338 338
339 339 """
340 340
341 341 # Import numpy as np/pyplot as plt are conventions we're trying to
342 342 # somewhat standardize on. Making them available to users by default
343 343 # will greatly help this.
344 344 s = ("import numpy\n"
345 345 "import matplotlib\n"
346 346 "from matplotlib import pylab, mlab, pyplot\n"
347 347 "np = numpy\n"
348 348 "plt = pyplot\n"
349 349 )
350 350 exec(s, user_ns)
351 351
352 352 if import_all:
353 353 s = ("from matplotlib.pylab import *\n"
354 354 "from numpy import *\n")
355 355 exec(s, user_ns)
356 356
357 357 # IPython symbols to add
358 358 user_ns['figsize'] = figsize
359 359 from IPython.display import display
360 360 # Add display and getfigs to the user's namespace
361 361 user_ns['display'] = display
362 362 user_ns['getfigs'] = getfigs
363 363
364 364
365 365 def configure_inline_support(shell, backend):
366 366 """
367 367 .. deprecated: 7.23
368 368
369 369 use `matplotlib_inline.backend_inline.configure_inline_support()`
370 370
371 371 Configure an IPython shell object for matplotlib use.
372 372
373 373 Parameters
374 374 ----------
375 375 shell : InteractiveShell instance
376 376
377 377 backend : matplotlib backend
378 378 """
379 379 warnings.warn(
380 380 "`configure_inline_support` is deprecated since IPython 7.23, directly "
381 381 "use `matplotlib_inline.backend_inline.configure_inline_support()`",
382 382 DeprecationWarning,
383 383 stacklevel=2,
384 384 )
385 385
386 from matplotlib_inline.backend_inline import configure_inline_support as configure_inline_support_orig
386 from matplotlib_inline.backend_inline import (
387 configure_inline_support as configure_inline_support_orig,
388 )
387 389
388 390 configure_inline_support_orig(shell, backend)
General Comments 0
You need to be logged in to leave comments. Login now