Show More
@@ -0,0 +1,3 | |||
|
1 | * added ``InlineBackend.print_figure_kwargs`` to allow passing keyword arguments | |
|
2 | to matplotlib's ``Canvas.print_figure``. This can be used to change the value of | |
|
3 | ``bbox_inches``, which is 'tight' by default, or set the quality of JPEG figures. |
@@ -1,772 +1,778 | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """Top-level display functions for displaying object in different formats. |
|
3 | 3 | |
|
4 | 4 | Authors: |
|
5 | 5 | |
|
6 | 6 | * Brian Granger |
|
7 | 7 | """ |
|
8 | 8 | |
|
9 | 9 | #----------------------------------------------------------------------------- |
|
10 | 10 | # Copyright (C) 2013 The IPython Development Team |
|
11 | 11 | # |
|
12 | 12 | # Distributed under the terms of the BSD License. The full license is in |
|
13 | 13 | # the file COPYING, distributed as part of this software. |
|
14 | 14 | #----------------------------------------------------------------------------- |
|
15 | 15 | |
|
16 | 16 | #----------------------------------------------------------------------------- |
|
17 | 17 | # Imports |
|
18 | 18 | #----------------------------------------------------------------------------- |
|
19 | 19 | |
|
20 | 20 | from __future__ import print_function |
|
21 | 21 | |
|
22 | 22 | import os |
|
23 | 23 | import struct |
|
24 | 24 | |
|
25 | 25 | from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode, |
|
26 | 26 | unicode_type) |
|
27 | 27 | from IPython.testing.skipdoctest import skip_doctest |
|
28 | 28 | from .displaypub import publish_display_data |
|
29 | 29 | |
|
30 | 30 | #----------------------------------------------------------------------------- |
|
31 | 31 | # utility functions |
|
32 | 32 | #----------------------------------------------------------------------------- |
|
33 | 33 | |
|
34 | 34 | def _safe_exists(path): |
|
35 | 35 | """Check path, but don't let exceptions raise""" |
|
36 | 36 | try: |
|
37 | 37 | return os.path.exists(path) |
|
38 | 38 | except Exception: |
|
39 | 39 | return False |
|
40 | 40 | |
|
41 | 41 | def _merge(d1, d2): |
|
42 | 42 | """Like update, but merges sub-dicts instead of clobbering at the top level. |
|
43 | 43 | |
|
44 | 44 | Updates d1 in-place |
|
45 | 45 | """ |
|
46 | 46 | |
|
47 | 47 | if not isinstance(d2, dict) or not isinstance(d1, dict): |
|
48 | 48 | return d2 |
|
49 | 49 | for key, value in d2.items(): |
|
50 | 50 | d1[key] = _merge(d1.get(key), value) |
|
51 | 51 | return d1 |
|
52 | 52 | |
|
53 | 53 | def _display_mimetype(mimetype, objs, raw=False, metadata=None): |
|
54 | 54 | """internal implementation of all display_foo methods |
|
55 | 55 | |
|
56 | 56 | Parameters |
|
57 | 57 | ---------- |
|
58 | 58 | mimetype : str |
|
59 | 59 | The mimetype to be published (e.g. 'image/png') |
|
60 | 60 | objs : tuple of objects |
|
61 | 61 | The Python objects to display, or if raw=True raw text data to |
|
62 | 62 | display. |
|
63 | 63 | raw : bool |
|
64 | 64 | Are the data objects raw data or Python objects that need to be |
|
65 | 65 | formatted before display? [default: False] |
|
66 | 66 | metadata : dict (optional) |
|
67 | 67 | Metadata to be associated with the specific mimetype output. |
|
68 | 68 | """ |
|
69 | 69 | if metadata: |
|
70 | 70 | metadata = {mimetype: metadata} |
|
71 | 71 | if raw: |
|
72 | 72 | # turn list of pngdata into list of { 'image/png': pngdata } |
|
73 | 73 | objs = [ {mimetype: obj} for obj in objs ] |
|
74 | 74 | display(*objs, raw=raw, metadata=metadata, include=[mimetype]) |
|
75 | 75 | |
|
76 | 76 | #----------------------------------------------------------------------------- |
|
77 | 77 | # Main functions |
|
78 | 78 | #----------------------------------------------------------------------------- |
|
79 | 79 | |
|
80 | 80 | def display(*objs, **kwargs): |
|
81 | 81 | """Display a Python object in all frontends. |
|
82 | 82 | |
|
83 | 83 | By default all representations will be computed and sent to the frontends. |
|
84 | 84 | Frontends can decide which representation is used and how. |
|
85 | 85 | |
|
86 | 86 | Parameters |
|
87 | 87 | ---------- |
|
88 | 88 | objs : tuple of objects |
|
89 | 89 | The Python objects to display. |
|
90 | 90 | raw : bool, optional |
|
91 | 91 | Are the objects to be displayed already mimetype-keyed dicts of raw display data, |
|
92 | 92 | or Python objects that need to be formatted before display? [default: False] |
|
93 | 93 | include : list or tuple, optional |
|
94 | 94 | A list of format type strings (MIME types) to include in the |
|
95 | 95 | format data dict. If this is set *only* the format types included |
|
96 | 96 | in this list will be computed. |
|
97 | 97 | exclude : list or tuple, optional |
|
98 | 98 | A list of format type strings (MIME types) to exclude in the format |
|
99 | 99 | data dict. If this is set all format types will be computed, |
|
100 | 100 | except for those included in this argument. |
|
101 | 101 | metadata : dict, optional |
|
102 | 102 | A dictionary of metadata to associate with the output. |
|
103 | 103 | mime-type keys in this dictionary will be associated with the individual |
|
104 | 104 | representation formats, if they exist. |
|
105 | 105 | """ |
|
106 | 106 | raw = kwargs.get('raw', False) |
|
107 | 107 | include = kwargs.get('include') |
|
108 | 108 | exclude = kwargs.get('exclude') |
|
109 | 109 | metadata = kwargs.get('metadata') |
|
110 | 110 | |
|
111 | 111 | from IPython.core.interactiveshell import InteractiveShell |
|
112 | 112 | |
|
113 | 113 | if not raw: |
|
114 | 114 | format = InteractiveShell.instance().display_formatter.format |
|
115 | 115 | |
|
116 | 116 | for obj in objs: |
|
117 | 117 | |
|
118 | 118 | # If _ipython_display_ is defined, use that to display this object. |
|
119 | 119 | display_method = getattr(obj, '_ipython_display_', None) |
|
120 | 120 | if display_method is not None: |
|
121 | 121 | try: |
|
122 | 122 | display_method(**kwargs) |
|
123 | 123 | except NotImplementedError: |
|
124 | 124 | pass |
|
125 | 125 | else: |
|
126 | 126 | continue |
|
127 | 127 | if raw: |
|
128 | 128 | publish_display_data('display', obj, metadata) |
|
129 | 129 | else: |
|
130 | 130 | format_dict, md_dict = format(obj, include=include, exclude=exclude) |
|
131 | 131 | if metadata: |
|
132 | 132 | # kwarg-specified metadata gets precedence |
|
133 | 133 | _merge(md_dict, metadata) |
|
134 | 134 | publish_display_data('display', format_dict, md_dict) |
|
135 | 135 | |
|
136 | 136 | |
|
137 | 137 | def display_pretty(*objs, **kwargs): |
|
138 | 138 | """Display the pretty (default) representation of an object. |
|
139 | 139 | |
|
140 | 140 | Parameters |
|
141 | 141 | ---------- |
|
142 | 142 | objs : tuple of objects |
|
143 | 143 | The Python objects to display, or if raw=True raw text data to |
|
144 | 144 | display. |
|
145 | 145 | raw : bool |
|
146 | 146 | Are the data objects raw data or Python objects that need to be |
|
147 | 147 | formatted before display? [default: False] |
|
148 | 148 | metadata : dict (optional) |
|
149 | 149 | Metadata to be associated with the specific mimetype output. |
|
150 | 150 | """ |
|
151 | 151 | _display_mimetype('text/plain', objs, **kwargs) |
|
152 | 152 | |
|
153 | 153 | |
|
154 | 154 | def display_html(*objs, **kwargs): |
|
155 | 155 | """Display the HTML representation of an object. |
|
156 | 156 | |
|
157 | 157 | Parameters |
|
158 | 158 | ---------- |
|
159 | 159 | objs : tuple of objects |
|
160 | 160 | The Python objects to display, or if raw=True raw HTML data to |
|
161 | 161 | display. |
|
162 | 162 | raw : bool |
|
163 | 163 | Are the data objects raw data or Python objects that need to be |
|
164 | 164 | formatted before display? [default: False] |
|
165 | 165 | metadata : dict (optional) |
|
166 | 166 | Metadata to be associated with the specific mimetype output. |
|
167 | 167 | """ |
|
168 | 168 | _display_mimetype('text/html', objs, **kwargs) |
|
169 | 169 | |
|
170 | 170 | |
|
171 | 171 | def display_svg(*objs, **kwargs): |
|
172 | 172 | """Display the SVG representation of an object. |
|
173 | 173 | |
|
174 | 174 | Parameters |
|
175 | 175 | ---------- |
|
176 | 176 | objs : tuple of objects |
|
177 | 177 | The Python objects to display, or if raw=True raw svg data to |
|
178 | 178 | display. |
|
179 | 179 | raw : bool |
|
180 | 180 | Are the data objects raw data or Python objects that need to be |
|
181 | 181 | formatted before display? [default: False] |
|
182 | 182 | metadata : dict (optional) |
|
183 | 183 | Metadata to be associated with the specific mimetype output. |
|
184 | 184 | """ |
|
185 | 185 | _display_mimetype('image/svg+xml', objs, **kwargs) |
|
186 | 186 | |
|
187 | 187 | |
|
188 | 188 | def display_png(*objs, **kwargs): |
|
189 | 189 | """Display the PNG representation of an object. |
|
190 | 190 | |
|
191 | 191 | Parameters |
|
192 | 192 | ---------- |
|
193 | 193 | objs : tuple of objects |
|
194 | 194 | The Python objects to display, or if raw=True raw png data to |
|
195 | 195 | display. |
|
196 | 196 | raw : bool |
|
197 | 197 | Are the data objects raw data or Python objects that need to be |
|
198 | 198 | formatted before display? [default: False] |
|
199 | 199 | metadata : dict (optional) |
|
200 | 200 | Metadata to be associated with the specific mimetype output. |
|
201 | 201 | """ |
|
202 | 202 | _display_mimetype('image/png', objs, **kwargs) |
|
203 | 203 | |
|
204 | 204 | |
|
205 | 205 | def display_jpeg(*objs, **kwargs): |
|
206 | 206 | """Display the JPEG representation of an object. |
|
207 | 207 | |
|
208 | 208 | Parameters |
|
209 | 209 | ---------- |
|
210 | 210 | objs : tuple of objects |
|
211 | 211 | The Python objects to display, or if raw=True raw JPEG data to |
|
212 | 212 | display. |
|
213 | 213 | raw : bool |
|
214 | 214 | Are the data objects raw data or Python objects that need to be |
|
215 | 215 | formatted before display? [default: False] |
|
216 | 216 | metadata : dict (optional) |
|
217 | 217 | Metadata to be associated with the specific mimetype output. |
|
218 | 218 | """ |
|
219 | 219 | _display_mimetype('image/jpeg', objs, **kwargs) |
|
220 | 220 | |
|
221 | 221 | |
|
222 | 222 | def display_latex(*objs, **kwargs): |
|
223 | 223 | """Display the LaTeX representation of an object. |
|
224 | 224 | |
|
225 | 225 | Parameters |
|
226 | 226 | ---------- |
|
227 | 227 | objs : tuple of objects |
|
228 | 228 | The Python objects to display, or if raw=True raw latex data to |
|
229 | 229 | display. |
|
230 | 230 | raw : bool |
|
231 | 231 | Are the data objects raw data or Python objects that need to be |
|
232 | 232 | formatted before display? [default: False] |
|
233 | 233 | metadata : dict (optional) |
|
234 | 234 | Metadata to be associated with the specific mimetype output. |
|
235 | 235 | """ |
|
236 | 236 | _display_mimetype('text/latex', objs, **kwargs) |
|
237 | 237 | |
|
238 | 238 | |
|
239 | 239 | def display_json(*objs, **kwargs): |
|
240 | 240 | """Display the JSON representation of an object. |
|
241 | 241 | |
|
242 | 242 | Note that not many frontends support displaying JSON. |
|
243 | 243 | |
|
244 | 244 | Parameters |
|
245 | 245 | ---------- |
|
246 | 246 | objs : tuple of objects |
|
247 | 247 | The Python objects to display, or if raw=True raw json data to |
|
248 | 248 | display. |
|
249 | 249 | raw : bool |
|
250 | 250 | Are the data objects raw data or Python objects that need to be |
|
251 | 251 | formatted before display? [default: False] |
|
252 | 252 | metadata : dict (optional) |
|
253 | 253 | Metadata to be associated with the specific mimetype output. |
|
254 | 254 | """ |
|
255 | 255 | _display_mimetype('application/json', objs, **kwargs) |
|
256 | 256 | |
|
257 | 257 | |
|
258 | 258 | def display_javascript(*objs, **kwargs): |
|
259 | 259 | """Display the Javascript representation of an object. |
|
260 | 260 | |
|
261 | 261 | Parameters |
|
262 | 262 | ---------- |
|
263 | 263 | objs : tuple of objects |
|
264 | 264 | The Python objects to display, or if raw=True raw javascript data to |
|
265 | 265 | display. |
|
266 | 266 | raw : bool |
|
267 | 267 | Are the data objects raw data or Python objects that need to be |
|
268 | 268 | formatted before display? [default: False] |
|
269 | 269 | metadata : dict (optional) |
|
270 | 270 | Metadata to be associated with the specific mimetype output. |
|
271 | 271 | """ |
|
272 | 272 | _display_mimetype('application/javascript', objs, **kwargs) |
|
273 | 273 | |
|
274 | 274 | |
|
275 | 275 | def display_pdf(*objs, **kwargs): |
|
276 | 276 | """Display the PDF representation of an object. |
|
277 | 277 | |
|
278 | 278 | Parameters |
|
279 | 279 | ---------- |
|
280 | 280 | objs : tuple of objects |
|
281 | 281 | The Python objects to display, or if raw=True raw javascript data to |
|
282 | 282 | display. |
|
283 | 283 | raw : bool |
|
284 | 284 | Are the data objects raw data or Python objects that need to be |
|
285 | 285 | formatted before display? [default: False] |
|
286 | 286 | metadata : dict (optional) |
|
287 | 287 | Metadata to be associated with the specific mimetype output. |
|
288 | 288 | """ |
|
289 | 289 | _display_mimetype('application/pdf', objs, **kwargs) |
|
290 | 290 | |
|
291 | 291 | |
|
292 | 292 | #----------------------------------------------------------------------------- |
|
293 | 293 | # Smart classes |
|
294 | 294 | #----------------------------------------------------------------------------- |
|
295 | 295 | |
|
296 | 296 | |
|
297 | 297 | class DisplayObject(object): |
|
298 | 298 | """An object that wraps data to be displayed.""" |
|
299 | 299 | |
|
300 | 300 | _read_flags = 'r' |
|
301 | 301 | |
|
302 | 302 | def __init__(self, data=None, url=None, filename=None): |
|
303 | 303 | """Create a display object given raw data. |
|
304 | 304 | |
|
305 | 305 | When this object is returned by an expression or passed to the |
|
306 | 306 | display function, it will result in the data being displayed |
|
307 | 307 | in the frontend. The MIME type of the data should match the |
|
308 | 308 | subclasses used, so the Png subclass should be used for 'image/png' |
|
309 | 309 | data. If the data is a URL, the data will first be downloaded |
|
310 | 310 | and then displayed. If |
|
311 | 311 | |
|
312 | 312 | Parameters |
|
313 | 313 | ---------- |
|
314 | 314 | data : unicode, str or bytes |
|
315 | 315 | The raw data or a URL or file to load the data from |
|
316 | 316 | url : unicode |
|
317 | 317 | A URL to download the data from. |
|
318 | 318 | filename : unicode |
|
319 | 319 | Path to a local file to load the data from. |
|
320 | 320 | """ |
|
321 | 321 | if data is not None and isinstance(data, string_types): |
|
322 | 322 | if data.startswith('http') and url is None: |
|
323 | 323 | url = data |
|
324 | 324 | filename = None |
|
325 | 325 | data = None |
|
326 | 326 | elif _safe_exists(data) and filename is None: |
|
327 | 327 | url = None |
|
328 | 328 | filename = data |
|
329 | 329 | data = None |
|
330 | 330 | |
|
331 | 331 | self.data = data |
|
332 | 332 | self.url = url |
|
333 | 333 | self.filename = None if filename is None else unicode_type(filename) |
|
334 | 334 | |
|
335 | 335 | self.reload() |
|
336 | 336 | self._check_data() |
|
337 | 337 | |
|
338 | 338 | def _check_data(self): |
|
339 | 339 | """Override in subclasses if there's something to check.""" |
|
340 | 340 | pass |
|
341 | 341 | |
|
342 | 342 | def reload(self): |
|
343 | 343 | """Reload the raw data from file or URL.""" |
|
344 | 344 | if self.filename is not None: |
|
345 | 345 | with open(self.filename, self._read_flags) as f: |
|
346 | 346 | self.data = f.read() |
|
347 | 347 | elif self.url is not None: |
|
348 | 348 | try: |
|
349 | 349 | try: |
|
350 | 350 | from urllib.request import urlopen # Py3 |
|
351 | 351 | except ImportError: |
|
352 | 352 | from urllib2 import urlopen |
|
353 | 353 | response = urlopen(self.url) |
|
354 | 354 | self.data = response.read() |
|
355 | 355 | # extract encoding from header, if there is one: |
|
356 | 356 | encoding = None |
|
357 | 357 | for sub in response.headers['content-type'].split(';'): |
|
358 | 358 | sub = sub.strip() |
|
359 | 359 | if sub.startswith('charset'): |
|
360 | 360 | encoding = sub.split('=')[-1].strip() |
|
361 | 361 | break |
|
362 | 362 | # decode data, if an encoding was specified |
|
363 | 363 | if encoding: |
|
364 | 364 | self.data = self.data.decode(encoding, 'replace') |
|
365 | 365 | except: |
|
366 | 366 | self.data = None |
|
367 | 367 | |
|
368 | 368 | class TextDisplayObject(DisplayObject): |
|
369 | 369 | """Validate that display data is text""" |
|
370 | 370 | def _check_data(self): |
|
371 | 371 | if self.data is not None and not isinstance(self.data, string_types): |
|
372 | 372 | raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data)) |
|
373 | 373 | |
|
374 | 374 | class Pretty(TextDisplayObject): |
|
375 | 375 | |
|
376 | 376 | def _repr_pretty_(self): |
|
377 | 377 | return self.data |
|
378 | 378 | |
|
379 | 379 | |
|
380 | 380 | class HTML(TextDisplayObject): |
|
381 | 381 | |
|
382 | 382 | def _repr_html_(self): |
|
383 | 383 | return self.data |
|
384 | 384 | |
|
385 | 385 | def __html__(self): |
|
386 | 386 | """ |
|
387 | 387 | This method exists to inform other HTML-using modules (e.g. Markupsafe, |
|
388 | 388 | htmltag, etc) that this object is HTML and does not need things like |
|
389 | 389 | special characters (<>&) escaped. |
|
390 | 390 | """ |
|
391 | 391 | return self._repr_html_() |
|
392 | 392 | |
|
393 | 393 | |
|
394 | 394 | class Math(TextDisplayObject): |
|
395 | 395 | |
|
396 | 396 | def _repr_latex_(self): |
|
397 | 397 | s = self.data.strip('$') |
|
398 | 398 | return "$$%s$$" % s |
|
399 | 399 | |
|
400 | 400 | |
|
401 | 401 | class Latex(TextDisplayObject): |
|
402 | 402 | |
|
403 | 403 | def _repr_latex_(self): |
|
404 | 404 | return self.data |
|
405 | 405 | |
|
406 | 406 | |
|
407 | 407 | class SVG(DisplayObject): |
|
408 | 408 | |
|
409 | 409 | # wrap data in a property, which extracts the <svg> tag, discarding |
|
410 | 410 | # document headers |
|
411 | 411 | _data = None |
|
412 | 412 | |
|
413 | 413 | @property |
|
414 | 414 | def data(self): |
|
415 | 415 | return self._data |
|
416 | 416 | |
|
417 | 417 | @data.setter |
|
418 | 418 | def data(self, svg): |
|
419 | 419 | if svg is None: |
|
420 | 420 | self._data = None |
|
421 | 421 | return |
|
422 | 422 | # parse into dom object |
|
423 | 423 | from xml.dom import minidom |
|
424 | 424 | svg = cast_bytes_py2(svg) |
|
425 | 425 | x = minidom.parseString(svg) |
|
426 | 426 | # get svg tag (should be 1) |
|
427 | 427 | found_svg = x.getElementsByTagName('svg') |
|
428 | 428 | if found_svg: |
|
429 | 429 | svg = found_svg[0].toxml() |
|
430 | 430 | else: |
|
431 | 431 | # fallback on the input, trust the user |
|
432 | 432 | # but this is probably an error. |
|
433 | 433 | pass |
|
434 | 434 | svg = cast_unicode(svg) |
|
435 | 435 | self._data = svg |
|
436 | 436 | |
|
437 | 437 | def _repr_svg_(self): |
|
438 | 438 | return self.data |
|
439 | 439 | |
|
440 | 440 | |
|
441 | 441 | class JSON(TextDisplayObject): |
|
442 | 442 | |
|
443 | 443 | def _repr_json_(self): |
|
444 | 444 | return self.data |
|
445 | 445 | |
|
446 | 446 | css_t = """$("head").append($("<link/>").attr({ |
|
447 | 447 | rel: "stylesheet", |
|
448 | 448 | type: "text/css", |
|
449 | 449 | href: "%s" |
|
450 | 450 | })); |
|
451 | 451 | """ |
|
452 | 452 | |
|
453 | 453 | lib_t1 = """$.getScript("%s", function () { |
|
454 | 454 | """ |
|
455 | 455 | lib_t2 = """}); |
|
456 | 456 | """ |
|
457 | 457 | |
|
458 | 458 | class Javascript(TextDisplayObject): |
|
459 | 459 | |
|
460 | 460 | def __init__(self, data=None, url=None, filename=None, lib=None, css=None): |
|
461 | 461 | """Create a Javascript display object given raw data. |
|
462 | 462 | |
|
463 | 463 | When this object is returned by an expression or passed to the |
|
464 | 464 | display function, it will result in the data being displayed |
|
465 | 465 | in the frontend. If the data is a URL, the data will first be |
|
466 | 466 | downloaded and then displayed. |
|
467 | 467 | |
|
468 | 468 | In the Notebook, the containing element will be available as `element`, |
|
469 | 469 | and jQuery will be available. The output area starts hidden, so if |
|
470 | 470 | the js appends content to `element` that should be visible, then |
|
471 | 471 | it must call `container.show()` to unhide the area. |
|
472 | 472 | |
|
473 | 473 | Parameters |
|
474 | 474 | ---------- |
|
475 | 475 | data : unicode, str or bytes |
|
476 | 476 | The Javascript source code or a URL to download it from. |
|
477 | 477 | url : unicode |
|
478 | 478 | A URL to download the data from. |
|
479 | 479 | filename : unicode |
|
480 | 480 | Path to a local file to load the data from. |
|
481 | 481 | lib : list or str |
|
482 | 482 | A sequence of Javascript library URLs to load asynchronously before |
|
483 | 483 | running the source code. The full URLs of the libraries should |
|
484 | 484 | be given. A single Javascript library URL can also be given as a |
|
485 | 485 | string. |
|
486 | 486 | css: : list or str |
|
487 | 487 | A sequence of css files to load before running the source code. |
|
488 | 488 | The full URLs of the css files should be given. A single css URL |
|
489 | 489 | can also be given as a string. |
|
490 | 490 | """ |
|
491 | 491 | if isinstance(lib, string_types): |
|
492 | 492 | lib = [lib] |
|
493 | 493 | elif lib is None: |
|
494 | 494 | lib = [] |
|
495 | 495 | if isinstance(css, string_types): |
|
496 | 496 | css = [css] |
|
497 | 497 | elif css is None: |
|
498 | 498 | css = [] |
|
499 | 499 | if not isinstance(lib, (list,tuple)): |
|
500 | 500 | raise TypeError('expected sequence, got: %r' % lib) |
|
501 | 501 | if not isinstance(css, (list,tuple)): |
|
502 | 502 | raise TypeError('expected sequence, got: %r' % css) |
|
503 | 503 | self.lib = lib |
|
504 | 504 | self.css = css |
|
505 | 505 | super(Javascript, self).__init__(data=data, url=url, filename=filename) |
|
506 | 506 | |
|
507 | 507 | def _repr_javascript_(self): |
|
508 | 508 | r = '' |
|
509 | 509 | for c in self.css: |
|
510 | 510 | r += css_t % c |
|
511 | 511 | for l in self.lib: |
|
512 | 512 | r += lib_t1 % l |
|
513 | 513 | r += self.data |
|
514 | 514 | r += lib_t2*len(self.lib) |
|
515 | 515 | return r |
|
516 | 516 | |
|
517 | 517 | # constants for identifying png/jpeg data |
|
518 | 518 | _PNG = b'\x89PNG\r\n\x1a\n' |
|
519 | 519 | _JPEG = b'\xff\xd8' |
|
520 | 520 | |
|
521 | 521 | def _pngxy(data): |
|
522 | 522 | """read the (width, height) from a PNG header""" |
|
523 | 523 | ihdr = data.index(b'IHDR') |
|
524 | 524 | # next 8 bytes are width/height |
|
525 | 525 | w4h4 = data[ihdr+4:ihdr+12] |
|
526 | 526 | return struct.unpack('>ii', w4h4) |
|
527 | 527 | |
|
528 | 528 | def _jpegxy(data): |
|
529 | 529 | """read the (width, height) from a JPEG header""" |
|
530 | 530 | # adapted from http://www.64lines.com/jpeg-width-height |
|
531 | 531 | |
|
532 | 532 | idx = 4 |
|
533 | 533 | while True: |
|
534 | 534 | block_size = struct.unpack('>H', data[idx:idx+2])[0] |
|
535 | 535 | idx = idx + block_size |
|
536 | 536 | if data[idx:idx+2] == b'\xFF\xC0': |
|
537 | 537 | # found Start of Frame |
|
538 | 538 | iSOF = idx |
|
539 | 539 | break |
|
540 | 540 | else: |
|
541 | 541 | # read another block |
|
542 | 542 | idx += 2 |
|
543 | 543 | |
|
544 | 544 | h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9]) |
|
545 | 545 | return w, h |
|
546 | 546 | |
|
547 | 547 | class Image(DisplayObject): |
|
548 | 548 | |
|
549 | 549 | _read_flags = 'rb' |
|
550 | 550 | _FMT_JPEG = u'jpeg' |
|
551 | 551 | _FMT_PNG = u'png' |
|
552 | 552 | _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG] |
|
553 | 553 | |
|
554 | 554 | def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False): |
|
555 | 555 | """Create a PNG/JPEG image object given raw data. |
|
556 | 556 | |
|
557 | 557 | When this object is returned by an input cell or passed to the |
|
558 | 558 | display function, it will result in the image being displayed |
|
559 | 559 | in the frontend. |
|
560 | 560 | |
|
561 | 561 | Parameters |
|
562 | 562 | ---------- |
|
563 | 563 | data : unicode, str or bytes |
|
564 | 564 | The raw image data or a URL or filename to load the data from. |
|
565 | 565 | This always results in embedded image data. |
|
566 | 566 | url : unicode |
|
567 | 567 | A URL to download the data from. If you specify `url=`, |
|
568 | 568 | the image data will not be embedded unless you also specify `embed=True`. |
|
569 | 569 | filename : unicode |
|
570 | 570 | Path to a local file to load the data from. |
|
571 | 571 | Images from a file are always embedded. |
|
572 | 572 | format : unicode |
|
573 | 573 | The format of the image data (png/jpeg/jpg). If a filename or URL is given |
|
574 | 574 | for format will be inferred from the filename extension. |
|
575 | 575 | embed : bool |
|
576 | 576 | Should the image data be embedded using a data URI (True) or be |
|
577 | 577 | loaded using an <img> tag. Set this to True if you want the image |
|
578 | 578 | to be viewable later with no internet connection in the notebook. |
|
579 | 579 | |
|
580 | 580 | Default is `True`, unless the keyword argument `url` is set, then |
|
581 | 581 | default value is `False`. |
|
582 | 582 | |
|
583 | 583 | Note that QtConsole is not able to display images if `embed` is set to `False` |
|
584 | 584 | width : int |
|
585 | 585 | Width to which to constrain the image in html |
|
586 | 586 | height : int |
|
587 | 587 | Height to which to constrain the image in html |
|
588 | 588 | retina : bool |
|
589 | 589 | Automatically set the width and height to half of the measured |
|
590 | 590 | width and height. |
|
591 | 591 | This only works for embedded images because it reads the width/height |
|
592 | 592 | from image data. |
|
593 | 593 | For non-embedded images, you can just set the desired display width |
|
594 | 594 | and height directly. |
|
595 | 595 | |
|
596 | 596 | Examples |
|
597 | 597 | -------- |
|
598 | 598 | # embedded image data, works in qtconsole and notebook |
|
599 | 599 | # when passed positionally, the first arg can be any of raw image data, |
|
600 | 600 | # a URL, or a filename from which to load image data. |
|
601 | 601 | # The result is always embedding image data for inline images. |
|
602 | 602 | Image('http://www.google.fr/images/srpr/logo3w.png') |
|
603 | 603 | Image('/path/to/image.jpg') |
|
604 | 604 | Image(b'RAW_PNG_DATA...') |
|
605 | 605 | |
|
606 | 606 | # Specifying Image(url=...) does not embed the image data, |
|
607 | 607 | # it only generates `<img>` tag with a link to the source. |
|
608 | 608 | # This will not work in the qtconsole or offline. |
|
609 | 609 | Image(url='http://www.google.fr/images/srpr/logo3w.png') |
|
610 | 610 | |
|
611 | 611 | """ |
|
612 | 612 | if filename is not None: |
|
613 | 613 | ext = self._find_ext(filename) |
|
614 | 614 | elif url is not None: |
|
615 | 615 | ext = self._find_ext(url) |
|
616 | 616 | elif data is None: |
|
617 | 617 | raise ValueError("No image data found. Expecting filename, url, or data.") |
|
618 | 618 | elif isinstance(data, string_types) and ( |
|
619 | 619 | data.startswith('http') or _safe_exists(data) |
|
620 | 620 | ): |
|
621 | 621 | ext = self._find_ext(data) |
|
622 | 622 | else: |
|
623 | 623 | ext = None |
|
624 | 624 | |
|
625 | 625 | if ext is not None: |
|
626 | 626 | format = ext.lower() |
|
627 | 627 | if ext == u'jpg' or ext == u'jpeg': |
|
628 | 628 | format = self._FMT_JPEG |
|
629 | 629 | if ext == u'png': |
|
630 | 630 | format = self._FMT_PNG |
|
631 | 631 | elif isinstance(data, bytes) and format == 'png': |
|
632 | 632 | # infer image type from image data header, |
|
633 | 633 | # only if format might not have been specified. |
|
634 | 634 | if data[:2] == _JPEG: |
|
635 | 635 | format = 'jpeg' |
|
636 | 636 | |
|
637 | 637 | self.format = unicode_type(format).lower() |
|
638 | 638 | self.embed = embed if embed is not None else (url is None) |
|
639 | 639 | |
|
640 | 640 | if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS: |
|
641 | 641 | raise ValueError("Cannot embed the '%s' image format" % (self.format)) |
|
642 | 642 | self.width = width |
|
643 | 643 | self.height = height |
|
644 | 644 | self.retina = retina |
|
645 | 645 | super(Image, self).__init__(data=data, url=url, filename=filename) |
|
646 | 646 | |
|
647 | 647 | if retina: |
|
648 | 648 | self._retina_shape() |
|
649 | 649 | |
|
650 | 650 | def _retina_shape(self): |
|
651 | 651 | """load pixel-doubled width and height from image data""" |
|
652 | 652 | if not self.embed: |
|
653 | 653 | return |
|
654 | 654 | if self.format == 'png': |
|
655 | 655 | w, h = _pngxy(self.data) |
|
656 | 656 | elif self.format == 'jpeg': |
|
657 | 657 | w, h = _jpegxy(self.data) |
|
658 | 658 | else: |
|
659 | 659 | # retina only supports png |
|
660 | 660 | return |
|
661 | 661 | self.width = w // 2 |
|
662 | 662 | self.height = h // 2 |
|
663 | 663 | |
|
664 | 664 | def reload(self): |
|
665 | 665 | """Reload the raw data from file or URL.""" |
|
666 | 666 | if self.embed: |
|
667 | 667 | super(Image,self).reload() |
|
668 | 668 | if self.retina: |
|
669 | 669 | self._retina_shape() |
|
670 | 670 | |
|
671 | 671 | def _repr_html_(self): |
|
672 | 672 | if not self.embed: |
|
673 | 673 | width = height = '' |
|
674 | 674 | if self.width: |
|
675 | 675 | width = ' width="%d"' % self.width |
|
676 | 676 | if self.height: |
|
677 | 677 | height = ' height="%d"' % self.height |
|
678 | 678 | return u'<img src="%s"%s%s/>' % (self.url, width, height) |
|
679 | 679 | |
|
680 | 680 | def _data_and_metadata(self): |
|
681 | 681 | """shortcut for returning metadata with shape information, if defined""" |
|
682 | 682 | md = {} |
|
683 | 683 | if self.width: |
|
684 | 684 | md['width'] = self.width |
|
685 | 685 | if self.height: |
|
686 | 686 | md['height'] = self.height |
|
687 | 687 | if md: |
|
688 | 688 | return self.data, md |
|
689 | 689 | else: |
|
690 | 690 | return self.data |
|
691 | 691 | |
|
692 | 692 | def _repr_png_(self): |
|
693 | 693 | if self.embed and self.format == u'png': |
|
694 | 694 | return self._data_and_metadata() |
|
695 | 695 | |
|
696 | 696 | def _repr_jpeg_(self): |
|
697 | 697 | if self.embed and (self.format == u'jpeg' or self.format == u'jpg'): |
|
698 | 698 | return self._data_and_metadata() |
|
699 | 699 | |
|
700 | 700 | def _find_ext(self, s): |
|
701 | 701 | return unicode_type(s.split('.')[-1].lower()) |
|
702 | 702 | |
|
703 | 703 | |
|
704 | 704 | def clear_output(wait=False): |
|
705 | 705 | """Clear the output of the current cell receiving output. |
|
706 | 706 | |
|
707 | 707 | Parameters |
|
708 | 708 | ---------- |
|
709 | 709 | wait : bool [default: false] |
|
710 | 710 | Wait to clear the output until new output is available to replace it.""" |
|
711 | 711 | from IPython.core.interactiveshell import InteractiveShell |
|
712 | 712 | if InteractiveShell.initialized(): |
|
713 | 713 | InteractiveShell.instance().display_pub.clear_output(wait) |
|
714 | 714 | else: |
|
715 | 715 | from IPython.utils import io |
|
716 | 716 | print('\033[2K\r', file=io.stdout, end='') |
|
717 | 717 | io.stdout.flush() |
|
718 | 718 | print('\033[2K\r', file=io.stderr, end='') |
|
719 | 719 | io.stderr.flush() |
|
720 | 720 | |
|
721 | 721 | |
|
722 | 722 | @skip_doctest |
|
723 | 723 | def set_matplotlib_formats(*formats, **kwargs): |
|
724 | 724 | """Select figure formats for the inline backend. Optionally pass quality for JPEG. |
|
725 | 725 | |
|
726 | 726 | For example, this enables PNG and JPEG output with a JPEG quality of 90%:: |
|
727 | 727 | |
|
728 | 728 | In [1]: set_matplotlib_formats('png', 'jpeg', quality=90) |
|
729 | 729 | |
|
730 | 730 | To set this in your config files use the following:: |
|
731 | 731 | |
|
732 |
c.InlineBackend.figure_formats = {'p |
|
|
733 |
c.InlineBackend.quality |
|
|
732 | c.InlineBackend.figure_formats = {'png', 'jpeg'} | |
|
733 | c.InlineBackend.print_figure_kwargs.update({'quality' : 90}) | |
|
734 | 734 | |
|
735 | 735 | Parameters |
|
736 | 736 | ---------- |
|
737 |
*formats : |
|
|
738 |
One or |
|
|
739 | quality : int | |
|
740 | A percentage for the quality of JPEG figures. Defaults to 90. | |
|
737 | *formats : strs | |
|
738 | One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'. | |
|
739 | **kwargs : | |
|
740 | Keyword args will be relayed to ``figure.canvas.print_figure``. | |
|
741 | 741 | """ |
|
742 | 742 | from IPython.core.interactiveshell import InteractiveShell |
|
743 | 743 | from IPython.core.pylabtools import select_figure_formats |
|
744 | from IPython.kernel.zmq.pylab.config import InlineBackend | |
|
745 | # build kwargs, starting with InlineBackend config | |
|
746 | kw = {} | |
|
747 | cfg = InlineBackend.instance() | |
|
748 | kw.update(cfg.print_figure_kwargs) | |
|
749 | kw.update(**kwargs) | |
|
744 | 750 | shell = InteractiveShell.instance() |
|
745 |
select_figure_formats(shell, formats, |
|
|
751 | select_figure_formats(shell, formats, **kw) | |
|
746 | 752 | |
|
747 | 753 | @skip_doctest |
|
748 | def set_matplotlib_close(close): | |
|
754 | def set_matplotlib_close(close=True): | |
|
749 | 755 | """Set whether the inline backend closes all figures automatically or not. |
|
750 | 756 | |
|
751 | 757 | By default, the inline backend used in the IPython Notebook will close all |
|
752 | 758 | matplotlib figures automatically after each cell is run. This means that |
|
753 | 759 | plots in different cells won't interfere. Sometimes, you may want to make |
|
754 | 760 | a plot in one cell and then refine it in later cells. This can be accomplished |
|
755 | 761 | by:: |
|
756 | 762 | |
|
757 | 763 | In [1]: set_matplotlib_close(False) |
|
758 | 764 | |
|
759 | 765 | To set this in your config files use the following:: |
|
760 | 766 | |
|
761 | 767 | c.InlineBackend.close_figures = False |
|
762 | 768 | |
|
763 | 769 | Parameters |
|
764 | 770 | ---------- |
|
765 | 771 | close : bool |
|
766 | 772 | Should all matplotlib figures be automatically closed after each cell is |
|
767 | 773 | run? |
|
768 | 774 | """ |
|
769 |
from IPython.kernel.zmq.pylab. |
|
|
770 |
|
|
|
771 |
|
|
|
775 | from IPython.kernel.zmq.pylab.config import InlineBackend | |
|
776 | cfg = InlineBackend.instance() | |
|
777 | cfg.close_figures = close | |
|
772 | 778 |
@@ -1,358 +1,374 | |||
|
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 | from IPython.utils import py3compat |
|
29 | 29 | |
|
30 | 30 | # If user specifies a GUI, that dictates the backend, otherwise we read the |
|
31 | 31 | # user's mpl default from the mpl rc structure |
|
32 | 32 | backends = {'tk': 'TkAgg', |
|
33 | 33 | 'gtk': 'GTKAgg', |
|
34 | 34 | 'gtk3': 'GTK3Agg', |
|
35 | 35 | 'wx': 'WXAgg', |
|
36 | 36 | 'qt': 'Qt4Agg', # qt3 not supported |
|
37 | 37 | 'qt4': 'Qt4Agg', |
|
38 | 38 | 'osx': 'MacOSX', |
|
39 | 39 | 'inline' : 'module://IPython.kernel.zmq.pylab.backend_inline'} |
|
40 | 40 | |
|
41 | 41 | # We also need a reverse backends2guis mapping that will properly choose which |
|
42 | 42 | # GUI support to activate based on the desired matplotlib backend. For the |
|
43 | 43 | # most part it's just a reverse of the above dict, but we also need to add a |
|
44 | 44 | # few others that map to the same GUI manually: |
|
45 | 45 | backend2gui = dict(zip(backends.values(), backends.keys())) |
|
46 | 46 | # Our tests expect backend2gui to just return 'qt' |
|
47 | 47 | backend2gui['Qt4Agg'] = 'qt' |
|
48 | 48 | # In the reverse mapping, there are a few extra valid matplotlib backends that |
|
49 | 49 | # map to the same GUI support |
|
50 | 50 | backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk' |
|
51 | 51 | backend2gui['GTK3Cairo'] = 'gtk3' |
|
52 | 52 | backend2gui['WX'] = 'wx' |
|
53 | 53 | backend2gui['CocoaAgg'] = 'osx' |
|
54 | 54 | |
|
55 | 55 | #----------------------------------------------------------------------------- |
|
56 | 56 | # Matplotlib utilities |
|
57 | 57 | #----------------------------------------------------------------------------- |
|
58 | 58 | |
|
59 | 59 | |
|
60 | 60 | def getfigs(*fig_nums): |
|
61 | 61 | """Get a list of matplotlib figures by figure numbers. |
|
62 | 62 | |
|
63 | 63 | If no arguments are given, all available figures are returned. If the |
|
64 | 64 | argument list contains references to invalid figures, a warning is printed |
|
65 | 65 | but the function continues pasting further figures. |
|
66 | 66 | |
|
67 | 67 | Parameters |
|
68 | 68 | ---------- |
|
69 | 69 | figs : tuple |
|
70 | 70 | A tuple of ints giving the figure numbers of the figures to return. |
|
71 | 71 | """ |
|
72 | 72 | from matplotlib._pylab_helpers import Gcf |
|
73 | 73 | if not fig_nums: |
|
74 | 74 | fig_managers = Gcf.get_all_fig_managers() |
|
75 | 75 | return [fm.canvas.figure for fm in fig_managers] |
|
76 | 76 | else: |
|
77 | 77 | figs = [] |
|
78 | 78 | for num in fig_nums: |
|
79 | 79 | f = Gcf.figs.get(num) |
|
80 | 80 | if f is None: |
|
81 | 81 | print('Warning: figure %s not available.' % num) |
|
82 | 82 | else: |
|
83 | 83 | figs.append(f.canvas.figure) |
|
84 | 84 | return figs |
|
85 | 85 | |
|
86 | 86 | |
|
87 | 87 | def figsize(sizex, sizey): |
|
88 | 88 | """Set the default figure size to be [sizex, sizey]. |
|
89 | 89 | |
|
90 | 90 | This is just an easy to remember, convenience wrapper that sets:: |
|
91 | 91 | |
|
92 | 92 | matplotlib.rcParams['figure.figsize'] = [sizex, sizey] |
|
93 | 93 | """ |
|
94 | 94 | import matplotlib |
|
95 | 95 | matplotlib.rcParams['figure.figsize'] = [sizex, sizey] |
|
96 | 96 | |
|
97 | 97 | |
|
98 |
def print_figure(fig, fmt='png', |
|
|
99 | """Convert a figure to svg, png or jpg for inline display. | |
|
100 | Quality is only relevant for jpg. | |
|
98 | def print_figure(fig, fmt='png', bbox_inches='tight', **kwargs): | |
|
99 | """Print a figure to an image, and return the resulting bytes | |
|
100 | ||
|
101 | Any keyword args are passed to fig.canvas.print_figure, | |
|
102 | such as ``quality`` or ``bbox_inches``. | |
|
101 | 103 | """ |
|
102 | 104 | from matplotlib import rcParams |
|
103 | 105 | # When there's an empty figure, we shouldn't return anything, otherwise we |
|
104 | 106 | # get big blank areas in the qt console. |
|
105 | 107 | if not fig.axes and not fig.lines: |
|
106 | 108 | return |
|
107 | 109 | |
|
108 | fc = fig.get_facecolor() | |
|
109 | ec = fig.get_edgecolor() | |
|
110 | bytes_io = BytesIO() | |
|
111 | 110 | dpi = rcParams['savefig.dpi'] |
|
112 | 111 | if fmt == 'retina': |
|
113 | 112 | dpi = dpi * 2 |
|
114 | 113 | fmt = 'png' |
|
115 | fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight', | |
|
116 | facecolor=fc, edgecolor=ec, dpi=dpi, quality=quality) | |
|
117 | data = bytes_io.getvalue() | |
|
118 | return data | |
|
119 | 114 | |
|
120 | def retina_figure(fig): | |
|
115 | # build keyword args | |
|
116 | kw = dict( | |
|
117 | format=fmt, | |
|
118 | fc=fig.get_facecolor(), | |
|
119 | ec=fig.get_edgecolor(), | |
|
120 | dpi=dpi, | |
|
121 | bbox_inches=bbox_inches, | |
|
122 | ) | |
|
123 | # **kwargs get higher priority | |
|
124 | kw.update(kwargs) | |
|
125 | ||
|
126 | bytes_io = BytesIO() | |
|
127 | fig.canvas.print_figure(bytes_io, **kw) | |
|
128 | return bytes_io.getvalue() | |
|
129 | ||
|
130 | def retina_figure(fig, **kwargs): | |
|
121 | 131 | """format a figure as a pixel-doubled (retina) PNG""" |
|
122 | pngdata = print_figure(fig, fmt='retina') | |
|
132 | pngdata = print_figure(fig, fmt='retina', **kwargs) | |
|
123 | 133 | w, h = _pngxy(pngdata) |
|
124 | 134 | metadata = dict(width=w//2, height=h//2) |
|
125 | 135 | return pngdata, metadata |
|
126 | 136 | |
|
127 | 137 | # We need a little factory function here to create the closure where |
|
128 | 138 | # safe_execfile can live. |
|
129 | 139 | def mpl_runner(safe_execfile): |
|
130 | 140 | """Factory to return a matplotlib-enabled runner for %run. |
|
131 | 141 | |
|
132 | 142 | Parameters |
|
133 | 143 | ---------- |
|
134 | 144 | safe_execfile : function |
|
135 | 145 | This must be a function with the same interface as the |
|
136 | 146 | :meth:`safe_execfile` method of IPython. |
|
137 | 147 | |
|
138 | 148 | Returns |
|
139 | 149 | ------- |
|
140 | 150 | A function suitable for use as the ``runner`` argument of the %run magic |
|
141 | 151 | function. |
|
142 | 152 | """ |
|
143 | 153 | |
|
144 | 154 | def mpl_execfile(fname,*where,**kw): |
|
145 | 155 | """matplotlib-aware wrapper around safe_execfile. |
|
146 | 156 | |
|
147 | 157 | Its interface is identical to that of the :func:`execfile` builtin. |
|
148 | 158 | |
|
149 | 159 | This is ultimately a call to execfile(), but wrapped in safeties to |
|
150 | 160 | properly handle interactive rendering.""" |
|
151 | 161 | |
|
152 | 162 | import matplotlib |
|
153 | 163 | import matplotlib.pylab as pylab |
|
154 | 164 | |
|
155 | 165 | #print '*** Matplotlib runner ***' # dbg |
|
156 | 166 | # turn off rendering until end of script |
|
157 | 167 | is_interactive = matplotlib.rcParams['interactive'] |
|
158 | 168 | matplotlib.interactive(False) |
|
159 | 169 | safe_execfile(fname,*where,**kw) |
|
160 | 170 | matplotlib.interactive(is_interactive) |
|
161 | 171 | # make rendering call now, if the user tried to do it |
|
162 | 172 | if pylab.draw_if_interactive.called: |
|
163 | 173 | pylab.draw() |
|
164 | 174 | pylab.draw_if_interactive.called = False |
|
165 | 175 | |
|
166 | 176 | return mpl_execfile |
|
167 | 177 | |
|
168 | 178 | |
|
169 |
def select_figure_formats(shell, formats, |
|
|
179 | def select_figure_formats(shell, formats, **kwargs): | |
|
170 | 180 | """Select figure formats for the inline backend. |
|
171 | 181 | |
|
172 | 182 | Parameters |
|
173 | 183 | ========== |
|
174 | 184 | shell : InteractiveShell |
|
175 | 185 | The main IPython instance. |
|
176 |
formats : |
|
|
186 | formats : str or set | |
|
177 | 187 | One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'. |
|
178 | quality : int | |
|
179 | A percentage for the quality of JPEG figures. | |
|
188 | **kwargs : any | |
|
189 | Extra keyword arguments to be passed to fig.canvas.print_figure. | |
|
180 | 190 | """ |
|
181 | 191 | from matplotlib.figure import Figure |
|
182 | 192 | from IPython.kernel.zmq.pylab import backend_inline |
|
183 | 193 | |
|
184 | 194 | svg_formatter = shell.display_formatter.formatters['image/svg+xml'] |
|
185 | 195 | png_formatter = shell.display_formatter.formatters['image/png'] |
|
186 | 196 | jpg_formatter = shell.display_formatter.formatters['image/jpeg'] |
|
187 | 197 | pdf_formatter = shell.display_formatter.formatters['application/pdf'] |
|
188 | 198 | |
|
189 | 199 | if isinstance(formats, py3compat.string_types): |
|
190 | 200 | formats = {formats} |
|
191 | ||
|
192 | [ f.type_printers.pop(Figure, None) for f in {svg_formatter, png_formatter, jpg_formatter} ] | |
|
193 | ||
|
194 | for fmt in formats: | |
|
195 | if fmt == 'png': | |
|
196 | png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png')) | |
|
197 | elif fmt in ('png2x', 'retina'): | |
|
198 | png_formatter.for_type(Figure, retina_figure) | |
|
199 | elif fmt in ('jpg', 'jpeg'): | |
|
200 | jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', quality)) | |
|
201 | elif fmt == 'svg': | |
|
202 | svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg')) | |
|
203 | elif fmt == 'pdf': | |
|
204 |
|
|
|
205 | else: | |
|
206 | raise ValueError("supported formats are: 'png', 'retina', 'svg', 'jpg', 'pdf' not %r" % fmt) | |
|
201 | # cast in case of list / tuple | |
|
202 | formats = set(formats) | |
|
203 | ||
|
204 | [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ] | |
|
205 | ||
|
206 | supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'} | |
|
207 | bad = formats.difference(supported) | |
|
208 | if bad: | |
|
209 | bs = "%s" % ','.join([repr(f) for f in bad]) | |
|
210 | gs = "%s" % ','.join([repr(f) for f in supported]) | |
|
211 | raise ValueError("supported formats are: %s not %s" % (gs, bs)) | |
|
212 | ||
|
213 | if 'png' in formats: | |
|
214 | png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs)) | |
|
215 | if 'retina' in formats or 'png2x' in formats: | |
|
216 | png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs)) | |
|
217 | if 'jpg' in formats or 'jpeg' in formats: | |
|
218 | jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', **kwargs)) | |
|
219 | if 'svg' in formats: | |
|
220 | svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg', **kwargs)) | |
|
221 | if 'pdf' in formats: | |
|
222 | pdf_formatter.for_type(Figure, lambda fig: print_figure(fig, 'pdf', **kwargs)) | |
|
207 | 223 | |
|
208 | 224 | #----------------------------------------------------------------------------- |
|
209 | 225 | # Code for initializing matplotlib and importing pylab |
|
210 | 226 | #----------------------------------------------------------------------------- |
|
211 | 227 | |
|
212 | 228 | |
|
213 | 229 | def find_gui_and_backend(gui=None, gui_select=None): |
|
214 | 230 | """Given a gui string return the gui and mpl backend. |
|
215 | 231 | |
|
216 | 232 | Parameters |
|
217 | 233 | ---------- |
|
218 | 234 | gui : str |
|
219 | 235 | Can be one of ('tk','gtk','wx','qt','qt4','inline'). |
|
220 | 236 | gui_select : str |
|
221 | 237 | Can be one of ('tk','gtk','wx','qt','qt4','inline'). |
|
222 | 238 | This is any gui already selected by the shell. |
|
223 | 239 | |
|
224 | 240 | Returns |
|
225 | 241 | ------- |
|
226 | 242 | A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg', |
|
227 | 243 | 'WXAgg','Qt4Agg','module://IPython.kernel.zmq.pylab.backend_inline'). |
|
228 | 244 | """ |
|
229 | 245 | |
|
230 | 246 | import matplotlib |
|
231 | 247 | |
|
232 | 248 | if gui and gui != 'auto': |
|
233 | 249 | # select backend based on requested gui |
|
234 | 250 | backend = backends[gui] |
|
235 | 251 | else: |
|
236 | 252 | # We need to read the backend from the original data structure, *not* |
|
237 | 253 | # from mpl.rcParams, since a prior invocation of %matplotlib may have |
|
238 | 254 | # overwritten that. |
|
239 | 255 | # WARNING: this assumes matplotlib 1.1 or newer!! |
|
240 | 256 | backend = matplotlib.rcParamsOrig['backend'] |
|
241 | 257 | # In this case, we need to find what the appropriate gui selection call |
|
242 | 258 | # should be for IPython, so we can activate inputhook accordingly |
|
243 | 259 | gui = backend2gui.get(backend, None) |
|
244 | 260 | |
|
245 | 261 | # If we have already had a gui active, we need it and inline are the |
|
246 | 262 | # ones allowed. |
|
247 | 263 | if gui_select and gui != gui_select: |
|
248 | 264 | gui = gui_select |
|
249 | 265 | backend = backends[gui] |
|
250 | 266 | |
|
251 | 267 | return gui, backend |
|
252 | 268 | |
|
253 | 269 | |
|
254 | 270 | def activate_matplotlib(backend): |
|
255 | 271 | """Activate the given backend and set interactive to True.""" |
|
256 | 272 | |
|
257 | 273 | import matplotlib |
|
258 | 274 | matplotlib.interactive(True) |
|
259 | 275 | |
|
260 | 276 | # Matplotlib had a bug where even switch_backend could not force |
|
261 | 277 | # the rcParam to update. This needs to be set *before* the module |
|
262 | 278 | # magic of switch_backend(). |
|
263 | 279 | matplotlib.rcParams['backend'] = backend |
|
264 | 280 | |
|
265 | 281 | import matplotlib.pyplot |
|
266 | 282 | matplotlib.pyplot.switch_backend(backend) |
|
267 | 283 | |
|
268 | 284 | # This must be imported last in the matplotlib series, after |
|
269 | 285 | # backend/interactivity choices have been made |
|
270 | 286 | import matplotlib.pylab as pylab |
|
271 | 287 | |
|
272 | 288 | pylab.show._needmain = False |
|
273 | 289 | # We need to detect at runtime whether show() is called by the user. |
|
274 | 290 | # For this, we wrap it into a decorator which adds a 'called' flag. |
|
275 | 291 | pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive) |
|
276 | 292 | |
|
277 | 293 | |
|
278 | 294 | def import_pylab(user_ns, import_all=True): |
|
279 | 295 | """Populate the namespace with pylab-related values. |
|
280 | 296 | |
|
281 | 297 | Imports matplotlib, pylab, numpy, and everything from pylab and numpy. |
|
282 | 298 | |
|
283 | 299 | Also imports a few names from IPython (figsize, display, getfigs) |
|
284 | 300 | |
|
285 | 301 | """ |
|
286 | 302 | |
|
287 | 303 | # Import numpy as np/pyplot as plt are conventions we're trying to |
|
288 | 304 | # somewhat standardize on. Making them available to users by default |
|
289 | 305 | # will greatly help this. |
|
290 | 306 | s = ("import numpy\n" |
|
291 | 307 | "import matplotlib\n" |
|
292 | 308 | "from matplotlib import pylab, mlab, pyplot\n" |
|
293 | 309 | "np = numpy\n" |
|
294 | 310 | "plt = pyplot\n" |
|
295 | 311 | ) |
|
296 | 312 | exec(s, user_ns) |
|
297 | 313 | |
|
298 | 314 | if import_all: |
|
299 | 315 | s = ("from matplotlib.pylab import *\n" |
|
300 | 316 | "from numpy import *\n") |
|
301 | 317 | exec(s, user_ns) |
|
302 | 318 | |
|
303 | 319 | # IPython symbols to add |
|
304 | 320 | user_ns['figsize'] = figsize |
|
305 | 321 | from IPython.core.display import display |
|
306 | 322 | # Add display and getfigs to the user's namespace |
|
307 | 323 | user_ns['display'] = display |
|
308 | 324 | user_ns['getfigs'] = getfigs |
|
309 | 325 | |
|
310 | 326 | |
|
311 | 327 | def configure_inline_support(shell, backend): |
|
312 | 328 | """Configure an IPython shell object for matplotlib use. |
|
313 | 329 | |
|
314 | 330 | Parameters |
|
315 | 331 | ---------- |
|
316 | 332 | shell : InteractiveShell instance |
|
317 | 333 | |
|
318 | 334 | backend : matplotlib backend |
|
319 | 335 | """ |
|
320 | 336 | # If using our svg payload backend, register the post-execution |
|
321 | 337 | # function that will pick up the results for display. This can only be |
|
322 | 338 | # done with access to the real shell object. |
|
323 | 339 | |
|
324 | 340 | # Note: if we can't load the inline backend, then there's no point |
|
325 | 341 | # continuing (such as in terminal-only shells in environments without |
|
326 | 342 | # zeromq available). |
|
327 | 343 | try: |
|
328 | 344 | from IPython.kernel.zmq.pylab.backend_inline import InlineBackend |
|
329 | 345 | except ImportError: |
|
330 | 346 | return |
|
331 | 347 | from matplotlib import pyplot |
|
332 | 348 | |
|
333 | 349 | cfg = InlineBackend.instance(parent=shell) |
|
334 | 350 | cfg.shell = shell |
|
335 | 351 | if cfg not in shell.configurables: |
|
336 | 352 | shell.configurables.append(cfg) |
|
337 | 353 | |
|
338 | 354 | if backend == backends['inline']: |
|
339 | 355 | from IPython.kernel.zmq.pylab.backend_inline import flush_figures |
|
340 | 356 | shell.register_post_execute(flush_figures) |
|
341 | 357 | |
|
342 | 358 | # Save rcParams that will be overwrittern |
|
343 | 359 | shell._saved_rcParams = dict() |
|
344 | 360 | for k in cfg.rc: |
|
345 | 361 | shell._saved_rcParams[k] = pyplot.rcParams[k] |
|
346 | 362 | # load inline_rc |
|
347 | 363 | pyplot.rcParams.update(cfg.rc) |
|
348 | 364 | else: |
|
349 | 365 | from IPython.kernel.zmq.pylab.backend_inline import flush_figures |
|
350 | 366 | if flush_figures in shell._post_execute: |
|
351 | 367 | shell._post_execute.pop(flush_figures) |
|
352 | 368 | if hasattr(shell, '_saved_rcParams'): |
|
353 | 369 | pyplot.rcParams.update(shell._saved_rcParams) |
|
354 | 370 | del shell._saved_rcParams |
|
355 | 371 | |
|
356 | 372 | # Setup the default figure format |
|
357 |
select_figure_formats(shell, cfg.figure_formats, cfg. |
|
|
373 | select_figure_formats(shell, cfg.figure_formats, **cfg.print_figure_kwargs) | |
|
358 | 374 |
@@ -1,60 +1,118 | |||
|
1 | 1 | #----------------------------------------------------------------------------- |
|
2 | 2 | # Copyright (C) 2010-2011 The IPython Development Team. |
|
3 | 3 | # |
|
4 | 4 | # Distributed under the terms of the BSD License. |
|
5 | 5 | # |
|
6 | 6 | # The full license is in the file COPYING.txt, distributed with this software. |
|
7 | 7 | #----------------------------------------------------------------------------- |
|
8 | 8 | import os |
|
9 | 9 | |
|
10 | 10 | import nose.tools as nt |
|
11 | 11 | |
|
12 | 12 | from IPython.core import display |
|
13 | from IPython.core.getipython import get_ipython | |
|
13 | 14 | from IPython.utils import path as ipath |
|
14 | 15 | |
|
16 | import IPython.testing.decorators as dec | |
|
17 | ||
|
15 | 18 | def test_image_size(): |
|
16 | 19 | """Simple test for display.Image(args, width=x,height=y)""" |
|
17 | 20 | thisurl = 'http://www.google.fr/images/srpr/logo3w.png' |
|
18 | 21 | img = display.Image(url=thisurl, width=200, height=200) |
|
19 | 22 | nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_()) |
|
20 | 23 | img = display.Image(url=thisurl, width=200) |
|
21 | 24 | nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_()) |
|
22 | 25 | img = display.Image(url=thisurl) |
|
23 | 26 | nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_()) |
|
24 | 27 | |
|
25 | 28 | def test_retina_png(): |
|
26 | 29 | here = os.path.dirname(__file__) |
|
27 | 30 | img = display.Image(os.path.join(here, "2x2.png"), retina=True) |
|
28 | 31 | nt.assert_equal(img.height, 1) |
|
29 | 32 | nt.assert_equal(img.width, 1) |
|
30 | 33 | data, md = img._repr_png_() |
|
31 | 34 | nt.assert_equal(md['width'], 1) |
|
32 | 35 | nt.assert_equal(md['height'], 1) |
|
33 | 36 | |
|
34 | 37 | def test_retina_jpeg(): |
|
35 | 38 | here = os.path.dirname(__file__) |
|
36 | 39 | img = display.Image(os.path.join(here, "2x2.jpg"), retina=True) |
|
37 | 40 | nt.assert_equal(img.height, 1) |
|
38 | 41 | nt.assert_equal(img.width, 1) |
|
39 | 42 | data, md = img._repr_jpeg_() |
|
40 | 43 | nt.assert_equal(md['width'], 1) |
|
41 | 44 | nt.assert_equal(md['height'], 1) |
|
42 | 45 | |
|
43 | 46 | def test_image_filename_defaults(): |
|
44 | 47 | '''test format constraint, and validity of jpeg and png''' |
|
45 | 48 | tpath = ipath.get_ipython_package_dir() |
|
46 | 49 | nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.gif'), |
|
47 | 50 | embed=True) |
|
48 | 51 | nt.assert_raises(ValueError, display.Image) |
|
49 | 52 | nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True) |
|
50 | 53 | from IPython.html import DEFAULT_STATIC_FILES_PATH |
|
51 | 54 | # check boths paths to allow packages to test at build and install time |
|
52 | 55 | imgfile = os.path.join(tpath, 'html/static/base/images/ipynblogo.png') |
|
53 | 56 | if not os.path.exists(imgfile): |
|
54 | 57 | imgfile = os.path.join(DEFAULT_STATIC_FILES_PATH, 'base/images/ipynblogo.png') |
|
55 | 58 | img = display.Image(filename=imgfile) |
|
56 | 59 | nt.assert_equal('png', img.format) |
|
57 | 60 | nt.assert_is_not_none(img._repr_png_()) |
|
58 | 61 | img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False) |
|
59 | 62 | nt.assert_equal('jpeg', img.format) |
|
60 | 63 | nt.assert_is_none(img._repr_jpeg_()) |
|
64 | ||
|
65 | def _get_inline_config(): | |
|
66 | from IPython.kernel.zmq.pylab.config import InlineBackend | |
|
67 | return InlineBackend.instance() | |
|
68 | ||
|
69 | @dec.skip_without('matplotlib') | |
|
70 | def test_set_matplotlib_close(): | |
|
71 | cfg = _get_inline_config() | |
|
72 | cfg.close_figures = False | |
|
73 | display.set_matplotlib_close() | |
|
74 | assert cfg.close_figures | |
|
75 | display.set_matplotlib_close(False) | |
|
76 | assert not cfg.close_figures | |
|
77 | ||
|
78 | _fmt_mime_map = { | |
|
79 | 'png': 'image/png', | |
|
80 | 'jpeg': 'image/jpeg', | |
|
81 | 'pdf': 'application/pdf', | |
|
82 | 'retina': 'image/png', | |
|
83 | 'svg': 'image/svg+xml', | |
|
84 | } | |
|
85 | ||
|
86 | @dec.skip_without('matplotlib') | |
|
87 | def test_set_matplotlib_formats(): | |
|
88 | from matplotlib.figure import Figure | |
|
89 | formatters = get_ipython().display_formatter.formatters | |
|
90 | for formats in [ | |
|
91 | ('png',), | |
|
92 | ('pdf', 'svg'), | |
|
93 | ('jpeg', 'retina', 'png'), | |
|
94 | (), | |
|
95 | ]: | |
|
96 | active_mimes = {_fmt_mime_map[fmt] for fmt in formats} | |
|
97 | display.set_matplotlib_formats(*formats) | |
|
98 | for mime, f in formatters.items(): | |
|
99 | if mime in active_mimes: | |
|
100 | nt.assert_in(Figure, f) | |
|
101 | else: | |
|
102 | nt.assert_not_in(Figure, f) | |
|
103 | ||
|
104 | @dec.skip_without('matplotlib') | |
|
105 | def test_set_matplotlib_formats_kwargs(): | |
|
106 | from matplotlib.figure import Figure | |
|
107 | ip = get_ipython() | |
|
108 | cfg = _get_inline_config() | |
|
109 | cfg.print_figure_kwargs.update(dict(foo='bar')) | |
|
110 | kwargs = dict(quality=10) | |
|
111 | display.set_matplotlib_formats('png', **kwargs) | |
|
112 | formatter = ip.display_formatter.formatters['image/png'] | |
|
113 | f = formatter.lookup_by_type(Figure) | |
|
114 | cell = f.__closure__[0].cell_contents | |
|
115 | expected = kwargs | |
|
116 | expected.update(cfg.print_figure_kwargs) | |
|
117 | nt.assert_equal(cell, expected) | |
|
118 |
@@ -1,153 +1,224 | |||
|
1 | 1 | """Tests for pylab tools module. |
|
2 | 2 | """ |
|
3 | 3 | #----------------------------------------------------------------------------- |
|
4 | 4 | # Copyright (c) 2011, the IPython Development Team. |
|
5 | 5 | # |
|
6 | 6 | # Distributed under the terms of the Modified BSD License. |
|
7 | 7 | # |
|
8 | 8 | # The full license is in the file COPYING.txt, distributed with this software. |
|
9 | 9 | #----------------------------------------------------------------------------- |
|
10 | 10 | |
|
11 | 11 | #----------------------------------------------------------------------------- |
|
12 | 12 | # Imports |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | from __future__ import print_function |
|
15 | 15 | |
|
16 | # Stdlib imports | |
|
16 | import matplotlib | |
|
17 | matplotlib.use('Agg') | |
|
18 | from matplotlib.figure import Figure | |
|
17 | 19 | |
|
18 | # Third-party imports | |
|
19 | import matplotlib; matplotlib.use('Agg') | |
|
20 | 20 | import nose.tools as nt |
|
21 | 21 | |
|
22 | 22 | from matplotlib import pyplot as plt |
|
23 | 23 | import numpy as np |
|
24 | 24 | |
|
25 | 25 | # Our own imports |
|
26 | from IPython.core.getipython import get_ipython | |
|
26 | 27 | from IPython.core.interactiveshell import InteractiveShell |
|
28 | from IPython.core.display import _PNG, _JPEG | |
|
27 | 29 | from .. import pylabtools as pt |
|
28 | 30 | |
|
29 | 31 | from IPython.testing import decorators as dec |
|
30 | 32 | |
|
31 | 33 | #----------------------------------------------------------------------------- |
|
32 | 34 | # Globals and constants |
|
33 | 35 | #----------------------------------------------------------------------------- |
|
34 | 36 | |
|
35 | 37 | #----------------------------------------------------------------------------- |
|
36 | 38 | # Local utilities |
|
37 | 39 | #----------------------------------------------------------------------------- |
|
38 | 40 | |
|
39 | 41 | #----------------------------------------------------------------------------- |
|
40 | 42 | # Classes and functions |
|
41 | 43 | #----------------------------------------------------------------------------- |
|
42 | 44 | |
|
43 | 45 | def test_figure_to_svg(): |
|
44 | 46 | # simple empty-figure test |
|
45 | 47 | fig = plt.figure() |
|
46 | 48 | nt.assert_equal(pt.print_figure(fig, 'svg'), None) |
|
47 | 49 | |
|
48 | 50 | plt.close('all') |
|
49 | 51 | |
|
50 | 52 | # simple check for at least svg-looking output |
|
51 | 53 | fig = plt.figure() |
|
52 | 54 | ax = fig.add_subplot(1,1,1) |
|
53 | 55 | ax.plot([1,2,3]) |
|
54 | 56 | plt.draw() |
|
55 | 57 | svg = pt.print_figure(fig, 'svg')[:100].lower() |
|
56 | 58 | nt.assert_in(b'doctype svg', svg) |
|
57 | 59 | |
|
58 | 60 | @dec.skip_without("PIL.Image") |
|
59 | 61 | def test_figure_to_jpg(): |
|
60 | 62 | # simple check for at least jpg-looking output |
|
61 | 63 | fig = plt.figure() |
|
62 | 64 | ax = fig.add_subplot(1,1,1) |
|
63 | 65 | ax.plot([1,2,3]) |
|
64 | 66 | plt.draw() |
|
65 | jpg = pt.print_figure(fig, 'jpg')[:100].lower() | |
|
66 |
assert jpg.startswith( |
|
|
67 | jpg = pt.print_figure(fig, 'jpg', quality=50)[:100].lower() | |
|
68 | assert jpg.startswith(_JPEG) | |
|
67 | 69 | |
|
70 | def test_retina_figure(): | |
|
71 | fig = plt.figure() | |
|
72 | ax = fig.add_subplot(1,1,1) | |
|
73 | ax.plot([1,2,3]) | |
|
74 | plt.draw() | |
|
75 | png, md = pt.retina_figure(fig) | |
|
76 | assert png.startswith(_PNG) | |
|
77 | nt.assert_in('width', md) | |
|
78 | nt.assert_in('height', md) | |
|
79 | ||
|
80 | _fmt_mime_map = { | |
|
81 | 'png': 'image/png', | |
|
82 | 'jpeg': 'image/jpeg', | |
|
83 | 'pdf': 'application/pdf', | |
|
84 | 'retina': 'image/png', | |
|
85 | 'svg': 'image/svg+xml', | |
|
86 | } | |
|
87 | ||
|
88 | def test_select_figure_formats_str(): | |
|
89 | ip = get_ipython() | |
|
90 | for fmt, active_mime in _fmt_mime_map.items(): | |
|
91 | pt.select_figure_formats(ip, fmt) | |
|
92 | for mime, f in ip.display_formatter.formatters.items(): | |
|
93 | if mime == active_mime: | |
|
94 | nt.assert_in(Figure, f) | |
|
95 | else: | |
|
96 | nt.assert_not_in(Figure, f) | |
|
97 | ||
|
98 | def test_select_figure_formats_kwargs(): | |
|
99 | ip = get_ipython() | |
|
100 | kwargs = dict(quality=10, bbox_inches='tight') | |
|
101 | pt.select_figure_formats(ip, 'png', **kwargs) | |
|
102 | formatter = ip.display_formatter.formatters['image/png'] | |
|
103 | f = formatter.lookup_by_type(Figure) | |
|
104 | cell = f.__closure__[0].cell_contents | |
|
105 | nt.assert_equal(cell, kwargs) | |
|
106 | ||
|
107 | # check that the formatter doesn't raise | |
|
108 | fig = plt.figure() | |
|
109 | ax = fig.add_subplot(1,1,1) | |
|
110 | ax.plot([1,2,3]) | |
|
111 | plt.draw() | |
|
112 | formatter.enabled = True | |
|
113 | png = formatter(fig) | |
|
114 | assert png.startswith(_PNG) | |
|
68 | 115 | |
|
69 | def test_import_pylab(): | |
|
116 | def test_select_figure_formats_set(): | |
|
70 | 117 | ip = get_ipython() |
|
118 | for fmts in [ | |
|
119 | {'png', 'svg'}, | |
|
120 | ['png'], | |
|
121 | ('jpeg', 'pdf', 'retina'), | |
|
122 | {'svg'}, | |
|
123 | ]: | |
|
124 | active_mimes = {_fmt_mime_map[fmt] for fmt in fmts} | |
|
125 | pt.select_figure_formats(ip, fmts) | |
|
126 | for mime, f in ip.display_formatter.formatters.items(): | |
|
127 | if mime in active_mimes: | |
|
128 | nt.assert_in(Figure, f) | |
|
129 | else: | |
|
130 | nt.assert_not_in(Figure, f) | |
|
131 | ||
|
132 | def test_select_figure_formats_bad(): | |
|
133 | ip = get_ipython() | |
|
134 | with nt.assert_raises(ValueError): | |
|
135 | pt.select_figure_formats(ip, 'foo') | |
|
136 | with nt.assert_raises(ValueError): | |
|
137 | pt.select_figure_formats(ip, {'png', 'foo'}) | |
|
138 | with nt.assert_raises(ValueError): | |
|
139 | pt.select_figure_formats(ip, ['retina', 'pdf', 'bar', 'bad']) | |
|
140 | ||
|
141 | def test_import_pylab(): | |
|
71 | 142 | ns = {} |
|
72 | 143 | pt.import_pylab(ns, import_all=False) |
|
73 | 144 | nt.assert_true('plt' in ns) |
|
74 | 145 | nt.assert_equal(ns['np'], np) |
|
75 | 146 | |
|
76 | 147 | class TestPylabSwitch(object): |
|
77 | 148 | class Shell(InteractiveShell): |
|
78 | 149 | def enable_gui(self, gui): |
|
79 | 150 | pass |
|
80 | 151 | |
|
81 | 152 | def setup(self): |
|
82 | 153 | import matplotlib |
|
83 | 154 | def act_mpl(backend): |
|
84 | 155 | matplotlib.rcParams['backend'] = backend |
|
85 | 156 | |
|
86 | 157 | # Save rcParams since they get modified |
|
87 | 158 | self._saved_rcParams = matplotlib.rcParams |
|
88 | 159 | self._saved_rcParamsOrig = matplotlib.rcParamsOrig |
|
89 | 160 | matplotlib.rcParams = dict(backend='Qt4Agg') |
|
90 | 161 | matplotlib.rcParamsOrig = dict(backend='Qt4Agg') |
|
91 | 162 | |
|
92 | 163 | # Mock out functions |
|
93 | 164 | self._save_am = pt.activate_matplotlib |
|
94 | 165 | pt.activate_matplotlib = act_mpl |
|
95 | 166 | self._save_ip = pt.import_pylab |
|
96 | 167 | pt.import_pylab = lambda *a,**kw:None |
|
97 | 168 | self._save_cis = pt.configure_inline_support |
|
98 | 169 | pt.configure_inline_support = lambda *a,**kw:None |
|
99 | 170 | |
|
100 | 171 | def teardown(self): |
|
101 | 172 | pt.activate_matplotlib = self._save_am |
|
102 | 173 | pt.import_pylab = self._save_ip |
|
103 | 174 | pt.configure_inline_support = self._save_cis |
|
104 | 175 | import matplotlib |
|
105 | 176 | matplotlib.rcParams = self._saved_rcParams |
|
106 | 177 | matplotlib.rcParamsOrig = self._saved_rcParamsOrig |
|
107 | 178 | |
|
108 | 179 | def test_qt(self): |
|
109 | 180 | s = self.Shell() |
|
110 | 181 | gui, backend = s.enable_matplotlib(None) |
|
111 | 182 | nt.assert_equal(gui, 'qt') |
|
112 | 183 | nt.assert_equal(s.pylab_gui_select, 'qt') |
|
113 | 184 | |
|
114 | 185 | gui, backend = s.enable_matplotlib('inline') |
|
115 | 186 | nt.assert_equal(gui, 'inline') |
|
116 | 187 | nt.assert_equal(s.pylab_gui_select, 'qt') |
|
117 | 188 | |
|
118 | 189 | gui, backend = s.enable_matplotlib('qt') |
|
119 | 190 | nt.assert_equal(gui, 'qt') |
|
120 | 191 | nt.assert_equal(s.pylab_gui_select, 'qt') |
|
121 | 192 | |
|
122 | 193 | gui, backend = s.enable_matplotlib('inline') |
|
123 | 194 | nt.assert_equal(gui, 'inline') |
|
124 | 195 | nt.assert_equal(s.pylab_gui_select, 'qt') |
|
125 | 196 | |
|
126 | 197 | gui, backend = s.enable_matplotlib() |
|
127 | 198 | nt.assert_equal(gui, 'qt') |
|
128 | 199 | nt.assert_equal(s.pylab_gui_select, 'qt') |
|
129 | 200 | |
|
130 | 201 | def test_inline(self): |
|
131 | 202 | s = self.Shell() |
|
132 | 203 | gui, backend = s.enable_matplotlib('inline') |
|
133 | 204 | nt.assert_equal(gui, 'inline') |
|
134 | 205 | nt.assert_equal(s.pylab_gui_select, None) |
|
135 | 206 | |
|
136 | 207 | gui, backend = s.enable_matplotlib('inline') |
|
137 | 208 | nt.assert_equal(gui, 'inline') |
|
138 | 209 | nt.assert_equal(s.pylab_gui_select, None) |
|
139 | 210 | |
|
140 | 211 | gui, backend = s.enable_matplotlib('qt') |
|
141 | 212 | nt.assert_equal(gui, 'qt') |
|
142 | 213 | nt.assert_equal(s.pylab_gui_select, 'qt') |
|
143 | 214 | |
|
144 | 215 | def test_qt_gtk(self): |
|
145 | 216 | s = self.Shell() |
|
146 | 217 | gui, backend = s.enable_matplotlib('qt') |
|
147 | 218 | nt.assert_equal(gui, 'qt') |
|
148 | 219 | nt.assert_equal(s.pylab_gui_select, 'qt') |
|
149 | 220 | |
|
150 | 221 | gui, backend = s.enable_matplotlib('gtk') |
|
151 | 222 | nt.assert_equal(gui, 'qt') |
|
152 | 223 | nt.assert_equal(s.pylab_gui_select, 'qt') |
|
153 | 224 |
@@ -1,115 +1,117 | |||
|
1 | 1 | """Configurable for configuring the IPython inline backend |
|
2 | 2 | |
|
3 | 3 | This module does not import anything from matplotlib. |
|
4 | 4 | """ |
|
5 | 5 | #----------------------------------------------------------------------------- |
|
6 | 6 | # Copyright (C) 2011 The IPython Development Team |
|
7 | 7 | # |
|
8 | 8 | # Distributed under the terms of the BSD License. The full license is in |
|
9 | 9 | # the file COPYING, distributed as part of this software. |
|
10 | 10 | #----------------------------------------------------------------------------- |
|
11 | 11 | |
|
12 | 12 | #----------------------------------------------------------------------------- |
|
13 | 13 | # Imports |
|
14 | 14 | #----------------------------------------------------------------------------- |
|
15 | 15 | |
|
16 | 16 | from IPython.config.configurable import SingletonConfigurable |
|
17 | 17 | from IPython.utils.traitlets import ( |
|
18 | 18 | Dict, Instance, CaselessStrEnum, Set, Bool, Int, TraitError, Unicode |
|
19 | 19 | ) |
|
20 | 20 | from IPython.utils.warn import warn |
|
21 | 21 | |
|
22 | 22 | #----------------------------------------------------------------------------- |
|
23 | 23 | # Configurable for inline backend options |
|
24 | 24 | #----------------------------------------------------------------------------- |
|
25 | 25 | |
|
26 | 26 | def pil_available(): |
|
27 | 27 | """Test if PIL/Pillow is available""" |
|
28 | 28 | out = False |
|
29 | 29 | try: |
|
30 | 30 | from PIL import Image |
|
31 | 31 | out = True |
|
32 | 32 | except: |
|
33 | 33 | pass |
|
34 | 34 | return out |
|
35 | 35 | |
|
36 | 36 | # inherit from InlineBackendConfig for deprecation purposes |
|
37 | 37 | class InlineBackendConfig(SingletonConfigurable): |
|
38 | 38 | pass |
|
39 | 39 | |
|
40 | 40 | class InlineBackend(InlineBackendConfig): |
|
41 | 41 | """An object to store configuration of the inline backend.""" |
|
42 | 42 | |
|
43 | 43 | def _config_changed(self, name, old, new): |
|
44 | 44 | # warn on change of renamed config section |
|
45 | 45 | if new.InlineBackendConfig != old.InlineBackendConfig: |
|
46 | 46 | warn("InlineBackendConfig has been renamed to InlineBackend") |
|
47 | 47 | super(InlineBackend, self)._config_changed(name, old, new) |
|
48 | 48 | |
|
49 | 49 | # The typical default figure size is too large for inline use, |
|
50 | 50 | # so we shrink the figure size to 6x4, and tweak fonts to |
|
51 | 51 | # make that fit. |
|
52 | 52 | rc = Dict({'figure.figsize': (6.0,4.0), |
|
53 | 53 | # play nicely with white background in the Qt and notebook frontend |
|
54 | 54 | 'figure.facecolor': (1,1,1,0), |
|
55 | 55 | 'figure.edgecolor': (1,1,1,0), |
|
56 | 56 | # 12pt labels get cutoff on 6x4 logplots, so use 10pt. |
|
57 | 57 | 'font.size': 10, |
|
58 | 58 | # 72 dpi matches SVG/qtconsole |
|
59 | 59 | # this only affects PNG export, as SVG has no dpi setting |
|
60 | 60 | 'savefig.dpi': 72, |
|
61 | 61 | # 10pt still needs a little more room on the xlabel: |
|
62 | 62 | 'figure.subplot.bottom' : .125 |
|
63 | 63 | }, config=True, |
|
64 | 64 | help="""Subset of matplotlib rcParams that should be different for the |
|
65 | 65 | inline backend.""" |
|
66 | 66 | ) |
|
67 | 67 | |
|
68 | 68 | figure_formats = Set({'png'}, config=True, |
|
69 | 69 | help="""A set of figure formats to enable: 'png', |
|
70 | 70 | 'retina', 'jpeg', 'svg', 'pdf'.""") |
|
71 | 71 | |
|
72 | def _update_figure_formatters(self): | |
|
73 | if self.shell is not None: | |
|
74 | select_figure_formats(self.shell, self.figure_formats, **self.print_figure_kwargs) | |
|
75 | ||
|
72 | 76 | def _figure_formats_changed(self, name, old, new): |
|
73 | 77 | from IPython.core.pylabtools import select_figure_formats |
|
74 | 78 | if 'jpg' in new or 'jpeg' in new: |
|
75 | 79 | if not pil_available(): |
|
76 | 80 | raise TraitError("Requires PIL/Pillow for JPG figures") |
|
77 | if self.shell is None: | |
|
78 | return | |
|
79 | else: | |
|
80 | select_figure_formats(self.shell, new) | |
|
81 | self._update_figure_formatters() | |
|
81 | 82 | |
|
82 | 83 | figure_format = Unicode(config=True, help="""The figure format to enable (deprecated |
|
83 | 84 | use `figure_formats` instead)""") |
|
84 | 85 | |
|
85 | 86 | def _figure_format_changed(self, name, old, new): |
|
86 | 87 | if new: |
|
87 | 88 | self.figure_formats = {new} |
|
88 | 89 | |
|
89 | quality = Int(default_value=90, config=True, | |
|
90 | help="Quality of compression [10-100], currently for lossy JPEG only.") | |
|
90 | print_figure_kwargs = Dict({'bbox_inches' : 'tight'}, config=True, | |
|
91 | help="""Extra kwargs to be passed to fig.canvas.print_figure. | |
|
91 | 92 | |
|
92 | def _quality_changed(self, name, old, new): | |
|
93 | if new < 10 or new > 100: | |
|
94 | raise TraitError("figure JPEG quality must be in [10-100] range.") | |
|
93 | Logical examples include: bbox_inches, quality (for jpeg figures), etc. | |
|
94 | """ | |
|
95 | ) | |
|
96 | _print_figure_kwargs_changed = _update_figure_formatters | |
|
95 | 97 | |
|
96 | 98 | close_figures = Bool(True, config=True, |
|
97 | 99 | help="""Close all figures at the end of each cell. |
|
98 | 100 | |
|
99 | 101 | When True, ensures that each cell starts with no active figures, but it |
|
100 | 102 | also means that one must keep track of references in order to edit or |
|
101 | 103 | redraw figures in subsequent cells. This mode is ideal for the notebook, |
|
102 | 104 | where residual plots from other cells might be surprising. |
|
103 | 105 | |
|
104 | 106 | When False, one must call figure() to create new figures. This means |
|
105 | 107 | that gcf() and getfigs() can reference figures created in other cells, |
|
106 | 108 | and the active figure can continue to be edited with pylab/pyplot |
|
107 | 109 | methods that reference the current active figure. This mode facilitates |
|
108 | 110 | iterative editing of figures, and behaves most consistently with |
|
109 | 111 | other matplotlib backends, but figure barriers between cells must |
|
110 | 112 | be explicit. |
|
111 | 113 | """) |
|
112 | 114 | |
|
113 | 115 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') |
|
114 | 116 | |
|
115 | 117 |
@@ -1,285 +1,283 | |||
|
1 | 1 | ===================== |
|
2 | 2 | Development version |
|
3 | 3 | ===================== |
|
4 | 4 | |
|
5 | 5 | This document describes in-flight development work. |
|
6 | 6 | |
|
7 | 7 | .. warning:: |
|
8 | 8 | |
|
9 | 9 | Please do not edit this file by hand (doing so will likely cause merge |
|
10 | 10 | conflicts for other Pull Requests). Instead, create a new file in the |
|
11 | 11 | `docs/source/whatsnew/pr` folder |
|
12 | 12 | |
|
13 | 13 | Select Notebook Name When Renaming a Notebook |
|
14 | 14 | --------------------------------------------- |
|
15 | 15 | |
|
16 | 16 | The default notebook name is Untitled. It's unlikely you want to keep this name |
|
17 | 17 | or part of it when naming your notebook. Instead, IPython will select the text |
|
18 | 18 | in the input field so the user can easily type over the name and change it. |
|
19 | 19 | |
|
20 | 20 | clear_output changes |
|
21 | 21 | -------------------- |
|
22 | 22 | |
|
23 | 23 | * There is no longer a 500ms delay when calling ``clear_output``. |
|
24 | 24 | * The ability to clear stderr and stdout individually was removed. |
|
25 | 25 | * A new ``wait`` flag that prevents ``clear_output`` from being executed until new |
|
26 | 26 | output is available. This eliminates animation flickering by allowing the |
|
27 | 27 | user to double buffer the output. |
|
28 | 28 | * The output div height is remembered when the ``wait=True`` flag is used. |
|
29 | 29 | |
|
30 | 30 | Extending Configurable Containers |
|
31 | 31 | --------------------------------- |
|
32 | 32 | |
|
33 | 33 | Some configurable traits are containers (list, dict, set) |
|
34 | 34 | Config objects now support calling ``extend``, ``update``, ``insert``, etc. |
|
35 | 35 | on traits in config files, which will ultimately result in calling |
|
36 | 36 | those methods on the original object. |
|
37 | 37 | |
|
38 | 38 | The effect being that you can now add to containers without having to copy/paste |
|
39 | 39 | the initial value:: |
|
40 | 40 | |
|
41 | 41 | c = get_config() |
|
42 | 42 | c.InlineBackend.rc.update({ 'figure.figsize' : (6, 4) }) |
|
43 | 43 | |
|
44 | 44 | Single codebase Python 3 support |
|
45 | 45 | -------------------------------- |
|
46 | 46 | |
|
47 | 47 | IPython previously supported Python 3 by running 2to3 during setup. We |
|
48 | 48 | have now switched to a single codebase which runs natively on Python 2.7 |
|
49 | 49 | and 3.3. |
|
50 | 50 | |
|
51 | 51 | For notes on how to maintain this, see :doc:`/development/pycompat`. |
|
52 | 52 | |
|
53 | 53 | changes to hidden namespace on startup |
|
54 | 54 | -------------------------------------- |
|
55 | 55 | |
|
56 | 56 | Previously, all names declared in code run at startup |
|
57 | 57 | (startup files, ``ipython -i script.py``, etc.) |
|
58 | 58 | were added to the hidden namespace, which hides the names from tools like ``%whos``. |
|
59 | 59 | There are two changes to this behavior: |
|
60 | 60 | |
|
61 | 61 | 1. Scripts run on the command-line ``ipython -i script.py``now behave the same as if they were |
|
62 | 62 | passed to ``%run``, so their variables are never hidden. |
|
63 | 63 | 2. A boolean config flag ``InteractiveShellApp.hide_initial_ns`` has been added to optionally |
|
64 | 64 | disable the hidden behavior altogether. The default behavior is unchanged. |
|
65 | 65 | |
|
66 | 66 | Using dill to expand serialization support |
|
67 | 67 | ------------------------------------------ |
|
68 | 68 | |
|
69 | 69 | adds :func:`~IPython.utils.pickleutil.use_dill` for allowing |
|
70 | 70 | dill to extend serialization support in :mod:`IPython.parallel` (closures, etc.). |
|
71 | 71 | Also adds :meth:`DirectView.use_dill` convenience method for enabling dill |
|
72 | 72 | locally and on all engines with one call. |
|
73 | 73 | |
|
74 | 74 | New IPython Console Lexer |
|
75 | 75 | ------------------------- |
|
76 | 76 | |
|
77 | 77 | The IPython console lexer has been rewritten and now supports tracebacks |
|
78 | 78 | and customized input/output prompts. An entire suite of lexers is now |
|
79 | 79 | available at :mod:`IPython.nbconvert.utils.lexers`. These include: |
|
80 | 80 | |
|
81 | 81 | IPythonLexer & IPython3Lexer |
|
82 | 82 | Lexers for pure IPython (python + magic/shell commands) |
|
83 | 83 | |
|
84 | 84 | IPythonPartialTracebackLexer & IPythonTracebackLexer |
|
85 | 85 | Supports 2.x and 3.x via the keyword `python3`. The partial traceback |
|
86 | 86 | lexer reads everything but the Python code appearing in a traceback. |
|
87 | 87 | The full lexer combines the partial lexer with an IPython lexer. |
|
88 | 88 | |
|
89 | 89 | IPythonConsoleLexer |
|
90 | 90 | A lexer for IPython console sessions, with support for tracebacks. |
|
91 | 91 | Supports 2.x and 3.x via the keyword `python3`. |
|
92 | 92 | |
|
93 | 93 | IPyLexer |
|
94 | 94 | A friendly lexer which examines the first line of text and from it, |
|
95 | 95 | decides whether to use an IPython lexer or an IPython console lexer. |
|
96 | 96 | Supports 2.x and 3.x via the keyword `python3`. |
|
97 | 97 | |
|
98 | 98 | Previously, the :class:`IPythonConsoleLexer` class was available at |
|
99 | 99 | :mod:`IPython.sphinxext.ipython_console_hightlight`. It was inserted |
|
100 | 100 | into Pygments' list of available lexers under the name `ipython`. It should |
|
101 | 101 | be mentioned that this name is inaccurate, since an IPython console session |
|
102 | 102 | is not the same as IPython code (which itself is a superset of the Python |
|
103 | 103 | language). |
|
104 | 104 | |
|
105 | 105 | Now, the Sphinx extension inserts two console lexers into Pygments' list of |
|
106 | 106 | available lexers. Both are IPyLexer instances under the names: `ipython` and |
|
107 | 107 | `ipython3`. Although the names can be confusing (as mentioned above), their |
|
108 | 108 | continued use is, in part, to maintain backwards compatibility and to |
|
109 | 109 | aid typical usage. If a project needs to make Pygments aware of more than just |
|
110 | 110 | the IPyLexer class, then one should not make the IPyLexer class available under |
|
111 | 111 | the name `ipython` and use `ipy` or some other non-conflicting value. |
|
112 | 112 | |
|
113 | 113 | Code blocks such as: |
|
114 | 114 | |
|
115 | 115 | .. code-block:: rst |
|
116 | 116 | |
|
117 | 117 | .. code-block:: ipython |
|
118 | 118 | |
|
119 | 119 | In [1]: 2**2 |
|
120 | 120 | Out[1]: 4 |
|
121 | 121 | |
|
122 | 122 | will continue to work as before, but now, they will also properly highlight |
|
123 | 123 | tracebacks. For pure IPython code, the same lexer will also work: |
|
124 | 124 | |
|
125 | 125 | .. code-block:: rst |
|
126 | 126 | |
|
127 | 127 | .. code-block:: ipython |
|
128 | 128 | |
|
129 | 129 | x = ''.join(map(str, range(10))) |
|
130 | 130 | !echo $x |
|
131 | 131 | |
|
132 | 132 | Since the first line of the block did not begin with a standard IPython console |
|
133 | 133 | prompt, the entire block is assumed to consist of IPython code instead. |
|
134 | 134 | |
|
135 | 135 | DisplayFormatter changes |
|
136 | 136 | ------------------------ |
|
137 | 137 | |
|
138 | 138 | There was no official way to query or remove callbacks in the Formatter API. |
|
139 | 139 | To remedy this, the following methods are added to :class:`BaseFormatter`: |
|
140 | 140 | |
|
141 | 141 | - ``lookup(instance)`` - return appropriate callback or a given object |
|
142 | 142 | - ``lookup_by_type(type_or_str)`` - return appropriate callback for a given type or ``'mod.name'`` type string |
|
143 | 143 | - ``pop(type_or_str)`` - remove a type (by type or string). |
|
144 | 144 | Pass a second argument to avoid KeyError (like dict). |
|
145 | 145 | |
|
146 | 146 | All of the above methods raise a KeyError if no match is found. |
|
147 | 147 | |
|
148 | 148 | And the following methods are changed: |
|
149 | 149 | |
|
150 | 150 | - ``for_type(type_or_str)`` - behaves the same as before, only adding support for ``'mod.name'`` |
|
151 | 151 | type strings in addition to plain types. This removes the need for ``for_type_by_name()``, |
|
152 | 152 | but it remains for backward compatibility. |
|
153 | 153 | |
|
154 | 154 | Notebook Widgets |
|
155 | 155 | ---------------- |
|
156 | 156 | |
|
157 | 157 | Available in the new `IPython.html.widgets` namespace, widgets provide an easy |
|
158 | 158 | way for IPython notebook users to display GUI controls in the IPython notebook. |
|
159 | 159 | IPython comes with bundle of built-in widgets and also the ability for users |
|
160 | 160 | to define their own widgets. A widget is displayed in the front-end using |
|
161 | 161 | using a view. For example, a FloatRangeWidget can be displayed using a |
|
162 | 162 | FloatSliderView (which is the default if no view is specified when displaying |
|
163 | 163 | the widget). IPython also comes with a bundle of views and the ability for the |
|
164 | 164 | user to define custom views. One widget can be displayed multiple times, in on |
|
165 | 165 | or more cells, using one or more views. All views will automatically remain in |
|
166 | 166 | sync with the widget which is accessible in the back-end. |
|
167 | 167 | |
|
168 | 168 | The widget layer provides an MVC-like architecture on top of the comm layer. |
|
169 | 169 | It's useful for widgets that can be expressed via a list of properties. |
|
170 | 170 | Widgets work by synchronizing IPython traitlet models in the back-end with |
|
171 | 171 | backbone models in the front-end. The widget layer automatically handles |
|
172 | 172 | |
|
173 | 173 | * delta compression (only sending the state information that has changed) |
|
174 | 174 | * wiring the message callbacks to the correct cells automatically |
|
175 | 175 | * inter-view synchronization (handled by backbone) |
|
176 | 176 | * message throttling (to avoid flooding the kernel) |
|
177 | 177 | * parent/child relationships between views (which one can override to specify custom parent/child relationships) |
|
178 | 178 | * ability to manipulate the widget view's DOM from python using CSS, $().addClass, and $().removeClass methods |
|
179 | 179 | |
|
180 | 180 | Signing Notebooks |
|
181 | 181 | ----------------- |
|
182 | 182 | |
|
183 | 183 | To prevent untrusted code from executing on users' behalf when notebooks open, |
|
184 | 184 | we have added a signature to the notebook, stored in metadata. |
|
185 | 185 | |
|
186 | 186 | For more information, see :ref:`signing_notebooks`. |
|
187 | 187 | |
|
188 | 188 | Other changes |
|
189 | 189 | ------------- |
|
190 | 190 | |
|
191 | 191 | * `%%capture` cell magic now captures the rich display output, not just |
|
192 | 192 | stdout/stderr |
|
193 | 193 | |
|
194 | 194 | * In notebook, Showing tooltip on tab has been disables to avoid conflict with |
|
195 | 195 | completion, Shift-Tab could still be used to invoke tooltip when inside |
|
196 | 196 | function signature and/or on selection. |
|
197 | 197 | |
|
198 | 198 | * ``object_info_request`` as been replaced by ``object_info`` for consistency in the javascript API. |
|
199 | 199 | ``object_info`` as a simpler interface to register callback that is incompatible with ``object_info_request``. |
|
200 | 200 | |
|
201 | 201 | * Previous versions of IPython on Linux would use the XDG config directory, |
|
202 | 202 | creating :file:`~/.config/ipython` by default. We have decided to go |
|
203 | 203 | back to :file:`~/.ipython` for consistency among systems. IPython will |
|
204 | 204 | issue a warning if it finds the XDG location, and will move it to the new |
|
205 | 205 | location if there isn't already a directory there. |
|
206 | 206 | |
|
207 | 207 | * Equations, images and tables are now centered in Markdown cells. |
|
208 | 208 | * Multiline equations are now centered in output areas; single line equations |
|
209 | 209 | remain left justified. |
|
210 | 210 | |
|
211 | 211 | * IPython config objects can be loaded from and serialized to JSON. |
|
212 | 212 | JSON config file have the same base name as their ``.py`` counterpart, |
|
213 | 213 | and will be loaded with higher priority if found. |
|
214 | 214 | |
|
215 | 215 | * bash completion updated with support for all ipython subcommands and flags, including nbconvert |
|
216 | 216 | |
|
217 | 217 | * ``ipython history trim``: added ``--keep=<N>`` as an alias for the more verbose |
|
218 | 218 | ``--HistoryTrim.keep=<N>`` |
|
219 | 219 | * new ``ipython history clear`` subcommand, which is the same as the newly supported |
|
220 | 220 | ``ipython history trim --keep=0`` |
|
221 | 221 | |
|
222 | 222 | * You can now run notebooks in an interactive session via ``%run notebook.ipynb``. |
|
223 | 223 | |
|
224 | 224 | * Print preview is back in the notebook menus, along with options to |
|
225 | 225 | download the open notebook in various formats. This is powered by |
|
226 | 226 | nbconvert. |
|
227 | 227 | |
|
228 | 228 | * :exc:`~IPython.nbconvert.utils.pandoc.PandocMissing` exceptions will be |
|
229 | 229 | raised if Pandoc is unavailable, and warnings will be printed if the version |
|
230 | 230 | found is too old. The recommended Pandoc version for use with nbconvert is |
|
231 | 231 | 1.12.1. |
|
232 | 232 | |
|
233 | 233 | * The InlineBackend.figure_format flag now supports JPEG output if PIL/Pillow is available. |
|
234 | * The new ``InlineBackend.quality`` flag is a Integer in the range [10, 100] which controls | |
|
235 | the quality of figures where higher values give nicer images (currently JPEG only). | |
|
236 | 234 | |
|
237 | 235 | * Input transformers (see :doc:`/config/inputtransforms`) may now raise |
|
238 | 236 | :exc:`SyntaxError` if they determine that input is invalid. The input |
|
239 | 237 | transformation machinery in IPython will handle displaying the exception to |
|
240 | 238 | the user and resetting state. |
|
241 | 239 | |
|
242 | 240 | .. DO NOT EDIT THIS LINE BEFORE RELEASE. FEATURE INSERTION POINT. |
|
243 | 241 | |
|
244 | 242 | Backwards incompatible changes |
|
245 | 243 | ------------------------------ |
|
246 | 244 | |
|
247 | 245 | * Python 2.6 and 3.2 are no longer supported: the minimum required |
|
248 | 246 | Python versions are now 2.7 and 3.3. |
|
249 | 247 | * The Transformer classes have been renamed to Preprocessor in nbconvert and |
|
250 | 248 | their `call` methods for them have been renamed to `preprocess`. |
|
251 | 249 | * The `call` methods of nbconvert post-processsors have been renamed to |
|
252 | 250 | `postprocess`. |
|
253 | 251 | |
|
254 | 252 | * The module ``IPython.core.fakemodule`` has been removed. |
|
255 | 253 | |
|
256 | 254 | * The alias system has been reimplemented to use magic functions. There should be little |
|
257 | 255 | visible difference while automagics are enabled, as they are by default, but parts of the |
|
258 | 256 | :class:`~IPython.core.alias.AliasManager` API have been removed. |
|
259 | 257 | |
|
260 | 258 | * We fixed an issue with switching between matplotlib inline and GUI backends, |
|
261 | 259 | but the fix requires matplotlib 1.1 or newer. So from now on, we consider |
|
262 | 260 | matplotlib 1.1 to be the minimally supported version for IPython. Older |
|
263 | 261 | versions for the most part will work, but we make no guarantees about it. |
|
264 | 262 | |
|
265 | 263 | * The :command:`pycolor` command has been removed. We recommend the much more capable |
|
266 | 264 | :command:`pygmentize` command from the `Pygments <http://pygments.org/>`_ project. |
|
267 | 265 | If you need to keep the exact output of :command:`pycolor`, you can still use |
|
268 | 266 | ``python -m IPython.utils.PyColorize foo.py``. |
|
269 | 267 | |
|
270 | 268 | * :mod:`IPython.lib.irunner` and its command-line entry point have been removed. |
|
271 | 269 | It had fallen out of use long ago. |
|
272 | 270 | |
|
273 | 271 | * The ``input_prefilter`` hook has been removed, as it was never |
|
274 | 272 | actually used by the code. The input transformer system offers much |
|
275 | 273 | more powerful APIs to work with input code. See |
|
276 | 274 | :doc:`/config/inputtransforms` for details. |
|
277 | 275 | |
|
278 | 276 | * :class:`IPython.core.inputsplitter.IPythonInputSplitter` no longer has a method |
|
279 | 277 | ``source_raw_reset()``, but gains :meth:`~IPython.core.inputsplitter.IPythonInputSplitter.raw_reset` |
|
280 | 278 | instead. Use of ``source_raw_reset`` can be replaced with:: |
|
281 | 279 | |
|
282 | 280 | raw = isp.source_raw |
|
283 | 281 | transformed = isp.source_reset() |
|
284 | 282 | |
|
285 | 283 | .. DO NOT EDIT THIS LINE BEFORE RELEASE. INCOMPAT INSERTION POINT. |
General Comments 0
You need to be logged in to leave comments.
Login now