##// END OF EJS Templates
Switch correctly to the user's default matplotlib backend after inline....
Fernando Perez -
Show More
@@ -1,335 +1,338 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 backend = matplotlib.rcParams['backend']
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
219 # overwritten that.
220 backend = matplotlib.rcParamsOrig['backend']
218 # In this case, we need to find what the appropriate gui selection call
221 # In this case, we need to find what the appropriate gui selection call
219 # should be for IPython, so we can activate inputhook accordingly
222 # should be for IPython, so we can activate inputhook accordingly
220 gui = backend2gui.get(backend, None)
223 gui = backend2gui.get(backend, None)
221
224
222 # If we have already had a gui active, we need it and inline are the
225 # If we have already had a gui active, we need it and inline are the
223 # ones allowed.
226 # ones allowed.
224 if gui_select and gui != gui_select:
227 if gui_select and gui != gui_select:
225 gui = gui_select
228 gui = gui_select
226 backend = backends[gui]
229 backend = backends[gui]
227
230
228 return gui, backend
231 return gui, backend
229
232
230
233
231 def activate_matplotlib(backend):
234 def activate_matplotlib(backend):
232 """Activate the given backend and set interactive to True."""
235 """Activate the given backend and set interactive to True."""
233
236
234 import matplotlib
237 import matplotlib
235 matplotlib.interactive(True)
238 matplotlib.interactive(True)
236
239
237 # Matplotlib had a bug where even switch_backend could not force
240 # Matplotlib had a bug where even switch_backend could not force
238 # the rcParam to update. This needs to be set *before* the module
241 # the rcParam to update. This needs to be set *before* the module
239 # magic of switch_backend().
242 # magic of switch_backend().
240 matplotlib.rcParams['backend'] = backend
243 matplotlib.rcParams['backend'] = backend
241
244
242 import matplotlib.pyplot
245 import matplotlib.pyplot
243 matplotlib.pyplot.switch_backend(backend)
246 matplotlib.pyplot.switch_backend(backend)
244
247
245 # This must be imported last in the matplotlib series, after
248 # This must be imported last in the matplotlib series, after
246 # backend/interactivity choices have been made
249 # backend/interactivity choices have been made
247 import matplotlib.pylab as pylab
250 import matplotlib.pylab as pylab
248
251
249 pylab.show._needmain = False
252 pylab.show._needmain = False
250 # We need to detect at runtime whether show() is called by the user.
253 # We need to detect at runtime whether show() is called by the user.
251 # For this, we wrap it into a decorator which adds a 'called' flag.
254 # For this, we wrap it into a decorator which adds a 'called' flag.
252 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
255 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
253
256
254
257
255 def import_pylab(user_ns, import_all=True):
258 def import_pylab(user_ns, import_all=True):
256 """Populate the namespace with pylab-related values.
259 """Populate the namespace with pylab-related values.
257
260
258 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
261 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
259
262
260 Also imports a few names from IPython (figsize, display, getfigs)
263 Also imports a few names from IPython (figsize, display, getfigs)
261
264
262 """
265 """
263
266
264 # Import numpy as np/pyplot as plt are conventions we're trying to
267 # Import numpy as np/pyplot as plt are conventions we're trying to
265 # somewhat standardize on. Making them available to users by default
268 # somewhat standardize on. Making them available to users by default
266 # will greatly help this.
269 # will greatly help this.
267 s = ("import numpy\n"
270 s = ("import numpy\n"
268 "import matplotlib\n"
271 "import matplotlib\n"
269 "from matplotlib import pylab, mlab, pyplot\n"
272 "from matplotlib import pylab, mlab, pyplot\n"
270 "np = numpy\n"
273 "np = numpy\n"
271 "plt = pyplot\n"
274 "plt = pyplot\n"
272 )
275 )
273 exec s in user_ns
276 exec s in user_ns
274
277
275 if import_all:
278 if import_all:
276 s = ("from matplotlib.pylab import *\n"
279 s = ("from matplotlib.pylab import *\n"
277 "from numpy import *\n")
280 "from numpy import *\n")
278 exec s in user_ns
281 exec s in user_ns
279
282
280 # IPython symbols to add
283 # IPython symbols to add
281 user_ns['figsize'] = figsize
284 user_ns['figsize'] = figsize
282 from IPython.core.display import display
285 from IPython.core.display import display
283 # Add display and getfigs to the user's namespace
286 # Add display and getfigs to the user's namespace
284 user_ns['display'] = display
287 user_ns['display'] = display
285 user_ns['getfigs'] = getfigs
288 user_ns['getfigs'] = getfigs
286
289
287
290
288 def configure_inline_support(shell, backend):
291 def configure_inline_support(shell, backend):
289 """Configure an IPython shell object for matplotlib use.
292 """Configure an IPython shell object for matplotlib use.
290
293
291 Parameters
294 Parameters
292 ----------
295 ----------
293 shell : InteractiveShell instance
296 shell : InteractiveShell instance
294
297
295 backend : matplotlib backend
298 backend : matplotlib backend
296 """
299 """
297 # If using our svg payload backend, register the post-execution
300 # If using our svg payload backend, register the post-execution
298 # function that will pick up the results for display. This can only be
301 # function that will pick up the results for display. This can only be
299 # done with access to the real shell object.
302 # done with access to the real shell object.
300
303
301 # Note: if we can't load the inline backend, then there's no point
304 # Note: if we can't load the inline backend, then there's no point
302 # continuing (such as in terminal-only shells in environments without
305 # continuing (such as in terminal-only shells in environments without
303 # zeromq available).
306 # zeromq available).
304 try:
307 try:
305 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
308 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
306 except ImportError:
309 except ImportError:
307 return
310 return
308 from matplotlib import pyplot
311 from matplotlib import pyplot
309
312
310 cfg = InlineBackend.instance(parent=shell)
313 cfg = InlineBackend.instance(parent=shell)
311 cfg.shell = shell
314 cfg.shell = shell
312 if cfg not in shell.configurables:
315 if cfg not in shell.configurables:
313 shell.configurables.append(cfg)
316 shell.configurables.append(cfg)
314
317
315 if backend == backends['inline']:
318 if backend == backends['inline']:
316 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
319 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
317 shell.register_post_execute(flush_figures)
320 shell.register_post_execute(flush_figures)
318
321
319 # Save rcParams that will be overwrittern
322 # Save rcParams that will be overwrittern
320 shell._saved_rcParams = dict()
323 shell._saved_rcParams = dict()
321 for k in cfg.rc:
324 for k in cfg.rc:
322 shell._saved_rcParams[k] = pyplot.rcParams[k]
325 shell._saved_rcParams[k] = pyplot.rcParams[k]
323 # load inline_rc
326 # load inline_rc
324 pyplot.rcParams.update(cfg.rc)
327 pyplot.rcParams.update(cfg.rc)
325 else:
328 else:
326 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
329 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
327 if flush_figures in shell._post_execute:
330 if flush_figures in shell._post_execute:
328 shell._post_execute.pop(flush_figures)
331 shell._post_execute.pop(flush_figures)
329 if hasattr(shell, '_saved_rcParams'):
332 if hasattr(shell, '_saved_rcParams'):
330 pyplot.rcParams.update(shell._saved_rcParams)
333 pyplot.rcParams.update(shell._saved_rcParams)
331 del shell._saved_rcParams
334 del shell._saved_rcParams
332
335
333 # Setup the default figure format
336 # Setup the default figure format
334 select_figure_format(shell, cfg.figure_format)
337 select_figure_format(shell, cfg.figure_format)
335
338
General Comments 0
You need to be logged in to leave comments. Login now