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