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