##// END OF EJS Templates
Merge pull request #9287 from tacaswell/fix_nbagg_lookup...
Min RK -
r22144:6619a4cb merge
parent child Browse files
Show More
@@ -1,395 +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 176 def _reshow_nbagg_figure(fig):
177 177 """reshow an nbagg figure"""
178 178 try:
179 179 reshow = fig.canvas.manager.reshow
180 180 except AttributeError:
181 181 raise NotImplementedError()
182 182 else:
183 183 reshow()
184 184
185 185
186 186 def select_figure_formats(shell, formats, **kwargs):
187 187 """Select figure formats for the inline backend.
188 188
189 189 Parameters
190 190 ==========
191 191 shell : InteractiveShell
192 192 The main IPython instance.
193 193 formats : str or set
194 194 One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
195 195 **kwargs : any
196 196 Extra keyword arguments to be passed to fig.canvas.print_figure.
197 197 """
198 198 import matplotlib
199 199 from matplotlib.figure import Figure
200 200 from ipykernel.pylab import backend_inline
201 201
202 202 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
203 203 png_formatter = shell.display_formatter.formatters['image/png']
204 204 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
205 205 pdf_formatter = shell.display_formatter.formatters['application/pdf']
206 206
207 207 if isinstance(formats, py3compat.string_types):
208 208 formats = {formats}
209 209 # cast in case of list / tuple
210 210 formats = set(formats)
211 211
212 212 [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]
213 213
214 if matplotlib.backends.backend.lower() == 'nbagg':
214 if matplotlib.get_backend().lower() == 'nbagg':
215 215 formatter = shell.display_formatter.ipython_display_formatter
216 216 formatter.for_type(Figure, _reshow_nbagg_figure)
217 217
218 218 supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'}
219 219 bad = formats.difference(supported)
220 220 if bad:
221 221 bs = "%s" % ','.join([repr(f) for f in bad])
222 222 gs = "%s" % ','.join([repr(f) for f in supported])
223 223 raise ValueError("supported formats are: %s not %s" % (gs, bs))
224 224
225 225 if 'png' in formats:
226 226 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
227 227 if 'retina' in formats or 'png2x' in formats:
228 228 png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))
229 229 if 'jpg' in formats or 'jpeg' in formats:
230 230 jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', **kwargs))
231 231 if 'svg' in formats:
232 232 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg', **kwargs))
233 233 if 'pdf' in formats:
234 234 pdf_formatter.for_type(Figure, lambda fig: print_figure(fig, 'pdf', **kwargs))
235 235
236 236 #-----------------------------------------------------------------------------
237 237 # Code for initializing matplotlib and importing pylab
238 238 #-----------------------------------------------------------------------------
239 239
240 240
241 241 def find_gui_and_backend(gui=None, gui_select=None):
242 242 """Given a gui string return the gui and mpl backend.
243 243
244 244 Parameters
245 245 ----------
246 246 gui : str
247 247 Can be one of ('tk','gtk','wx','qt','qt4','inline').
248 248 gui_select : str
249 249 Can be one of ('tk','gtk','wx','qt','qt4','inline').
250 250 This is any gui already selected by the shell.
251 251
252 252 Returns
253 253 -------
254 254 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
255 255 'WXAgg','Qt4Agg','module://ipykernel.pylab.backend_inline').
256 256 """
257 257
258 258 import matplotlib
259 259
260 260 if gui and gui != 'auto':
261 261 # select backend based on requested gui
262 262 backend = backends[gui]
263 263 else:
264 264 # We need to read the backend from the original data structure, *not*
265 265 # from mpl.rcParams, since a prior invocation of %matplotlib may have
266 266 # overwritten that.
267 267 # WARNING: this assumes matplotlib 1.1 or newer!!
268 268 backend = matplotlib.rcParamsOrig['backend']
269 269 # In this case, we need to find what the appropriate gui selection call
270 270 # should be for IPython, so we can activate inputhook accordingly
271 271 gui = backend2gui.get(backend, None)
272 272
273 273 # If we have already had a gui active, we need it and inline are the
274 274 # ones allowed.
275 275 if gui_select and gui != gui_select:
276 276 gui = gui_select
277 277 backend = backends[gui]
278 278
279 279 return gui, backend
280 280
281 281
282 282 def activate_matplotlib(backend):
283 283 """Activate the given backend and set interactive to True."""
284 284
285 285 import matplotlib
286 286 matplotlib.interactive(True)
287 287
288 288 # Matplotlib had a bug where even switch_backend could not force
289 289 # the rcParam to update. This needs to be set *before* the module
290 290 # magic of switch_backend().
291 291 matplotlib.rcParams['backend'] = backend
292 292
293 293 import matplotlib.pyplot
294 294 matplotlib.pyplot.switch_backend(backend)
295 295
296 296 # This must be imported last in the matplotlib series, after
297 297 # backend/interactivity choices have been made
298 298 import matplotlib.pylab as pylab
299 299
300 300 pylab.show._needmain = False
301 301 # We need to detect at runtime whether show() is called by the user.
302 302 # For this, we wrap it into a decorator which adds a 'called' flag.
303 303 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
304 304
305 305
306 306 def import_pylab(user_ns, import_all=True):
307 307 """Populate the namespace with pylab-related values.
308 308
309 309 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
310 310
311 311 Also imports a few names from IPython (figsize, display, getfigs)
312 312
313 313 """
314 314
315 315 # Import numpy as np/pyplot as plt are conventions we're trying to
316 316 # somewhat standardize on. Making them available to users by default
317 317 # will greatly help this.
318 318 s = ("import numpy\n"
319 319 "import matplotlib\n"
320 320 "from matplotlib import pylab, mlab, pyplot\n"
321 321 "np = numpy\n"
322 322 "plt = pyplot\n"
323 323 )
324 324 exec(s, user_ns)
325 325
326 326 if import_all:
327 327 s = ("from matplotlib.pylab import *\n"
328 328 "from numpy import *\n")
329 329 exec(s, user_ns)
330 330
331 331 # IPython symbols to add
332 332 user_ns['figsize'] = figsize
333 333 from IPython.core.display import display
334 334 # Add display and getfigs to the user's namespace
335 335 user_ns['display'] = display
336 336 user_ns['getfigs'] = getfigs
337 337
338 338
339 339 def configure_inline_support(shell, backend):
340 340 """Configure an IPython shell object for matplotlib use.
341 341
342 342 Parameters
343 343 ----------
344 344 shell : InteractiveShell instance
345 345
346 346 backend : matplotlib backend
347 347 """
348 348 # If using our svg payload backend, register the post-execution
349 349 # function that will pick up the results for display. This can only be
350 350 # done with access to the real shell object.
351 351
352 352 # Note: if we can't load the inline backend, then there's no point
353 353 # continuing (such as in terminal-only shells in environments without
354 354 # zeromq available).
355 355 try:
356 356 from ipykernel.pylab.backend_inline import InlineBackend
357 357 except ImportError:
358 358 return
359 359 from matplotlib import pyplot
360 360
361 361 cfg = InlineBackend.instance(parent=shell)
362 362 cfg.shell = shell
363 363 if cfg not in shell.configurables:
364 364 shell.configurables.append(cfg)
365 365
366 366 if backend == backends['inline']:
367 367 from ipykernel.pylab.backend_inline import flush_figures
368 368 shell.events.register('post_execute', flush_figures)
369 369
370 370 # Save rcParams that will be overwrittern
371 371 shell._saved_rcParams = dict()
372 372 for k in cfg.rc:
373 373 shell._saved_rcParams[k] = pyplot.rcParams[k]
374 374 # load inline_rc
375 375 pyplot.rcParams.update(cfg.rc)
376 376 new_backend_name = "inline"
377 377 else:
378 378 from ipykernel.pylab.backend_inline import flush_figures
379 379 try:
380 380 shell.events.unregister('post_execute', flush_figures)
381 381 except ValueError:
382 382 pass
383 383 if hasattr(shell, '_saved_rcParams'):
384 384 pyplot.rcParams.update(shell._saved_rcParams)
385 385 del shell._saved_rcParams
386 386 new_backend_name = "other"
387 387
388 388 # only enable the formats once -> don't change the enabled formats (which the user may
389 389 # has changed) when getting another "%matplotlib inline" call.
390 390 # See https://github.com/ipython/ipykernel/issues/29
391 391 cur_backend = getattr(configure_inline_support, "current_backend", "unset")
392 392 if new_backend_name != cur_backend:
393 393 # Setup the default figure format
394 394 select_figure_formats(shell, cfg.figure_formats, **cfg.print_figure_kwargs)
395 395 configure_inline_support.current_backend = new_backend_name
General Comments 0
You need to be logged in to leave comments. Login now