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