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