##// END OF EJS Templates
add InlineBackendConfig...
MinRK -
Show More
@@ -1,309 +1,323 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 from cStringIO import StringIO
23 23
24 24 from IPython.utils.decorators import flag_calls
25 25
26 26 # If user specifies a GUI, that dictates the backend, otherwise we read the
27 27 # user's mpl default from the mpl rc structure
28 28 backends = {'tk': 'TkAgg',
29 29 'gtk': 'GTKAgg',
30 30 'wx': 'WXAgg',
31 31 'qt': 'Qt4Agg', # qt3 not supported
32 32 'qt4': 'Qt4Agg',
33 33 'osx': 'MacOSX',
34 34 'inline' : 'module://IPython.zmq.pylab.backend_inline'}
35 35
36 36 # We also need a reverse backends2guis mapping that will properly choose which
37 37 # GUI support to activate based on the desired matplotlib backend. For the
38 38 # most part it's just a reverse of the above dict, but we also need to add a
39 39 # few others that map to the same GUI manually:
40 40 backend2gui = dict(zip(backends.values(), backends.keys()))
41 41 # In the reverse mapping, there are a few extra valid matplotlib backends that
42 42 # map to the same GUI support
43 43 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
44 44 backend2gui['WX'] = 'wx'
45 45 backend2gui['CocoaAgg'] = 'osx'
46 46
47 47 #-----------------------------------------------------------------------------
48 48 # Matplotlib utilities
49 49 #-----------------------------------------------------------------------------
50 50
51 51
52 52 def getfigs(*fig_nums):
53 53 """Get a list of matplotlib figures by figure numbers.
54 54
55 55 If no arguments are given, all available figures are returned. If the
56 56 argument list contains references to invalid figures, a warning is printed
57 57 but the function continues pasting further figures.
58 58
59 59 Parameters
60 60 ----------
61 61 figs : tuple
62 62 A tuple of ints giving the figure numbers of the figures to return.
63 63 """
64 64 from matplotlib._pylab_helpers import Gcf
65 65 if not fig_nums:
66 66 fig_managers = Gcf.get_all_fig_managers()
67 67 return [fm.canvas.figure for fm in fig_managers]
68 68 else:
69 69 figs = []
70 70 for num in fig_nums:
71 71 f = Gcf.figs.get(num)
72 72 if f is None:
73 73 print('Warning: figure %s not available.' % num)
74 74 else:
75 75 figs.append(f.canvas.figure)
76 76 return figs
77 77
78 78
79 79 def figsize(sizex, sizey):
80 80 """Set the default figure size to be [sizex, sizey].
81 81
82 82 This is just an easy to remember, convenience wrapper that sets::
83 83
84 84 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
85 85 """
86 86 import matplotlib
87 87 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
88 88
89 89
90 def figure_to_svg(fig):
91 """Convert a figure to svg for inline display."""
90 def print_figure(fig, fmt='png'):
91 """Convert a figure to svg or png for inline display."""
92 92 # When there's an empty figure, we shouldn't return anything, otherwise we
93 93 # get big blank areas in the qt console.
94 94 if not fig.axes:
95 95 return
96 96
97 97 fc = fig.get_facecolor()
98 98 ec = fig.get_edgecolor()
99 99 fig.set_facecolor('white')
100 100 fig.set_edgecolor('white')
101 101 try:
102 102 string_io = StringIO()
103 fig.canvas.print_figure(string_io, format='svg')
104 svg = string_io.getvalue()
103 # use 72 dpi to match QTConsole's dpi
104 fig.canvas.print_figure(string_io, format=fmt, dpi=72)
105 data = string_io.getvalue()
105 106 finally:
106 107 fig.set_facecolor(fc)
107 108 fig.set_edgecolor(ec)
108 return svg
109 if fmt == 'png':
110 data = data.encode('base64')
111 return data
109 112
110 113
111 114 # We need a little factory function here to create the closure where
112 115 # safe_execfile can live.
113 116 def mpl_runner(safe_execfile):
114 117 """Factory to return a matplotlib-enabled runner for %run.
115 118
116 119 Parameters
117 120 ----------
118 121 safe_execfile : function
119 122 This must be a function with the same interface as the
120 123 :meth:`safe_execfile` method of IPython.
121 124
122 125 Returns
123 126 -------
124 127 A function suitable for use as the ``runner`` argument of the %run magic
125 128 function.
126 129 """
127 130
128 131 def mpl_execfile(fname,*where,**kw):
129 132 """matplotlib-aware wrapper around safe_execfile.
130 133
131 134 Its interface is identical to that of the :func:`execfile` builtin.
132 135
133 136 This is ultimately a call to execfile(), but wrapped in safeties to
134 137 properly handle interactive rendering."""
135 138
136 139 import matplotlib
137 140 import matplotlib.pylab as pylab
138 141
139 142 #print '*** Matplotlib runner ***' # dbg
140 143 # turn off rendering until end of script
141 144 is_interactive = matplotlib.rcParams['interactive']
142 145 matplotlib.interactive(False)
143 146 safe_execfile(fname,*where,**kw)
144 147 matplotlib.interactive(is_interactive)
145 148 # make rendering call now, if the user tried to do it
146 149 if pylab.draw_if_interactive.called:
147 150 pylab.draw()
148 151 pylab.draw_if_interactive.called = False
149 152
150 153 return mpl_execfile
151 154
152 155
156 def select_figure_format(shell, fmt):
157 """Select figure format for inline backend, either 'png' or 'svg'.
158
159 Using this method ensures only one figure format is active at a time.
160 """
161 from matplotlib.figure import Figure
162 from IPython.zmq.pylab import backend_inline
163
164 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
165 png_formatter = shell.display_formatter.formatters['image/png']
166
167 if fmt=='png':
168 svg_formatter.type_printers.pop(Figure, None)
169 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
170 elif fmt=='svg':
171 png_formatter.type_printers.pop(Figure, None)
172 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
173 else:
174 raise ValueError("supported formats are: 'png', 'svg', not %r"%fmt)
175
176 # set the format to be used in the backend()
177 backend_inline._figure_format = fmt
178
153 179 #-----------------------------------------------------------------------------
154 180 # Code for initializing matplotlib and importing pylab
155 181 #-----------------------------------------------------------------------------
156 182
157 183
158 184 def find_gui_and_backend(gui=None):
159 185 """Given a gui string return the gui and mpl backend.
160 186
161 187 Parameters
162 188 ----------
163 189 gui : str
164 190 Can be one of ('tk','gtk','wx','qt','qt4','inline').
165 191
166 192 Returns
167 193 -------
168 194 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
169 195 'WXAgg','Qt4Agg','module://IPython.zmq.pylab.backend_inline').
170 196 """
171 197
172 198 import matplotlib
173 199
174 200 if gui:
175 201 # select backend based on requested gui
176 202 backend = backends[gui]
177 203 else:
178 204 backend = matplotlib.rcParams['backend']
179 205 # In this case, we need to find what the appropriate gui selection call
180 206 # should be for IPython, so we can activate inputhook accordingly
181 207 gui = backend2gui.get(backend, None)
182 208 return gui, backend
183 209
184 210
185 211 def activate_matplotlib(backend):
186 212 """Activate the given backend and set interactive to True."""
187 213
188 214 import matplotlib
189 215 if backend.startswith('module://'):
190 216 # Work around bug in matplotlib: matplotlib.use converts the
191 217 # backend_id to lowercase even if a module name is specified!
192 218 matplotlib.rcParams['backend'] = backend
193 219 else:
194 220 matplotlib.use(backend)
195 221 matplotlib.interactive(True)
196 222
197 223 # This must be imported last in the matplotlib series, after
198 224 # backend/interactivity choices have been made
199 225 import matplotlib.pylab as pylab
200 226
201 227 # XXX For now leave this commented out, but depending on discussions with
202 228 # mpl-dev, we may be able to allow interactive switching...
203 229 #import matplotlib.pyplot
204 230 #matplotlib.pyplot.switch_backend(backend)
205 231
206 232 pylab.show._needmain = False
207 233 # We need to detect at runtime whether show() is called by the user.
208 234 # For this, we wrap it into a decorator which adds a 'called' flag.
209 235 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
210 236
211
212 237 def import_pylab(user_ns, backend, import_all=True, shell=None):
213 238 """Import the standard pylab symbols into user_ns."""
214 239
215 240 # Import numpy as np/pyplot as plt are conventions we're trying to
216 241 # somewhat standardize on. Making them available to users by default
217 242 # will greatly help this.
218 243 s = ("import numpy\n"
219 244 "import matplotlib\n"
220 245 "from matplotlib import pylab, mlab, pyplot\n"
221 246 "np = numpy\n"
222 247 "plt = pyplot\n"
223 248 )
224 249 exec s in user_ns
225 250
226 251 if shell is not None:
227 252 exec s in shell.user_ns_hidden
228 253 # If using our svg payload backend, register the post-execution
229 254 # function that will pick up the results for display. This can only be
230 255 # done with access to the real shell object.
256 #
257 from IPython.zmq.pylab.backend_inline import InlineBackendConfig
258
259 cfg = InlineBackendConfig.instance(config=shell.config)
260 cfg.shell = shell
261
231 262 if backend == backends['inline']:
232 from IPython.zmq.pylab.backend_inline import flush_svg
263 from IPython.zmq.pylab.backend_inline import flush_figures
233 264 from matplotlib import pyplot
234 shell.register_post_execute(flush_svg)
235 # The typical default figure size is too large for inline use,
236 # so we shrink the figure size to 6x4, and tweak fonts to
237 # make that fit. This is configurable via Global.pylab_inline_rc,
238 # or rather it will be once the zmq kernel is hooked up to
239 # the config system.
240
241 default_rc = {
242 'figure.figsize': (6.0,4.0),
243 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
244 'font.size': 10,
245 # 10pt still needs a little more room on the xlabel:
246 'figure.subplot.bottom' : .125
247 }
248 rc = getattr(shell.config.Global, 'pylab_inline_rc', default_rc)
249 pyplot.rcParams.update(rc)
250 shell.config.Global.pylab_inline_rc = rc
265 shell.register_post_execute(flush_figures)
266 # load inline_rc
267 pyplot.rcParams.update(cfg.rc)
251 268
252 269 # Add 'figsize' to pyplot and to the user's namespace
253 270 user_ns['figsize'] = pyplot.figsize = figsize
254 271 shell.user_ns_hidden['figsize'] = figsize
255 272
273 # Setup the default figure format
274 fmt = cfg.figure_format
275 select_figure_format(shell, fmt)
276
256 277 # The old pastefig function has been replaced by display
257 # Always add this svg formatter so display works.
258 from IPython.core.display import display, display_svg
259 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
260 svg_formatter.for_type_by_name(
261 'matplotlib.figure','Figure',figure_to_svg
262 )
278 from IPython.core.display import display
263 279 # Add display and display_png to the user's namespace
264 280 user_ns['display'] = display
265 281 shell.user_ns_hidden['display'] = display
266 user_ns['display_svg'] = display_svg
267 shell.user_ns_hidden['display_svg'] = display_svg
268 282 user_ns['getfigs'] = getfigs
269 283 shell.user_ns_hidden['getfigs'] = getfigs
270 284
271 285 if import_all:
272 286 s = ("from matplotlib.pylab import *\n"
273 287 "from numpy import *\n")
274 288 exec s in user_ns
275 289 if shell is not None:
276 290 exec s in shell.user_ns_hidden
277 291
278 292
279 293 def pylab_activate(user_ns, gui=None, import_all=True):
280 294 """Activate pylab mode in the user's namespace.
281 295
282 296 Loads and initializes numpy, matplotlib and friends for interactive use.
283 297
284 298 Parameters
285 299 ----------
286 300 user_ns : dict
287 301 Namespace where the imports will occur.
288 302
289 303 gui : optional, string
290 304 A valid gui name following the conventions of the %gui magic.
291 305
292 306 import_all : optional, boolean
293 307 If true, an 'import *' is done from numpy and pylab.
294 308
295 309 Returns
296 310 -------
297 311 The actual gui used (if not given as input, it was obtained from matplotlib
298 312 itself, and will be needed next to configure IPython's gui integration.
299 313 """
300 314 gui, backend = find_gui_and_backend(gui)
301 315 activate_matplotlib(backend)
302 316 import_pylab(user_ns, backend)
303 317
304 318 print """
305 319 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
306 320 For more information, type 'help(pylab)'.""" % backend
307 321
308 322 return gui
309 323
@@ -1,81 +1,117 b''
1 1 """Produce SVG versions of active plots for display by the rich Qt frontend.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Imports
5 5 #-----------------------------------------------------------------------------
6 6 from __future__ import print_function
7 7
8 8 # Standard library imports
9 9 import sys
10 10
11 11 # Third-party imports
12 12 import matplotlib
13 from matplotlib.backends.backend_svg import new_figure_manager
13 from matplotlib.backends.backend_agg import new_figure_manager
14 14 from matplotlib._pylab_helpers import Gcf
15 15
16 16 # Local imports.
17 from IPython.config.configurable import SingletonConfigurable
17 18 from IPython.core.displaypub import publish_display_data
18 from IPython.lib.pylabtools import figure_to_svg
19 from IPython.lib.pylabtools import print_figure, select_figure_format
20 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum
21 #-----------------------------------------------------------------------------
22 # Configurable for inline backend options
23 #-----------------------------------------------------------------------------
24
25 class InlineBackendConfig(SingletonConfigurable):
26 """An object to store configuration of the inline backend."""
27
28 # The typical default figure size is too large for inline use,
29 # so we shrink the figure size to 6x4, and tweak fonts to
30 # make that fit. This is configurable via Global.pylab_inline_rc,
31 # or rather it will be once the zmq kernel is hooked up to
32 # the config system.
33 rc = Dict({'figure.figsize': (6.0,4.0),
34 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
35 'font.size': 10,
36 # 10pt still needs a little more room on the xlabel:
37 'figure.subplot.bottom' : .125
38 }, config=True,
39 help="""Subset of matplotlib rcParams that should be different for the
40 inline backend."""
41 )
42 figure_format = CaselessStrEnum(['svg', 'png'], default_value='png', config=True,
43 help="The image format for figures with the inline backend.")
44
45 def _format_changed(self, name, old, new):
46 if self.shell is None:
47 return
48 else:
49 select_figure_format(self.shell, new)
50
51 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
52
19 53
20 54 #-----------------------------------------------------------------------------
21 55 # Functions
22 56 #-----------------------------------------------------------------------------
23 57
24 58 def show(close=True):
25 59 """Show all figures as SVG payloads sent to the IPython clients.
26 60
27 61 Parameters
28 62 ----------
29 63 close : bool, optional
30 64 If true, a ``plt.close('all')`` call is automatically issued after
31 65 sending all the SVG figures. If this is set, the figures will entirely
32 66 removed from the internal list of figures.
33 67 """
34 68 for figure_manager in Gcf.get_all_fig_managers():
35 send_svg_figure(figure_manager.canvas.figure)
69 send_figure(figure_manager.canvas.figure)
36 70 if close:
37 71 matplotlib.pyplot.close('all')
38 72
39 73
40 74 # This flag will be reset by draw_if_interactive when called
41 75 show._draw_called = False
42 76
43 77
44 78 def draw_if_interactive():
45 79 """
46 80 Is called after every pylab drawing command
47 81 """
48 82 # We simply flag we were called and otherwise do nothing. At the end of
49 83 # the code execution, a separate call to show_close() will act upon this.
50 84 show._draw_called = True
51 85
52 86
53 def flush_svg():
54 """Call show, close all open figures, sending all SVG images.
87 def flush_figures():
88 """Call show, close all open figures, sending all figure images.
55 89
56 90 This is meant to be called automatically and will call show() if, during
57 91 prior code execution, there had been any calls to draw_if_interactive.
58 92 """
59 93 if show._draw_called:
60 94 show()
61 95 show._draw_called = False
62 96
63 97
64 def send_svg_figure(fig):
65 """Draw the current figure and send it as an SVG payload.
98 def send_figure(fig):
99 """Draw the current figure and send it as a PNG payload.
66 100 """
67 101 # For an empty figure, don't even bother calling figure_to_svg, to avoid
68 102 # big blank spaces in the qt console
69 103 if not fig.axes:
70 104 return
71
72 svg = figure_to_svg(fig)
105 fmt = InlineBackendConfig.instance().figure_format
106 data = print_figure(fig, fmt)
107 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
108 mime = mimetypes[fmt]
73 109 # flush text streams before sending figures, helps a little with output
74 110 # synchronization in the console (though it's a bandaid, not a real sln)
75 111 sys.stdout.flush(); sys.stderr.flush()
76 112 publish_display_data(
77 'IPython.zmq.pylab.backend_inline.send_svg_figure',
113 'IPython.zmq.pylab.backend_inline.send_figure',
78 114 'Matplotlib Plot',
79 {'image/svg+xml' : svg}
115 {mime : data}
80 116 )
81 117
General Comments 0
You need to be logged in to leave comments. Login now