##// END OF EJS Templates
Adding support for multiple figure formats in InlineBackend.
Brian E. Granger -
Show More
@@ -1,346 +1,356 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Pylab (matplotlib) support utilities.
2 """Pylab (matplotlib) support utilities.
3
3
4 Authors
4 Authors
5 -------
5 -------
6
6
7 * Fernando Perez.
7 * Fernando Perez.
8 * Brian Granger
8 * Brian Granger
9 """
9 """
10 from __future__ import print_function
10 from __future__ import print_function
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2009 The IPython Development Team
13 # Copyright (C) 2009 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 import sys
23 import sys
24 from io import BytesIO
24 from io import BytesIO
25
25
26 from IPython.core.display import _pngxy
26 from IPython.core.display import _pngxy
27 from IPython.utils.decorators import flag_calls
27 from IPython.utils.decorators import flag_calls
28 from IPython.utils import py3compat
28
29
29 # If user specifies a GUI, that dictates the backend, otherwise we read the
30 # If user specifies a GUI, that dictates the backend, otherwise we read the
30 # user's mpl default from the mpl rc structure
31 # user's mpl default from the mpl rc structure
31 backends = {'tk': 'TkAgg',
32 backends = {'tk': 'TkAgg',
32 'gtk': 'GTKAgg',
33 'gtk': 'GTKAgg',
33 'gtk3': 'GTK3Agg',
34 'gtk3': 'GTK3Agg',
34 'wx': 'WXAgg',
35 'wx': 'WXAgg',
35 'qt': 'Qt4Agg', # qt3 not supported
36 'qt': 'Qt4Agg', # qt3 not supported
36 'qt4': 'Qt4Agg',
37 'qt4': 'Qt4Agg',
37 'osx': 'MacOSX',
38 'osx': 'MacOSX',
38 'inline' : 'module://IPython.kernel.zmq.pylab.backend_inline'}
39 'inline' : 'module://IPython.kernel.zmq.pylab.backend_inline'}
39
40
40 # We also need a reverse backends2guis mapping that will properly choose which
41 # We also need a reverse backends2guis mapping that will properly choose which
41 # GUI support to activate based on the desired matplotlib backend. For the
42 # GUI support to activate based on the desired matplotlib backend. For the
42 # most part it's just a reverse of the above dict, but we also need to add a
43 # most part it's just a reverse of the above dict, but we also need to add a
43 # few others that map to the same GUI manually:
44 # few others that map to the same GUI manually:
44 backend2gui = dict(zip(backends.values(), backends.keys()))
45 backend2gui = dict(zip(backends.values(), backends.keys()))
45 # Our tests expect backend2gui to just return 'qt'
46 # Our tests expect backend2gui to just return 'qt'
46 backend2gui['Qt4Agg'] = 'qt'
47 backend2gui['Qt4Agg'] = 'qt'
47 # In the reverse mapping, there are a few extra valid matplotlib backends that
48 # In the reverse mapping, there are a few extra valid matplotlib backends that
48 # map to the same GUI support
49 # map to the same GUI support
49 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
50 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
50 backend2gui['GTK3Cairo'] = 'gtk3'
51 backend2gui['GTK3Cairo'] = 'gtk3'
51 backend2gui['WX'] = 'wx'
52 backend2gui['WX'] = 'wx'
52 backend2gui['CocoaAgg'] = 'osx'
53 backend2gui['CocoaAgg'] = 'osx'
53
54
54 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
55 # Matplotlib utilities
56 # Matplotlib utilities
56 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
57
58
58
59
59 def getfigs(*fig_nums):
60 def getfigs(*fig_nums):
60 """Get a list of matplotlib figures by figure numbers.
61 """Get a list of matplotlib figures by figure numbers.
61
62
62 If no arguments are given, all available figures are returned. If the
63 If no arguments are given, all available figures are returned. If the
63 argument list contains references to invalid figures, a warning is printed
64 argument list contains references to invalid figures, a warning is printed
64 but the function continues pasting further figures.
65 but the function continues pasting further figures.
65
66
66 Parameters
67 Parameters
67 ----------
68 ----------
68 figs : tuple
69 figs : tuple
69 A tuple of ints giving the figure numbers of the figures to return.
70 A tuple of ints giving the figure numbers of the figures to return.
70 """
71 """
71 from matplotlib._pylab_helpers import Gcf
72 from matplotlib._pylab_helpers import Gcf
72 if not fig_nums:
73 if not fig_nums:
73 fig_managers = Gcf.get_all_fig_managers()
74 fig_managers = Gcf.get_all_fig_managers()
74 return [fm.canvas.figure for fm in fig_managers]
75 return [fm.canvas.figure for fm in fig_managers]
75 else:
76 else:
76 figs = []
77 figs = []
77 for num in fig_nums:
78 for num in fig_nums:
78 f = Gcf.figs.get(num)
79 f = Gcf.figs.get(num)
79 if f is None:
80 if f is None:
80 print('Warning: figure %s not available.' % num)
81 print('Warning: figure %s not available.' % num)
81 else:
82 else:
82 figs.append(f.canvas.figure)
83 figs.append(f.canvas.figure)
83 return figs
84 return figs
84
85
85
86
86 def figsize(sizex, sizey):
87 def figsize(sizex, sizey):
87 """Set the default figure size to be [sizex, sizey].
88 """Set the default figure size to be [sizex, sizey].
88
89
89 This is just an easy to remember, convenience wrapper that sets::
90 This is just an easy to remember, convenience wrapper that sets::
90
91
91 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
92 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
92 """
93 """
93 import matplotlib
94 import matplotlib
94 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
95 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
95
96
96
97
97 def print_figure(fig, fmt='png', quality=90):
98 def print_figure(fig, fmt='png', quality=90):
98 """Convert a figure to svg, png or jpg for inline display.
99 """Convert a figure to svg, png or jpg for inline display.
99 Quality is only relevant for jpg.
100 Quality is only relevant for jpg.
100 """
101 """
101 from matplotlib import rcParams
102 from matplotlib import rcParams
102 # When there's an empty figure, we shouldn't return anything, otherwise we
103 # When there's an empty figure, we shouldn't return anything, otherwise we
103 # get big blank areas in the qt console.
104 # get big blank areas in the qt console.
104 if not fig.axes and not fig.lines:
105 if not fig.axes and not fig.lines:
105 return
106 return
106
107
107 fc = fig.get_facecolor()
108 fc = fig.get_facecolor()
108 ec = fig.get_edgecolor()
109 ec = fig.get_edgecolor()
109 bytes_io = BytesIO()
110 bytes_io = BytesIO()
110 dpi = rcParams['savefig.dpi']
111 dpi = rcParams['savefig.dpi']
111 if fmt == 'retina':
112 if fmt == 'retina':
112 dpi = dpi * 2
113 dpi = dpi * 2
113 fmt = 'png'
114 fmt = 'png'
114 fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight',
115 fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight',
115 facecolor=fc, edgecolor=ec, dpi=dpi, quality=quality)
116 facecolor=fc, edgecolor=ec, dpi=dpi, quality=quality)
116 data = bytes_io.getvalue()
117 data = bytes_io.getvalue()
117 return data
118 return data
118
119
119 def retina_figure(fig):
120 def retina_figure(fig):
120 """format a figure as a pixel-doubled (retina) PNG"""
121 """format a figure as a pixel-doubled (retina) PNG"""
121 pngdata = print_figure(fig, fmt='retina')
122 pngdata = print_figure(fig, fmt='retina')
122 w, h = _pngxy(pngdata)
123 w, h = _pngxy(pngdata)
123 metadata = dict(width=w//2, height=h//2)
124 metadata = dict(width=w//2, height=h//2)
124 return pngdata, metadata
125 return pngdata, metadata
125
126
126 # We need a little factory function here to create the closure where
127 # We need a little factory function here to create the closure where
127 # safe_execfile can live.
128 # safe_execfile can live.
128 def mpl_runner(safe_execfile):
129 def mpl_runner(safe_execfile):
129 """Factory to return a matplotlib-enabled runner for %run.
130 """Factory to return a matplotlib-enabled runner for %run.
130
131
131 Parameters
132 Parameters
132 ----------
133 ----------
133 safe_execfile : function
134 safe_execfile : function
134 This must be a function with the same interface as the
135 This must be a function with the same interface as the
135 :meth:`safe_execfile` method of IPython.
136 :meth:`safe_execfile` method of IPython.
136
137
137 Returns
138 Returns
138 -------
139 -------
139 A function suitable for use as the ``runner`` argument of the %run magic
140 A function suitable for use as the ``runner`` argument of the %run magic
140 function.
141 function.
141 """
142 """
142
143
143 def mpl_execfile(fname,*where,**kw):
144 def mpl_execfile(fname,*where,**kw):
144 """matplotlib-aware wrapper around safe_execfile.
145 """matplotlib-aware wrapper around safe_execfile.
145
146
146 Its interface is identical to that of the :func:`execfile` builtin.
147 Its interface is identical to that of the :func:`execfile` builtin.
147
148
148 This is ultimately a call to execfile(), but wrapped in safeties to
149 This is ultimately a call to execfile(), but wrapped in safeties to
149 properly handle interactive rendering."""
150 properly handle interactive rendering."""
150
151
151 import matplotlib
152 import matplotlib
152 import matplotlib.pylab as pylab
153 import matplotlib.pylab as pylab
153
154
154 #print '*** Matplotlib runner ***' # dbg
155 #print '*** Matplotlib runner ***' # dbg
155 # turn off rendering until end of script
156 # turn off rendering until end of script
156 is_interactive = matplotlib.rcParams['interactive']
157 is_interactive = matplotlib.rcParams['interactive']
157 matplotlib.interactive(False)
158 matplotlib.interactive(False)
158 safe_execfile(fname,*where,**kw)
159 safe_execfile(fname,*where,**kw)
159 matplotlib.interactive(is_interactive)
160 matplotlib.interactive(is_interactive)
160 # make rendering call now, if the user tried to do it
161 # make rendering call now, if the user tried to do it
161 if pylab.draw_if_interactive.called:
162 if pylab.draw_if_interactive.called:
162 pylab.draw()
163 pylab.draw()
163 pylab.draw_if_interactive.called = False
164 pylab.draw_if_interactive.called = False
164
165
165 return mpl_execfile
166 return mpl_execfile
166
167
167
168
168 def select_figure_format(shell, fmt, quality=90):
169 def select_figure_format(shell, formats, quality=90):
169 """Select figure format for inline backend, can be 'png', 'retina', 'jpg', or 'svg'.
170 """Select figure formats for the inline backend.
170
171
171 Using this method ensures only one figure format is active at a time.
172 Parameters
173 ==========
174 shell : InteractiveShell
175 The main IPython instance
176 formats : list
177 One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
172 """
178 """
173 from matplotlib.figure import Figure
179 from matplotlib.figure import Figure
174 from IPython.kernel.zmq.pylab import backend_inline
180 from IPython.kernel.zmq.pylab import backend_inline
175
181
176 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
182 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
177 png_formatter = shell.display_formatter.formatters['image/png']
183 png_formatter = shell.display_formatter.formatters['image/png']
178 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
184 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
185 pdf_formatter = shell.display_formatter.formatters['application/pdf']
186
187 if isinstance(formats, py3compat.string_types):
188 formats = {formats}
179
189
180 [ f.type_printers.pop(Figure, None) for f in {svg_formatter, png_formatter, jpg_formatter} ]
190 [ f.type_printers.pop(Figure, None) for f in {svg_formatter, png_formatter, jpg_formatter} ]
181
191
192 for fmt in formats:
182 if fmt == 'png':
193 if fmt == 'png':
183 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
194 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
184 elif fmt in ('png2x', 'retina'):
195 elif fmt in ('png2x', 'retina'):
185 png_formatter.for_type(Figure, retina_figure)
196 png_formatter.for_type(Figure, retina_figure)
186 elif fmt in ('jpg', 'jpeg'):
197 elif fmt in ('jpg', 'jpeg'):
187 jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', quality))
198 jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', quality))
188 elif fmt == 'svg':
199 elif fmt == 'svg':
189 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
200 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
201 elif fmt == 'pdf':
202 pdf_formatter.for_type(Figure, lambda fig: print_figure(fig, 'pdf'))
190 else:
203 else:
191 raise ValueError("supported formats are: 'png', 'retina', 'svg', 'jpg', not %r" % fmt)
204 raise ValueError("supported formats are: 'png', 'retina', 'svg', 'jpg', 'pdf' not %r" % fmt)
192
193 # set the format to be used in the backend()
194 backend_inline._figure_format = fmt
195
205
196 #-----------------------------------------------------------------------------
206 #-----------------------------------------------------------------------------
197 # Code for initializing matplotlib and importing pylab
207 # Code for initializing matplotlib and importing pylab
198 #-----------------------------------------------------------------------------
208 #-----------------------------------------------------------------------------
199
209
200
210
201 def find_gui_and_backend(gui=None, gui_select=None):
211 def find_gui_and_backend(gui=None, gui_select=None):
202 """Given a gui string return the gui and mpl backend.
212 """Given a gui string return the gui and mpl backend.
203
213
204 Parameters
214 Parameters
205 ----------
215 ----------
206 gui : str
216 gui : str
207 Can be one of ('tk','gtk','wx','qt','qt4','inline').
217 Can be one of ('tk','gtk','wx','qt','qt4','inline').
208 gui_select : str
218 gui_select : str
209 Can be one of ('tk','gtk','wx','qt','qt4','inline').
219 Can be one of ('tk','gtk','wx','qt','qt4','inline').
210 This is any gui already selected by the shell.
220 This is any gui already selected by the shell.
211
221
212 Returns
222 Returns
213 -------
223 -------
214 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
224 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
215 'WXAgg','Qt4Agg','module://IPython.kernel.zmq.pylab.backend_inline').
225 'WXAgg','Qt4Agg','module://IPython.kernel.zmq.pylab.backend_inline').
216 """
226 """
217
227
218 import matplotlib
228 import matplotlib
219
229
220 if gui and gui != 'auto':
230 if gui and gui != 'auto':
221 # select backend based on requested gui
231 # select backend based on requested gui
222 backend = backends[gui]
232 backend = backends[gui]
223 else:
233 else:
224 # We need to read the backend from the original data structure, *not*
234 # We need to read the backend from the original data structure, *not*
225 # from mpl.rcParams, since a prior invocation of %matplotlib may have
235 # from mpl.rcParams, since a prior invocation of %matplotlib may have
226 # overwritten that.
236 # overwritten that.
227 # WARNING: this assumes matplotlib 1.1 or newer!!
237 # WARNING: this assumes matplotlib 1.1 or newer!!
228 backend = matplotlib.rcParamsOrig['backend']
238 backend = matplotlib.rcParamsOrig['backend']
229 # In this case, we need to find what the appropriate gui selection call
239 # In this case, we need to find what the appropriate gui selection call
230 # should be for IPython, so we can activate inputhook accordingly
240 # should be for IPython, so we can activate inputhook accordingly
231 gui = backend2gui.get(backend, None)
241 gui = backend2gui.get(backend, None)
232
242
233 # If we have already had a gui active, we need it and inline are the
243 # If we have already had a gui active, we need it and inline are the
234 # ones allowed.
244 # ones allowed.
235 if gui_select and gui != gui_select:
245 if gui_select and gui != gui_select:
236 gui = gui_select
246 gui = gui_select
237 backend = backends[gui]
247 backend = backends[gui]
238
248
239 return gui, backend
249 return gui, backend
240
250
241
251
242 def activate_matplotlib(backend):
252 def activate_matplotlib(backend):
243 """Activate the given backend and set interactive to True."""
253 """Activate the given backend and set interactive to True."""
244
254
245 import matplotlib
255 import matplotlib
246 matplotlib.interactive(True)
256 matplotlib.interactive(True)
247
257
248 # Matplotlib had a bug where even switch_backend could not force
258 # Matplotlib had a bug where even switch_backend could not force
249 # the rcParam to update. This needs to be set *before* the module
259 # the rcParam to update. This needs to be set *before* the module
250 # magic of switch_backend().
260 # magic of switch_backend().
251 matplotlib.rcParams['backend'] = backend
261 matplotlib.rcParams['backend'] = backend
252
262
253 import matplotlib.pyplot
263 import matplotlib.pyplot
254 matplotlib.pyplot.switch_backend(backend)
264 matplotlib.pyplot.switch_backend(backend)
255
265
256 # This must be imported last in the matplotlib series, after
266 # This must be imported last in the matplotlib series, after
257 # backend/interactivity choices have been made
267 # backend/interactivity choices have been made
258 import matplotlib.pylab as pylab
268 import matplotlib.pylab as pylab
259
269
260 pylab.show._needmain = False
270 pylab.show._needmain = False
261 # We need to detect at runtime whether show() is called by the user.
271 # We need to detect at runtime whether show() is called by the user.
262 # For this, we wrap it into a decorator which adds a 'called' flag.
272 # For this, we wrap it into a decorator which adds a 'called' flag.
263 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
273 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
264
274
265
275
266 def import_pylab(user_ns, import_all=True):
276 def import_pylab(user_ns, import_all=True):
267 """Populate the namespace with pylab-related values.
277 """Populate the namespace with pylab-related values.
268
278
269 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
279 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
270
280
271 Also imports a few names from IPython (figsize, display, getfigs)
281 Also imports a few names from IPython (figsize, display, getfigs)
272
282
273 """
283 """
274
284
275 # Import numpy as np/pyplot as plt are conventions we're trying to
285 # Import numpy as np/pyplot as plt are conventions we're trying to
276 # somewhat standardize on. Making them available to users by default
286 # somewhat standardize on. Making them available to users by default
277 # will greatly help this.
287 # will greatly help this.
278 s = ("import numpy\n"
288 s = ("import numpy\n"
279 "import matplotlib\n"
289 "import matplotlib\n"
280 "from matplotlib import pylab, mlab, pyplot\n"
290 "from matplotlib import pylab, mlab, pyplot\n"
281 "np = numpy\n"
291 "np = numpy\n"
282 "plt = pyplot\n"
292 "plt = pyplot\n"
283 )
293 )
284 exec(s, user_ns)
294 exec(s, user_ns)
285
295
286 if import_all:
296 if import_all:
287 s = ("from matplotlib.pylab import *\n"
297 s = ("from matplotlib.pylab import *\n"
288 "from numpy import *\n")
298 "from numpy import *\n")
289 exec(s, user_ns)
299 exec(s, user_ns)
290
300
291 # IPython symbols to add
301 # IPython symbols to add
292 user_ns['figsize'] = figsize
302 user_ns['figsize'] = figsize
293 from IPython.core.display import display
303 from IPython.core.display import display
294 # Add display and getfigs to the user's namespace
304 # Add display and getfigs to the user's namespace
295 user_ns['display'] = display
305 user_ns['display'] = display
296 user_ns['getfigs'] = getfigs
306 user_ns['getfigs'] = getfigs
297
307
298
308
299 def configure_inline_support(shell, backend):
309 def configure_inline_support(shell, backend):
300 """Configure an IPython shell object for matplotlib use.
310 """Configure an IPython shell object for matplotlib use.
301
311
302 Parameters
312 Parameters
303 ----------
313 ----------
304 shell : InteractiveShell instance
314 shell : InteractiveShell instance
305
315
306 backend : matplotlib backend
316 backend : matplotlib backend
307 """
317 """
308 # If using our svg payload backend, register the post-execution
318 # If using our svg payload backend, register the post-execution
309 # function that will pick up the results for display. This can only be
319 # function that will pick up the results for display. This can only be
310 # done with access to the real shell object.
320 # done with access to the real shell object.
311
321
312 # Note: if we can't load the inline backend, then there's no point
322 # Note: if we can't load the inline backend, then there's no point
313 # continuing (such as in terminal-only shells in environments without
323 # continuing (such as in terminal-only shells in environments without
314 # zeromq available).
324 # zeromq available).
315 try:
325 try:
316 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
326 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
317 except ImportError:
327 except ImportError:
318 return
328 return
319 from matplotlib import pyplot
329 from matplotlib import pyplot
320
330
321 cfg = InlineBackend.instance(parent=shell)
331 cfg = InlineBackend.instance(parent=shell)
322 cfg.shell = shell
332 cfg.shell = shell
323 if cfg not in shell.configurables:
333 if cfg not in shell.configurables:
324 shell.configurables.append(cfg)
334 shell.configurables.append(cfg)
325
335
326 if backend == backends['inline']:
336 if backend == backends['inline']:
327 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
337 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
328 shell.register_post_execute(flush_figures)
338 shell.register_post_execute(flush_figures)
329
339
330 # Save rcParams that will be overwrittern
340 # Save rcParams that will be overwrittern
331 shell._saved_rcParams = dict()
341 shell._saved_rcParams = dict()
332 for k in cfg.rc:
342 for k in cfg.rc:
333 shell._saved_rcParams[k] = pyplot.rcParams[k]
343 shell._saved_rcParams[k] = pyplot.rcParams[k]
334 # load inline_rc
344 # load inline_rc
335 pyplot.rcParams.update(cfg.rc)
345 pyplot.rcParams.update(cfg.rc)
336 else:
346 else:
337 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
347 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
338 if flush_figures in shell._post_execute:
348 if flush_figures in shell._post_execute:
339 shell._post_execute.pop(flush_figures)
349 shell._post_execute.pop(flush_figures)
340 if hasattr(shell, '_saved_rcParams'):
350 if hasattr(shell, '_saved_rcParams'):
341 pyplot.rcParams.update(shell._saved_rcParams)
351 pyplot.rcParams.update(shell._saved_rcParams)
342 del shell._saved_rcParams
352 del shell._saved_rcParams
343
353
344 # Setup the default figure format
354 # Setup the default figure format
345 select_figure_format(shell, cfg.figure_format, cfg.quality)
355 select_figure_format(shell, cfg.figure_formats, cfg.quality)
346
356
@@ -1,108 +1,114 b''
1 """Configurable for configuring the IPython inline backend
1 """Configurable for configuring the IPython inline backend
2
2
3 This module does not import anything from matplotlib.
3 This module does not import anything from matplotlib.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2011 The IPython Development Team
6 # Copyright (C) 2011 The IPython Development Team
7 #
7 #
8 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING, distributed as part of this software.
9 # the file COPYING, distributed as part of this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 from IPython.config.configurable import SingletonConfigurable
16 from IPython.config.configurable import SingletonConfigurable
17 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, Bool, Int, TraitError
17 from IPython.utils.traitlets import (
18 Dict, Instance, CaselessStrEnum, Set, Bool, Int, TraitError, Unicode
19 )
18 from IPython.utils.warn import warn
20 from IPython.utils.warn import warn
19
21
20 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
21 # Configurable for inline backend options
23 # Configurable for inline backend options
22 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
23
25
24 def pil_available():
26 def pil_available():
25 """Test if PIL/Pillow is available"""
27 """Test if PIL/Pillow is available"""
26 out = False
28 out = False
27 try:
29 try:
28 from PIL import Image
30 from PIL import Image
29 out = True
31 out = True
30 except:
32 except:
31 pass
33 pass
32 return out
34 return out
33
35
34 # inherit from InlineBackendConfig for deprecation purposes
36 # inherit from InlineBackendConfig for deprecation purposes
35 class InlineBackendConfig(SingletonConfigurable):
37 class InlineBackendConfig(SingletonConfigurable):
36 pass
38 pass
37
39
38 class InlineBackend(InlineBackendConfig):
40 class InlineBackend(InlineBackendConfig):
39 """An object to store configuration of the inline backend."""
41 """An object to store configuration of the inline backend."""
40
42
41 def _config_changed(self, name, old, new):
43 def _config_changed(self, name, old, new):
42 # warn on change of renamed config section
44 # warn on change of renamed config section
43 if new.InlineBackendConfig != old.InlineBackendConfig:
45 if new.InlineBackendConfig != old.InlineBackendConfig:
44 warn("InlineBackendConfig has been renamed to InlineBackend")
46 warn("InlineBackendConfig has been renamed to InlineBackend")
45 super(InlineBackend, self)._config_changed(name, old, new)
47 super(InlineBackend, self)._config_changed(name, old, new)
46
48
47 # The typical default figure size is too large for inline use,
49 # The typical default figure size is too large for inline use,
48 # so we shrink the figure size to 6x4, and tweak fonts to
50 # so we shrink the figure size to 6x4, and tweak fonts to
49 # make that fit.
51 # make that fit.
50 rc = Dict({'figure.figsize': (6.0,4.0),
52 rc = Dict({'figure.figsize': (6.0,4.0),
51 # play nicely with white background in the Qt and notebook frontend
53 # play nicely with white background in the Qt and notebook frontend
52 'figure.facecolor': (1,1,1,0),
54 'figure.facecolor': (1,1,1,0),
53 'figure.edgecolor': (1,1,1,0),
55 'figure.edgecolor': (1,1,1,0),
54 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
56 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
55 'font.size': 10,
57 'font.size': 10,
56 # 72 dpi matches SVG/qtconsole
58 # 72 dpi matches SVG/qtconsole
57 # this only affects PNG export, as SVG has no dpi setting
59 # this only affects PNG export, as SVG has no dpi setting
58 'savefig.dpi': 72,
60 'savefig.dpi': 72,
59 # 10pt still needs a little more room on the xlabel:
61 # 10pt still needs a little more room on the xlabel:
60 'figure.subplot.bottom' : .125
62 'figure.subplot.bottom' : .125
61 }, config=True,
63 }, config=True,
62 help="""Subset of matplotlib rcParams that should be different for the
64 help="""Subset of matplotlib rcParams that should be different for the
63 inline backend."""
65 inline backend."""
64 )
66 )
65
67
68 figure_formats = Set({'png'}, config=True,
69 help="""A set of figure formats to enable: 'png',
70 'retina', 'jpeg', 'svg', 'pdf'.""")
66
71
67 figure_format = CaselessStrEnum(['svg', 'png', 'retina', 'jpg'],
72 def _figure_formats_changed(self, name, old, new):
68 default_value='png', config=True,
69 help="""The image format for figures with the inline
70 backend. JPEG requires the PIL/Pillow library.""")
71
72 def _figure_format_changed(self, name, old, new):
73 from IPython.core.pylabtools import select_figure_format
73 from IPython.core.pylabtools import select_figure_format
74 if new in {"jpg", "jpeg"}:
74 if 'jpg' in new or 'jpeg' in new:
75 if not pil_available():
75 if not pil_available():
76 raise TraitError("Requires PIL/Pillow for JPG figures")
76 raise TraitError("Requires PIL/Pillow for JPG figures")
77 if self.shell is None:
77 if self.shell is None:
78 return
78 return
79 else:
79 else:
80 select_figure_format(self.shell, new)
80 select_figure_format(self.shell, new)
81
81
82 figure_format = Unicode()
83
84 def _figure_format_changed(self, name, old, new):
85 if new:
86 self.figure_formats = {new}
87
82 quality = Int(default_value=90, config=True,
88 quality = Int(default_value=90, config=True,
83 help="Quality of compression [10-100], currently for lossy JPEG only.")
89 help="Quality of compression [10-100], currently for lossy JPEG only.")
84
90
85 def _quality_changed(self, name, old, new):
91 def _quality_changed(self, name, old, new):
86 if new < 10 or new > 100:
92 if new < 10 or new > 100:
87 raise TraitError("figure JPEG quality must be in [10-100] range.")
93 raise TraitError("figure JPEG quality must be in [10-100] range.")
88
94
89 close_figures = Bool(True, config=True,
95 close_figures = Bool(True, config=True,
90 help="""Close all figures at the end of each cell.
96 help="""Close all figures at the end of each cell.
91
97
92 When True, ensures that each cell starts with no active figures, but it
98 When True, ensures that each cell starts with no active figures, but it
93 also means that one must keep track of references in order to edit or
99 also means that one must keep track of references in order to edit or
94 redraw figures in subsequent cells. This mode is ideal for the notebook,
100 redraw figures in subsequent cells. This mode is ideal for the notebook,
95 where residual plots from other cells might be surprising.
101 where residual plots from other cells might be surprising.
96
102
97 When False, one must call figure() to create new figures. This means
103 When False, one must call figure() to create new figures. This means
98 that gcf() and getfigs() can reference figures created in other cells,
104 that gcf() and getfigs() can reference figures created in other cells,
99 and the active figure can continue to be edited with pylab/pyplot
105 and the active figure can continue to be edited with pylab/pyplot
100 methods that reference the current active figure. This mode facilitates
106 methods that reference the current active figure. This mode facilitates
101 iterative editing of figures, and behaves most consistently with
107 iterative editing of figures, and behaves most consistently with
102 other matplotlib backends, but figure barriers between cells must
108 other matplotlib backends, but figure barriers between cells must
103 be explicit.
109 be explicit.
104 """)
110 """)
105
111
106 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
112 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
107
113
108
114
General Comments 0
You need to be logged in to leave comments. Login now