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