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