##// END OF EJS Templates
add unconfined, raw metadata to display.Image...
Min RK -
Show More
@@ -1,948 +1,967 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Top-level display functions for displaying object in different formats."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 from __future__ import print_function
8 8
9 9 import json
10 10 import mimetypes
11 11 import os
12 12 import struct
13 13 import warnings
14 14
15 15 from IPython.core.formatters import _safe_get_formatter_method
16 16 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
17 17 unicode_type)
18 18 from IPython.testing.skipdoctest import skip_doctest
19 19
20 20 __all__ = ['display', 'display_pretty', 'display_html', 'display_markdown',
21 21 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
22 22 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
23 23 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'JSON', 'Javascript',
24 24 'Image', 'clear_output', 'set_matplotlib_formats', 'set_matplotlib_close',
25 25 'publish_display_data']
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # utility functions
29 29 #-----------------------------------------------------------------------------
30 30
31 31 def _safe_exists(path):
32 32 """Check path, but don't let exceptions raise"""
33 33 try:
34 34 return os.path.exists(path)
35 35 except Exception:
36 36 return False
37 37
38 38 def _merge(d1, d2):
39 39 """Like update, but merges sub-dicts instead of clobbering at the top level.
40 40
41 41 Updates d1 in-place
42 42 """
43 43
44 44 if not isinstance(d2, dict) or not isinstance(d1, dict):
45 45 return d2
46 46 for key, value in d2.items():
47 47 d1[key] = _merge(d1.get(key), value)
48 48 return d1
49 49
50 50 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
51 51 """internal implementation of all display_foo methods
52 52
53 53 Parameters
54 54 ----------
55 55 mimetype : str
56 56 The mimetype to be published (e.g. 'image/png')
57 57 objs : tuple of objects
58 58 The Python objects to display, or if raw=True raw text data to
59 59 display.
60 60 raw : bool
61 61 Are the data objects raw data or Python objects that need to be
62 62 formatted before display? [default: False]
63 63 metadata : dict (optional)
64 64 Metadata to be associated with the specific mimetype output.
65 65 """
66 66 if metadata:
67 67 metadata = {mimetype: metadata}
68 68 if raw:
69 69 # turn list of pngdata into list of { 'image/png': pngdata }
70 70 objs = [ {mimetype: obj} for obj in objs ]
71 71 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
72 72
73 73 #-----------------------------------------------------------------------------
74 74 # Main functions
75 75 #-----------------------------------------------------------------------------
76 76
77 77 def publish_display_data(data, metadata=None, source=None):
78 78 """Publish data and metadata to all frontends.
79 79
80 80 See the ``display_data`` message in the messaging documentation for
81 81 more details about this message type.
82 82
83 83 The following MIME types are currently implemented:
84 84
85 85 * text/plain
86 86 * text/html
87 87 * text/markdown
88 88 * text/latex
89 89 * application/json
90 90 * application/javascript
91 91 * image/png
92 92 * image/jpeg
93 93 * image/svg+xml
94 94
95 95 Parameters
96 96 ----------
97 97 data : dict
98 98 A dictionary having keys that are valid MIME types (like
99 99 'text/plain' or 'image/svg+xml') and values that are the data for
100 100 that MIME type. The data itself must be a JSON'able data
101 101 structure. Minimally all data should have the 'text/plain' data,
102 102 which can be displayed by all frontends. If more than the plain
103 103 text is given, it is up to the frontend to decide which
104 104 representation to use.
105 105 metadata : dict
106 106 A dictionary for metadata related to the data. This can contain
107 107 arbitrary key, value pairs that frontends can use to interpret
108 108 the data. mime-type keys matching those in data can be used
109 109 to specify metadata about particular representations.
110 110 source : str, deprecated
111 111 Unused.
112 112 """
113 113 from IPython.core.interactiveshell import InteractiveShell
114 114 InteractiveShell.instance().display_pub.publish(
115 115 data=data,
116 116 metadata=metadata,
117 117 )
118 118
119 119 def display(*objs, **kwargs):
120 120 """Display a Python object in all frontends.
121 121
122 122 By default all representations will be computed and sent to the frontends.
123 123 Frontends can decide which representation is used and how.
124 124
125 125 Parameters
126 126 ----------
127 127 objs : tuple of objects
128 128 The Python objects to display.
129 129 raw : bool, optional
130 130 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
131 131 or Python objects that need to be formatted before display? [default: False]
132 132 include : list or tuple, optional
133 133 A list of format type strings (MIME types) to include in the
134 134 format data dict. If this is set *only* the format types included
135 135 in this list will be computed.
136 136 exclude : list or tuple, optional
137 137 A list of format type strings (MIME types) to exclude in the format
138 138 data dict. If this is set all format types will be computed,
139 139 except for those included in this argument.
140 140 metadata : dict, optional
141 141 A dictionary of metadata to associate with the output.
142 142 mime-type keys in this dictionary will be associated with the individual
143 143 representation formats, if they exist.
144 144 """
145 145 raw = kwargs.get('raw', False)
146 146 include = kwargs.get('include')
147 147 exclude = kwargs.get('exclude')
148 148 metadata = kwargs.get('metadata')
149 149
150 150 from IPython.core.interactiveshell import InteractiveShell
151 151
152 152 if not raw:
153 153 format = InteractiveShell.instance().display_formatter.format
154 154
155 155 for obj in objs:
156 156 if raw:
157 157 publish_display_data(data=obj, metadata=metadata)
158 158 else:
159 159 format_dict, md_dict = format(obj, include=include, exclude=exclude)
160 160 if not format_dict:
161 161 # nothing to display (e.g. _ipython_display_ took over)
162 162 continue
163 163 if metadata:
164 164 # kwarg-specified metadata gets precedence
165 165 _merge(md_dict, metadata)
166 166 publish_display_data(data=format_dict, metadata=md_dict)
167 167
168 168
169 169 def display_pretty(*objs, **kwargs):
170 170 """Display the pretty (default) representation of an object.
171 171
172 172 Parameters
173 173 ----------
174 174 objs : tuple of objects
175 175 The Python objects to display, or if raw=True raw text data to
176 176 display.
177 177 raw : bool
178 178 Are the data objects raw data or Python objects that need to be
179 179 formatted before display? [default: False]
180 180 metadata : dict (optional)
181 181 Metadata to be associated with the specific mimetype output.
182 182 """
183 183 _display_mimetype('text/plain', objs, **kwargs)
184 184
185 185
186 186 def display_html(*objs, **kwargs):
187 187 """Display the HTML representation of an object.
188 188
189 189 Parameters
190 190 ----------
191 191 objs : tuple of objects
192 192 The Python objects to display, or if raw=True raw HTML data to
193 193 display.
194 194 raw : bool
195 195 Are the data objects raw data or Python objects that need to be
196 196 formatted before display? [default: False]
197 197 metadata : dict (optional)
198 198 Metadata to be associated with the specific mimetype output.
199 199 """
200 200 _display_mimetype('text/html', objs, **kwargs)
201 201
202 202
203 203 def display_markdown(*objs, **kwargs):
204 204 """Displays the Markdown representation of an object.
205 205
206 206 Parameters
207 207 ----------
208 208 objs : tuple of objects
209 209 The Python objects to display, or if raw=True raw markdown data to
210 210 display.
211 211 raw : bool
212 212 Are the data objects raw data or Python objects that need to be
213 213 formatted before display? [default: False]
214 214 metadata : dict (optional)
215 215 Metadata to be associated with the specific mimetype output.
216 216 """
217 217
218 218 _display_mimetype('text/markdown', objs, **kwargs)
219 219
220 220
221 221 def display_svg(*objs, **kwargs):
222 222 """Display the SVG representation of an object.
223 223
224 224 Parameters
225 225 ----------
226 226 objs : tuple of objects
227 227 The Python objects to display, or if raw=True raw svg data to
228 228 display.
229 229 raw : bool
230 230 Are the data objects raw data or Python objects that need to be
231 231 formatted before display? [default: False]
232 232 metadata : dict (optional)
233 233 Metadata to be associated with the specific mimetype output.
234 234 """
235 235 _display_mimetype('image/svg+xml', objs, **kwargs)
236 236
237 237
238 238 def display_png(*objs, **kwargs):
239 239 """Display the PNG representation of an object.
240 240
241 241 Parameters
242 242 ----------
243 243 objs : tuple of objects
244 244 The Python objects to display, or if raw=True raw png data to
245 245 display.
246 246 raw : bool
247 247 Are the data objects raw data or Python objects that need to be
248 248 formatted before display? [default: False]
249 249 metadata : dict (optional)
250 250 Metadata to be associated with the specific mimetype output.
251 251 """
252 252 _display_mimetype('image/png', objs, **kwargs)
253 253
254 254
255 255 def display_jpeg(*objs, **kwargs):
256 256 """Display the JPEG representation of an object.
257 257
258 258 Parameters
259 259 ----------
260 260 objs : tuple of objects
261 261 The Python objects to display, or if raw=True raw JPEG data to
262 262 display.
263 263 raw : bool
264 264 Are the data objects raw data or Python objects that need to be
265 265 formatted before display? [default: False]
266 266 metadata : dict (optional)
267 267 Metadata to be associated with the specific mimetype output.
268 268 """
269 269 _display_mimetype('image/jpeg', objs, **kwargs)
270 270
271 271
272 272 def display_latex(*objs, **kwargs):
273 273 """Display the LaTeX representation of an object.
274 274
275 275 Parameters
276 276 ----------
277 277 objs : tuple of objects
278 278 The Python objects to display, or if raw=True raw latex data to
279 279 display.
280 280 raw : bool
281 281 Are the data objects raw data or Python objects that need to be
282 282 formatted before display? [default: False]
283 283 metadata : dict (optional)
284 284 Metadata to be associated with the specific mimetype output.
285 285 """
286 286 _display_mimetype('text/latex', objs, **kwargs)
287 287
288 288
289 289 def display_json(*objs, **kwargs):
290 290 """Display the JSON representation of an object.
291 291
292 292 Note that not many frontends support displaying JSON.
293 293
294 294 Parameters
295 295 ----------
296 296 objs : tuple of objects
297 297 The Python objects to display, or if raw=True raw json data to
298 298 display.
299 299 raw : bool
300 300 Are the data objects raw data or Python objects that need to be
301 301 formatted before display? [default: False]
302 302 metadata : dict (optional)
303 303 Metadata to be associated with the specific mimetype output.
304 304 """
305 305 _display_mimetype('application/json', objs, **kwargs)
306 306
307 307
308 308 def display_javascript(*objs, **kwargs):
309 309 """Display the Javascript representation of an object.
310 310
311 311 Parameters
312 312 ----------
313 313 objs : tuple of objects
314 314 The Python objects to display, or if raw=True raw javascript data to
315 315 display.
316 316 raw : bool
317 317 Are the data objects raw data or Python objects that need to be
318 318 formatted before display? [default: False]
319 319 metadata : dict (optional)
320 320 Metadata to be associated with the specific mimetype output.
321 321 """
322 322 _display_mimetype('application/javascript', objs, **kwargs)
323 323
324 324
325 325 def display_pdf(*objs, **kwargs):
326 326 """Display the PDF representation of an object.
327 327
328 328 Parameters
329 329 ----------
330 330 objs : tuple of objects
331 331 The Python objects to display, or if raw=True raw javascript data to
332 332 display.
333 333 raw : bool
334 334 Are the data objects raw data or Python objects that need to be
335 335 formatted before display? [default: False]
336 336 metadata : dict (optional)
337 337 Metadata to be associated with the specific mimetype output.
338 338 """
339 339 _display_mimetype('application/pdf', objs, **kwargs)
340 340
341 341
342 342 #-----------------------------------------------------------------------------
343 343 # Smart classes
344 344 #-----------------------------------------------------------------------------
345 345
346 346
347 347 class DisplayObject(object):
348 348 """An object that wraps data to be displayed."""
349 349
350 350 _read_flags = 'r'
351 351 _show_mem_addr = False
352 352
353 353 def __init__(self, data=None, url=None, filename=None):
354 354 """Create a display object given raw data.
355 355
356 356 When this object is returned by an expression or passed to the
357 357 display function, it will result in the data being displayed
358 358 in the frontend. The MIME type of the data should match the
359 359 subclasses used, so the Png subclass should be used for 'image/png'
360 360 data. If the data is a URL, the data will first be downloaded
361 361 and then displayed. If
362 362
363 363 Parameters
364 364 ----------
365 365 data : unicode, str or bytes
366 366 The raw data or a URL or file to load the data from
367 367 url : unicode
368 368 A URL to download the data from.
369 369 filename : unicode
370 370 Path to a local file to load the data from.
371 371 """
372 372 if data is not None and isinstance(data, string_types):
373 373 if data.startswith('http') and url is None:
374 374 url = data
375 375 filename = None
376 376 data = None
377 377 elif _safe_exists(data) and filename is None:
378 378 url = None
379 379 filename = data
380 380 data = None
381 381
382 382 self.data = data
383 383 self.url = url
384 384 self.filename = None if filename is None else unicode_type(filename)
385 385
386 386 self.reload()
387 387 self._check_data()
388 388
389 389 def __repr__(self):
390 390 if not self._show_mem_addr:
391 391 cls = self.__class__
392 392 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
393 393 else:
394 394 r = super(DisplayObject, self).__repr__()
395 395 return r
396 396
397 397 def _check_data(self):
398 398 """Override in subclasses if there's something to check."""
399 399 pass
400 400
401 401 def reload(self):
402 402 """Reload the raw data from file or URL."""
403 403 if self.filename is not None:
404 404 with open(self.filename, self._read_flags) as f:
405 405 self.data = f.read()
406 406 elif self.url is not None:
407 407 try:
408 408 try:
409 409 from urllib.request import urlopen # Py3
410 410 except ImportError:
411 411 from urllib2 import urlopen
412 412 response = urlopen(self.url)
413 413 self.data = response.read()
414 414 # extract encoding from header, if there is one:
415 415 encoding = None
416 416 for sub in response.headers['content-type'].split(';'):
417 417 sub = sub.strip()
418 418 if sub.startswith('charset'):
419 419 encoding = sub.split('=')[-1].strip()
420 420 break
421 421 # decode data, if an encoding was specified
422 422 if encoding:
423 423 self.data = self.data.decode(encoding, 'replace')
424 424 except:
425 425 self.data = None
426 426
427 427 class TextDisplayObject(DisplayObject):
428 428 """Validate that display data is text"""
429 429 def _check_data(self):
430 430 if self.data is not None and not isinstance(self.data, string_types):
431 431 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
432 432
433 433 class Pretty(TextDisplayObject):
434 434
435 435 def _repr_pretty_(self):
436 436 return self.data
437 437
438 438
439 439 class HTML(TextDisplayObject):
440 440
441 441 def _repr_html_(self):
442 442 return self.data
443 443
444 444 def __html__(self):
445 445 """
446 446 This method exists to inform other HTML-using modules (e.g. Markupsafe,
447 447 htmltag, etc) that this object is HTML and does not need things like
448 448 special characters (<>&) escaped.
449 449 """
450 450 return self._repr_html_()
451 451
452 452
453 453 class Markdown(TextDisplayObject):
454 454
455 455 def _repr_markdown_(self):
456 456 return self.data
457 457
458 458
459 459 class Math(TextDisplayObject):
460 460
461 461 def _repr_latex_(self):
462 462 s = self.data.strip('$')
463 463 return "$$%s$$" % s
464 464
465 465
466 466 class Latex(TextDisplayObject):
467 467
468 468 def _repr_latex_(self):
469 469 return self.data
470 470
471 471
472 472 class SVG(DisplayObject):
473 473
474 474 # wrap data in a property, which extracts the <svg> tag, discarding
475 475 # document headers
476 476 _data = None
477 477
478 478 @property
479 479 def data(self):
480 480 return self._data
481 481
482 482 @data.setter
483 483 def data(self, svg):
484 484 if svg is None:
485 485 self._data = None
486 486 return
487 487 # parse into dom object
488 488 from xml.dom import minidom
489 489 svg = cast_bytes_py2(svg)
490 490 x = minidom.parseString(svg)
491 491 # get svg tag (should be 1)
492 492 found_svg = x.getElementsByTagName('svg')
493 493 if found_svg:
494 494 svg = found_svg[0].toxml()
495 495 else:
496 496 # fallback on the input, trust the user
497 497 # but this is probably an error.
498 498 pass
499 499 svg = cast_unicode(svg)
500 500 self._data = svg
501 501
502 502 def _repr_svg_(self):
503 503 return self.data
504 504
505 505
506 506 class JSON(DisplayObject):
507 507 """JSON expects a JSON-able dict or list
508 508
509 509 not an already-serialized JSON string.
510 510
511 511 Scalar types (None, number, string) are not allowed, only dict or list containers.
512 512 """
513 513 # wrap data in a property, which warns about passing already-serialized JSON
514 514 _data = None
515 515 def _check_data(self):
516 516 if self.data is not None and not isinstance(self.data, (dict, list)):
517 517 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
518 518
519 519 @property
520 520 def data(self):
521 521 return self._data
522 522
523 523 @data.setter
524 524 def data(self, data):
525 525 if isinstance(data, string_types):
526 526 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
527 527 data = json.loads(data)
528 528 self._data = data
529 529
530 530 def _repr_json_(self):
531 531 return self.data
532 532
533 533 css_t = """$("head").append($("<link/>").attr({
534 534 rel: "stylesheet",
535 535 type: "text/css",
536 536 href: "%s"
537 537 }));
538 538 """
539 539
540 540 lib_t1 = """$.getScript("%s", function () {
541 541 """
542 542 lib_t2 = """});
543 543 """
544 544
545 545 class Javascript(TextDisplayObject):
546 546
547 547 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
548 548 """Create a Javascript display object given raw data.
549 549
550 550 When this object is returned by an expression or passed to the
551 551 display function, it will result in the data being displayed
552 552 in the frontend. If the data is a URL, the data will first be
553 553 downloaded and then displayed.
554 554
555 555 In the Notebook, the containing element will be available as `element`,
556 556 and jQuery will be available. Content appended to `element` will be
557 557 visible in the output area.
558 558
559 559 Parameters
560 560 ----------
561 561 data : unicode, str or bytes
562 562 The Javascript source code or a URL to download it from.
563 563 url : unicode
564 564 A URL to download the data from.
565 565 filename : unicode
566 566 Path to a local file to load the data from.
567 567 lib : list or str
568 568 A sequence of Javascript library URLs to load asynchronously before
569 569 running the source code. The full URLs of the libraries should
570 570 be given. A single Javascript library URL can also be given as a
571 571 string.
572 572 css: : list or str
573 573 A sequence of css files to load before running the source code.
574 574 The full URLs of the css files should be given. A single css URL
575 575 can also be given as a string.
576 576 """
577 577 if isinstance(lib, string_types):
578 578 lib = [lib]
579 579 elif lib is None:
580 580 lib = []
581 581 if isinstance(css, string_types):
582 582 css = [css]
583 583 elif css is None:
584 584 css = []
585 585 if not isinstance(lib, (list,tuple)):
586 586 raise TypeError('expected sequence, got: %r' % lib)
587 587 if not isinstance(css, (list,tuple)):
588 588 raise TypeError('expected sequence, got: %r' % css)
589 589 self.lib = lib
590 590 self.css = css
591 591 super(Javascript, self).__init__(data=data, url=url, filename=filename)
592 592
593 593 def _repr_javascript_(self):
594 594 r = ''
595 595 for c in self.css:
596 596 r += css_t % c
597 597 for l in self.lib:
598 598 r += lib_t1 % l
599 599 r += self.data
600 600 r += lib_t2*len(self.lib)
601 601 return r
602 602
603 603 # constants for identifying png/jpeg data
604 604 _PNG = b'\x89PNG\r\n\x1a\n'
605 605 _JPEG = b'\xff\xd8'
606 606
607 607 def _pngxy(data):
608 608 """read the (width, height) from a PNG header"""
609 609 ihdr = data.index(b'IHDR')
610 610 # next 8 bytes are width/height
611 611 w4h4 = data[ihdr+4:ihdr+12]
612 612 return struct.unpack('>ii', w4h4)
613 613
614 614 def _jpegxy(data):
615 615 """read the (width, height) from a JPEG header"""
616 616 # adapted from http://www.64lines.com/jpeg-width-height
617 617
618 618 idx = 4
619 619 while True:
620 620 block_size = struct.unpack('>H', data[idx:idx+2])[0]
621 621 idx = idx + block_size
622 622 if data[idx:idx+2] == b'\xFF\xC0':
623 623 # found Start of Frame
624 624 iSOF = idx
625 625 break
626 626 else:
627 627 # read another block
628 628 idx += 2
629 629
630 630 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
631 631 return w, h
632 632
633 633 class Image(DisplayObject):
634 634
635 635 _read_flags = 'rb'
636 636 _FMT_JPEG = u'jpeg'
637 637 _FMT_PNG = u'png'
638 638 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
639 639
640 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
640 def __init__(self, data=None, url=None, filename=None, format=u'png',
641 embed=None, width=None, height=None, retina=False,
642 unconfined=False, metadata=None):
641 643 """Create a PNG/JPEG image object given raw data.
642 644
643 645 When this object is returned by an input cell or passed to the
644 646 display function, it will result in the image being displayed
645 647 in the frontend.
646 648
647 649 Parameters
648 650 ----------
649 651 data : unicode, str or bytes
650 652 The raw image data or a URL or filename to load the data from.
651 653 This always results in embedded image data.
652 654 url : unicode
653 655 A URL to download the data from. If you specify `url=`,
654 656 the image data will not be embedded unless you also specify `embed=True`.
655 657 filename : unicode
656 658 Path to a local file to load the data from.
657 659 Images from a file are always embedded.
658 660 format : unicode
659 661 The format of the image data (png/jpeg/jpg). If a filename or URL is given
660 662 for format will be inferred from the filename extension.
661 663 embed : bool
662 664 Should the image data be embedded using a data URI (True) or be
663 665 loaded using an <img> tag. Set this to True if you want the image
664 666 to be viewable later with no internet connection in the notebook.
665 667
666 668 Default is `True`, unless the keyword argument `url` is set, then
667 669 default value is `False`.
668 670
669 671 Note that QtConsole is not able to display images if `embed` is set to `False`
670 672 width : int
671 673 Width to which to constrain the image in html
672 674 height : int
673 675 Height to which to constrain the image in html
674 676 retina : bool
675 677 Automatically set the width and height to half of the measured
676 678 width and height.
677 679 This only works for embedded images because it reads the width/height
678 680 from image data.
679 681 For non-embedded images, you can just set the desired display width
680 682 and height directly.
683 unconfined: bool
684 Set unconfined=True to disable max-width confinement of the image.
685 metadata: dict
686 Specify extra metadata to attach to the image.
681 687
682 688 Examples
683 689 --------
684 690 # embedded image data, works in qtconsole and notebook
685 691 # when passed positionally, the first arg can be any of raw image data,
686 692 # a URL, or a filename from which to load image data.
687 693 # The result is always embedding image data for inline images.
688 694 Image('http://www.google.fr/images/srpr/logo3w.png')
689 695 Image('/path/to/image.jpg')
690 696 Image(b'RAW_PNG_DATA...')
691 697
692 698 # Specifying Image(url=...) does not embed the image data,
693 699 # it only generates `<img>` tag with a link to the source.
694 700 # This will not work in the qtconsole or offline.
695 701 Image(url='http://www.google.fr/images/srpr/logo3w.png')
696 702
697 703 """
698 704 if filename is not None:
699 705 ext = self._find_ext(filename)
700 706 elif url is not None:
701 707 ext = self._find_ext(url)
702 708 elif data is None:
703 709 raise ValueError("No image data found. Expecting filename, url, or data.")
704 710 elif isinstance(data, string_types) and (
705 711 data.startswith('http') or _safe_exists(data)
706 712 ):
707 713 ext = self._find_ext(data)
708 714 else:
709 715 ext = None
710 716
711 717 if ext is not None:
712 718 format = ext.lower()
713 719 if ext == u'jpg' or ext == u'jpeg':
714 720 format = self._FMT_JPEG
715 721 if ext == u'png':
716 722 format = self._FMT_PNG
717 723 elif isinstance(data, bytes) and format == 'png':
718 724 # infer image type from image data header,
719 725 # only if format might not have been specified.
720 726 if data[:2] == _JPEG:
721 727 format = 'jpeg'
722 728
723 729 self.format = unicode_type(format).lower()
724 730 self.embed = embed if embed is not None else (url is None)
725 731
726 732 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
727 733 raise ValueError("Cannot embed the '%s' image format" % (self.format))
728 734 self.width = width
729 735 self.height = height
730 736 self.retina = retina
737 self.unconfined = unconfined
738 self.metadata = metadata
731 739 super(Image, self).__init__(data=data, url=url, filename=filename)
732 740
733 741 if retina:
734 742 self._retina_shape()
735 743
736 744 def _retina_shape(self):
737 745 """load pixel-doubled width and height from image data"""
738 746 if not self.embed:
739 747 return
740 748 if self.format == 'png':
741 749 w, h = _pngxy(self.data)
742 750 elif self.format == 'jpeg':
743 751 w, h = _jpegxy(self.data)
744 752 else:
745 753 # retina only supports png
746 754 return
747 755 self.width = w // 2
748 756 self.height = h // 2
749 757
750 758 def reload(self):
751 759 """Reload the raw data from file or URL."""
752 760 if self.embed:
753 761 super(Image,self).reload()
754 762 if self.retina:
755 763 self._retina_shape()
756 764
757 765 def _repr_html_(self):
758 766 if not self.embed:
759 width = height = ''
767 width = height = klass = ''
760 768 if self.width:
761 769 width = ' width="%d"' % self.width
762 770 if self.height:
763 771 height = ' height="%d"' % self.height
764 return u'<img src="%s"%s%s/>' % (self.url, width, height)
772 if self.unconfined:
773 klass = ' class="unconfined"'
774 return u'<img src="{url}"{width}{height}{klass}/>'.format(
775 url=self.url,
776 width=width,
777 height=height,
778 klass=klass,
779 )
765 780
766 781 def _data_and_metadata(self):
767 782 """shortcut for returning metadata with shape information, if defined"""
768 783 md = {}
769 784 if self.width:
770 785 md['width'] = self.width
771 786 if self.height:
772 787 md['height'] = self.height
788 if self.unconfined:
789 md['unconfined'] = self.unconfined
790 if self.metadata:
791 md.update(self.metadata)
773 792 if md:
774 793 return self.data, md
775 794 else:
776 795 return self.data
777 796
778 797 def _repr_png_(self):
779 798 if self.embed and self.format == u'png':
780 799 return self._data_and_metadata()
781 800
782 801 def _repr_jpeg_(self):
783 802 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
784 803 return self._data_and_metadata()
785 804
786 805 def _find_ext(self, s):
787 806 return unicode_type(s.split('.')[-1].lower())
788 807
789 808 class Video(DisplayObject):
790 809
791 810 def __init__(self, data=None, url=None, filename=None, embed=None, mimetype=None):
792 811 """Create a video object given raw data or an URL.
793 812
794 813 When this object is returned by an input cell or passed to the
795 814 display function, it will result in the video being displayed
796 815 in the frontend.
797 816
798 817 Parameters
799 818 ----------
800 819 data : unicode, str or bytes
801 820 The raw image data or a URL or filename to load the data from.
802 821 This always results in embedded image data.
803 822 url : unicode
804 823 A URL to download the data from. If you specify `url=`,
805 824 the image data will not be embedded unless you also specify `embed=True`.
806 825 filename : unicode
807 826 Path to a local file to load the data from.
808 827 Videos from a file are always embedded.
809 828 embed : bool
810 829 Should the image data be embedded using a data URI (True) or be
811 830 loaded using an <img> tag. Set this to True if you want the image
812 831 to be viewable later with no internet connection in the notebook.
813 832
814 833 Default is `True`, unless the keyword argument `url` is set, then
815 834 default value is `False`.
816 835
817 836 Note that QtConsole is not able to display images if `embed` is set to `False`
818 837 mimetype: unicode
819 838 Specify the mimetype in case you load in a encoded video.
820 839 Examples
821 840 --------
822 841 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
823 842 Video('path/to/video.mp4')
824 843 Video('path/to/video.mp4', embed=False)
825 844 """
826 845 if url is None and (data.startswith('http') or data.startswith('https')):
827 846 url = data
828 847 data = None
829 848 embed = False
830 849 elif os.path.exists(data):
831 850 filename = data
832 851 data = None
833 852
834 853 self.mimetype = mimetype
835 854 self.embed = embed if embed is not None else (filename is not None)
836 855 super(Video, self).__init__(data=data, url=url, filename=filename)
837 856
838 857 def _repr_html_(self):
839 858 # External URLs and potentially local files are not embedded into the
840 859 # notebook output.
841 860 if not self.embed:
842 861 url = self.url if self.url is not None else self.filename
843 862 output = """<video src="{0}" controls>
844 863 Your browser does not support the <code>video</code> element.
845 864 </video>""".format(url)
846 865 return output
847 866 # Embedded videos uses base64 encoded videos.
848 867 if self.filename is not None:
849 868 mimetypes.init()
850 869 mimetype, encoding = mimetypes.guess_type(self.filename)
851 870
852 871 video = open(self.filename, 'rb').read()
853 872 video_encoded = video.encode('base64')
854 873 else:
855 874 video_encoded = self.data
856 875 mimetype = self.mimetype
857 876 output = """<video controls>
858 877 <source src="data:{0};base64,{1}" type="{0}">
859 878 Your browser does not support the video tag.
860 879 </video>""".format(mimetype, video_encoded)
861 880 return output
862 881
863 882 def reload(self):
864 883 # TODO
865 884 pass
866 885
867 886 def _repr_png_(self):
868 887 # TODO
869 888 pass
870 889 def _repr_jpeg_(self):
871 890 # TODO
872 891 pass
873 892
874 893 def clear_output(wait=False):
875 894 """Clear the output of the current cell receiving output.
876 895
877 896 Parameters
878 897 ----------
879 898 wait : bool [default: false]
880 899 Wait to clear the output until new output is available to replace it."""
881 900 from IPython.core.interactiveshell import InteractiveShell
882 901 if InteractiveShell.initialized():
883 902 InteractiveShell.instance().display_pub.clear_output(wait)
884 903 else:
885 904 from IPython.utils import io
886 905 print('\033[2K\r', file=io.stdout, end='')
887 906 io.stdout.flush()
888 907 print('\033[2K\r', file=io.stderr, end='')
889 908 io.stderr.flush()
890 909
891 910
892 911 @skip_doctest
893 912 def set_matplotlib_formats(*formats, **kwargs):
894 913 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
895 914
896 915 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
897 916
898 917 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
899 918
900 919 To set this in your config files use the following::
901 920
902 921 c.InlineBackend.figure_formats = {'png', 'jpeg'}
903 922 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
904 923
905 924 Parameters
906 925 ----------
907 926 *formats : strs
908 927 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
909 928 **kwargs :
910 929 Keyword args will be relayed to ``figure.canvas.print_figure``.
911 930 """
912 931 from IPython.core.interactiveshell import InteractiveShell
913 932 from IPython.core.pylabtools import select_figure_formats
914 933 from IPython.kernel.zmq.pylab.config import InlineBackend
915 934 # build kwargs, starting with InlineBackend config
916 935 kw = {}
917 936 cfg = InlineBackend.instance()
918 937 kw.update(cfg.print_figure_kwargs)
919 938 kw.update(**kwargs)
920 939 shell = InteractiveShell.instance()
921 940 select_figure_formats(shell, formats, **kw)
922 941
923 942 @skip_doctest
924 943 def set_matplotlib_close(close=True):
925 944 """Set whether the inline backend closes all figures automatically or not.
926 945
927 946 By default, the inline backend used in the IPython Notebook will close all
928 947 matplotlib figures automatically after each cell is run. This means that
929 948 plots in different cells won't interfere. Sometimes, you may want to make
930 949 a plot in one cell and then refine it in later cells. This can be accomplished
931 950 by::
932 951
933 952 In [1]: set_matplotlib_close(False)
934 953
935 954 To set this in your config files use the following::
936 955
937 956 c.InlineBackend.close_figures = False
938 957
939 958 Parameters
940 959 ----------
941 960 close : bool
942 961 Should all matplotlib figures be automatically closed after each cell is
943 962 run?
944 963 """
945 964 from IPython.kernel.zmq.pylab.config import InlineBackend
946 965 cfg = InlineBackend.instance()
947 966 cfg.close_figures = close
948 967
@@ -1,153 +1,155 b''
1 1 # Copyright (c) IPython Development Team.
2 2 # Distributed under the terms of the Modified BSD License.
3 3
4 4 import json
5 5 import os
6 6 import warnings
7 7
8 8 import nose.tools as nt
9 9
10 10 from IPython.core import display
11 11 from IPython.core.getipython import get_ipython
12 12 from IPython.utils import path as ipath
13 13
14 14 import IPython.testing.decorators as dec
15 15
16 16 def test_image_size():
17 17 """Simple test for display.Image(args, width=x,height=y)"""
18 18 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
19 19 img = display.Image(url=thisurl, width=200, height=200)
20 20 nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_())
21 21 img = display.Image(url=thisurl, width=200)
22 22 nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_())
23 23 img = display.Image(url=thisurl)
24 24 nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_())
25 img = display.Image(url=thisurl, unconfined=True)
26 nt.assert_equal(u'<img src="%s" class="unconfined"/>' % (thisurl), img._repr_html_())
25 27
26 28 def test_retina_png():
27 29 here = os.path.dirname(__file__)
28 30 img = display.Image(os.path.join(here, "2x2.png"), retina=True)
29 31 nt.assert_equal(img.height, 1)
30 32 nt.assert_equal(img.width, 1)
31 33 data, md = img._repr_png_()
32 34 nt.assert_equal(md['width'], 1)
33 35 nt.assert_equal(md['height'], 1)
34 36
35 37 def test_retina_jpeg():
36 38 here = os.path.dirname(__file__)
37 39 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
38 40 nt.assert_equal(img.height, 1)
39 41 nt.assert_equal(img.width, 1)
40 42 data, md = img._repr_jpeg_()
41 43 nt.assert_equal(md['width'], 1)
42 44 nt.assert_equal(md['height'], 1)
43 45
44 46 def test_image_filename_defaults():
45 47 '''test format constraint, and validity of jpeg and png'''
46 48 tpath = ipath.get_ipython_package_dir()
47 49 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.gif'),
48 50 embed=True)
49 51 nt.assert_raises(ValueError, display.Image)
50 52 nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
51 53 from IPython.html import DEFAULT_STATIC_FILES_PATH
52 54 # check boths paths to allow packages to test at build and install time
53 55 imgfile = os.path.join(tpath, 'html/static/base/images/logo.png')
54 56 if not os.path.exists(imgfile):
55 57 imgfile = os.path.join(DEFAULT_STATIC_FILES_PATH, 'base/images/logo.png')
56 58 img = display.Image(filename=imgfile)
57 59 nt.assert_equal('png', img.format)
58 60 nt.assert_is_not_none(img._repr_png_())
59 61 img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False)
60 62 nt.assert_equal('jpeg', img.format)
61 63 nt.assert_is_none(img._repr_jpeg_())
62 64
63 65 def _get_inline_config():
64 66 from IPython.kernel.zmq.pylab.config import InlineBackend
65 67 return InlineBackend.instance()
66 68
67 69 @dec.skip_without('matplotlib')
68 70 def test_set_matplotlib_close():
69 71 cfg = _get_inline_config()
70 72 cfg.close_figures = False
71 73 display.set_matplotlib_close()
72 74 assert cfg.close_figures
73 75 display.set_matplotlib_close(False)
74 76 assert not cfg.close_figures
75 77
76 78 _fmt_mime_map = {
77 79 'png': 'image/png',
78 80 'jpeg': 'image/jpeg',
79 81 'pdf': 'application/pdf',
80 82 'retina': 'image/png',
81 83 'svg': 'image/svg+xml',
82 84 }
83 85
84 86 @dec.skip_without('matplotlib')
85 87 def test_set_matplotlib_formats():
86 88 from matplotlib.figure import Figure
87 89 formatters = get_ipython().display_formatter.formatters
88 90 for formats in [
89 91 ('png',),
90 92 ('pdf', 'svg'),
91 93 ('jpeg', 'retina', 'png'),
92 94 (),
93 95 ]:
94 96 active_mimes = {_fmt_mime_map[fmt] for fmt in formats}
95 97 display.set_matplotlib_formats(*formats)
96 98 for mime, f in formatters.items():
97 99 if mime in active_mimes:
98 100 nt.assert_in(Figure, f)
99 101 else:
100 102 nt.assert_not_in(Figure, f)
101 103
102 104 @dec.skip_without('matplotlib')
103 105 def test_set_matplotlib_formats_kwargs():
104 106 from matplotlib.figure import Figure
105 107 ip = get_ipython()
106 108 cfg = _get_inline_config()
107 109 cfg.print_figure_kwargs.update(dict(foo='bar'))
108 110 kwargs = dict(quality=10)
109 111 display.set_matplotlib_formats('png', **kwargs)
110 112 formatter = ip.display_formatter.formatters['image/png']
111 113 f = formatter.lookup_by_type(Figure)
112 114 cell = f.__closure__[0].cell_contents
113 115 expected = kwargs
114 116 expected.update(cfg.print_figure_kwargs)
115 117 nt.assert_equal(cell, expected)
116 118
117 119 def test_displayobject_repr():
118 120 h = display.HTML('<br />')
119 121 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
120 122 h._show_mem_addr = True
121 123 nt.assert_equal(repr(h), object.__repr__(h))
122 124 h._show_mem_addr = False
123 125 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
124 126
125 127 j = display.Javascript('')
126 128 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
127 129 j._show_mem_addr = True
128 130 nt.assert_equal(repr(j), object.__repr__(j))
129 131 j._show_mem_addr = False
130 132 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
131 133
132 134 def test_json():
133 135 d = {'a': 5}
134 136 lis = [d]
135 137 j = display.JSON(d)
136 138 nt.assert_equal(j._repr_json_(), d)
137 139
138 140 with warnings.catch_warnings(record=True) as w:
139 141 warnings.simplefilter("always")
140 142 j = display.JSON(json.dumps(d))
141 143 nt.assert_equal(len(w), 1)
142 144 nt.assert_equal(j._repr_json_(), d)
143 145
144 146 j = display.JSON(lis)
145 147 nt.assert_equal(j._repr_json_(), lis)
146 148
147 149 with warnings.catch_warnings(record=True) as w:
148 150 warnings.simplefilter("always")
149 151 j = display.JSON(json.dumps(lis))
150 152 nt.assert_equal(len(w), 1)
151 153 nt.assert_equal(j._repr_json_(), lis)
152 154
153 155
General Comments 0
You need to be logged in to leave comments. Login now