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