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