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