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