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