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