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