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