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