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