##// END OF EJS Templates
Update example notebook for new progressbar.
Marius van Niekerk -
Show More
@@ -1,1387 +1,1399 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 self._display_id = hexlify(os.urandom(8)).decode('ascii')
744 743 self.total = total
745 744 self._progress = 0
745 self.html_width = '60ex'
746 self.text_width = 60
747 self._display_id = hexlify(os.urandom(8)).decode('ascii')
748
749 def __repr__(self):
750 fraction = self.progress / self.total
751 filled = '=' * int(fraction * self.text_width)
752 rest = ' ' * (self.text_width - len(filled))
753 return '[{}{}] {}/{}'.format(
754 filled, rest,
755 self.progress, self.total,
756 )
746 757
747 758 def _repr_html_(self):
748 return "<progress style='width:100%' max='{}' value='{}'></progress>".format(self.total, self.progress)
759 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
760 self.html_width, self.total, self.progress)
749 761
750 762 def display(self):
751 763 display(self, display_id=self._display_id)
752 764
753 765 def update(self):
754 766 display(self, display_id=self._display_id, update=True)
755 767
756 768 @property
757 769 def progress(self):
758 770 return self._progress
759 771
760 772 @progress.setter
761 773 def progress(self, value):
762 774 self._progress = value
763 775 self.update()
764 776
765 777 class JSON(DisplayObject):
766 778 """JSON expects a JSON-able dict or list
767 779
768 780 not an already-serialized JSON string.
769 781
770 782 Scalar types (None, number, string) are not allowed, only dict or list containers.
771 783 """
772 784 # wrap data in a property, which warns about passing already-serialized JSON
773 785 _data = None
774 786 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, **kwargs):
775 787 """Create a JSON display object given raw data.
776 788
777 789 Parameters
778 790 ----------
779 791 data : dict or list
780 792 JSON data to display. Not an already-serialized JSON string.
781 793 Scalar types (None, number, string) are not allowed, only dict
782 794 or list containers.
783 795 url : unicode
784 796 A URL to download the data from.
785 797 filename : unicode
786 798 Path to a local file to load the data from.
787 799 expanded : boolean
788 800 Metadata to control whether a JSON display component is expanded.
789 801 metadata: dict
790 802 Specify extra metadata to attach to the json display object.
791 803 """
792 804 self.metadata = {'expanded': expanded}
793 805 if metadata:
794 806 self.metadata.update(metadata)
795 807 if kwargs:
796 808 self.metadata.update(kwargs)
797 809 super(JSON, self).__init__(data=data, url=url, filename=filename)
798 810
799 811 def _check_data(self):
800 812 if self.data is not None and not isinstance(self.data, (dict, list)):
801 813 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
802 814
803 815 @property
804 816 def data(self):
805 817 return self._data
806 818
807 819 @data.setter
808 820 def data(self, data):
809 821 if isinstance(data, str):
810 822 if getattr(self, 'filename', None) is None:
811 823 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
812 824 data = json.loads(data)
813 825 self._data = data
814 826
815 827 def _data_and_metadata(self):
816 828 return self.data, self.metadata
817 829
818 830 def _repr_json_(self):
819 831 return self._data_and_metadata()
820 832
821 833 _css_t = """$("head").append($("<link/>").attr({
822 834 rel: "stylesheet",
823 835 type: "text/css",
824 836 href: "%s"
825 837 }));
826 838 """
827 839
828 840 _lib_t1 = """$.getScript("%s", function () {
829 841 """
830 842 _lib_t2 = """});
831 843 """
832 844
833 845 class GeoJSON(JSON):
834 846 """GeoJSON expects JSON-able dict
835 847
836 848 not an already-serialized JSON string.
837 849
838 850 Scalar types (None, number, string) are not allowed, only dict containers.
839 851 """
840 852
841 853 def __init__(self, *args, **kwargs):
842 854 """Create a GeoJSON display object given raw data.
843 855
844 856 Parameters
845 857 ----------
846 858 data : dict or list
847 859 VegaLite data. Not an already-serialized JSON string.
848 860 Scalar types (None, number, string) are not allowed, only dict
849 861 or list containers.
850 862 url_template : string
851 863 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
852 864 layer_options : dict
853 865 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
854 866 url : unicode
855 867 A URL to download the data from.
856 868 filename : unicode
857 869 Path to a local file to load the data from.
858 870 metadata: dict
859 871 Specify extra metadata to attach to the json display object.
860 872
861 873 Examples
862 874 --------
863 875
864 876 The following will display an interactive map of Mars with a point of
865 877 interest on frontend that do support GeoJSON display.
866 878
867 879 >>> from IPython.display import GeoJSON
868 880
869 881 >>> GeoJSON(data={
870 882 ... "type": "Feature",
871 883 ... "geometry": {
872 884 ... "type": "Point",
873 885 ... "coordinates": [-81.327, 296.038]
874 886 ... }
875 887 ... },
876 888 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
877 889 ... layer_options={
878 890 ... "basemap_id": "celestia_mars-shaded-16k_global",
879 891 ... "attribution" : "Celestia/praesepe",
880 892 ... "minZoom" : 0,
881 893 ... "maxZoom" : 18,
882 894 ... })
883 895 <IPython.core.display.GeoJSON object>
884 896
885 897 In the terminal IPython, you will only see the text representation of
886 898 the GeoJSON object.
887 899
888 900 """
889 901
890 902 super(GeoJSON, self).__init__(*args, **kwargs)
891 903
892 904
893 905 def _ipython_display_(self):
894 906 bundle = {
895 907 'application/geo+json': self.data,
896 908 'text/plain': '<IPython.display.GeoJSON object>'
897 909 }
898 910 metadata = {
899 911 'application/geo+json': self.metadata
900 912 }
901 913 display(bundle, metadata=metadata, raw=True)
902 914
903 915 class Javascript(TextDisplayObject):
904 916
905 917 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
906 918 """Create a Javascript display object given raw data.
907 919
908 920 When this object is returned by an expression or passed to the
909 921 display function, it will result in the data being displayed
910 922 in the frontend. If the data is a URL, the data will first be
911 923 downloaded and then displayed.
912 924
913 925 In the Notebook, the containing element will be available as `element`,
914 926 and jQuery will be available. Content appended to `element` will be
915 927 visible in the output area.
916 928
917 929 Parameters
918 930 ----------
919 931 data : unicode, str or bytes
920 932 The Javascript source code or a URL to download it from.
921 933 url : unicode
922 934 A URL to download the data from.
923 935 filename : unicode
924 936 Path to a local file to load the data from.
925 937 lib : list or str
926 938 A sequence of Javascript library URLs to load asynchronously before
927 939 running the source code. The full URLs of the libraries should
928 940 be given. A single Javascript library URL can also be given as a
929 941 string.
930 942 css: : list or str
931 943 A sequence of css files to load before running the source code.
932 944 The full URLs of the css files should be given. A single css URL
933 945 can also be given as a string.
934 946 """
935 947 if isinstance(lib, str):
936 948 lib = [lib]
937 949 elif lib is None:
938 950 lib = []
939 951 if isinstance(css, str):
940 952 css = [css]
941 953 elif css is None:
942 954 css = []
943 955 if not isinstance(lib, (list,tuple)):
944 956 raise TypeError('expected sequence, got: %r' % lib)
945 957 if not isinstance(css, (list,tuple)):
946 958 raise TypeError('expected sequence, got: %r' % css)
947 959 self.lib = lib
948 960 self.css = css
949 961 super(Javascript, self).__init__(data=data, url=url, filename=filename)
950 962
951 963 def _repr_javascript_(self):
952 964 r = ''
953 965 for c in self.css:
954 966 r += _css_t % c
955 967 for l in self.lib:
956 968 r += _lib_t1 % l
957 969 r += self.data
958 970 r += _lib_t2*len(self.lib)
959 971 return r
960 972
961 973 # constants for identifying png/jpeg data
962 974 _PNG = b'\x89PNG\r\n\x1a\n'
963 975 _JPEG = b'\xff\xd8'
964 976
965 977 def _pngxy(data):
966 978 """read the (width, height) from a PNG header"""
967 979 ihdr = data.index(b'IHDR')
968 980 # next 8 bytes are width/height
969 981 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
970 982
971 983 def _jpegxy(data):
972 984 """read the (width, height) from a JPEG header"""
973 985 # adapted from http://www.64lines.com/jpeg-width-height
974 986
975 987 idx = 4
976 988 while True:
977 989 block_size = struct.unpack('>H', data[idx:idx+2])[0]
978 990 idx = idx + block_size
979 991 if data[idx:idx+2] == b'\xFF\xC0':
980 992 # found Start of Frame
981 993 iSOF = idx
982 994 break
983 995 else:
984 996 # read another block
985 997 idx += 2
986 998
987 999 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
988 1000 return w, h
989 1001
990 1002 def _gifxy(data):
991 1003 """read the (width, height) from a GIF header"""
992 1004 return struct.unpack('<HH', data[6:10])
993 1005
994 1006
995 1007 class Image(DisplayObject):
996 1008
997 1009 _read_flags = 'rb'
998 1010 _FMT_JPEG = u'jpeg'
999 1011 _FMT_PNG = u'png'
1000 1012 _FMT_GIF = u'gif'
1001 1013 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
1002 1014 _MIMETYPES = {
1003 1015 _FMT_PNG: 'image/png',
1004 1016 _FMT_JPEG: 'image/jpeg',
1005 1017 _FMT_GIF: 'image/gif',
1006 1018 }
1007 1019
1008 1020 def __init__(self, data=None, url=None, filename=None, format=None,
1009 1021 embed=None, width=None, height=None, retina=False,
1010 1022 unconfined=False, metadata=None):
1011 1023 """Create a PNG/JPEG/GIF image object given raw data.
1012 1024
1013 1025 When this object is returned by an input cell or passed to the
1014 1026 display function, it will result in the image being displayed
1015 1027 in the frontend.
1016 1028
1017 1029 Parameters
1018 1030 ----------
1019 1031 data : unicode, str or bytes
1020 1032 The raw image data or a URL or filename to load the data from.
1021 1033 This always results in embedded image data.
1022 1034 url : unicode
1023 1035 A URL to download the data from. If you specify `url=`,
1024 1036 the image data will not be embedded unless you also specify `embed=True`.
1025 1037 filename : unicode
1026 1038 Path to a local file to load the data from.
1027 1039 Images from a file are always embedded.
1028 1040 format : unicode
1029 1041 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
1030 1042 for format will be inferred from the filename extension.
1031 1043 embed : bool
1032 1044 Should the image data be embedded using a data URI (True) or be
1033 1045 loaded using an <img> tag. Set this to True if you want the image
1034 1046 to be viewable later with no internet connection in the notebook.
1035 1047
1036 1048 Default is `True`, unless the keyword argument `url` is set, then
1037 1049 default value is `False`.
1038 1050
1039 1051 Note that QtConsole is not able to display images if `embed` is set to `False`
1040 1052 width : int
1041 1053 Width in pixels to which to constrain the image in html
1042 1054 height : int
1043 1055 Height in pixels to which to constrain the image in html
1044 1056 retina : bool
1045 1057 Automatically set the width and height to half of the measured
1046 1058 width and height.
1047 1059 This only works for embedded images because it reads the width/height
1048 1060 from image data.
1049 1061 For non-embedded images, you can just set the desired display width
1050 1062 and height directly.
1051 1063 unconfined: bool
1052 1064 Set unconfined=True to disable max-width confinement of the image.
1053 1065 metadata: dict
1054 1066 Specify extra metadata to attach to the image.
1055 1067
1056 1068 Examples
1057 1069 --------
1058 1070 # embedded image data, works in qtconsole and notebook
1059 1071 # when passed positionally, the first arg can be any of raw image data,
1060 1072 # a URL, or a filename from which to load image data.
1061 1073 # The result is always embedding image data for inline images.
1062 1074 Image('http://www.google.fr/images/srpr/logo3w.png')
1063 1075 Image('/path/to/image.jpg')
1064 1076 Image(b'RAW_PNG_DATA...')
1065 1077
1066 1078 # Specifying Image(url=...) does not embed the image data,
1067 1079 # it only generates `<img>` tag with a link to the source.
1068 1080 # This will not work in the qtconsole or offline.
1069 1081 Image(url='http://www.google.fr/images/srpr/logo3w.png')
1070 1082
1071 1083 """
1072 1084 if filename is not None:
1073 1085 ext = self._find_ext(filename)
1074 1086 elif url is not None:
1075 1087 ext = self._find_ext(url)
1076 1088 elif data is None:
1077 1089 raise ValueError("No image data found. Expecting filename, url, or data.")
1078 1090 elif isinstance(data, str) and (
1079 1091 data.startswith('http') or _safe_exists(data)
1080 1092 ):
1081 1093 ext = self._find_ext(data)
1082 1094 else:
1083 1095 ext = None
1084 1096
1085 1097 if format is None:
1086 1098 if ext is not None:
1087 1099 if ext == u'jpg' or ext == u'jpeg':
1088 1100 format = self._FMT_JPEG
1089 1101 if ext == u'png':
1090 1102 format = self._FMT_PNG
1091 1103 if ext == u'gif':
1092 1104 format = self._FMT_GIF
1093 1105 else:
1094 1106 format = ext.lower()
1095 1107 elif isinstance(data, bytes):
1096 1108 # infer image type from image data header,
1097 1109 # only if format has not been specified.
1098 1110 if data[:2] == _JPEG:
1099 1111 format = self._FMT_JPEG
1100 1112
1101 1113 # failed to detect format, default png
1102 1114 if format is None:
1103 1115 format = self._FMT_PNG
1104 1116
1105 1117 if format.lower() == 'jpg':
1106 1118 # jpg->jpeg
1107 1119 format = self._FMT_JPEG
1108 1120
1109 1121 self.format = format.lower()
1110 1122 self.embed = embed if embed is not None else (url is None)
1111 1123
1112 1124 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
1113 1125 raise ValueError("Cannot embed the '%s' image format" % (self.format))
1114 1126 if self.embed:
1115 1127 self._mimetype = self._MIMETYPES.get(self.format)
1116 1128
1117 1129 self.width = width
1118 1130 self.height = height
1119 1131 self.retina = retina
1120 1132 self.unconfined = unconfined
1121 1133 super(Image, self).__init__(data=data, url=url, filename=filename,
1122 1134 metadata=metadata)
1123 1135
1124 1136 if self.width is None and self.metadata.get('width', {}):
1125 1137 self.width = metadata['width']
1126 1138
1127 1139 if self.height is None and self.metadata.get('height', {}):
1128 1140 self.height = metadata['height']
1129 1141
1130 1142 if retina:
1131 1143 self._retina_shape()
1132 1144
1133 1145
1134 1146 def _retina_shape(self):
1135 1147 """load pixel-doubled width and height from image data"""
1136 1148 if not self.embed:
1137 1149 return
1138 1150 if self.format == self._FMT_PNG:
1139 1151 w, h = _pngxy(self.data)
1140 1152 elif self.format == self._FMT_JPEG:
1141 1153 w, h = _jpegxy(self.data)
1142 1154 elif self.format == self._FMT_GIF:
1143 1155 w, h = _gifxy(self.data)
1144 1156 else:
1145 1157 # retina only supports png
1146 1158 return
1147 1159 self.width = w // 2
1148 1160 self.height = h // 2
1149 1161
1150 1162 def reload(self):
1151 1163 """Reload the raw data from file or URL."""
1152 1164 if self.embed:
1153 1165 super(Image,self).reload()
1154 1166 if self.retina:
1155 1167 self._retina_shape()
1156 1168
1157 1169 def _repr_html_(self):
1158 1170 if not self.embed:
1159 1171 width = height = klass = ''
1160 1172 if self.width:
1161 1173 width = ' width="%d"' % self.width
1162 1174 if self.height:
1163 1175 height = ' height="%d"' % self.height
1164 1176 if self.unconfined:
1165 1177 klass = ' class="unconfined"'
1166 1178 return u'<img src="{url}"{width}{height}{klass}/>'.format(
1167 1179 url=self.url,
1168 1180 width=width,
1169 1181 height=height,
1170 1182 klass=klass,
1171 1183 )
1172 1184
1173 1185 def _repr_mimebundle_(self, include=None, exclude=None):
1174 1186 """Return the image as a mimebundle
1175 1187
1176 1188 Any new mimetype support should be implemented here.
1177 1189 """
1178 1190 if self.embed:
1179 1191 mimetype = self._mimetype
1180 1192 data, metadata = self._data_and_metadata(always_both=True)
1181 1193 if metadata:
1182 1194 metadata = {mimetype: metadata}
1183 1195 return {mimetype: data}, metadata
1184 1196 else:
1185 1197 return {'text/html': self._repr_html_()}
1186 1198
1187 1199 def _data_and_metadata(self, always_both=False):
1188 1200 """shortcut for returning metadata with shape information, if defined"""
1189 1201 b64_data = b2a_base64(self.data).decode('ascii')
1190 1202 md = {}
1191 1203 if self.metadata:
1192 1204 md.update(self.metadata)
1193 1205 if self.width:
1194 1206 md['width'] = self.width
1195 1207 if self.height:
1196 1208 md['height'] = self.height
1197 1209 if self.unconfined:
1198 1210 md['unconfined'] = self.unconfined
1199 1211 if md or always_both:
1200 1212 return b64_data, md
1201 1213 else:
1202 1214 return b64_data
1203 1215
1204 1216 def _repr_png_(self):
1205 1217 if self.embed and self.format == self._FMT_PNG:
1206 1218 return self._data_and_metadata()
1207 1219
1208 1220 def _repr_jpeg_(self):
1209 1221 if self.embed and self.format == self._FMT_JPEG:
1210 1222 return self._data_and_metadata()
1211 1223
1212 1224 def _find_ext(self, s):
1213 1225 return s.split('.')[-1].lower()
1214 1226
1215 1227
1216 1228 class Video(DisplayObject):
1217 1229
1218 1230 def __init__(self, data=None, url=None, filename=None, embed=False, mimetype=None):
1219 1231 """Create a video object given raw data or an URL.
1220 1232
1221 1233 When this object is returned by an input cell or passed to the
1222 1234 display function, it will result in the video being displayed
1223 1235 in the frontend.
1224 1236
1225 1237 Parameters
1226 1238 ----------
1227 1239 data : unicode, str or bytes
1228 1240 The raw video data or a URL or filename to load the data from.
1229 1241 Raw data will require passing `embed=True`.
1230 1242 url : unicode
1231 1243 A URL for the video. If you specify `url=`,
1232 1244 the image data will not be embedded.
1233 1245 filename : unicode
1234 1246 Path to a local file containing the video.
1235 1247 Will be interpreted as a local URL unless `embed=True`.
1236 1248 embed : bool
1237 1249 Should the video be embedded using a data URI (True) or be
1238 1250 loaded using a <video> tag (False).
1239 1251
1240 1252 Since videos are large, embedding them should be avoided, if possible.
1241 1253 You must confirm embedding as your intention by passing `embed=True`.
1242 1254
1243 1255 Local files can be displayed with URLs without embedding the content, via::
1244 1256
1245 1257 Video('./video.mp4')
1246 1258
1247 1259 mimetype: unicode
1248 1260 Specify the mimetype for embedded videos.
1249 1261 Default will be guessed from file extension, if available.
1250 1262
1251 1263 Examples
1252 1264 --------
1253 1265
1254 1266 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1255 1267 Video('path/to/video.mp4')
1256 1268 Video('path/to/video.mp4', embed=True)
1257 1269 Video(b'raw-videodata', embed=True)
1258 1270 """
1259 1271 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1260 1272 url = data
1261 1273 data = None
1262 1274 elif os.path.exists(data):
1263 1275 filename = data
1264 1276 data = None
1265 1277
1266 1278 if data and not embed:
1267 1279 msg = ''.join([
1268 1280 "To embed videos, you must pass embed=True ",
1269 1281 "(this may make your notebook files huge)\n",
1270 1282 "Consider passing Video(url='...')",
1271 1283 ])
1272 1284 raise ValueError(msg)
1273 1285
1274 1286 self.mimetype = mimetype
1275 1287 self.embed = embed
1276 1288 super(Video, self).__init__(data=data, url=url, filename=filename)
1277 1289
1278 1290 def _repr_html_(self):
1279 1291 # External URLs and potentially local files are not embedded into the
1280 1292 # notebook output.
1281 1293 if not self.embed:
1282 1294 url = self.url if self.url is not None else self.filename
1283 1295 output = """<video src="{0}" controls>
1284 1296 Your browser does not support the <code>video</code> element.
1285 1297 </video>""".format(url)
1286 1298 return output
1287 1299
1288 1300 # Embedded videos are base64-encoded.
1289 1301 mimetype = self.mimetype
1290 1302 if self.filename is not None:
1291 1303 if not mimetype:
1292 1304 mimetype, _ = mimetypes.guess_type(self.filename)
1293 1305
1294 1306 with open(self.filename, 'rb') as f:
1295 1307 video = f.read()
1296 1308 else:
1297 1309 video = self.data
1298 1310 if isinstance(video, str):
1299 1311 # unicode input is already b64-encoded
1300 1312 b64_video = video
1301 1313 else:
1302 1314 b64_video = b2a_base64(video).decode('ascii').rstrip()
1303 1315
1304 1316 output = """<video controls>
1305 1317 <source src="data:{0};base64,{1}" type="{0}">
1306 1318 Your browser does not support the video tag.
1307 1319 </video>""".format(mimetype, b64_video)
1308 1320 return output
1309 1321
1310 1322 def reload(self):
1311 1323 # TODO
1312 1324 pass
1313 1325
1314 1326
1315 1327 def clear_output(wait=False):
1316 1328 """Clear the output of the current cell receiving output.
1317 1329
1318 1330 Parameters
1319 1331 ----------
1320 1332 wait : bool [default: false]
1321 1333 Wait to clear the output until new output is available to replace it."""
1322 1334 from IPython.core.interactiveshell import InteractiveShell
1323 1335 if InteractiveShell.initialized():
1324 1336 InteractiveShell.instance().display_pub.clear_output(wait)
1325 1337 else:
1326 1338 print('\033[2K\r', end='')
1327 1339 sys.stdout.flush()
1328 1340 print('\033[2K\r', end='')
1329 1341 sys.stderr.flush()
1330 1342
1331 1343
1332 1344 @skip_doctest
1333 1345 def set_matplotlib_formats(*formats, **kwargs):
1334 1346 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1335 1347
1336 1348 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1337 1349
1338 1350 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1339 1351
1340 1352 To set this in your config files use the following::
1341 1353
1342 1354 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1343 1355 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1344 1356
1345 1357 Parameters
1346 1358 ----------
1347 1359 *formats : strs
1348 1360 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1349 1361 **kwargs :
1350 1362 Keyword args will be relayed to ``figure.canvas.print_figure``.
1351 1363 """
1352 1364 from IPython.core.interactiveshell import InteractiveShell
1353 1365 from IPython.core.pylabtools import select_figure_formats
1354 1366 # build kwargs, starting with InlineBackend config
1355 1367 kw = {}
1356 1368 from ipykernel.pylab.config import InlineBackend
1357 1369 cfg = InlineBackend.instance()
1358 1370 kw.update(cfg.print_figure_kwargs)
1359 1371 kw.update(**kwargs)
1360 1372 shell = InteractiveShell.instance()
1361 1373 select_figure_formats(shell, formats, **kw)
1362 1374
1363 1375 @skip_doctest
1364 1376 def set_matplotlib_close(close=True):
1365 1377 """Set whether the inline backend closes all figures automatically or not.
1366 1378
1367 1379 By default, the inline backend used in the IPython Notebook will close all
1368 1380 matplotlib figures automatically after each cell is run. This means that
1369 1381 plots in different cells won't interfere. Sometimes, you may want to make
1370 1382 a plot in one cell and then refine it in later cells. This can be accomplished
1371 1383 by::
1372 1384
1373 1385 In [1]: set_matplotlib_close(False)
1374 1386
1375 1387 To set this in your config files use the following::
1376 1388
1377 1389 c.InlineBackend.close_figures = False
1378 1390
1379 1391 Parameters
1380 1392 ----------
1381 1393 close : bool
1382 1394 Should all matplotlib figures be automatically closed after each cell is
1383 1395 run?
1384 1396 """
1385 1397 from ipykernel.pylab.config import InlineBackend
1386 1398 cfg = InlineBackend.instance()
1387 1399 cfg.close_figures = close
@@ -1,379 +1,378 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_equal(repr(p), '<IPython.core.display.ProgressBar object>')
200 p._show_mem_addr = True
199 nt.assert_true('0/10' in repr(p))
201 200 nt.assert_equal(p._repr_html_(), "<progress style='width:100%' max='10' value='0'></progress>")
202 201
203 202 def test_json():
204 203 d = {'a': 5}
205 204 lis = [d]
206 205 md = {'expanded': False}
207 206 md2 = {'expanded': True}
208 207 j = display.JSON(d)
209 208 j2 = display.JSON(d, expanded=True)
210 209 nt.assert_equal(j._repr_json_(), (d, md))
211 210 nt.assert_equal(j2._repr_json_(), (d, md2))
212 211
213 212 with warnings.catch_warnings(record=True) as w:
214 213 warnings.simplefilter("always")
215 214 j = display.JSON(json.dumps(d))
216 215 nt.assert_equal(len(w), 1)
217 216 nt.assert_equal(j._repr_json_(), (d, md))
218 217 nt.assert_equal(j2._repr_json_(), (d, md2))
219 218
220 219 j = display.JSON(lis)
221 220 j2 = display.JSON(lis, expanded=True)
222 221 nt.assert_equal(j._repr_json_(), (lis, md))
223 222 nt.assert_equal(j2._repr_json_(), (lis, md2))
224 223
225 224 with warnings.catch_warnings(record=True) as w:
226 225 warnings.simplefilter("always")
227 226 j = display.JSON(json.dumps(lis))
228 227 nt.assert_equal(len(w), 1)
229 228 nt.assert_equal(j._repr_json_(), (lis, md))
230 229 nt.assert_equal(j2._repr_json_(), (lis, md2))
231 230
232 231 def test_video_embedding():
233 232 """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash"""
234 233 v = display.Video("http://ignored")
235 234 assert not v.embed
236 235 html = v._repr_html_()
237 236 nt.assert_not_in('src="data:', html)
238 237 nt.assert_in('src="http://ignored"', html)
239 238
240 239 with nt.assert_raises(ValueError):
241 240 v = display.Video(b'abc')
242 241
243 242 with NamedFileInTemporaryDirectory('test.mp4') as f:
244 243 f.write(b'abc')
245 244 f.close()
246 245
247 246 v = display.Video(f.name)
248 247 assert not v.embed
249 248 html = v._repr_html_()
250 249 nt.assert_not_in('src="data:', html)
251 250
252 251 v = display.Video(f.name, embed=True)
253 252 html = v._repr_html_()
254 253 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
255 254
256 255 v = display.Video(f.name, embed=True, mimetype='video/other')
257 256 html = v._repr_html_()
258 257 nt.assert_in('src="data:video/other;base64,YWJj"',html)
259 258
260 259 v = display.Video(b'abc', embed=True, mimetype='video/mp4')
261 260 html = v._repr_html_()
262 261 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
263 262
264 263 v = display.Video(u'YWJj', embed=True, mimetype='video/xyz')
265 264 html = v._repr_html_()
266 265 nt.assert_in('src="data:video/xyz;base64,YWJj"',html)
267 266
268 267
269 268 def test_display_id():
270 269 ip = get_ipython()
271 270 with mock.patch.object(ip.display_pub, 'publish') as pub:
272 271 handle = display.display('x')
273 272 nt.assert_is(handle, None)
274 273 handle = display.display('y', display_id='secret')
275 274 nt.assert_is_instance(handle, display.DisplayHandle)
276 275 handle2 = display.display('z', display_id=True)
277 276 nt.assert_is_instance(handle2, display.DisplayHandle)
278 277 nt.assert_not_equal(handle.display_id, handle2.display_id)
279 278
280 279 nt.assert_equal(pub.call_count, 3)
281 280 args, kwargs = pub.call_args_list[0]
282 281 nt.assert_equal(args, ())
283 282 nt.assert_equal(kwargs, {
284 283 'data': {
285 284 'text/plain': repr('x')
286 285 },
287 286 'metadata': {},
288 287 })
289 288 args, kwargs = pub.call_args_list[1]
290 289 nt.assert_equal(args, ())
291 290 nt.assert_equal(kwargs, {
292 291 'data': {
293 292 'text/plain': repr('y')
294 293 },
295 294 'metadata': {},
296 295 'transient': {
297 296 'display_id': handle.display_id,
298 297 },
299 298 })
300 299 args, kwargs = pub.call_args_list[2]
301 300 nt.assert_equal(args, ())
302 301 nt.assert_equal(kwargs, {
303 302 'data': {
304 303 'text/plain': repr('z')
305 304 },
306 305 'metadata': {},
307 306 'transient': {
308 307 'display_id': handle2.display_id,
309 308 },
310 309 })
311 310
312 311
313 312 def test_update_display():
314 313 ip = get_ipython()
315 314 with mock.patch.object(ip.display_pub, 'publish') as pub:
316 315 with nt.assert_raises(TypeError):
317 316 display.update_display('x')
318 317 display.update_display('x', display_id='1')
319 318 display.update_display('y', display_id='2')
320 319 args, kwargs = pub.call_args_list[0]
321 320 nt.assert_equal(args, ())
322 321 nt.assert_equal(kwargs, {
323 322 'data': {
324 323 'text/plain': repr('x')
325 324 },
326 325 'metadata': {},
327 326 'transient': {
328 327 'display_id': '1',
329 328 },
330 329 'update': True,
331 330 })
332 331 args, kwargs = pub.call_args_list[1]
333 332 nt.assert_equal(args, ())
334 333 nt.assert_equal(kwargs, {
335 334 'data': {
336 335 'text/plain': repr('y')
337 336 },
338 337 'metadata': {},
339 338 'transient': {
340 339 'display_id': '2',
341 340 },
342 341 'update': True,
343 342 })
344 343
345 344
346 345 def test_display_handle():
347 346 ip = get_ipython()
348 347 handle = display.DisplayHandle()
349 348 nt.assert_is_instance(handle.display_id, str)
350 349 handle = display.DisplayHandle('my-id')
351 350 nt.assert_equal(handle.display_id, 'my-id')
352 351 with mock.patch.object(ip.display_pub, 'publish') as pub:
353 352 handle.display('x')
354 353 handle.update('y')
355 354
356 355 args, kwargs = pub.call_args_list[0]
357 356 nt.assert_equal(args, ())
358 357 nt.assert_equal(kwargs, {
359 358 'data': {
360 359 'text/plain': repr('x')
361 360 },
362 361 'metadata': {},
363 362 'transient': {
364 363 'display_id': handle.display_id,
365 364 }
366 365 })
367 366 args, kwargs = pub.call_args_list[1]
368 367 nt.assert_equal(args, ())
369 368 nt.assert_equal(kwargs, {
370 369 'data': {
371 370 'text/plain': repr('y')
372 371 },
373 372 'metadata': {},
374 373 'transient': {
375 374 'display_id': handle.display_id,
376 375 },
377 376 'update': True,
378 377 })
379 378
@@ -1,359 +1,313 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 24 "execution_count": 10,
25 25 "metadata": {
26 26 "collapsed": true
27 27 },
28 28 "outputs": [],
29 29 "source": [
30 30 "from IPython.display import display, update_display"
31 31 ]
32 32 },
33 33 {
34 34 "cell_type": "code",
35 35 "execution_count": 13,
36 36 "metadata": {},
37 37 "outputs": [
38 38 {
39 39 "data": {
40 40 "text/plain": [
41 41 "'z'"
42 42 ]
43 43 },
44 44 "metadata": {},
45 45 "output_type": "display_data"
46 46 },
47 47 {
48 48 "data": {
49 49 "text/plain": [
50 50 "<DisplayHandle display_id=update-me>"
51 51 ]
52 52 },
53 53 "execution_count": 13,
54 54 "metadata": {},
55 55 "output_type": "execute_result"
56 56 }
57 57 ],
58 58 "source": [
59 59 "handle = display('x', display_id='update-me')\n",
60 60 "handle"
61 61 ]
62 62 },
63 63 {
64 64 "cell_type": "markdown",
65 65 "metadata": {},
66 66 "source": [
67 67 "When we call `handle.display('y')`, we get a new display of 'y',\n",
68 68 "but in addition to that, we updated the previous display."
69 69 ]
70 70 },
71 71 {
72 72 "cell_type": "code",
73 73 "execution_count": 14,
74 74 "metadata": {},
75 75 "outputs": [
76 76 {
77 77 "data": {
78 78 "text/plain": [
79 79 "'z'"
80 80 ]
81 81 },
82 82 "metadata": {},
83 83 "output_type": "display_data"
84 84 }
85 85 ],
86 86 "source": [
87 87 "handle.display('y')"
88 88 ]
89 89 },
90 90 {
91 91 "cell_type": "markdown",
92 92 "metadata": {},
93 93 "source": [
94 94 "We can also *just* update the existing displays,\n",
95 95 "without creating a new display:"
96 96 ]
97 97 },
98 98 {
99 99 "cell_type": "code",
100 100 "execution_count": 15,
101 101 "metadata": {
102 102 "collapsed": true
103 103 },
104 104 "outputs": [],
105 105 "source": [
106 106 "handle.update('z')"
107 107 ]
108 108 },
109 109 {
110 110 "cell_type": "markdown",
111 111 "metadata": {},
112 112 "source": [
113 113 "You don't have to generate display_ids yourself,\n",
114 114 "if you specify `display_id=True`, then a unique ID will be assigned:"
115 115 ]
116 116 },
117 117 {
118 118 "cell_type": "code",
119 119 "execution_count": 16,
120 120 "metadata": {},
121 121 "outputs": [
122 122 {
123 123 "data": {
124 124 "text/plain": [
125 125 "'hello'"
126 126 ]
127 127 },
128 128 "metadata": {},
129 129 "output_type": "display_data"
130 130 },
131 131 {
132 132 "data": {
133 133 "text/plain": [
134 134 "<DisplayHandle display_id=07fc47b2ef652ccb70addeee3eb0981a>"
135 135 ]
136 136 },
137 137 "execution_count": 16,
138 138 "metadata": {},
139 139 "output_type": "execute_result"
140 140 }
141 141 ],
142 142 "source": [
143 143 "handle = display(\"hello\", display_id=True)\n",
144 144 "handle"
145 145 ]
146 146 },
147 147 {
148 148 "cell_type": "markdown",
149 149 "metadata": {},
150 150 "source": [
151 151 "Calling `handle.display(obj)` is the same as calling `display(obj, handle.display_id)`,\n",
152 152 "so you don't need to use the handle objects if you don't want to:"
153 153 ]
154 154 },
155 155 {
156 156 "cell_type": "code",
157 157 "execution_count": 17,
158 158 "metadata": {},
159 159 "outputs": [
160 160 {
161 161 "data": {
162 162 "text/plain": [
163 163 "'z'"
164 164 ]
165 165 },
166 166 "metadata": {},
167 167 "output_type": "display_data"
168 168 }
169 169 ],
170 170 "source": [
171 171 "display('x', display_id='here');"
172 172 ]
173 173 },
174 174 {
175 175 "cell_type": "code",
176 176 "execution_count": 18,
177 177 "metadata": {},
178 178 "outputs": [
179 179 {
180 180 "data": {
181 181 "text/plain": [
182 182 "'z'"
183 183 ]
184 184 },
185 185 "metadata": {},
186 186 "output_type": "display_data"
187 187 }
188 188 ],
189 189 "source": [
190 190 "display('y', display_id='here');"
191 191 ]
192 192 },
193 193 {
194 194 "cell_type": "markdown",
195 195 "metadata": {},
196 196 "source": [
197 197 "And just like `display`, there is now `update_display`,\n",
198 198 "which is what `DisplayHandle.update` calls:"
199 199 ]
200 200 },
201 201 {
202 202 "cell_type": "code",
203 203 "execution_count": 19,
204 204 "metadata": {
205 205 "collapsed": true
206 206 },
207 207 "outputs": [],
208 208 "source": [
209 209 "update_display('z', display_id='here')"
210 210 ]
211 211 },
212 212 {
213 213 "cell_type": "markdown",
214 214 "metadata": {},
215 215 "source": [
216 216 "## More detailed example\n",
217 217 "\n",
218 218 "One of the motivating use cases for this is simple progress bars.\n",
219 219 "\n",
220 220 "Here is an example ProgressBar using these APIs:"
221 221 ]
222 222 },
223 223 {
224 224 "cell_type": "code",
225 "execution_count": 35,
225 "execution_count": 14,
226 226 "metadata": {},
227 227 "outputs": [
228 228 {
229 229 "data": {
230 230 "text/html": [
231 "<progress\n",
232 " value=10\n",
233 " max=10\n",
234 " style=\"width: 60ex\"/>\n",
235 " 10 / 10\n",
236 " "
231 "<progress style='width:100%' max='10' value='10'></progress>"
237 232 ],
238 233 "text/plain": [
239 "[============================================================] 10/10"
234 "<IPython.core.display.ProgressBar object>"
240 235 ]
241 236 },
242 237 "metadata": {},
243 238 "output_type": "display_data"
244 239 }
245 240 ],
246 241 "source": [
247 "import os\n",
248 "from binascii import hexlify\n",
249 "\n",
250 "class ProgressBar(object):\n",
251 " def __init__(self, capacity):\n",
252 " self.progress = 0\n",
253 " self.capacity = capacity\n",
254 " self.html_width = '60ex'\n",
255 " self.text_width = 60\n",
256 " self._display_id = hexlify(os.urandom(8)).decode('ascii')\n",
257 " \n",
258 " def __repr__(self):\n",
259 " fraction = self.progress / self.capacity\n",
260 " filled = '=' * int(fraction * self.text_width)\n",
261 " rest = ' ' * (self.text_width - len(filled))\n",
262 " return '[{}{}] {}/{}'.format(\n",
263 " filled, rest,\n",
264 " self.progress, self.capacity,\n",
265 " )\n",
266 " \n",
267 " def _repr_html_(self):\n",
268 " return \"\"\"<progress\n",
269 " value={progress}\n",
270 " max={capacity}\n",
271 " style=\"width: {width}\"/>\n",
272 " {progress} / {capacity}\n",
273 " \"\"\".format(\n",
274 " progress=self.progress,\n",
275 " capacity=self.capacity,\n",
276 " width=self.html_width,\n",
277 " )\n",
278 " \n",
279 " def display(self):\n",
280 " display(self, display_id=self._display_id)\n",
281 " \n",
282 " def update(self):\n",
283 " update_display(self, display_id=self._display_id)\n",
242 "from IPython.display import ProgressBar\n",
284 243 "\n",
285 244 "bar = ProgressBar(10)\n",
286 245 "bar.display()"
287 246 ]
288 247 },
289 248 {
290 249 "cell_type": "markdown",
291 250 "metadata": {},
292 251 "source": [
293 252 "And the ProgressBar has `.display` and `.update` methods:"
294 253 ]
295 254 },
296 255 {
297 256 "cell_type": "code",
298 "execution_count": 36,
257 "execution_count": 15,
299 258 "metadata": {},
300 259 "outputs": [
301 260 {
302 261 "data": {
303 262 "text/html": [
304 "<progress\n",
305 " value=10\n",
306 " max=10\n",
307 " style=\"width: 60ex\"/>\n",
308 " 10 / 10\n",
309 " "
263 "<progress style='width:100%' max='10' value='10'></progress>"
310 264 ],
311 265 "text/plain": [
312 "[============================================================] 10/10"
266 "<IPython.core.display.ProgressBar object>"
313 267 ]
314 268 },
315 269 "metadata": {},
316 270 "output_type": "display_data"
317 271 }
318 272 ],
319 273 "source": [
320 274 "import time\n",
321 275 "\n",
322 276 "bar.display()\n",
323 277 "\n",
324 278 "for i in range(11):\n",
325 279 " bar.progress = i\n",
326 280 " bar.update()\n",
327 281 " time.sleep(0.25)"
328 282 ]
329 283 },
330 284 {
331 285 "cell_type": "markdown",
332 286 "metadata": {},
333 287 "source": [
334 288 "We would encourage any updatable-display objects that track their own display_ids to follow-suit with `.display()` and `.update()` or `.update_display()` methods."
335 289 ]
336 290 }
337 291 ],
338 292 "metadata": {
339 293 "kernelspec": {
340 294 "display_name": "Python 3",
341 295 "language": "python",
342 296 "name": "python3"
343 297 },
344 298 "language_info": {
345 299 "codemirror_mode": {
346 300 "name": "ipython",
347 301 "version": 3
348 302 },
349 303 "file_extension": ".py",
350 304 "mimetype": "text/x-python",
351 305 "name": "python",
352 306 "nbconvert_exporter": "python",
353 307 "pygments_lexer": "ipython3",
354 "version": "3.5.1"
308 "version": "3.6.2"
355 309 }
356 310 },
357 311 "nbformat": 4,
358 312 "nbformat_minor": 1
359 313 }
General Comments 0
You need to be logged in to leave comments. Login now