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