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