##// END OF EJS Templates
enable retina matplotlib figures
MinRK -
Show More
@@ -1,370 +1,391 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
11 11 #-----------------------------------------------------------------------------
12 12 # Copyright (C) 2009-2011 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 import struct
22 23 import sys
23 24 from io import BytesIO
24 25
25 26 from IPython.utils.decorators import flag_calls
26 27
27 28 # If user specifies a GUI, that dictates the backend, otherwise we read the
28 29 # user's mpl default from the mpl rc structure
29 30 backends = {'tk': 'TkAgg',
30 31 'gtk': 'GTKAgg',
31 32 'wx': 'WXAgg',
32 33 'qt': 'Qt4Agg', # qt3 not supported
33 34 'qt4': 'Qt4Agg',
34 35 'osx': 'MacOSX',
35 36 'inline' : 'module://IPython.kernel.zmq.pylab.backend_inline'}
36 37
37 38 # We also need a reverse backends2guis mapping that will properly choose which
38 39 # GUI support to activate based on the desired matplotlib backend. For the
39 40 # most part it's just a reverse of the above dict, but we also need to add a
40 41 # few others that map to the same GUI manually:
41 42 backend2gui = dict(zip(backends.values(), backends.keys()))
42 43 # In the reverse mapping, there are a few extra valid matplotlib backends that
43 44 # map to the same GUI support
44 45 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
45 46 backend2gui['WX'] = 'wx'
46 47 backend2gui['CocoaAgg'] = 'osx'
47 48
48 49 #-----------------------------------------------------------------------------
49 50 # Matplotlib utilities
50 51 #-----------------------------------------------------------------------------
51 52
52 53
53 54 def getfigs(*fig_nums):
54 55 """Get a list of matplotlib figures by figure numbers.
55 56
56 57 If no arguments are given, all available figures are returned. If the
57 58 argument list contains references to invalid figures, a warning is printed
58 59 but the function continues pasting further figures.
59 60
60 61 Parameters
61 62 ----------
62 63 figs : tuple
63 64 A tuple of ints giving the figure numbers of the figures to return.
64 65 """
65 66 from matplotlib._pylab_helpers import Gcf
66 67 if not fig_nums:
67 68 fig_managers = Gcf.get_all_fig_managers()
68 69 return [fm.canvas.figure for fm in fig_managers]
69 70 else:
70 71 figs = []
71 72 for num in fig_nums:
72 73 f = Gcf.figs.get(num)
73 74 if f is None:
74 75 print('Warning: figure %s not available.' % num)
75 76 else:
76 77 figs.append(f.canvas.figure)
77 78 return figs
78 79
79 80
80 81 def figsize(sizex, sizey):
81 82 """Set the default figure size to be [sizex, sizey].
82 83
83 84 This is just an easy to remember, convenience wrapper that sets::
84 85
85 86 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
86 87 """
87 88 import matplotlib
88 89 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
89 90
90 91
91 92 def print_figure(fig, fmt='png'):
92 93 """Convert a figure to svg or png for inline display."""
94 from matplotlib import rcParams
93 95 # When there's an empty figure, we shouldn't return anything, otherwise we
94 96 # get big blank areas in the qt console.
95 97 if not fig.axes and not fig.lines:
96 98 return
97 99
98 100 fc = fig.get_facecolor()
99 101 ec = fig.get_edgecolor()
100 102 bytes_io = BytesIO()
103 dpi = rcParams['savefig.dpi']
104 if fmt == 'retina':
105 dpi = dpi * 2
101 106 fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight',
102 facecolor=fc, edgecolor=ec)
107 facecolor=fc, edgecolor=ec, dpi=dpi)
103 108 data = bytes_io.getvalue()
104 109 return data
105
110
111 def pngxy(data):
112 """read the width/height from a PNG header"""
113 ihdr = data.index(b'IHDR')
114 # next 8 bytes are width/height
115 w4h4 = data[ihdr+4:ihdr+12]
116 return struct.unpack('>ii', w4h4)
117
118 def retina_figure(fig):
119 """format a figure as a pixel-doubled (retina) PNG"""
120 pngdata = print_figure(fig, fmt='retina')
121 w, h = pngxy(pngdata)
122 metadata = dict(width=w//2, height=h//2)
123 return pngdata, metadata
106 124
107 125 # We need a little factory function here to create the closure where
108 126 # safe_execfile can live.
109 127 def mpl_runner(safe_execfile):
110 128 """Factory to return a matplotlib-enabled runner for %run.
111 129
112 130 Parameters
113 131 ----------
114 132 safe_execfile : function
115 133 This must be a function with the same interface as the
116 134 :meth:`safe_execfile` method of IPython.
117 135
118 136 Returns
119 137 -------
120 138 A function suitable for use as the ``runner`` argument of the %run magic
121 139 function.
122 140 """
123 141
124 142 def mpl_execfile(fname,*where,**kw):
125 143 """matplotlib-aware wrapper around safe_execfile.
126 144
127 145 Its interface is identical to that of the :func:`execfile` builtin.
128 146
129 147 This is ultimately a call to execfile(), but wrapped in safeties to
130 148 properly handle interactive rendering."""
131 149
132 150 import matplotlib
133 151 import matplotlib.pylab as pylab
134 152
135 153 #print '*** Matplotlib runner ***' # dbg
136 154 # turn off rendering until end of script
137 155 is_interactive = matplotlib.rcParams['interactive']
138 156 matplotlib.interactive(False)
139 157 safe_execfile(fname,*where,**kw)
140 158 matplotlib.interactive(is_interactive)
141 159 # make rendering call now, if the user tried to do it
142 160 if pylab.draw_if_interactive.called:
143 161 pylab.draw()
144 162 pylab.draw_if_interactive.called = False
145 163
146 164 return mpl_execfile
147 165
148 166
149 167 def select_figure_format(shell, fmt):
150 """Select figure format for inline backend, either 'png' or 'svg'.
168 """Select figure format for inline backend, can be 'png', 'retina', or 'svg'.
151 169
152 170 Using this method ensures only one figure format is active at a time.
153 171 """
154 172 from matplotlib.figure import Figure
155 173 from IPython.kernel.zmq.pylab import backend_inline
156 174
157 175 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
158 176 png_formatter = shell.display_formatter.formatters['image/png']
159 177
160 if fmt=='png':
178 if fmt == 'png':
161 179 svg_formatter.type_printers.pop(Figure, None)
162 180 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
163 elif fmt=='svg':
181 elif fmt in ('png2x', 'retina'):
182 svg_formatter.type_printers.pop(Figure, None)
183 png_formatter.for_type(Figure, retina_figure)
184 elif fmt == 'svg':
164 185 png_formatter.type_printers.pop(Figure, None)
165 186 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
166 187 else:
167 raise ValueError("supported formats are: 'png', 'svg', not %r"%fmt)
188 raise ValueError("supported formats are: 'png', 'retina', 'svg', not %r" % fmt)
168 189
169 190 # set the format to be used in the backend()
170 191 backend_inline._figure_format = fmt
171 192
172 193 #-----------------------------------------------------------------------------
173 194 # Code for initializing matplotlib and importing pylab
174 195 #-----------------------------------------------------------------------------
175 196
176 197
177 198 def find_gui_and_backend(gui=None, gui_select=None):
178 199 """Given a gui string return the gui and mpl backend.
179 200
180 201 Parameters
181 202 ----------
182 203 gui : str
183 204 Can be one of ('tk','gtk','wx','qt','qt4','inline').
184 205 gui_select : str
185 206 Can be one of ('tk','gtk','wx','qt','qt4','inline').
186 207 This is any gui already selected by the shell.
187 208
188 209 Returns
189 210 -------
190 211 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
191 212 'WXAgg','Qt4Agg','module://IPython.kernel.zmq.pylab.backend_inline').
192 213 """
193 214
194 215 import matplotlib
195 216
196 217 if gui and gui != 'auto':
197 218 # select backend based on requested gui
198 219 backend = backends[gui]
199 220 else:
200 221 backend = matplotlib.rcParams['backend']
201 222 # In this case, we need to find what the appropriate gui selection call
202 223 # should be for IPython, so we can activate inputhook accordingly
203 224 gui = backend2gui.get(backend, None)
204 225
205 226 # If we have already had a gui active, we need it and inline are the
206 227 # ones allowed.
207 228 if gui_select and gui != gui_select:
208 229 gui = gui_select
209 230 backend = backends[gui]
210 231
211 232 return gui, backend
212 233
213 234
214 235 def activate_matplotlib(backend):
215 236 """Activate the given backend and set interactive to True."""
216 237
217 238 import matplotlib
218 239 matplotlib.interactive(True)
219 240
220 241 # Matplotlib had a bug where even switch_backend could not force
221 242 # the rcParam to update. This needs to be set *before* the module
222 243 # magic of switch_backend().
223 244 matplotlib.rcParams['backend'] = backend
224 245
225 246 import matplotlib.pyplot
226 247 matplotlib.pyplot.switch_backend(backend)
227 248
228 249 # This must be imported last in the matplotlib series, after
229 250 # backend/interactivity choices have been made
230 251 import matplotlib.pylab as pylab
231 252
232 253 pylab.show._needmain = False
233 254 # We need to detect at runtime whether show() is called by the user.
234 255 # For this, we wrap it into a decorator which adds a 'called' flag.
235 256 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
236 257
237 258
238 259 def import_pylab(user_ns, import_all=True):
239 260 """Import the standard pylab symbols into user_ns."""
240 261
241 262 # Import numpy as np/pyplot as plt are conventions we're trying to
242 263 # somewhat standardize on. Making them available to users by default
243 264 # will greatly help this.
244 265 s = ("import numpy\n"
245 266 "import matplotlib\n"
246 267 "from matplotlib import pylab, mlab, pyplot\n"
247 268 "np = numpy\n"
248 269 "plt = pyplot\n"
249 270 )
250 271 exec s in user_ns
251 272
252 273 if import_all:
253 274 s = ("from matplotlib.pylab import *\n"
254 275 "from numpy import *\n")
255 276 exec s in user_ns
256 277
257 278
258 279 def configure_inline_support(shell, backend, user_ns=None):
259 280 """Configure an IPython shell object for matplotlib use.
260 281
261 282 Parameters
262 283 ----------
263 284 shell : InteractiveShell instance
264 285
265 286 backend : matplotlib backend
266 287
267 288 user_ns : dict
268 289 A namespace where all configured variables will be placed. If not given,
269 290 the `user_ns` attribute of the shell object is used.
270 291 """
271 292 # If using our svg payload backend, register the post-execution
272 293 # function that will pick up the results for display. This can only be
273 294 # done with access to the real shell object.
274 295
275 296 # Note: if we can't load the inline backend, then there's no point
276 297 # continuing (such as in terminal-only shells in environments without
277 298 # zeromq available).
278 299 try:
279 300 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
280 301 except ImportError:
281 302 return
282 303 from matplotlib import pyplot
283 304
284 305 user_ns = shell.user_ns if user_ns is None else user_ns
285 306
286 307 cfg = InlineBackend.instance(config=shell.config)
287 308 cfg.shell = shell
288 309 if cfg not in shell.configurables:
289 310 shell.configurables.append(cfg)
290 311
291 312 if backend == backends['inline']:
292 313 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
293 314 shell.register_post_execute(flush_figures)
294 315
295 316 # Save rcParams that will be overwrittern
296 317 shell._saved_rcParams = dict()
297 318 for k in cfg.rc:
298 319 shell._saved_rcParams[k] = pyplot.rcParams[k]
299 320 # load inline_rc
300 321 pyplot.rcParams.update(cfg.rc)
301 322 # Add 'figsize' to pyplot and to the user's namespace
302 323 user_ns['figsize'] = pyplot.figsize = figsize
303 324 else:
304 325 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
305 326 if flush_figures in shell._post_execute:
306 327 shell._post_execute.pop(flush_figures)
307 328 if hasattr(shell, '_saved_rcParams'):
308 329 pyplot.rcParams.update(shell._saved_rcParams)
309 330 del shell._saved_rcParams
310 331
311 332 # Setup the default figure format
312 333 fmt = cfg.figure_format
313 334 select_figure_format(shell, fmt)
314 335
315 336 # The old pastefig function has been replaced by display
316 337 from IPython.core.display import display
317 338 # Add display and getfigs to the user's namespace
318 339 user_ns['display'] = display
319 340 user_ns['getfigs'] = getfigs
320 341
321 342
322 343 def pylab_activate(user_ns, gui=None, import_all=True, shell=None, welcome_message=False):
323 344 """Activate pylab mode in the user's namespace.
324 345
325 346 Loads and initializes numpy, matplotlib and friends for interactive use.
326 347
327 348 Parameters
328 349 ----------
329 350 user_ns : dict
330 351 Namespace where the imports will occur.
331 352
332 353 gui : optional, string
333 354 A valid gui name following the conventions of the %gui magic.
334 355
335 356 import_all : optional, boolean
336 357 If true, an 'import *' is done from numpy and pylab.
337 358
338 359 welcome_message : optional, boolean
339 360 If true, print a welcome message about pylab, which includes the backend
340 361 being used.
341 362
342 363 Returns
343 364 -------
344 365 The actual gui used (if not given as input, it was obtained from matplotlib
345 366 itself, and will be needed next to configure IPython's gui integration.
346 367 """
347 368 pylab_gui_select = shell.pylab_gui_select if shell is not None else None
348 369 # Try to find the appropriate gui and backend for the settings
349 370 gui, backend = find_gui_and_backend(gui, pylab_gui_select)
350 371 if shell is not None and gui != 'inline':
351 372 # If we have our first gui selection, store it
352 373 if pylab_gui_select is None:
353 374 shell.pylab_gui_select = gui
354 375 # Otherwise if they are different
355 376 elif gui != pylab_gui_select:
356 377 print ('Warning: Cannot change to a different GUI toolkit: %s.'
357 378 ' Using %s instead.' % (gui, pylab_gui_select))
358 379 gui, backend = find_gui_and_backend(pylab_gui_select)
359 380 activate_matplotlib(backend)
360 381 import_pylab(user_ns, import_all)
361 382 if shell is not None:
362 383 configure_inline_support(shell, backend, user_ns)
363 384 if welcome_message:
364 385 print """
365 386 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
366 387 For more information, type 'help(pylab)'.""" % backend
367 388 # flush stdout, just to be safe
368 389 sys.stdout.flush()
369 390
370 391 return gui
@@ -1,215 +1,215 b''
1 1 """Produce SVG versions of active plots for display by the rich Qt frontend.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Imports
5 5 #-----------------------------------------------------------------------------
6 6 from __future__ import print_function
7 7
8 8 # Standard library imports
9 9 import sys
10 10
11 11 # Third-party imports
12 12 import matplotlib
13 13 from matplotlib.backends.backend_agg import new_figure_manager, FigureCanvasAgg
14 14 from matplotlib._pylab_helpers import Gcf
15 15
16 16 # Local imports.
17 17 from IPython.config.configurable import SingletonConfigurable
18 18 from IPython.core.display import display
19 19 from IPython.core.displaypub import publish_display_data
20 20 from IPython.core.pylabtools import print_figure, select_figure_format
21 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, CBool
21 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, Bool
22 22 from IPython.utils.warn import warn
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Configurable for inline backend options
26 26 #-----------------------------------------------------------------------------
27 27 # inherit from InlineBackendConfig for deprecation purposes
28 28 class InlineBackendConfig(SingletonConfigurable):
29 29 pass
30 30
31 31 class InlineBackend(InlineBackendConfig):
32 32 """An object to store configuration of the inline backend."""
33 33
34 34 def _config_changed(self, name, old, new):
35 35 # warn on change of renamed config section
36 36 if new.InlineBackendConfig != old.InlineBackendConfig:
37 37 warn("InlineBackendConfig has been renamed to InlineBackend")
38 38 super(InlineBackend, self)._config_changed(name, old, new)
39 39
40 40 # The typical default figure size is too large for inline use,
41 41 # so we shrink the figure size to 6x4, and tweak fonts to
42 42 # make that fit.
43 43 rc = Dict({'figure.figsize': (6.0,4.0),
44 44 # play nicely with white background in the Qt and notebook frontend
45 45 'figure.facecolor': 'white',
46 46 'figure.edgecolor': 'white',
47 47 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
48 48 'font.size': 10,
49 49 # 72 dpi matches SVG/qtconsole
50 50 # this only affects PNG export, as SVG has no dpi setting
51 51 'savefig.dpi': 72,
52 52 # 10pt still needs a little more room on the xlabel:
53 53 'figure.subplot.bottom' : .125
54 54 }, config=True,
55 55 help="""Subset of matplotlib rcParams that should be different for the
56 56 inline backend."""
57 57 )
58 58
59 figure_format = CaselessStrEnum(['svg', 'png'], default_value='png', config=True,
59 figure_format = CaselessStrEnum(['svg', 'png', 'retina'], default_value='png', config=True,
60 60 help="The image format for figures with the inline backend.")
61 61
62 62 def _figure_format_changed(self, name, old, new):
63 63 if self.shell is None:
64 64 return
65 65 else:
66 66 select_figure_format(self.shell, new)
67 67
68 close_figures = CBool(True, config=True,
68 close_figures = Bool(True, config=True,
69 69 help="""Close all figures at the end of each cell.
70 70
71 71 When True, ensures that each cell starts with no active figures, but it
72 72 also means that one must keep track of references in order to edit or
73 73 redraw figures in subsequent cells. This mode is ideal for the notebook,
74 74 where residual plots from other cells might be surprising.
75 75
76 76 When False, one must call figure() to create new figures. This means
77 77 that gcf() and getfigs() can reference figures created in other cells,
78 78 and the active figure can continue to be edited with pylab/pyplot
79 79 methods that reference the current active figure. This mode facilitates
80 80 iterative editing of figures, and behaves most consistently with
81 81 other matplotlib backends, but figure barriers between cells must
82 82 be explicit.
83 83 """)
84 84
85 85 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
86 86
87 87
88 88 #-----------------------------------------------------------------------------
89 89 # Functions
90 90 #-----------------------------------------------------------------------------
91 91
92 92 def show(close=None):
93 93 """Show all figures as SVG/PNG payloads sent to the IPython clients.
94 94
95 95 Parameters
96 96 ----------
97 97 close : bool, optional
98 98 If true, a ``plt.close('all')`` call is automatically issued after
99 99 sending all the figures. If this is set, the figures will entirely
100 100 removed from the internal list of figures.
101 101 """
102 102 if close is None:
103 103 close = InlineBackend.instance().close_figures
104 104 try:
105 105 for figure_manager in Gcf.get_all_fig_managers():
106 106 display(figure_manager.canvas.figure)
107 107 finally:
108 108 show._to_draw = []
109 109 if close:
110 110 matplotlib.pyplot.close('all')
111 111
112 112
113 113
114 114 # This flag will be reset by draw_if_interactive when called
115 115 show._draw_called = False
116 116 # list of figures to draw when flush_figures is called
117 117 show._to_draw = []
118 118
119 119
120 120 def draw_if_interactive():
121 121 """
122 122 Is called after every pylab drawing command
123 123 """
124 124 # signal that the current active figure should be sent at the end of
125 125 # execution. Also sets the _draw_called flag, signaling that there will be
126 126 # something to send. At the end of the code execution, a separate call to
127 127 # flush_figures() will act upon these values
128 128
129 129 fig = Gcf.get_active().canvas.figure
130 130
131 131 # Hack: matplotlib FigureManager objects in interacive backends (at least
132 132 # in some of them) monkeypatch the figure object and add a .show() method
133 133 # to it. This applies the same monkeypatch in order to support user code
134 134 # that might expect `.show()` to be part of the official API of figure
135 135 # objects.
136 136 # For further reference:
137 137 # https://github.com/ipython/ipython/issues/1612
138 138 # https://github.com/matplotlib/matplotlib/issues/835
139 139
140 140 if not hasattr(fig, 'show'):
141 141 # Queue up `fig` for display
142 142 fig.show = lambda *a: display(fig)
143 143
144 144 # If matplotlib was manually set to non-interactive mode, this function
145 145 # should be a no-op (otherwise we'll generate duplicate plots, since a user
146 146 # who set ioff() manually expects to make separate draw/show calls).
147 147 if not matplotlib.is_interactive():
148 148 return
149 149
150 150 # ensure current figure will be drawn, and each subsequent call
151 151 # of draw_if_interactive() moves the active figure to ensure it is
152 152 # drawn last
153 153 try:
154 154 show._to_draw.remove(fig)
155 155 except ValueError:
156 156 # ensure it only appears in the draw list once
157 157 pass
158 158 # Queue up the figure for drawing in next show() call
159 159 show._to_draw.append(fig)
160 160 show._draw_called = True
161 161
162 162
163 163 def flush_figures():
164 164 """Send all figures that changed
165 165
166 166 This is meant to be called automatically and will call show() if, during
167 167 prior code execution, there had been any calls to draw_if_interactive.
168 168
169 169 This function is meant to be used as a post_execute callback in IPython,
170 170 so user-caused errors are handled with showtraceback() instead of being
171 171 allowed to raise. If this function is not called from within IPython,
172 172 then these exceptions will raise.
173 173 """
174 174 if not show._draw_called:
175 175 return
176 176
177 177 if InlineBackend.instance().close_figures:
178 178 # ignore the tracking, just draw and close all figures
179 179 try:
180 180 return show(True)
181 181 except Exception as e:
182 182 # safely show traceback if in IPython, else raise
183 183 try:
184 184 get_ipython
185 185 except NameError:
186 186 raise e
187 187 else:
188 188 get_ipython().showtraceback()
189 189 return
190 190 try:
191 191 # exclude any figures that were closed:
192 192 active = set([fm.canvas.figure for fm in Gcf.get_all_fig_managers()])
193 193 for fig in [ fig for fig in show._to_draw if fig in active ]:
194 194 try:
195 195 display(fig)
196 196 except Exception as e:
197 197 # safely show traceback if in IPython, else raise
198 198 try:
199 199 get_ipython
200 200 except NameError:
201 201 raise e
202 202 else:
203 203 get_ipython().showtraceback()
204 204 break
205 205 finally:
206 206 # clear flags for next round
207 207 show._to_draw = []
208 208 show._draw_called = False
209 209
210 210
211 211 # Changes to matplotlib in version 1.2 requires a mpl backend to supply a default
212 212 # figurecanvas. This is set here to a Agg canvas
213 213 # See https://github.com/matplotlib/matplotlib/pull/1125
214 214 FigureCanvas = FigureCanvasAgg
215 215
General Comments 0
You need to be logged in to leave comments. Login now