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