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