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