##// END OF EJS Templates
Adding iteration to ProgressBar to make it a more useful utility
Henry Fredrick Schreiner -
Show More
@@ -1,1399 +1,1412 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
8 8 from binascii import b2a_hex, b2a_base64, hexlify
9 9 import json
10 10 import mimetypes
11 11 import os
12 12 import struct
13 13 import sys
14 14 import warnings
15 15 from copy import deepcopy
16 16
17 17 from IPython.utils.py3compat import cast_unicode
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', 'ProgressBar', 'JSON',
24 24 'GeoJSON', 'Javascript', 'Image', 'clear_output', 'set_matplotlib_formats',
25 25 'set_matplotlib_close', 'publish_display_data', 'update_display', 'DisplayHandle',
26 26 'Video']
27 27
28 28 #-----------------------------------------------------------------------------
29 29 # utility functions
30 30 #-----------------------------------------------------------------------------
31 31
32 32 def _safe_exists(path):
33 33 """Check path, but don't let exceptions raise"""
34 34 try:
35 35 return os.path.exists(path)
36 36 except Exception:
37 37 return False
38 38
39 39 def _merge(d1, d2):
40 40 """Like update, but merges sub-dicts instead of clobbering at the top level.
41 41
42 42 Updates d1 in-place
43 43 """
44 44
45 45 if not isinstance(d2, dict) or not isinstance(d1, dict):
46 46 return d2
47 47 for key, value in d2.items():
48 48 d1[key] = _merge(d1.get(key), value)
49 49 return d1
50 50
51 51 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
52 52 """internal implementation of all display_foo methods
53 53
54 54 Parameters
55 55 ----------
56 56 mimetype : str
57 57 The mimetype to be published (e.g. 'image/png')
58 58 objs : tuple of objects
59 59 The Python objects to display, or if raw=True raw text data to
60 60 display.
61 61 raw : bool
62 62 Are the data objects raw data or Python objects that need to be
63 63 formatted before display? [default: False]
64 64 metadata : dict (optional)
65 65 Metadata to be associated with the specific mimetype output.
66 66 """
67 67 if metadata:
68 68 metadata = {mimetype: metadata}
69 69 if raw:
70 70 # turn list of pngdata into list of { 'image/png': pngdata }
71 71 objs = [ {mimetype: obj} for obj in objs ]
72 72 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
73 73
74 74 #-----------------------------------------------------------------------------
75 75 # Main functions
76 76 #-----------------------------------------------------------------------------
77 77
78 78 # use * to indicate transient is keyword-only
79 79 def publish_display_data(data, metadata=None, source=None, *, transient=None, **kwargs):
80 80 """Publish data and metadata to all frontends.
81 81
82 82 See the ``display_data`` message in the messaging documentation for
83 83 more details about this message type.
84 84
85 85 Keys of data and metadata can be any mime-type.
86 86
87 87 Parameters
88 88 ----------
89 89 data : dict
90 90 A dictionary having keys that are valid MIME types (like
91 91 'text/plain' or 'image/svg+xml') and values that are the data for
92 92 that MIME type. The data itself must be a JSON'able data
93 93 structure. Minimally all data should have the 'text/plain' data,
94 94 which can be displayed by all frontends. If more than the plain
95 95 text is given, it is up to the frontend to decide which
96 96 representation to use.
97 97 metadata : dict
98 98 A dictionary for metadata related to the data. This can contain
99 99 arbitrary key, value pairs that frontends can use to interpret
100 100 the data. mime-type keys matching those in data can be used
101 101 to specify metadata about particular representations.
102 102 source : str, deprecated
103 103 Unused.
104 104 transient : dict, keyword-only
105 105 A dictionary of transient data, such as display_id.
106 106 """
107 107 from IPython.core.interactiveshell import InteractiveShell
108 108
109 109 display_pub = InteractiveShell.instance().display_pub
110 110
111 111 # only pass transient if supplied,
112 112 # to avoid errors with older ipykernel.
113 113 # TODO: We could check for ipykernel version and provide a detailed upgrade message.
114 114 if transient:
115 115 kwargs['transient'] = transient
116 116
117 117 display_pub.publish(
118 118 data=data,
119 119 metadata=metadata,
120 120 **kwargs
121 121 )
122 122
123 123
124 124 def _new_id():
125 125 """Generate a new random text id with urandom"""
126 126 return b2a_hex(os.urandom(16)).decode('ascii')
127 127
128 128
129 129 def display(*objs, include=None, exclude=None, metadata=None, transient=None, display_id=None, **kwargs):
130 130 """Display a Python object in all frontends.
131 131
132 132 By default all representations will be computed and sent to the frontends.
133 133 Frontends can decide which representation is used and how.
134 134
135 135 In terminal IPython this will be similar to using :func:`print`, for use in richer
136 136 frontends see Jupyter notebook examples with rich display logic.
137 137
138 138 Parameters
139 139 ----------
140 140 objs : tuple of objects
141 141 The Python objects to display.
142 142 raw : bool, optional
143 143 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
144 144 or Python objects that need to be formatted before display? [default: False]
145 145 include : list, tuple or set, optional
146 146 A list of format type strings (MIME types) to include in the
147 147 format data dict. If this is set *only* the format types included
148 148 in this list will be computed.
149 149 exclude : list, tuple or set, optional
150 150 A list of format type strings (MIME types) to exclude in the format
151 151 data dict. If this is set all format types will be computed,
152 152 except for those included in this argument.
153 153 metadata : dict, optional
154 154 A dictionary of metadata to associate with the output.
155 155 mime-type keys in this dictionary will be associated with the individual
156 156 representation formats, if they exist.
157 157 transient : dict, optional
158 158 A dictionary of transient data to associate with the output.
159 159 Data in this dict should not be persisted to files (e.g. notebooks).
160 160 display_id : str, bool optional
161 161 Set an id for the display.
162 162 This id can be used for updating this display area later via update_display.
163 163 If given as `True`, generate a new `display_id`
164 164 kwargs: additional keyword-args, optional
165 165 Additional keyword-arguments are passed through to the display publisher.
166 166
167 167 Returns
168 168 -------
169 169
170 170 handle: DisplayHandle
171 171 Returns a handle on updatable displays for use with :func:`update_display`,
172 172 if `display_id` is given. Returns :any:`None` if no `display_id` is given
173 173 (default).
174 174
175 175 Examples
176 176 --------
177 177
178 178 >>> class Json(object):
179 179 ... def __init__(self, json):
180 180 ... self.json = json
181 181 ... def _repr_pretty_(self, pp, cycle):
182 182 ... import json
183 183 ... pp.text(json.dumps(self.json, indent=2))
184 184 ... def __repr__(self):
185 185 ... return str(self.json)
186 186 ...
187 187
188 188 >>> d = Json({1:2, 3: {4:5}})
189 189
190 190 >>> print(d)
191 191 {1: 2, 3: {4: 5}}
192 192
193 193 >>> display(d)
194 194 {
195 195 "1": 2,
196 196 "3": {
197 197 "4": 5
198 198 }
199 199 }
200 200
201 201 >>> def int_formatter(integer, pp, cycle):
202 202 ... pp.text('I'*integer)
203 203
204 204 >>> plain = get_ipython().display_formatter.formatters['text/plain']
205 205 >>> plain.for_type(int, int_formatter)
206 206 <function _repr_pprint at 0x...>
207 207 >>> display(7-5)
208 208 II
209 209
210 210 >>> del plain.type_printers[int]
211 211 >>> display(7-5)
212 212 2
213 213
214 214 See Also
215 215 --------
216 216
217 217 :func:`update_display`
218 218
219 219 Notes
220 220 -----
221 221
222 222 In Python, objects can declare their textual representation using the
223 223 `__repr__` method. IPython expands on this idea and allows objects to declare
224 224 other, rich representations including:
225 225
226 226 - HTML
227 227 - JSON
228 228 - PNG
229 229 - JPEG
230 230 - SVG
231 231 - LaTeX
232 232
233 233 A single object can declare some or all of these representations; all are
234 234 handled by IPython's display system.
235 235
236 236 The main idea of the first approach is that you have to implement special
237 237 display methods when you define your class, one for each representation you
238 238 want to use. Here is a list of the names of the special methods and the
239 239 values they must return:
240 240
241 241 - `_repr_html_`: return raw HTML as a string
242 242 - `_repr_json_`: return a JSONable dict
243 243 - `_repr_jpeg_`: return raw JPEG data
244 244 - `_repr_png_`: return raw PNG data
245 245 - `_repr_svg_`: return raw SVG data as a string
246 246 - `_repr_latex_`: return LaTeX commands in a string surrounded by "$".
247 247 - `_repr_mimebundle_`: return a full mimebundle containing the mapping
248 248 from all mimetypes to data.
249 249 Use this for any mime-type not listed above.
250 250
251 251 When you are directly writing your own classes, you can adapt them for
252 252 display in IPython by following the above approach. But in practice, you
253 253 often need to work with existing classes that you can't easily modify.
254 254
255 255 You can refer to the documentation on IPython display formatters in order to
256 256 register custom formatters for already existing types.
257 257
258 258 .. versionadded:: 5.4 display available without import
259 259 .. versionadded:: 6.1 display available without import
260 260
261 261 Since IPython 5.4 and 6.1 :func:`display` is automatically made available to
262 262 the user without import. If you are using display in a document that might
263 263 be used in a pure python context or with older version of IPython, use the
264 264 following import at the top of your file::
265 265
266 266 from IPython.display import display
267 267
268 268 """
269 269 from IPython.core.interactiveshell import InteractiveShell
270 270
271 271 if not InteractiveShell.initialized():
272 272 # Directly print objects.
273 273 print(*objs)
274 274 return
275 275
276 276 raw = kwargs.pop('raw', False)
277 277 if transient is None:
278 278 transient = {}
279 279 if metadata is None:
280 280 metadata={}
281 281 if display_id:
282 282 if display_id is True:
283 283 display_id = _new_id()
284 284 transient['display_id'] = display_id
285 285 if kwargs.get('update') and 'display_id' not in transient:
286 286 raise TypeError('display_id required for update_display')
287 287 if transient:
288 288 kwargs['transient'] = transient
289 289
290 290 if not raw:
291 291 format = InteractiveShell.instance().display_formatter.format
292 292
293 293 for obj in objs:
294 294 if raw:
295 295 publish_display_data(data=obj, metadata=metadata, **kwargs)
296 296 else:
297 297 format_dict, md_dict = format(obj, include=include, exclude=exclude)
298 298 if not format_dict:
299 299 # nothing to display (e.g. _ipython_display_ took over)
300 300 continue
301 301 if metadata:
302 302 # kwarg-specified metadata gets precedence
303 303 _merge(md_dict, metadata)
304 304 publish_display_data(data=format_dict, metadata=md_dict, **kwargs)
305 305 if display_id:
306 306 return DisplayHandle(display_id)
307 307
308 308
309 309 # use * for keyword-only display_id arg
310 310 def update_display(obj, *, display_id, **kwargs):
311 311 """Update an existing display by id
312 312
313 313 Parameters
314 314 ----------
315 315
316 316 obj:
317 317 The object with which to update the display
318 318 display_id: keyword-only
319 319 The id of the display to update
320 320
321 321 See Also
322 322 --------
323 323
324 324 :func:`display`
325 325 """
326 326 kwargs['update'] = True
327 327 display(obj, display_id=display_id, **kwargs)
328 328
329 329
330 330 class DisplayHandle(object):
331 331 """A handle on an updatable display
332 332
333 333 Call `.update(obj)` to display a new object.
334 334
335 335 Call `.display(obj`) to add a new instance of this display,
336 336 and update existing instances.
337 337
338 338 See Also
339 339 --------
340 340
341 341 :func:`display`, :func:`update_display`
342 342
343 343 """
344 344
345 345 def __init__(self, display_id=None):
346 346 if display_id is None:
347 347 display_id = _new_id()
348 348 self.display_id = display_id
349 349
350 350 def __repr__(self):
351 351 return "<%s display_id=%s>" % (self.__class__.__name__, self.display_id)
352 352
353 353 def display(self, obj, **kwargs):
354 354 """Make a new display with my id, updating existing instances.
355 355
356 356 Parameters
357 357 ----------
358 358
359 359 obj:
360 360 object to display
361 361 **kwargs:
362 362 additional keyword arguments passed to display
363 363 """
364 364 display(obj, display_id=self.display_id, **kwargs)
365 365
366 366 def update(self, obj, **kwargs):
367 367 """Update existing displays with my id
368 368
369 369 Parameters
370 370 ----------
371 371
372 372 obj:
373 373 object to display
374 374 **kwargs:
375 375 additional keyword arguments passed to update_display
376 376 """
377 377 update_display(obj, display_id=self.display_id, **kwargs)
378 378
379 379
380 380 def display_pretty(*objs, **kwargs):
381 381 """Display the pretty (default) representation of an object.
382 382
383 383 Parameters
384 384 ----------
385 385 objs : tuple of objects
386 386 The Python objects to display, or if raw=True raw text data to
387 387 display.
388 388 raw : bool
389 389 Are the data objects raw data or Python objects that need to be
390 390 formatted before display? [default: False]
391 391 metadata : dict (optional)
392 392 Metadata to be associated with the specific mimetype output.
393 393 """
394 394 _display_mimetype('text/plain', objs, **kwargs)
395 395
396 396
397 397 def display_html(*objs, **kwargs):
398 398 """Display the HTML representation of an object.
399 399
400 400 Note: If raw=False and the object does not have a HTML
401 401 representation, no HTML will be shown.
402 402
403 403 Parameters
404 404 ----------
405 405 objs : tuple of objects
406 406 The Python objects to display, or if raw=True raw HTML data to
407 407 display.
408 408 raw : bool
409 409 Are the data objects raw data or Python objects that need to be
410 410 formatted before display? [default: False]
411 411 metadata : dict (optional)
412 412 Metadata to be associated with the specific mimetype output.
413 413 """
414 414 _display_mimetype('text/html', objs, **kwargs)
415 415
416 416
417 417 def display_markdown(*objs, **kwargs):
418 418 """Displays the Markdown representation of an object.
419 419
420 420 Parameters
421 421 ----------
422 422 objs : tuple of objects
423 423 The Python objects to display, or if raw=True raw markdown data to
424 424 display.
425 425 raw : bool
426 426 Are the data objects raw data or Python objects that need to be
427 427 formatted before display? [default: False]
428 428 metadata : dict (optional)
429 429 Metadata to be associated with the specific mimetype output.
430 430 """
431 431
432 432 _display_mimetype('text/markdown', objs, **kwargs)
433 433
434 434
435 435 def display_svg(*objs, **kwargs):
436 436 """Display the SVG representation of an object.
437 437
438 438 Parameters
439 439 ----------
440 440 objs : tuple of objects
441 441 The Python objects to display, or if raw=True raw svg data to
442 442 display.
443 443 raw : bool
444 444 Are the data objects raw data or Python objects that need to be
445 445 formatted before display? [default: False]
446 446 metadata : dict (optional)
447 447 Metadata to be associated with the specific mimetype output.
448 448 """
449 449 _display_mimetype('image/svg+xml', objs, **kwargs)
450 450
451 451
452 452 def display_png(*objs, **kwargs):
453 453 """Display the PNG representation of an object.
454 454
455 455 Parameters
456 456 ----------
457 457 objs : tuple of objects
458 458 The Python objects to display, or if raw=True raw png data to
459 459 display.
460 460 raw : bool
461 461 Are the data objects raw data or Python objects that need to be
462 462 formatted before display? [default: False]
463 463 metadata : dict (optional)
464 464 Metadata to be associated with the specific mimetype output.
465 465 """
466 466 _display_mimetype('image/png', objs, **kwargs)
467 467
468 468
469 469 def display_jpeg(*objs, **kwargs):
470 470 """Display the JPEG representation of an object.
471 471
472 472 Parameters
473 473 ----------
474 474 objs : tuple of objects
475 475 The Python objects to display, or if raw=True raw JPEG data to
476 476 display.
477 477 raw : bool
478 478 Are the data objects raw data or Python objects that need to be
479 479 formatted before display? [default: False]
480 480 metadata : dict (optional)
481 481 Metadata to be associated with the specific mimetype output.
482 482 """
483 483 _display_mimetype('image/jpeg', objs, **kwargs)
484 484
485 485
486 486 def display_latex(*objs, **kwargs):
487 487 """Display the LaTeX representation of an object.
488 488
489 489 Parameters
490 490 ----------
491 491 objs : tuple of objects
492 492 The Python objects to display, or if raw=True raw latex data to
493 493 display.
494 494 raw : bool
495 495 Are the data objects raw data or Python objects that need to be
496 496 formatted before display? [default: False]
497 497 metadata : dict (optional)
498 498 Metadata to be associated with the specific mimetype output.
499 499 """
500 500 _display_mimetype('text/latex', objs, **kwargs)
501 501
502 502
503 503 def display_json(*objs, **kwargs):
504 504 """Display the JSON representation of an object.
505 505
506 506 Note that not many frontends support displaying JSON.
507 507
508 508 Parameters
509 509 ----------
510 510 objs : tuple of objects
511 511 The Python objects to display, or if raw=True raw json data to
512 512 display.
513 513 raw : bool
514 514 Are the data objects raw data or Python objects that need to be
515 515 formatted before display? [default: False]
516 516 metadata : dict (optional)
517 517 Metadata to be associated with the specific mimetype output.
518 518 """
519 519 _display_mimetype('application/json', objs, **kwargs)
520 520
521 521
522 522 def display_javascript(*objs, **kwargs):
523 523 """Display the Javascript representation of an object.
524 524
525 525 Parameters
526 526 ----------
527 527 objs : tuple of objects
528 528 The Python objects to display, or if raw=True raw javascript data to
529 529 display.
530 530 raw : bool
531 531 Are the data objects raw data or Python objects that need to be
532 532 formatted before display? [default: False]
533 533 metadata : dict (optional)
534 534 Metadata to be associated with the specific mimetype output.
535 535 """
536 536 _display_mimetype('application/javascript', objs, **kwargs)
537 537
538 538
539 539 def display_pdf(*objs, **kwargs):
540 540 """Display the PDF representation of an object.
541 541
542 542 Parameters
543 543 ----------
544 544 objs : tuple of objects
545 545 The Python objects to display, or if raw=True raw javascript data to
546 546 display.
547 547 raw : bool
548 548 Are the data objects raw data or Python objects that need to be
549 549 formatted before display? [default: False]
550 550 metadata : dict (optional)
551 551 Metadata to be associated with the specific mimetype output.
552 552 """
553 553 _display_mimetype('application/pdf', objs, **kwargs)
554 554
555 555
556 556 #-----------------------------------------------------------------------------
557 557 # Smart classes
558 558 #-----------------------------------------------------------------------------
559 559
560 560
561 561 class DisplayObject(object):
562 562 """An object that wraps data to be displayed."""
563 563
564 564 _read_flags = 'r'
565 565 _show_mem_addr = False
566 566 metadata = None
567 567
568 568 def __init__(self, data=None, url=None, filename=None, metadata=None):
569 569 """Create a display object given raw data.
570 570
571 571 When this object is returned by an expression or passed to the
572 572 display function, it will result in the data being displayed
573 573 in the frontend. The MIME type of the data should match the
574 574 subclasses used, so the Png subclass should be used for 'image/png'
575 575 data. If the data is a URL, the data will first be downloaded
576 576 and then displayed. If
577 577
578 578 Parameters
579 579 ----------
580 580 data : unicode, str or bytes
581 581 The raw data or a URL or file to load the data from
582 582 url : unicode
583 583 A URL to download the data from.
584 584 filename : unicode
585 585 Path to a local file to load the data from.
586 586 metadata : dict
587 587 Dict of metadata associated to be the object when displayed
588 588 """
589 589 if data is not None and isinstance(data, str):
590 590 if data.startswith('http') and url is None:
591 591 url = data
592 592 filename = None
593 593 data = None
594 594 elif _safe_exists(data) and filename is None:
595 595 url = None
596 596 filename = data
597 597 data = None
598 598
599 599 self.data = data
600 600 self.url = url
601 601 self.filename = filename
602 602
603 603 if metadata is not None:
604 604 self.metadata = metadata
605 605 elif self.metadata is None:
606 606 self.metadata = {}
607 607
608 608 self.reload()
609 609 self._check_data()
610 610
611 611 def __repr__(self):
612 612 if not self._show_mem_addr:
613 613 cls = self.__class__
614 614 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
615 615 else:
616 616 r = super(DisplayObject, self).__repr__()
617 617 return r
618 618
619 619 def _check_data(self):
620 620 """Override in subclasses if there's something to check."""
621 621 pass
622 622
623 623 def _data_and_metadata(self):
624 624 """shortcut for returning metadata with shape information, if defined"""
625 625 if self.metadata:
626 626 return self.data, deepcopy(self.metadata)
627 627 else:
628 628 return self.data
629 629
630 630 def reload(self):
631 631 """Reload the raw data from file or URL."""
632 632 if self.filename is not None:
633 633 with open(self.filename, self._read_flags) as f:
634 634 self.data = f.read()
635 635 elif self.url is not None:
636 636 try:
637 637 # Deferred import
638 638 from urllib.request import urlopen
639 639 response = urlopen(self.url)
640 640 self.data = response.read()
641 641 # extract encoding from header, if there is one:
642 642 encoding = None
643 643 for sub in response.headers['content-type'].split(';'):
644 644 sub = sub.strip()
645 645 if sub.startswith('charset'):
646 646 encoding = sub.split('=')[-1].strip()
647 647 break
648 648 # decode data, if an encoding was specified
649 649 if encoding:
650 650 self.data = self.data.decode(encoding, 'replace')
651 651 except:
652 652 self.data = None
653 653
654 654 class TextDisplayObject(DisplayObject):
655 655 """Validate that display data is text"""
656 656 def _check_data(self):
657 657 if self.data is not None and not isinstance(self.data, str):
658 658 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
659 659
660 660 class Pretty(TextDisplayObject):
661 661
662 662 def _repr_pretty_(self, pp, cycle):
663 663 return pp.text(self.data)
664 664
665 665
666 666 class HTML(TextDisplayObject):
667 667
668 668 def _repr_html_(self):
669 669 return self.data
670 670
671 671 def __html__(self):
672 672 """
673 673 This method exists to inform other HTML-using modules (e.g. Markupsafe,
674 674 htmltag, etc) that this object is HTML and does not need things like
675 675 special characters (<>&) escaped.
676 676 """
677 677 return self._repr_html_()
678 678
679 679
680 680 class Markdown(TextDisplayObject):
681 681
682 682 def _repr_markdown_(self):
683 683 return self.data
684 684
685 685
686 686 class Math(TextDisplayObject):
687 687
688 688 def _repr_latex_(self):
689 689 s = self.data.strip('$')
690 690 return "$$%s$$" % s
691 691
692 692
693 693 class Latex(TextDisplayObject):
694 694
695 695 def _repr_latex_(self):
696 696 return self.data
697 697
698 698
699 699 class SVG(DisplayObject):
700 700
701 701 _read_flags = 'rb'
702 702 # wrap data in a property, which extracts the <svg> tag, discarding
703 703 # document headers
704 704 _data = None
705 705
706 706 @property
707 707 def data(self):
708 708 return self._data
709 709
710 710 @data.setter
711 711 def data(self, svg):
712 712 if svg is None:
713 713 self._data = None
714 714 return
715 715 # parse into dom object
716 716 from xml.dom import minidom
717 717 x = minidom.parseString(svg)
718 718 # get svg tag (should be 1)
719 719 found_svg = x.getElementsByTagName('svg')
720 720 if found_svg:
721 721 svg = found_svg[0].toxml()
722 722 else:
723 723 # fallback on the input, trust the user
724 724 # but this is probably an error.
725 725 pass
726 726 svg = cast_unicode(svg)
727 727 self._data = svg
728 728
729 729 def _repr_svg_(self):
730 730 return self._data_and_metadata()
731 731
732 732 class ProgressBar(DisplayObject):
733 733 """Progressbar supports displaying a progressbar like element
734 734 """
735 735 def __init__(self, total):
736 736 """Creates a new progressbar
737 737
738 738 Parameters
739 739 ----------
740 740 total : int
741 741 maximum size of the progressbar
742 742 """
743 743 self.total = total
744 744 self._progress = 0
745 745 self.html_width = '60ex'
746 746 self.text_width = 60
747 747 self._display_id = hexlify(os.urandom(8)).decode('ascii')
748 748
749 749 def __repr__(self):
750 750 fraction = self.progress / self.total
751 751 filled = '=' * int(fraction * self.text_width)
752 752 rest = ' ' * (self.text_width - len(filled))
753 753 return '[{}{}] {}/{}'.format(
754 754 filled, rest,
755 755 self.progress, self.total,
756 756 )
757 757
758 758 def _repr_html_(self):
759 759 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
760 760 self.html_width, self.total, self.progress)
761 761
762 762 def display(self):
763 763 display(self, display_id=self._display_id)
764 764
765 765 def update(self):
766 766 display(self, display_id=self._display_id, update=True)
767 767
768 768 @property
769 769 def progress(self):
770 770 return self._progress
771 771
772 772 @progress.setter
773 773 def progress(self, value):
774 774 self._progress = value
775 775 self.update()
776 776
777 def __iter__(self):
778 self.display()
779 self._progress = -1 # First iteration is 0
780 return self
781
782 def __next__(self):
783 """Returns current value and increments display by one."""
784 self.progress += 1
785 if self.progress < self.total:
786 return self.progress
787 else:
788 raise StopIteration()
789
777 790 class JSON(DisplayObject):
778 791 """JSON expects a JSON-able dict or list
779 792
780 793 not an already-serialized JSON string.
781 794
782 795 Scalar types (None, number, string) are not allowed, only dict or list containers.
783 796 """
784 797 # wrap data in a property, which warns about passing already-serialized JSON
785 798 _data = None
786 799 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, **kwargs):
787 800 """Create a JSON display object given raw data.
788 801
789 802 Parameters
790 803 ----------
791 804 data : dict or list
792 805 JSON data to display. Not an already-serialized JSON string.
793 806 Scalar types (None, number, string) are not allowed, only dict
794 807 or list containers.
795 808 url : unicode
796 809 A URL to download the data from.
797 810 filename : unicode
798 811 Path to a local file to load the data from.
799 812 expanded : boolean
800 813 Metadata to control whether a JSON display component is expanded.
801 814 metadata: dict
802 815 Specify extra metadata to attach to the json display object.
803 816 """
804 817 self.metadata = {'expanded': expanded}
805 818 if metadata:
806 819 self.metadata.update(metadata)
807 820 if kwargs:
808 821 self.metadata.update(kwargs)
809 822 super(JSON, self).__init__(data=data, url=url, filename=filename)
810 823
811 824 def _check_data(self):
812 825 if self.data is not None and not isinstance(self.data, (dict, list)):
813 826 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
814 827
815 828 @property
816 829 def data(self):
817 830 return self._data
818 831
819 832 @data.setter
820 833 def data(self, data):
821 834 if isinstance(data, str):
822 835 if getattr(self, 'filename', None) is None:
823 836 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
824 837 data = json.loads(data)
825 838 self._data = data
826 839
827 840 def _data_and_metadata(self):
828 841 return self.data, self.metadata
829 842
830 843 def _repr_json_(self):
831 844 return self._data_and_metadata()
832 845
833 846 _css_t = """$("head").append($("<link/>").attr({
834 847 rel: "stylesheet",
835 848 type: "text/css",
836 849 href: "%s"
837 850 }));
838 851 """
839 852
840 853 _lib_t1 = """$.getScript("%s", function () {
841 854 """
842 855 _lib_t2 = """});
843 856 """
844 857
845 858 class GeoJSON(JSON):
846 859 """GeoJSON expects JSON-able dict
847 860
848 861 not an already-serialized JSON string.
849 862
850 863 Scalar types (None, number, string) are not allowed, only dict containers.
851 864 """
852 865
853 866 def __init__(self, *args, **kwargs):
854 867 """Create a GeoJSON display object given raw data.
855 868
856 869 Parameters
857 870 ----------
858 871 data : dict or list
859 872 VegaLite data. Not an already-serialized JSON string.
860 873 Scalar types (None, number, string) are not allowed, only dict
861 874 or list containers.
862 875 url_template : string
863 876 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
864 877 layer_options : dict
865 878 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
866 879 url : unicode
867 880 A URL to download the data from.
868 881 filename : unicode
869 882 Path to a local file to load the data from.
870 883 metadata: dict
871 884 Specify extra metadata to attach to the json display object.
872 885
873 886 Examples
874 887 --------
875 888
876 889 The following will display an interactive map of Mars with a point of
877 890 interest on frontend that do support GeoJSON display.
878 891
879 892 >>> from IPython.display import GeoJSON
880 893
881 894 >>> GeoJSON(data={
882 895 ... "type": "Feature",
883 896 ... "geometry": {
884 897 ... "type": "Point",
885 898 ... "coordinates": [-81.327, 296.038]
886 899 ... }
887 900 ... },
888 901 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
889 902 ... layer_options={
890 903 ... "basemap_id": "celestia_mars-shaded-16k_global",
891 904 ... "attribution" : "Celestia/praesepe",
892 905 ... "minZoom" : 0,
893 906 ... "maxZoom" : 18,
894 907 ... })
895 908 <IPython.core.display.GeoJSON object>
896 909
897 910 In the terminal IPython, you will only see the text representation of
898 911 the GeoJSON object.
899 912
900 913 """
901 914
902 915 super(GeoJSON, self).__init__(*args, **kwargs)
903 916
904 917
905 918 def _ipython_display_(self):
906 919 bundle = {
907 920 'application/geo+json': self.data,
908 921 'text/plain': '<IPython.display.GeoJSON object>'
909 922 }
910 923 metadata = {
911 924 'application/geo+json': self.metadata
912 925 }
913 926 display(bundle, metadata=metadata, raw=True)
914 927
915 928 class Javascript(TextDisplayObject):
916 929
917 930 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
918 931 """Create a Javascript display object given raw data.
919 932
920 933 When this object is returned by an expression or passed to the
921 934 display function, it will result in the data being displayed
922 935 in the frontend. If the data is a URL, the data will first be
923 936 downloaded and then displayed.
924 937
925 938 In the Notebook, the containing element will be available as `element`,
926 939 and jQuery will be available. Content appended to `element` will be
927 940 visible in the output area.
928 941
929 942 Parameters
930 943 ----------
931 944 data : unicode, str or bytes
932 945 The Javascript source code or a URL to download it from.
933 946 url : unicode
934 947 A URL to download the data from.
935 948 filename : unicode
936 949 Path to a local file to load the data from.
937 950 lib : list or str
938 951 A sequence of Javascript library URLs to load asynchronously before
939 952 running the source code. The full URLs of the libraries should
940 953 be given. A single Javascript library URL can also be given as a
941 954 string.
942 955 css: : list or str
943 956 A sequence of css files to load before running the source code.
944 957 The full URLs of the css files should be given. A single css URL
945 958 can also be given as a string.
946 959 """
947 960 if isinstance(lib, str):
948 961 lib = [lib]
949 962 elif lib is None:
950 963 lib = []
951 964 if isinstance(css, str):
952 965 css = [css]
953 966 elif css is None:
954 967 css = []
955 968 if not isinstance(lib, (list,tuple)):
956 969 raise TypeError('expected sequence, got: %r' % lib)
957 970 if not isinstance(css, (list,tuple)):
958 971 raise TypeError('expected sequence, got: %r' % css)
959 972 self.lib = lib
960 973 self.css = css
961 974 super(Javascript, self).__init__(data=data, url=url, filename=filename)
962 975
963 976 def _repr_javascript_(self):
964 977 r = ''
965 978 for c in self.css:
966 979 r += _css_t % c
967 980 for l in self.lib:
968 981 r += _lib_t1 % l
969 982 r += self.data
970 983 r += _lib_t2*len(self.lib)
971 984 return r
972 985
973 986 # constants for identifying png/jpeg data
974 987 _PNG = b'\x89PNG\r\n\x1a\n'
975 988 _JPEG = b'\xff\xd8'
976 989
977 990 def _pngxy(data):
978 991 """read the (width, height) from a PNG header"""
979 992 ihdr = data.index(b'IHDR')
980 993 # next 8 bytes are width/height
981 994 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
982 995
983 996 def _jpegxy(data):
984 997 """read the (width, height) from a JPEG header"""
985 998 # adapted from http://www.64lines.com/jpeg-width-height
986 999
987 1000 idx = 4
988 1001 while True:
989 1002 block_size = struct.unpack('>H', data[idx:idx+2])[0]
990 1003 idx = idx + block_size
991 1004 if data[idx:idx+2] == b'\xFF\xC0':
992 1005 # found Start of Frame
993 1006 iSOF = idx
994 1007 break
995 1008 else:
996 1009 # read another block
997 1010 idx += 2
998 1011
999 1012 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
1000 1013 return w, h
1001 1014
1002 1015 def _gifxy(data):
1003 1016 """read the (width, height) from a GIF header"""
1004 1017 return struct.unpack('<HH', data[6:10])
1005 1018
1006 1019
1007 1020 class Image(DisplayObject):
1008 1021
1009 1022 _read_flags = 'rb'
1010 1023 _FMT_JPEG = u'jpeg'
1011 1024 _FMT_PNG = u'png'
1012 1025 _FMT_GIF = u'gif'
1013 1026 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
1014 1027 _MIMETYPES = {
1015 1028 _FMT_PNG: 'image/png',
1016 1029 _FMT_JPEG: 'image/jpeg',
1017 1030 _FMT_GIF: 'image/gif',
1018 1031 }
1019 1032
1020 1033 def __init__(self, data=None, url=None, filename=None, format=None,
1021 1034 embed=None, width=None, height=None, retina=False,
1022 1035 unconfined=False, metadata=None):
1023 1036 """Create a PNG/JPEG/GIF image object given raw data.
1024 1037
1025 1038 When this object is returned by an input cell or passed to the
1026 1039 display function, it will result in the image being displayed
1027 1040 in the frontend.
1028 1041
1029 1042 Parameters
1030 1043 ----------
1031 1044 data : unicode, str or bytes
1032 1045 The raw image data or a URL or filename to load the data from.
1033 1046 This always results in embedded image data.
1034 1047 url : unicode
1035 1048 A URL to download the data from. If you specify `url=`,
1036 1049 the image data will not be embedded unless you also specify `embed=True`.
1037 1050 filename : unicode
1038 1051 Path to a local file to load the data from.
1039 1052 Images from a file are always embedded.
1040 1053 format : unicode
1041 1054 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
1042 1055 for format will be inferred from the filename extension.
1043 1056 embed : bool
1044 1057 Should the image data be embedded using a data URI (True) or be
1045 1058 loaded using an <img> tag. Set this to True if you want the image
1046 1059 to be viewable later with no internet connection in the notebook.
1047 1060
1048 1061 Default is `True`, unless the keyword argument `url` is set, then
1049 1062 default value is `False`.
1050 1063
1051 1064 Note that QtConsole is not able to display images if `embed` is set to `False`
1052 1065 width : int
1053 1066 Width in pixels to which to constrain the image in html
1054 1067 height : int
1055 1068 Height in pixels to which to constrain the image in html
1056 1069 retina : bool
1057 1070 Automatically set the width and height to half of the measured
1058 1071 width and height.
1059 1072 This only works for embedded images because it reads the width/height
1060 1073 from image data.
1061 1074 For non-embedded images, you can just set the desired display width
1062 1075 and height directly.
1063 1076 unconfined: bool
1064 1077 Set unconfined=True to disable max-width confinement of the image.
1065 1078 metadata: dict
1066 1079 Specify extra metadata to attach to the image.
1067 1080
1068 1081 Examples
1069 1082 --------
1070 1083 # embedded image data, works in qtconsole and notebook
1071 1084 # when passed positionally, the first arg can be any of raw image data,
1072 1085 # a URL, or a filename from which to load image data.
1073 1086 # The result is always embedding image data for inline images.
1074 1087 Image('http://www.google.fr/images/srpr/logo3w.png')
1075 1088 Image('/path/to/image.jpg')
1076 1089 Image(b'RAW_PNG_DATA...')
1077 1090
1078 1091 # Specifying Image(url=...) does not embed the image data,
1079 1092 # it only generates `<img>` tag with a link to the source.
1080 1093 # This will not work in the qtconsole or offline.
1081 1094 Image(url='http://www.google.fr/images/srpr/logo3w.png')
1082 1095
1083 1096 """
1084 1097 if filename is not None:
1085 1098 ext = self._find_ext(filename)
1086 1099 elif url is not None:
1087 1100 ext = self._find_ext(url)
1088 1101 elif data is None:
1089 1102 raise ValueError("No image data found. Expecting filename, url, or data.")
1090 1103 elif isinstance(data, str) and (
1091 1104 data.startswith('http') or _safe_exists(data)
1092 1105 ):
1093 1106 ext = self._find_ext(data)
1094 1107 else:
1095 1108 ext = None
1096 1109
1097 1110 if format is None:
1098 1111 if ext is not None:
1099 1112 if ext == u'jpg' or ext == u'jpeg':
1100 1113 format = self._FMT_JPEG
1101 1114 if ext == u'png':
1102 1115 format = self._FMT_PNG
1103 1116 if ext == u'gif':
1104 1117 format = self._FMT_GIF
1105 1118 else:
1106 1119 format = ext.lower()
1107 1120 elif isinstance(data, bytes):
1108 1121 # infer image type from image data header,
1109 1122 # only if format has not been specified.
1110 1123 if data[:2] == _JPEG:
1111 1124 format = self._FMT_JPEG
1112 1125
1113 1126 # failed to detect format, default png
1114 1127 if format is None:
1115 1128 format = self._FMT_PNG
1116 1129
1117 1130 if format.lower() == 'jpg':
1118 1131 # jpg->jpeg
1119 1132 format = self._FMT_JPEG
1120 1133
1121 1134 self.format = format.lower()
1122 1135 self.embed = embed if embed is not None else (url is None)
1123 1136
1124 1137 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
1125 1138 raise ValueError("Cannot embed the '%s' image format" % (self.format))
1126 1139 if self.embed:
1127 1140 self._mimetype = self._MIMETYPES.get(self.format)
1128 1141
1129 1142 self.width = width
1130 1143 self.height = height
1131 1144 self.retina = retina
1132 1145 self.unconfined = unconfined
1133 1146 super(Image, self).__init__(data=data, url=url, filename=filename,
1134 1147 metadata=metadata)
1135 1148
1136 1149 if self.width is None and self.metadata.get('width', {}):
1137 1150 self.width = metadata['width']
1138 1151
1139 1152 if self.height is None and self.metadata.get('height', {}):
1140 1153 self.height = metadata['height']
1141 1154
1142 1155 if retina:
1143 1156 self._retina_shape()
1144 1157
1145 1158
1146 1159 def _retina_shape(self):
1147 1160 """load pixel-doubled width and height from image data"""
1148 1161 if not self.embed:
1149 1162 return
1150 1163 if self.format == self._FMT_PNG:
1151 1164 w, h = _pngxy(self.data)
1152 1165 elif self.format == self._FMT_JPEG:
1153 1166 w, h = _jpegxy(self.data)
1154 1167 elif self.format == self._FMT_GIF:
1155 1168 w, h = _gifxy(self.data)
1156 1169 else:
1157 1170 # retina only supports png
1158 1171 return
1159 1172 self.width = w // 2
1160 1173 self.height = h // 2
1161 1174
1162 1175 def reload(self):
1163 1176 """Reload the raw data from file or URL."""
1164 1177 if self.embed:
1165 1178 super(Image,self).reload()
1166 1179 if self.retina:
1167 1180 self._retina_shape()
1168 1181
1169 1182 def _repr_html_(self):
1170 1183 if not self.embed:
1171 1184 width = height = klass = ''
1172 1185 if self.width:
1173 1186 width = ' width="%d"' % self.width
1174 1187 if self.height:
1175 1188 height = ' height="%d"' % self.height
1176 1189 if self.unconfined:
1177 1190 klass = ' class="unconfined"'
1178 1191 return u'<img src="{url}"{width}{height}{klass}/>'.format(
1179 1192 url=self.url,
1180 1193 width=width,
1181 1194 height=height,
1182 1195 klass=klass,
1183 1196 )
1184 1197
1185 1198 def _repr_mimebundle_(self, include=None, exclude=None):
1186 1199 """Return the image as a mimebundle
1187 1200
1188 1201 Any new mimetype support should be implemented here.
1189 1202 """
1190 1203 if self.embed:
1191 1204 mimetype = self._mimetype
1192 1205 data, metadata = self._data_and_metadata(always_both=True)
1193 1206 if metadata:
1194 1207 metadata = {mimetype: metadata}
1195 1208 return {mimetype: data}, metadata
1196 1209 else:
1197 1210 return {'text/html': self._repr_html_()}
1198 1211
1199 1212 def _data_and_metadata(self, always_both=False):
1200 1213 """shortcut for returning metadata with shape information, if defined"""
1201 1214 b64_data = b2a_base64(self.data).decode('ascii')
1202 1215 md = {}
1203 1216 if self.metadata:
1204 1217 md.update(self.metadata)
1205 1218 if self.width:
1206 1219 md['width'] = self.width
1207 1220 if self.height:
1208 1221 md['height'] = self.height
1209 1222 if self.unconfined:
1210 1223 md['unconfined'] = self.unconfined
1211 1224 if md or always_both:
1212 1225 return b64_data, md
1213 1226 else:
1214 1227 return b64_data
1215 1228
1216 1229 def _repr_png_(self):
1217 1230 if self.embed and self.format == self._FMT_PNG:
1218 1231 return self._data_and_metadata()
1219 1232
1220 1233 def _repr_jpeg_(self):
1221 1234 if self.embed and self.format == self._FMT_JPEG:
1222 1235 return self._data_and_metadata()
1223 1236
1224 1237 def _find_ext(self, s):
1225 1238 return s.split('.')[-1].lower()
1226 1239
1227 1240
1228 1241 class Video(DisplayObject):
1229 1242
1230 1243 def __init__(self, data=None, url=None, filename=None, embed=False, mimetype=None):
1231 1244 """Create a video object given raw data or an URL.
1232 1245
1233 1246 When this object is returned by an input cell or passed to the
1234 1247 display function, it will result in the video being displayed
1235 1248 in the frontend.
1236 1249
1237 1250 Parameters
1238 1251 ----------
1239 1252 data : unicode, str or bytes
1240 1253 The raw video data or a URL or filename to load the data from.
1241 1254 Raw data will require passing `embed=True`.
1242 1255 url : unicode
1243 1256 A URL for the video. If you specify `url=`,
1244 1257 the image data will not be embedded.
1245 1258 filename : unicode
1246 1259 Path to a local file containing the video.
1247 1260 Will be interpreted as a local URL unless `embed=True`.
1248 1261 embed : bool
1249 1262 Should the video be embedded using a data URI (True) or be
1250 1263 loaded using a <video> tag (False).
1251 1264
1252 1265 Since videos are large, embedding them should be avoided, if possible.
1253 1266 You must confirm embedding as your intention by passing `embed=True`.
1254 1267
1255 1268 Local files can be displayed with URLs without embedding the content, via::
1256 1269
1257 1270 Video('./video.mp4')
1258 1271
1259 1272 mimetype: unicode
1260 1273 Specify the mimetype for embedded videos.
1261 1274 Default will be guessed from file extension, if available.
1262 1275
1263 1276 Examples
1264 1277 --------
1265 1278
1266 1279 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1267 1280 Video('path/to/video.mp4')
1268 1281 Video('path/to/video.mp4', embed=True)
1269 1282 Video(b'raw-videodata', embed=True)
1270 1283 """
1271 1284 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1272 1285 url = data
1273 1286 data = None
1274 1287 elif os.path.exists(data):
1275 1288 filename = data
1276 1289 data = None
1277 1290
1278 1291 if data and not embed:
1279 1292 msg = ''.join([
1280 1293 "To embed videos, you must pass embed=True ",
1281 1294 "(this may make your notebook files huge)\n",
1282 1295 "Consider passing Video(url='...')",
1283 1296 ])
1284 1297 raise ValueError(msg)
1285 1298
1286 1299 self.mimetype = mimetype
1287 1300 self.embed = embed
1288 1301 super(Video, self).__init__(data=data, url=url, filename=filename)
1289 1302
1290 1303 def _repr_html_(self):
1291 1304 # External URLs and potentially local files are not embedded into the
1292 1305 # notebook output.
1293 1306 if not self.embed:
1294 1307 url = self.url if self.url is not None else self.filename
1295 1308 output = """<video src="{0}" controls>
1296 1309 Your browser does not support the <code>video</code> element.
1297 1310 </video>""".format(url)
1298 1311 return output
1299 1312
1300 1313 # Embedded videos are base64-encoded.
1301 1314 mimetype = self.mimetype
1302 1315 if self.filename is not None:
1303 1316 if not mimetype:
1304 1317 mimetype, _ = mimetypes.guess_type(self.filename)
1305 1318
1306 1319 with open(self.filename, 'rb') as f:
1307 1320 video = f.read()
1308 1321 else:
1309 1322 video = self.data
1310 1323 if isinstance(video, str):
1311 1324 # unicode input is already b64-encoded
1312 1325 b64_video = video
1313 1326 else:
1314 1327 b64_video = b2a_base64(video).decode('ascii').rstrip()
1315 1328
1316 1329 output = """<video controls>
1317 1330 <source src="data:{0};base64,{1}" type="{0}">
1318 1331 Your browser does not support the video tag.
1319 1332 </video>""".format(mimetype, b64_video)
1320 1333 return output
1321 1334
1322 1335 def reload(self):
1323 1336 # TODO
1324 1337 pass
1325 1338
1326 1339
1327 1340 def clear_output(wait=False):
1328 1341 """Clear the output of the current cell receiving output.
1329 1342
1330 1343 Parameters
1331 1344 ----------
1332 1345 wait : bool [default: false]
1333 1346 Wait to clear the output until new output is available to replace it."""
1334 1347 from IPython.core.interactiveshell import InteractiveShell
1335 1348 if InteractiveShell.initialized():
1336 1349 InteractiveShell.instance().display_pub.clear_output(wait)
1337 1350 else:
1338 1351 print('\033[2K\r', end='')
1339 1352 sys.stdout.flush()
1340 1353 print('\033[2K\r', end='')
1341 1354 sys.stderr.flush()
1342 1355
1343 1356
1344 1357 @skip_doctest
1345 1358 def set_matplotlib_formats(*formats, **kwargs):
1346 1359 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1347 1360
1348 1361 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1349 1362
1350 1363 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1351 1364
1352 1365 To set this in your config files use the following::
1353 1366
1354 1367 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1355 1368 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1356 1369
1357 1370 Parameters
1358 1371 ----------
1359 1372 *formats : strs
1360 1373 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1361 1374 **kwargs :
1362 1375 Keyword args will be relayed to ``figure.canvas.print_figure``.
1363 1376 """
1364 1377 from IPython.core.interactiveshell import InteractiveShell
1365 1378 from IPython.core.pylabtools import select_figure_formats
1366 1379 # build kwargs, starting with InlineBackend config
1367 1380 kw = {}
1368 1381 from ipykernel.pylab.config import InlineBackend
1369 1382 cfg = InlineBackend.instance()
1370 1383 kw.update(cfg.print_figure_kwargs)
1371 1384 kw.update(**kwargs)
1372 1385 shell = InteractiveShell.instance()
1373 1386 select_figure_formats(shell, formats, **kw)
1374 1387
1375 1388 @skip_doctest
1376 1389 def set_matplotlib_close(close=True):
1377 1390 """Set whether the inline backend closes all figures automatically or not.
1378 1391
1379 1392 By default, the inline backend used in the IPython Notebook will close all
1380 1393 matplotlib figures automatically after each cell is run. This means that
1381 1394 plots in different cells won't interfere. Sometimes, you may want to make
1382 1395 a plot in one cell and then refine it in later cells. This can be accomplished
1383 1396 by::
1384 1397
1385 1398 In [1]: set_matplotlib_close(False)
1386 1399
1387 1400 To set this in your config files use the following::
1388 1401
1389 1402 c.InlineBackend.close_figures = False
1390 1403
1391 1404 Parameters
1392 1405 ----------
1393 1406 close : bool
1394 1407 Should all matplotlib figures be automatically closed after each cell is
1395 1408 run?
1396 1409 """
1397 1410 from ipykernel.pylab.config import InlineBackend
1398 1411 cfg = InlineBackend.instance()
1399 1412 cfg.close_figures = close
@@ -1,380 +1,387 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 from unittest import mock
9 9
10 10 import nose.tools as nt
11 11
12 12 from IPython.core import display
13 13 from IPython.core.getipython import get_ipython
14 14 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
15 15 from IPython import paths as ipath
16 16 from IPython.testing.tools import AssertPrints, AssertNotPrints
17 17
18 18 import IPython.testing.decorators as dec
19 19
20 20 def test_image_size():
21 21 """Simple test for display.Image(args, width=x,height=y)"""
22 22 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
23 23 img = display.Image(url=thisurl, width=200, height=200)
24 24 nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_())
25 25 img = display.Image(url=thisurl, metadata={'width':200, 'height':200})
26 26 nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_())
27 27 img = display.Image(url=thisurl, width=200)
28 28 nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_())
29 29 img = display.Image(url=thisurl)
30 30 nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_())
31 31 img = display.Image(url=thisurl, unconfined=True)
32 32 nt.assert_equal(u'<img src="%s" class="unconfined"/>' % (thisurl), img._repr_html_())
33 33
34 34
35 35 def test_image_mimes():
36 36 fmt = get_ipython().display_formatter.format
37 37 for format in display.Image._ACCEPTABLE_EMBEDDINGS:
38 38 mime = display.Image._MIMETYPES[format]
39 39 img = display.Image(b'garbage', format=format)
40 40 data, metadata = fmt(img)
41 41 nt.assert_equal(sorted(data), sorted([mime, 'text/plain']))
42 42
43 43
44 44 def test_geojson():
45 45
46 46 gj = display.GeoJSON(data={
47 47 "type": "Feature",
48 48 "geometry": {
49 49 "type": "Point",
50 50 "coordinates": [-81.327, 296.038]
51 51 },
52 52 "properties": {
53 53 "name": "Inca City"
54 54 }
55 55 },
56 56 url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
57 57 layer_options={
58 58 "basemap_id": "celestia_mars-shaded-16k_global",
59 59 "attribution": "Celestia/praesepe",
60 60 "minZoom": 0,
61 61 "maxZoom": 18,
62 62 })
63 63 nt.assert_equal(u'<IPython.core.display.GeoJSON object>', str(gj))
64 64
65 65 def test_retina_png():
66 66 here = os.path.dirname(__file__)
67 67 img = display.Image(os.path.join(here, "2x2.png"), retina=True)
68 68 nt.assert_equal(img.height, 1)
69 69 nt.assert_equal(img.width, 1)
70 70 data, md = img._repr_png_()
71 71 nt.assert_equal(md['width'], 1)
72 72 nt.assert_equal(md['height'], 1)
73 73
74 74 def test_retina_jpeg():
75 75 here = os.path.dirname(__file__)
76 76 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
77 77 nt.assert_equal(img.height, 1)
78 78 nt.assert_equal(img.width, 1)
79 79 data, md = img._repr_jpeg_()
80 80 nt.assert_equal(md['width'], 1)
81 81 nt.assert_equal(md['height'], 1)
82 82
83 83 def test_base64image():
84 84 display.Image("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AAAAACAAHiIbwzAAAAAElFTkSuQmCC")
85 85
86 86 def test_image_filename_defaults():
87 87 '''test format constraint, and validity of jpeg and png'''
88 88 tpath = ipath.get_ipython_package_dir()
89 89 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.zip'),
90 90 embed=True)
91 91 nt.assert_raises(ValueError, display.Image)
92 92 nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
93 93 # check boths paths to allow packages to test at build and install time
94 94 imgfile = os.path.join(tpath, 'core/tests/2x2.png')
95 95 img = display.Image(filename=imgfile)
96 96 nt.assert_equal('png', img.format)
97 97 nt.assert_is_not_none(img._repr_png_())
98 98 img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False)
99 99 nt.assert_equal('jpeg', img.format)
100 100 nt.assert_is_none(img._repr_jpeg_())
101 101
102 102 def _get_inline_config():
103 103 from ipykernel.pylab.config import InlineBackend
104 104 return InlineBackend.instance()
105 105
106 106 @dec.skip_without('matplotlib')
107 107 def test_set_matplotlib_close():
108 108 cfg = _get_inline_config()
109 109 cfg.close_figures = False
110 110 display.set_matplotlib_close()
111 111 assert cfg.close_figures
112 112 display.set_matplotlib_close(False)
113 113 assert not cfg.close_figures
114 114
115 115 _fmt_mime_map = {
116 116 'png': 'image/png',
117 117 'jpeg': 'image/jpeg',
118 118 'pdf': 'application/pdf',
119 119 'retina': 'image/png',
120 120 'svg': 'image/svg+xml',
121 121 }
122 122
123 123 @dec.skip_without('matplotlib')
124 124 def test_set_matplotlib_formats():
125 125 from matplotlib.figure import Figure
126 126 formatters = get_ipython().display_formatter.formatters
127 127 for formats in [
128 128 ('png',),
129 129 ('pdf', 'svg'),
130 130 ('jpeg', 'retina', 'png'),
131 131 (),
132 132 ]:
133 133 active_mimes = {_fmt_mime_map[fmt] for fmt in formats}
134 134 display.set_matplotlib_formats(*formats)
135 135 for mime, f in formatters.items():
136 136 if mime in active_mimes:
137 137 nt.assert_in(Figure, f)
138 138 else:
139 139 nt.assert_not_in(Figure, f)
140 140
141 141 @dec.skip_without('matplotlib')
142 142 def test_set_matplotlib_formats_kwargs():
143 143 from matplotlib.figure import Figure
144 144 ip = get_ipython()
145 145 cfg = _get_inline_config()
146 146 cfg.print_figure_kwargs.update(dict(foo='bar'))
147 147 kwargs = dict(quality=10)
148 148 display.set_matplotlib_formats('png', **kwargs)
149 149 formatter = ip.display_formatter.formatters['image/png']
150 150 f = formatter.lookup_by_type(Figure)
151 151 cell = f.__closure__[0].cell_contents
152 152 expected = kwargs
153 153 expected.update(cfg.print_figure_kwargs)
154 154 nt.assert_equal(cell, expected)
155 155
156 156 def test_display_available():
157 157 """
158 158 Test that display is available without import
159 159
160 160 We don't really care if it's in builtin or anything else, but it should
161 161 always be available.
162 162 """
163 163 ip = get_ipython()
164 164 with AssertNotPrints('NameError'):
165 165 ip.run_cell('display')
166 166 try:
167 167 ip.run_cell('del display')
168 168 except NameError:
169 169 pass # it's ok, it might be in builtins
170 170 # even if deleted it should be back
171 171 with AssertNotPrints('NameError'):
172 172 ip.run_cell('display')
173 173
174 174 def test_textdisplayobj_pretty_repr():
175 175 p = display.Pretty("This is a simple test")
176 176 nt.assert_equal(repr(p), '<IPython.core.display.Pretty object>')
177 177 nt.assert_equal(p.data, 'This is a simple test')
178 178
179 179 p._show_mem_addr = True
180 180 nt.assert_equal(repr(p), object.__repr__(p))
181 181
182 182 def test_displayobject_repr():
183 183 h = display.HTML('<br />')
184 184 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
185 185 h._show_mem_addr = True
186 186 nt.assert_equal(repr(h), object.__repr__(h))
187 187 h._show_mem_addr = False
188 188 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
189 189
190 190 j = display.Javascript('')
191 191 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
192 192 j._show_mem_addr = True
193 193 nt.assert_equal(repr(j), object.__repr__(j))
194 194 j._show_mem_addr = False
195 195 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
196 196
197 197 def test_progress():
198 198 p = display.ProgressBar(10)
199 nt.assert_true('0/10' in repr(p))
199 nt.assert_in('0/10',repr(p))
200 200 p.html_width = '100%'
201 201 p.progress = 5
202 202 nt.assert_equal(p._repr_html_(), "<progress style='width:100%' max='10' value='5'></progress>")
203 203
204 def test_progress_iter(capsys):
205 for i in display.ProgressBar(5):
206 out, err = capsys.readouterr()
207 nt.assert_in('{0}/5'.format(i), out)
208 out, err = capsys.readouterr()
209 nt.assert_in('5/5', out)
210
204 211 def test_json():
205 212 d = {'a': 5}
206 213 lis = [d]
207 214 md = {'expanded': False}
208 215 md2 = {'expanded': True}
209 216 j = display.JSON(d)
210 217 j2 = display.JSON(d, expanded=True)
211 218 nt.assert_equal(j._repr_json_(), (d, md))
212 219 nt.assert_equal(j2._repr_json_(), (d, md2))
213 220
214 221 with warnings.catch_warnings(record=True) as w:
215 222 warnings.simplefilter("always")
216 223 j = display.JSON(json.dumps(d))
217 224 nt.assert_equal(len(w), 1)
218 225 nt.assert_equal(j._repr_json_(), (d, md))
219 226 nt.assert_equal(j2._repr_json_(), (d, md2))
220 227
221 228 j = display.JSON(lis)
222 229 j2 = display.JSON(lis, expanded=True)
223 230 nt.assert_equal(j._repr_json_(), (lis, md))
224 231 nt.assert_equal(j2._repr_json_(), (lis, md2))
225 232
226 233 with warnings.catch_warnings(record=True) as w:
227 234 warnings.simplefilter("always")
228 235 j = display.JSON(json.dumps(lis))
229 236 nt.assert_equal(len(w), 1)
230 237 nt.assert_equal(j._repr_json_(), (lis, md))
231 238 nt.assert_equal(j2._repr_json_(), (lis, md2))
232 239
233 240 def test_video_embedding():
234 241 """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash"""
235 242 v = display.Video("http://ignored")
236 243 assert not v.embed
237 244 html = v._repr_html_()
238 245 nt.assert_not_in('src="data:', html)
239 246 nt.assert_in('src="http://ignored"', html)
240 247
241 248 with nt.assert_raises(ValueError):
242 249 v = display.Video(b'abc')
243 250
244 251 with NamedFileInTemporaryDirectory('test.mp4') as f:
245 252 f.write(b'abc')
246 253 f.close()
247 254
248 255 v = display.Video(f.name)
249 256 assert not v.embed
250 257 html = v._repr_html_()
251 258 nt.assert_not_in('src="data:', html)
252 259
253 260 v = display.Video(f.name, embed=True)
254 261 html = v._repr_html_()
255 262 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
256 263
257 264 v = display.Video(f.name, embed=True, mimetype='video/other')
258 265 html = v._repr_html_()
259 266 nt.assert_in('src="data:video/other;base64,YWJj"',html)
260 267
261 268 v = display.Video(b'abc', embed=True, mimetype='video/mp4')
262 269 html = v._repr_html_()
263 270 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
264 271
265 272 v = display.Video(u'YWJj', embed=True, mimetype='video/xyz')
266 273 html = v._repr_html_()
267 274 nt.assert_in('src="data:video/xyz;base64,YWJj"',html)
268 275
269 276
270 277 def test_display_id():
271 278 ip = get_ipython()
272 279 with mock.patch.object(ip.display_pub, 'publish') as pub:
273 280 handle = display.display('x')
274 281 nt.assert_is(handle, None)
275 282 handle = display.display('y', display_id='secret')
276 283 nt.assert_is_instance(handle, display.DisplayHandle)
277 284 handle2 = display.display('z', display_id=True)
278 285 nt.assert_is_instance(handle2, display.DisplayHandle)
279 286 nt.assert_not_equal(handle.display_id, handle2.display_id)
280 287
281 288 nt.assert_equal(pub.call_count, 3)
282 289 args, kwargs = pub.call_args_list[0]
283 290 nt.assert_equal(args, ())
284 291 nt.assert_equal(kwargs, {
285 292 'data': {
286 293 'text/plain': repr('x')
287 294 },
288 295 'metadata': {},
289 296 })
290 297 args, kwargs = pub.call_args_list[1]
291 298 nt.assert_equal(args, ())
292 299 nt.assert_equal(kwargs, {
293 300 'data': {
294 301 'text/plain': repr('y')
295 302 },
296 303 'metadata': {},
297 304 'transient': {
298 305 'display_id': handle.display_id,
299 306 },
300 307 })
301 308 args, kwargs = pub.call_args_list[2]
302 309 nt.assert_equal(args, ())
303 310 nt.assert_equal(kwargs, {
304 311 'data': {
305 312 'text/plain': repr('z')
306 313 },
307 314 'metadata': {},
308 315 'transient': {
309 316 'display_id': handle2.display_id,
310 317 },
311 318 })
312 319
313 320
314 321 def test_update_display():
315 322 ip = get_ipython()
316 323 with mock.patch.object(ip.display_pub, 'publish') as pub:
317 324 with nt.assert_raises(TypeError):
318 325 display.update_display('x')
319 326 display.update_display('x', display_id='1')
320 327 display.update_display('y', display_id='2')
321 328 args, kwargs = pub.call_args_list[0]
322 329 nt.assert_equal(args, ())
323 330 nt.assert_equal(kwargs, {
324 331 'data': {
325 332 'text/plain': repr('x')
326 333 },
327 334 'metadata': {},
328 335 'transient': {
329 336 'display_id': '1',
330 337 },
331 338 'update': True,
332 339 })
333 340 args, kwargs = pub.call_args_list[1]
334 341 nt.assert_equal(args, ())
335 342 nt.assert_equal(kwargs, {
336 343 'data': {
337 344 'text/plain': repr('y')
338 345 },
339 346 'metadata': {},
340 347 'transient': {
341 348 'display_id': '2',
342 349 },
343 350 'update': True,
344 351 })
345 352
346 353
347 354 def test_display_handle():
348 355 ip = get_ipython()
349 356 handle = display.DisplayHandle()
350 357 nt.assert_is_instance(handle.display_id, str)
351 358 handle = display.DisplayHandle('my-id')
352 359 nt.assert_equal(handle.display_id, 'my-id')
353 360 with mock.patch.object(ip.display_pub, 'publish') as pub:
354 361 handle.display('x')
355 362 handle.update('y')
356 363
357 364 args, kwargs = pub.call_args_list[0]
358 365 nt.assert_equal(args, ())
359 366 nt.assert_equal(kwargs, {
360 367 'data': {
361 368 'text/plain': repr('x')
362 369 },
363 370 'metadata': {},
364 371 'transient': {
365 372 'display_id': handle.display_id,
366 373 }
367 374 })
368 375 args, kwargs = pub.call_args_list[1]
369 376 nt.assert_equal(args, ())
370 377 nt.assert_equal(kwargs, {
371 378 'data': {
372 379 'text/plain': repr('y')
373 380 },
374 381 'metadata': {},
375 382 'transient': {
376 383 'display_id': handle.display_id,
377 384 },
378 385 'update': True,
379 386 })
380 387
@@ -1,313 +1,337 b''
1 1 {
2 2 "cells": [
3 3 {
4 4 "cell_type": "markdown",
5 5 "metadata": {},
6 6 "source": [
7 7 "# Updatable Displays\n",
8 8 "\n",
9 9 "Note: This feature requires notebook >= 5.0 or JupyterLab, and \n",
10 10 "\n",
11 11 "\n",
12 12 "IPython 6 implements a new API as part of the Jupyter Protocol version 5.1 for easily updating displays.\n",
13 13 "\n",
14 14 "When you display something, you can now pass a `display_id` argument to attach an id to that output.\n",
15 15 "\n",
16 16 "Any future display with the same ID will also update other displays that had the same ID.\n",
17 17 "\n",
18 18 "`display` with a `display_id` will return a `DisplayHandle`\n",
19 19 "object, which gives you easy access to update the output:"
20 20 ]
21 21 },
22 22 {
23 23 "cell_type": "code",
24 "execution_count": 10,
25 "metadata": {
26 "collapsed": true
27 },
24 "execution_count": 1,
25 "metadata": {},
28 26 "outputs": [],
29 27 "source": [
30 28 "from IPython.display import display, update_display"
31 29 ]
32 30 },
33 31 {
34 32 "cell_type": "code",
35 "execution_count": 13,
33 "execution_count": 2,
36 34 "metadata": {},
37 35 "outputs": [
38 36 {
39 37 "data": {
40 38 "text/plain": [
41 39 "'z'"
42 40 ]
43 41 },
44 42 "metadata": {},
45 43 "output_type": "display_data"
46 44 },
47 45 {
48 46 "data": {
49 47 "text/plain": [
50 48 "<DisplayHandle display_id=update-me>"
51 49 ]
52 50 },
53 "execution_count": 13,
51 "execution_count": 2,
54 52 "metadata": {},
55 53 "output_type": "execute_result"
56 54 }
57 55 ],
58 56 "source": [
59 57 "handle = display('x', display_id='update-me')\n",
60 58 "handle"
61 59 ]
62 60 },
63 61 {
64 62 "cell_type": "markdown",
65 63 "metadata": {},
66 64 "source": [
67 65 "When we call `handle.display('y')`, we get a new display of 'y',\n",
68 66 "but in addition to that, we updated the previous display."
69 67 ]
70 68 },
71 69 {
72 70 "cell_type": "code",
73 "execution_count": 14,
71 "execution_count": 3,
74 72 "metadata": {},
75 73 "outputs": [
76 74 {
77 75 "data": {
78 76 "text/plain": [
79 77 "'z'"
80 78 ]
81 79 },
82 80 "metadata": {},
83 81 "output_type": "display_data"
84 82 }
85 83 ],
86 84 "source": [
87 85 "handle.display('y')"
88 86 ]
89 87 },
90 88 {
91 89 "cell_type": "markdown",
92 90 "metadata": {},
93 91 "source": [
94 92 "We can also *just* update the existing displays,\n",
95 93 "without creating a new display:"
96 94 ]
97 95 },
98 96 {
99 97 "cell_type": "code",
100 "execution_count": 15,
101 "metadata": {
102 "collapsed": true
103 },
98 "execution_count": 4,
99 "metadata": {},
104 100 "outputs": [],
105 101 "source": [
106 102 "handle.update('z')"
107 103 ]
108 104 },
109 105 {
110 106 "cell_type": "markdown",
111 107 "metadata": {},
112 108 "source": [
113 109 "You don't have to generate display_ids yourself,\n",
114 110 "if you specify `display_id=True`, then a unique ID will be assigned:"
115 111 ]
116 112 },
117 113 {
118 114 "cell_type": "code",
119 "execution_count": 16,
115 "execution_count": 5,
120 116 "metadata": {},
121 117 "outputs": [
122 118 {
123 119 "data": {
124 120 "text/plain": [
125 121 "'hello'"
126 122 ]
127 123 },
128 124 "metadata": {},
129 125 "output_type": "display_data"
130 126 },
131 127 {
132 128 "data": {
133 129 "text/plain": [
134 "<DisplayHandle display_id=07fc47b2ef652ccb70addeee3eb0981a>"
130 "<DisplayHandle display_id=118a56127f42348f8893440da7181c57>"
135 131 ]
136 132 },
137 "execution_count": 16,
133 "execution_count": 5,
138 134 "metadata": {},
139 135 "output_type": "execute_result"
140 136 }
141 137 ],
142 138 "source": [
143 139 "handle = display(\"hello\", display_id=True)\n",
144 140 "handle"
145 141 ]
146 142 },
147 143 {
148 144 "cell_type": "markdown",
149 145 "metadata": {},
150 146 "source": [
151 147 "Calling `handle.display(obj)` is the same as calling `display(obj, handle.display_id)`,\n",
152 148 "so you don't need to use the handle objects if you don't want to:"
153 149 ]
154 150 },
155 151 {
156 152 "cell_type": "code",
157 "execution_count": 17,
153 "execution_count": 6,
158 154 "metadata": {},
159 155 "outputs": [
160 156 {
161 157 "data": {
162 158 "text/plain": [
163 159 "'z'"
164 160 ]
165 161 },
166 162 "metadata": {},
167 163 "output_type": "display_data"
168 164 }
169 165 ],
170 166 "source": [
171 167 "display('x', display_id='here');"
172 168 ]
173 169 },
174 170 {
175 171 "cell_type": "code",
176 "execution_count": 18,
172 "execution_count": 7,
177 173 "metadata": {},
178 174 "outputs": [
179 175 {
180 176 "data": {
181 177 "text/plain": [
182 178 "'z'"
183 179 ]
184 180 },
185 181 "metadata": {},
186 182 "output_type": "display_data"
187 183 }
188 184 ],
189 185 "source": [
190 186 "display('y', display_id='here');"
191 187 ]
192 188 },
193 189 {
194 190 "cell_type": "markdown",
195 191 "metadata": {},
196 192 "source": [
197 193 "And just like `display`, there is now `update_display`,\n",
198 194 "which is what `DisplayHandle.update` calls:"
199 195 ]
200 196 },
201 197 {
202 198 "cell_type": "code",
203 "execution_count": 19,
204 "metadata": {
205 "collapsed": true
206 },
199 "execution_count": 8,
200 "metadata": {},
207 201 "outputs": [],
208 202 "source": [
209 203 "update_display('z', display_id='here')"
210 204 ]
211 205 },
212 206 {
213 207 "cell_type": "markdown",
214 208 "metadata": {},
215 209 "source": [
216 210 "## More detailed example\n",
217 211 "\n",
218 212 "One of the motivating use cases for this is simple progress bars.\n",
219 213 "\n",
220 214 "Here is an example ProgressBar using these APIs:"
221 215 ]
222 216 },
223 217 {
224 218 "cell_type": "code",
225 "execution_count": 14,
219 "execution_count": 9,
226 220 "metadata": {},
227 221 "outputs": [
228 222 {
229 223 "data": {
230 224 "text/html": [
231 "<progress style='width:100%' max='10' value='10'></progress>"
225 "<progress style='width:60ex' max='10' value='10'></progress>"
232 226 ],
233 227 "text/plain": [
234 "<IPython.core.display.ProgressBar object>"
228 "[============================================================] 10/10"
235 229 ]
236 230 },
237 231 "metadata": {},
238 232 "output_type": "display_data"
239 233 }
240 234 ],
241 235 "source": [
242 236 "from IPython.display import ProgressBar\n",
243 237 "\n",
244 238 "bar = ProgressBar(10)\n",
245 239 "bar.display()"
246 240 ]
247 241 },
248 242 {
249 243 "cell_type": "markdown",
250 244 "metadata": {},
251 245 "source": [
252 246 "And the ProgressBar has `.display` and `.update` methods:"
253 247 ]
254 248 },
255 249 {
256 250 "cell_type": "code",
257 "execution_count": 15,
251 "execution_count": 10,
258 252 "metadata": {},
259 253 "outputs": [
260 254 {
261 255 "data": {
262 256 "text/html": [
263 "<progress style='width:100%' max='10' value='10'></progress>"
257 "<progress style='width:60ex' max='10' value='10'></progress>"
264 258 ],
265 259 "text/plain": [
266 "<IPython.core.display.ProgressBar object>"
260 "[============================================================] 10/10"
267 261 ]
268 262 },
269 263 "metadata": {},
270 264 "output_type": "display_data"
271 265 }
272 266 ],
273 267 "source": [
274 268 "import time\n",
275 269 "\n",
276 270 "bar.display()\n",
277 271 "\n",
278 272 "for i in range(11):\n",
279 273 " bar.progress = i\n",
280 274 " bar.update()\n",
281 275 " time.sleep(0.25)"
282 276 ]
283 277 },
284 278 {
285 279 "cell_type": "markdown",
286 280 "metadata": {},
287 281 "source": [
282 "The ProgressBar also has an update built into iteration:"
283 ]
284 },
285 {
286 "cell_type": "code",
287 "execution_count": 11,
288 "metadata": {},
289 "outputs": [
290 {
291 "data": {
292 "text/html": [
293 "<progress style='width:60ex' max='10' value='10'></progress>"
294 ],
295 "text/plain": [
296 "[============================================================] 10/10"
297 ]
298 },
299 "metadata": {},
300 "output_type": "display_data"
301 }
302 ],
303 "source": [
304 "for i in ProgressBar(10):\n",
305 " time.sleep(0.25)"
306 ]
307 },
308 {
309 "cell_type": "markdown",
310 "metadata": {},
311 "source": [
288 312 "We would encourage any updatable-display objects that track their own display_ids to follow-suit with `.display()` and `.update()` or `.update_display()` methods."
289 313 ]
290 314 }
291 315 ],
292 316 "metadata": {
293 317 "kernelspec": {
294 318 "display_name": "Python 3",
295 319 "language": "python",
296 320 "name": "python3"
297 321 },
298 322 "language_info": {
299 323 "codemirror_mode": {
300 324 "name": "ipython",
301 325 "version": 3
302 326 },
303 327 "file_extension": ".py",
304 328 "mimetype": "text/x-python",
305 329 "name": "python",
306 330 "nbconvert_exporter": "python",
307 331 "pygments_lexer": "ipython3",
308 332 "version": "3.6.2"
309 333 }
310 334 },
311 335 "nbformat": 4,
312 336 "nbformat_minor": 1
313 337 }
General Comments 0
You need to be logged in to leave comments. Login now