##// END OF EJS Templates
Updated docstring for core.display.Image to make clear that the image is not necessarily displayed on creation.
Andrew Mark -
Show More
@@ -1,691 +1,691 b''
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
27 27 from .displaypub import publish_display_data
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # utility functions
31 31 #-----------------------------------------------------------------------------
32 32
33 33 def _safe_exists(path):
34 34 """Check path, but don't let exceptions raise"""
35 35 try:
36 36 return os.path.exists(path)
37 37 except Exception:
38 38 return False
39 39
40 40 def _merge(d1, d2):
41 41 """Like update, but merges sub-dicts instead of clobbering at the top level.
42 42
43 43 Updates d1 in-place
44 44 """
45 45
46 46 if not isinstance(d2, dict) or not isinstance(d1, dict):
47 47 return d2
48 48 for key, value in d2.items():
49 49 d1[key] = _merge(d1.get(key), value)
50 50 return d1
51 51
52 52 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
53 53 """internal implementation of all display_foo methods
54 54
55 55 Parameters
56 56 ----------
57 57 mimetype : str
58 58 The mimetype to be published (e.g. 'image/png')
59 59 objs : tuple of objects
60 60 The Python objects to display, or if raw=True raw text data to
61 61 display.
62 62 raw : bool
63 63 Are the data objects raw data or Python objects that need to be
64 64 formatted before display? [default: False]
65 65 metadata : dict (optional)
66 66 Metadata to be associated with the specific mimetype output.
67 67 """
68 68 if metadata:
69 69 metadata = {mimetype: metadata}
70 70 if raw:
71 71 # turn list of pngdata into list of { 'image/png': pngdata }
72 72 objs = [ {mimetype: obj} for obj in objs ]
73 73 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
74 74
75 75 #-----------------------------------------------------------------------------
76 76 # Main functions
77 77 #-----------------------------------------------------------------------------
78 78
79 79 def display(*objs, **kwargs):
80 80 """Display a Python object in all frontends.
81 81
82 82 By default all representations will be computed and sent to the frontends.
83 83 Frontends can decide which representation is used and how.
84 84
85 85 Parameters
86 86 ----------
87 87 objs : tuple of objects
88 88 The Python objects to display.
89 89 raw : bool, optional
90 90 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
91 91 or Python objects that need to be formatted before display? [default: False]
92 92 include : list or tuple, optional
93 93 A list of format type strings (MIME types) to include in the
94 94 format data dict. If this is set *only* the format types included
95 95 in this list will be computed.
96 96 exclude : list or tuple, optional
97 97 A list of format type strings (MIME types) to exclude in the format
98 98 data dict. If this is set all format types will be computed,
99 99 except for those included in this argument.
100 100 metadata : dict, optional
101 101 A dictionary of metadata to associate with the output.
102 102 mime-type keys in this dictionary will be associated with the individual
103 103 representation formats, if they exist.
104 104 """
105 105 raw = kwargs.get('raw', False)
106 106 include = kwargs.get('include')
107 107 exclude = kwargs.get('exclude')
108 108 metadata = kwargs.get('metadata')
109 109
110 110 from IPython.core.interactiveshell import InteractiveShell
111 111
112 112 if raw:
113 113 for obj in objs:
114 114 publish_display_data('display', obj, metadata)
115 115 else:
116 116 format = InteractiveShell.instance().display_formatter.format
117 117 for obj in objs:
118 118 format_dict, md_dict = format(obj, include=include, exclude=exclude)
119 119 if metadata:
120 120 # kwarg-specified metadata gets precedence
121 121 _merge(md_dict, metadata)
122 122 publish_display_data('display', format_dict, md_dict)
123 123
124 124
125 125 def display_pretty(*objs, **kwargs):
126 126 """Display the pretty (default) representation of an object.
127 127
128 128 Parameters
129 129 ----------
130 130 objs : tuple of objects
131 131 The Python objects to display, or if raw=True raw text data to
132 132 display.
133 133 raw : bool
134 134 Are the data objects raw data or Python objects that need to be
135 135 formatted before display? [default: False]
136 136 metadata : dict (optional)
137 137 Metadata to be associated with the specific mimetype output.
138 138 """
139 139 _display_mimetype('text/plain', objs, **kwargs)
140 140
141 141
142 142 def display_html(*objs, **kwargs):
143 143 """Display the HTML representation of an object.
144 144
145 145 Parameters
146 146 ----------
147 147 objs : tuple of objects
148 148 The Python objects to display, or if raw=True raw HTML data to
149 149 display.
150 150 raw : bool
151 151 Are the data objects raw data or Python objects that need to be
152 152 formatted before display? [default: False]
153 153 metadata : dict (optional)
154 154 Metadata to be associated with the specific mimetype output.
155 155 """
156 156 _display_mimetype('text/html', objs, **kwargs)
157 157
158 158
159 159 def display_svg(*objs, **kwargs):
160 160 """Display the SVG representation of an object.
161 161
162 162 Parameters
163 163 ----------
164 164 objs : tuple of objects
165 165 The Python objects to display, or if raw=True raw svg data to
166 166 display.
167 167 raw : bool
168 168 Are the data objects raw data or Python objects that need to be
169 169 formatted before display? [default: False]
170 170 metadata : dict (optional)
171 171 Metadata to be associated with the specific mimetype output.
172 172 """
173 173 _display_mimetype('image/svg+xml', objs, **kwargs)
174 174
175 175
176 176 def display_png(*objs, **kwargs):
177 177 """Display the PNG representation of an object.
178 178
179 179 Parameters
180 180 ----------
181 181 objs : tuple of objects
182 182 The Python objects to display, or if raw=True raw png data to
183 183 display.
184 184 raw : bool
185 185 Are the data objects raw data or Python objects that need to be
186 186 formatted before display? [default: False]
187 187 metadata : dict (optional)
188 188 Metadata to be associated with the specific mimetype output.
189 189 """
190 190 _display_mimetype('image/png', objs, **kwargs)
191 191
192 192
193 193 def display_jpeg(*objs, **kwargs):
194 194 """Display the JPEG representation of an object.
195 195
196 196 Parameters
197 197 ----------
198 198 objs : tuple of objects
199 199 The Python objects to display, or if raw=True raw JPEG data to
200 200 display.
201 201 raw : bool
202 202 Are the data objects raw data or Python objects that need to be
203 203 formatted before display? [default: False]
204 204 metadata : dict (optional)
205 205 Metadata to be associated with the specific mimetype output.
206 206 """
207 207 _display_mimetype('image/jpeg', objs, **kwargs)
208 208
209 209
210 210 def display_latex(*objs, **kwargs):
211 211 """Display the LaTeX representation of an object.
212 212
213 213 Parameters
214 214 ----------
215 215 objs : tuple of objects
216 216 The Python objects to display, or if raw=True raw latex data to
217 217 display.
218 218 raw : bool
219 219 Are the data objects raw data or Python objects that need to be
220 220 formatted before display? [default: False]
221 221 metadata : dict (optional)
222 222 Metadata to be associated with the specific mimetype output.
223 223 """
224 224 _display_mimetype('text/latex', objs, **kwargs)
225 225
226 226
227 227 def display_json(*objs, **kwargs):
228 228 """Display the JSON representation of an object.
229 229
230 230 Note that not many frontends support displaying JSON.
231 231
232 232 Parameters
233 233 ----------
234 234 objs : tuple of objects
235 235 The Python objects to display, or if raw=True raw json data to
236 236 display.
237 237 raw : bool
238 238 Are the data objects raw data or Python objects that need to be
239 239 formatted before display? [default: False]
240 240 metadata : dict (optional)
241 241 Metadata to be associated with the specific mimetype output.
242 242 """
243 243 _display_mimetype('application/json', objs, **kwargs)
244 244
245 245
246 246 def display_javascript(*objs, **kwargs):
247 247 """Display the Javascript representation of an object.
248 248
249 249 Parameters
250 250 ----------
251 251 objs : tuple of objects
252 252 The Python objects to display, or if raw=True raw javascript data to
253 253 display.
254 254 raw : bool
255 255 Are the data objects raw data or Python objects that need to be
256 256 formatted before display? [default: False]
257 257 metadata : dict (optional)
258 258 Metadata to be associated with the specific mimetype output.
259 259 """
260 260 _display_mimetype('application/javascript', objs, **kwargs)
261 261
262 262 #-----------------------------------------------------------------------------
263 263 # Smart classes
264 264 #-----------------------------------------------------------------------------
265 265
266 266
267 267 class DisplayObject(object):
268 268 """An object that wraps data to be displayed."""
269 269
270 270 _read_flags = 'r'
271 271
272 272 def __init__(self, data=None, url=None, filename=None):
273 273 """Create a display object given raw data.
274 274
275 275 When this object is returned by an expression or passed to the
276 276 display function, it will result in the data being displayed
277 277 in the frontend. The MIME type of the data should match the
278 278 subclasses used, so the Png subclass should be used for 'image/png'
279 279 data. If the data is a URL, the data will first be downloaded
280 280 and then displayed. If
281 281
282 282 Parameters
283 283 ----------
284 284 data : unicode, str or bytes
285 285 The raw data or a URL or file to load the data from
286 286 url : unicode
287 287 A URL to download the data from.
288 288 filename : unicode
289 289 Path to a local file to load the data from.
290 290 """
291 291 if data is not None and isinstance(data, string_types):
292 292 if data.startswith('http') and url is None:
293 293 url = data
294 294 filename = None
295 295 data = None
296 296 elif _safe_exists(data) and filename is None:
297 297 url = None
298 298 filename = data
299 299 data = None
300 300
301 301 self.data = data
302 302 self.url = url
303 303 self.filename = None if filename is None else unicode(filename)
304 304
305 305 self.reload()
306 306
307 307 def reload(self):
308 308 """Reload the raw data from file or URL."""
309 309 if self.filename is not None:
310 310 with open(self.filename, self._read_flags) as f:
311 311 self.data = f.read()
312 312 elif self.url is not None:
313 313 try:
314 314 import urllib2
315 315 response = urllib2.urlopen(self.url)
316 316 self.data = response.read()
317 317 # extract encoding from header, if there is one:
318 318 encoding = None
319 319 for sub in response.headers['content-type'].split(';'):
320 320 sub = sub.strip()
321 321 if sub.startswith('charset'):
322 322 encoding = sub.split('=')[-1].strip()
323 323 break
324 324 # decode data, if an encoding was specified
325 325 if encoding:
326 326 self.data = self.data.decode(encoding, 'replace')
327 327 except:
328 328 self.data = None
329 329
330 330 class Pretty(DisplayObject):
331 331
332 332 def _repr_pretty_(self):
333 333 return self.data
334 334
335 335
336 336 class HTML(DisplayObject):
337 337
338 338 def _repr_html_(self):
339 339 return self.data
340 340
341 341 def __html__(self):
342 342 """
343 343 This method exists to inform other HTML-using modules (e.g. Markupsafe,
344 344 htmltag, etc) that this object is HTML and does not need things like
345 345 special characters (<>&) escaped.
346 346 """
347 347 return self._repr_html_()
348 348
349 349
350 350 class Math(DisplayObject):
351 351
352 352 def _repr_latex_(self):
353 353 s = self.data.strip('$')
354 354 return "$$%s$$" % s
355 355
356 356
357 357 class Latex(DisplayObject):
358 358
359 359 def _repr_latex_(self):
360 360 return self.data
361 361
362 362
363 363 class SVG(DisplayObject):
364 364
365 365 # wrap data in a property, which extracts the <svg> tag, discarding
366 366 # document headers
367 367 _data = None
368 368
369 369 @property
370 370 def data(self):
371 371 return self._data
372 372
373 373 @data.setter
374 374 def data(self, svg):
375 375 if svg is None:
376 376 self._data = None
377 377 return
378 378 # parse into dom object
379 379 from xml.dom import minidom
380 380 svg = cast_bytes_py2(svg)
381 381 x = minidom.parseString(svg)
382 382 # get svg tag (should be 1)
383 383 found_svg = x.getElementsByTagName('svg')
384 384 if found_svg:
385 385 svg = found_svg[0].toxml()
386 386 else:
387 387 # fallback on the input, trust the user
388 388 # but this is probably an error.
389 389 pass
390 390 svg = cast_unicode(svg)
391 391 self._data = svg
392 392
393 393 def _repr_svg_(self):
394 394 return self.data
395 395
396 396
397 397 class JSON(DisplayObject):
398 398
399 399 def _repr_json_(self):
400 400 return self.data
401 401
402 402 css_t = """$("head").append($("<link/>").attr({
403 403 rel: "stylesheet",
404 404 type: "text/css",
405 405 href: "%s"
406 406 }));
407 407 """
408 408
409 409 lib_t1 = """$.getScript("%s", function () {
410 410 """
411 411 lib_t2 = """});
412 412 """
413 413
414 414 class Javascript(DisplayObject):
415 415
416 416 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
417 417 """Create a Javascript display object given raw data.
418 418
419 419 When this object is returned by an expression or passed to the
420 420 display function, it will result in the data being displayed
421 421 in the frontend. If the data is a URL, the data will first be
422 422 downloaded and then displayed.
423 423
424 424 In the Notebook, the containing element will be available as `element`,
425 425 and jQuery will be available. The output area starts hidden, so if
426 426 the js appends content to `element` that should be visible, then
427 427 it must call `container.show()` to unhide the area.
428 428
429 429 Parameters
430 430 ----------
431 431 data : unicode, str or bytes
432 432 The Javascript source code or a URL to download it from.
433 433 url : unicode
434 434 A URL to download the data from.
435 435 filename : unicode
436 436 Path to a local file to load the data from.
437 437 lib : list or str
438 438 A sequence of Javascript library URLs to load asynchronously before
439 439 running the source code. The full URLs of the libraries should
440 440 be given. A single Javascript library URL can also be given as a
441 441 string.
442 442 css: : list or str
443 443 A sequence of css files to load before running the source code.
444 444 The full URLs of the css files should be given. A single css URL
445 445 can also be given as a string.
446 446 """
447 447 if isinstance(lib, basestring):
448 448 lib = [lib]
449 449 elif lib is None:
450 450 lib = []
451 451 if isinstance(css, basestring):
452 452 css = [css]
453 453 elif css is None:
454 454 css = []
455 455 if not isinstance(lib, (list,tuple)):
456 456 raise TypeError('expected sequence, got: %r' % lib)
457 457 if not isinstance(css, (list,tuple)):
458 458 raise TypeError('expected sequence, got: %r' % css)
459 459 self.lib = lib
460 460 self.css = css
461 461 super(Javascript, self).__init__(data=data, url=url, filename=filename)
462 462
463 463 def _repr_javascript_(self):
464 464 r = ''
465 465 for c in self.css:
466 466 r += css_t % c
467 467 for l in self.lib:
468 468 r += lib_t1 % l
469 469 r += self.data
470 470 r += lib_t2*len(self.lib)
471 471 return r
472 472
473 473 # constants for identifying png/jpeg data
474 474 _PNG = b'\x89PNG\r\n\x1a\n'
475 475 _JPEG = b'\xff\xd8'
476 476
477 477 def _pngxy(data):
478 478 """read the (width, height) from a PNG header"""
479 479 ihdr = data.index(b'IHDR')
480 480 # next 8 bytes are width/height
481 481 w4h4 = data[ihdr+4:ihdr+12]
482 482 return struct.unpack('>ii', w4h4)
483 483
484 484 def _jpegxy(data):
485 485 """read the (width, height) from a JPEG header"""
486 486 # adapted from http://www.64lines.com/jpeg-width-height
487 487
488 488 idx = 4
489 489 while True:
490 490 block_size = struct.unpack('>H', data[idx:idx+2])[0]
491 491 idx = idx + block_size
492 492 if data[idx:idx+2] == b'\xFF\xC0':
493 493 # found Start of Frame
494 494 iSOF = idx
495 495 break
496 496 else:
497 497 # read another block
498 498 idx += 2
499 499
500 500 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
501 501 return w, h
502 502
503 503 class Image(DisplayObject):
504 504
505 505 _read_flags = 'rb'
506 506 _FMT_JPEG = u'jpeg'
507 507 _FMT_PNG = u'png'
508 508 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
509 509
510 510 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
511 """Create a display an PNG/JPEG image given raw data.
511 """Create a PNG/JPEG image object given raw data.
512 512
513 When this object is returned by an expression or passed to the
513 When this object is returned by an input cell or passed to the
514 514 display function, it will result in the image being displayed
515 515 in the frontend.
516 516
517 517 Parameters
518 518 ----------
519 519 data : unicode, str or bytes
520 520 The raw image data or a URL or filename to load the data from.
521 521 This always results in embedded image data.
522 522 url : unicode
523 523 A URL to download the data from. If you specify `url=`,
524 524 the image data will not be embedded unless you also specify `embed=True`.
525 525 filename : unicode
526 526 Path to a local file to load the data from.
527 527 Images from a file are always embedded.
528 528 format : unicode
529 529 The format of the image data (png/jpeg/jpg). If a filename or URL is given
530 530 for format will be inferred from the filename extension.
531 531 embed : bool
532 532 Should the image data be embedded using a data URI (True) or be
533 533 loaded using an <img> tag. Set this to True if you want the image
534 534 to be viewable later with no internet connection in the notebook.
535 535
536 536 Default is `True`, unless the keyword argument `url` is set, then
537 537 default value is `False`.
538 538
539 539 Note that QtConsole is not able to display images if `embed` is set to `False`
540 540 width : int
541 541 Width to which to constrain the image in html
542 542 height : int
543 543 Height to which to constrain the image in html
544 544 retina : bool
545 545 Automatically set the width and height to half of the measured
546 546 width and height.
547 547 This only works for embedded images because it reads the width/height
548 548 from image data.
549 549 For non-embedded images, you can just set the desired display width
550 550 and height directly.
551 551
552 552 Examples
553 553 --------
554 554 # embedded image data, works in qtconsole and notebook
555 555 # when passed positionally, the first arg can be any of raw image data,
556 556 # a URL, or a filename from which to load image data.
557 557 # The result is always embedding image data for inline images.
558 558 Image('http://www.google.fr/images/srpr/logo3w.png')
559 559 Image('/path/to/image.jpg')
560 560 Image(b'RAW_PNG_DATA...')
561 561
562 562 # Specifying Image(url=...) does not embed the image data,
563 563 # it only generates `<img>` tag with a link to the source.
564 564 # This will not work in the qtconsole or offline.
565 565 Image(url='http://www.google.fr/images/srpr/logo3w.png')
566 566
567 567 """
568 568 if filename is not None:
569 569 ext = self._find_ext(filename)
570 570 elif url is not None:
571 571 ext = self._find_ext(url)
572 572 elif data is None:
573 573 raise ValueError("No image data found. Expecting filename, url, or data.")
574 574 elif isinstance(data, string_types) and (
575 575 data.startswith('http') or _safe_exists(data)
576 576 ):
577 577 ext = self._find_ext(data)
578 578 else:
579 579 ext = None
580 580
581 581 if ext is not None:
582 582 format = ext.lower()
583 583 if ext == u'jpg' or ext == u'jpeg':
584 584 format = self._FMT_JPEG
585 585 if ext == u'png':
586 586 format = self._FMT_PNG
587 587 elif isinstance(data, bytes) and format == 'png':
588 588 # infer image type from image data header,
589 589 # only if format might not have been specified.
590 590 if data[:2] == _JPEG:
591 591 format = 'jpeg'
592 592
593 593 self.format = unicode(format).lower()
594 594 self.embed = embed if embed is not None else (url is None)
595 595
596 596 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
597 597 raise ValueError("Cannot embed the '%s' image format" % (self.format))
598 598 self.width = width
599 599 self.height = height
600 600 self.retina = retina
601 601 super(Image, self).__init__(data=data, url=url, filename=filename)
602 602
603 603 if retina:
604 604 self._retina_shape()
605 605
606 606 def _retina_shape(self):
607 607 """load pixel-doubled width and height from image data"""
608 608 if not self.embed:
609 609 return
610 610 if self.format == 'png':
611 611 w, h = _pngxy(self.data)
612 612 elif self.format == 'jpeg':
613 613 w, h = _jpegxy(self.data)
614 614 else:
615 615 # retina only supports png
616 616 return
617 617 self.width = w // 2
618 618 self.height = h // 2
619 619
620 620 def reload(self):
621 621 """Reload the raw data from file or URL."""
622 622 if self.embed:
623 623 super(Image,self).reload()
624 624 if self.retina:
625 625 self._retina_shape()
626 626
627 627 def _repr_html_(self):
628 628 if not self.embed:
629 629 width = height = ''
630 630 if self.width:
631 631 width = ' width="%d"' % self.width
632 632 if self.height:
633 633 height = ' height="%d"' % self.height
634 634 return u'<img src="%s"%s%s/>' % (self.url, width, height)
635 635
636 636 def _data_and_metadata(self):
637 637 """shortcut for returning metadata with shape information, if defined"""
638 638 md = {}
639 639 if self.width:
640 640 md['width'] = self.width
641 641 if self.height:
642 642 md['height'] = self.height
643 643 if md:
644 644 return self.data, md
645 645 else:
646 646 return self.data
647 647
648 648 def _repr_png_(self):
649 649 if self.embed and self.format == u'png':
650 650 return self._data_and_metadata()
651 651
652 652 def _repr_jpeg_(self):
653 653 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
654 654 return self._data_and_metadata()
655 655
656 656 def _find_ext(self, s):
657 657 return unicode(s.split('.')[-1].lower())
658 658
659 659
660 660 def clear_output(stdout=True, stderr=True, other=True):
661 661 """Clear the output of the current cell receiving output.
662 662
663 663 Optionally, each of stdout/stderr or other non-stream data (e.g. anything
664 664 produced by display()) can be excluded from the clear event.
665 665
666 666 By default, everything is cleared.
667 667
668 668 Parameters
669 669 ----------
670 670 stdout : bool [default: True]
671 671 Whether to clear stdout.
672 672 stderr : bool [default: True]
673 673 Whether to clear stderr.
674 674 other : bool [default: True]
675 675 Whether to clear everything else that is not stdout/stderr
676 676 (e.g. figures,images,HTML, any result of display()).
677 677 """
678 678 from IPython.core.interactiveshell import InteractiveShell
679 679 if InteractiveShell.initialized():
680 680 InteractiveShell.instance().display_pub.clear_output(
681 681 stdout=stdout, stderr=stderr, other=other,
682 682 )
683 683 else:
684 684 from IPython.utils import io
685 685 if stdout:
686 686 print('\033[2K\r', file=io.stdout, end='')
687 687 io.stdout.flush()
688 688 if stderr:
689 689 print('\033[2K\r', file=io.stderr, end='')
690 690 io.stderr.flush()
691 691
General Comments 0
You need to be logged in to leave comments. Login now