##// END OF EJS Templates
Don't emit a trailng newline in base64-encoded data like 'image/png'
Alexey Zaytsev -
Show More
@@ -1,1290 +1,1290 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_base64, hexlify
9 9 import html
10 10 import json
11 11 import mimetypes
12 12 import os
13 13 import struct
14 14 import warnings
15 15 from copy import deepcopy
16 16 from os.path import splitext
17 17 from pathlib import Path, PurePath
18 18
19 19 from IPython.utils.py3compat import cast_unicode
20 20 from IPython.testing.skipdoctest import skip_doctest
21 21 from . import display_functions
22 22
23 23
24 24 __all__ = ['display_pretty', 'display_html', 'display_markdown',
25 25 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
26 26 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
27 27 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
28 28 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats',
29 29 'set_matplotlib_close',
30 30 'Video']
31 31
32 32 _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"]
33 33
34 34 __all__ = __all__ + _deprecated_names
35 35
36 36
37 37 # ----- warn to import from IPython.display -----
38 38
39 39 from warnings import warn
40 40
41 41
42 42 def __getattr__(name):
43 43 if name in _deprecated_names:
44 44 warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2)
45 45 return getattr(display_functions, name)
46 46
47 47 if name in globals().keys():
48 48 return globals()[name]
49 49 else:
50 50 raise AttributeError(f"module {__name__} has no attribute {name}")
51 51
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # utility functions
55 55 #-----------------------------------------------------------------------------
56 56
57 57 def _safe_exists(path):
58 58 """Check path, but don't let exceptions raise"""
59 59 try:
60 60 return os.path.exists(path)
61 61 except Exception:
62 62 return False
63 63
64 64
65 65 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
66 66 """internal implementation of all display_foo methods
67 67
68 68 Parameters
69 69 ----------
70 70 mimetype : str
71 71 The mimetype to be published (e.g. 'image/png')
72 72 *objs : object
73 73 The Python objects to display, or if raw=True raw text data to
74 74 display.
75 75 raw : bool
76 76 Are the data objects raw data or Python objects that need to be
77 77 formatted before display? [default: False]
78 78 metadata : dict (optional)
79 79 Metadata to be associated with the specific mimetype output.
80 80 """
81 81 if metadata:
82 82 metadata = {mimetype: metadata}
83 83 if raw:
84 84 # turn list of pngdata into list of { 'image/png': pngdata }
85 85 objs = [ {mimetype: obj} for obj in objs ]
86 86 display_functions.display(*objs, raw=raw, metadata=metadata, include=[mimetype])
87 87
88 88 #-----------------------------------------------------------------------------
89 89 # Main functions
90 90 #-----------------------------------------------------------------------------
91 91
92 92
93 93 def display_pretty(*objs, **kwargs):
94 94 """Display the pretty (default) representation of an object.
95 95
96 96 Parameters
97 97 ----------
98 98 *objs : object
99 99 The Python objects to display, or if raw=True raw text data to
100 100 display.
101 101 raw : bool
102 102 Are the data objects raw data or Python objects that need to be
103 103 formatted before display? [default: False]
104 104 metadata : dict (optional)
105 105 Metadata to be associated with the specific mimetype output.
106 106 """
107 107 _display_mimetype('text/plain', objs, **kwargs)
108 108
109 109
110 110 def display_html(*objs, **kwargs):
111 111 """Display the HTML representation of an object.
112 112
113 113 Note: If raw=False and the object does not have a HTML
114 114 representation, no HTML will be shown.
115 115
116 116 Parameters
117 117 ----------
118 118 *objs : object
119 119 The Python objects to display, or if raw=True raw HTML data to
120 120 display.
121 121 raw : bool
122 122 Are the data objects raw data or Python objects that need to be
123 123 formatted before display? [default: False]
124 124 metadata : dict (optional)
125 125 Metadata to be associated with the specific mimetype output.
126 126 """
127 127 _display_mimetype('text/html', objs, **kwargs)
128 128
129 129
130 130 def display_markdown(*objs, **kwargs):
131 131 """Displays the Markdown representation of an object.
132 132
133 133 Parameters
134 134 ----------
135 135 *objs : object
136 136 The Python objects to display, or if raw=True raw markdown data to
137 137 display.
138 138 raw : bool
139 139 Are the data objects raw data or Python objects that need to be
140 140 formatted before display? [default: False]
141 141 metadata : dict (optional)
142 142 Metadata to be associated with the specific mimetype output.
143 143 """
144 144
145 145 _display_mimetype('text/markdown', objs, **kwargs)
146 146
147 147
148 148 def display_svg(*objs, **kwargs):
149 149 """Display the SVG representation of an object.
150 150
151 151 Parameters
152 152 ----------
153 153 *objs : object
154 154 The Python objects to display, or if raw=True raw svg data to
155 155 display.
156 156 raw : bool
157 157 Are the data objects raw data or Python objects that need to be
158 158 formatted before display? [default: False]
159 159 metadata : dict (optional)
160 160 Metadata to be associated with the specific mimetype output.
161 161 """
162 162 _display_mimetype('image/svg+xml', objs, **kwargs)
163 163
164 164
165 165 def display_png(*objs, **kwargs):
166 166 """Display the PNG representation of an object.
167 167
168 168 Parameters
169 169 ----------
170 170 *objs : object
171 171 The Python objects to display, or if raw=True raw png data to
172 172 display.
173 173 raw : bool
174 174 Are the data objects raw data or Python objects that need to be
175 175 formatted before display? [default: False]
176 176 metadata : dict (optional)
177 177 Metadata to be associated with the specific mimetype output.
178 178 """
179 179 _display_mimetype('image/png', objs, **kwargs)
180 180
181 181
182 182 def display_jpeg(*objs, **kwargs):
183 183 """Display the JPEG representation of an object.
184 184
185 185 Parameters
186 186 ----------
187 187 *objs : object
188 188 The Python objects to display, or if raw=True raw JPEG data to
189 189 display.
190 190 raw : bool
191 191 Are the data objects raw data or Python objects that need to be
192 192 formatted before display? [default: False]
193 193 metadata : dict (optional)
194 194 Metadata to be associated with the specific mimetype output.
195 195 """
196 196 _display_mimetype('image/jpeg', objs, **kwargs)
197 197
198 198
199 199 def display_latex(*objs, **kwargs):
200 200 """Display the LaTeX representation of an object.
201 201
202 202 Parameters
203 203 ----------
204 204 *objs : object
205 205 The Python objects to display, or if raw=True raw latex data to
206 206 display.
207 207 raw : bool
208 208 Are the data objects raw data or Python objects that need to be
209 209 formatted before display? [default: False]
210 210 metadata : dict (optional)
211 211 Metadata to be associated with the specific mimetype output.
212 212 """
213 213 _display_mimetype('text/latex', objs, **kwargs)
214 214
215 215
216 216 def display_json(*objs, **kwargs):
217 217 """Display the JSON representation of an object.
218 218
219 219 Note that not many frontends support displaying JSON.
220 220
221 221 Parameters
222 222 ----------
223 223 *objs : object
224 224 The Python objects to display, or if raw=True raw json data to
225 225 display.
226 226 raw : bool
227 227 Are the data objects raw data or Python objects that need to be
228 228 formatted before display? [default: False]
229 229 metadata : dict (optional)
230 230 Metadata to be associated with the specific mimetype output.
231 231 """
232 232 _display_mimetype('application/json', objs, **kwargs)
233 233
234 234
235 235 def display_javascript(*objs, **kwargs):
236 236 """Display the Javascript representation of an object.
237 237
238 238 Parameters
239 239 ----------
240 240 *objs : object
241 241 The Python objects to display, or if raw=True raw javascript data to
242 242 display.
243 243 raw : bool
244 244 Are the data objects raw data or Python objects that need to be
245 245 formatted before display? [default: False]
246 246 metadata : dict (optional)
247 247 Metadata to be associated with the specific mimetype output.
248 248 """
249 249 _display_mimetype('application/javascript', objs, **kwargs)
250 250
251 251
252 252 def display_pdf(*objs, **kwargs):
253 253 """Display the PDF representation of an object.
254 254
255 255 Parameters
256 256 ----------
257 257 *objs : object
258 258 The Python objects to display, or if raw=True raw javascript data to
259 259 display.
260 260 raw : bool
261 261 Are the data objects raw data or Python objects that need to be
262 262 formatted before display? [default: False]
263 263 metadata : dict (optional)
264 264 Metadata to be associated with the specific mimetype output.
265 265 """
266 266 _display_mimetype('application/pdf', objs, **kwargs)
267 267
268 268
269 269 #-----------------------------------------------------------------------------
270 270 # Smart classes
271 271 #-----------------------------------------------------------------------------
272 272
273 273
274 274 class DisplayObject(object):
275 275 """An object that wraps data to be displayed."""
276 276
277 277 _read_flags = 'r'
278 278 _show_mem_addr = False
279 279 metadata = None
280 280
281 281 def __init__(self, data=None, url=None, filename=None, metadata=None):
282 282 """Create a display object given raw data.
283 283
284 284 When this object is returned by an expression or passed to the
285 285 display function, it will result in the data being displayed
286 286 in the frontend. The MIME type of the data should match the
287 287 subclasses used, so the Png subclass should be used for 'image/png'
288 288 data. If the data is a URL, the data will first be downloaded
289 289 and then displayed. If
290 290
291 291 Parameters
292 292 ----------
293 293 data : unicode, str or bytes
294 294 The raw data or a URL or file to load the data from
295 295 url : unicode
296 296 A URL to download the data from.
297 297 filename : unicode
298 298 Path to a local file to load the data from.
299 299 metadata : dict
300 300 Dict of metadata associated to be the object when displayed
301 301 """
302 302 if isinstance(data, (Path, PurePath)):
303 303 data = str(data)
304 304
305 305 if data is not None and isinstance(data, str):
306 306 if data.startswith('http') and url is None:
307 307 url = data
308 308 filename = None
309 309 data = None
310 310 elif _safe_exists(data) and filename is None:
311 311 url = None
312 312 filename = data
313 313 data = None
314 314
315 315 self.url = url
316 316 self.filename = filename
317 317 # because of @data.setter methods in
318 318 # subclasses ensure url and filename are set
319 319 # before assigning to self.data
320 320 self.data = data
321 321
322 322 if metadata is not None:
323 323 self.metadata = metadata
324 324 elif self.metadata is None:
325 325 self.metadata = {}
326 326
327 327 self.reload()
328 328 self._check_data()
329 329
330 330 def __repr__(self):
331 331 if not self._show_mem_addr:
332 332 cls = self.__class__
333 333 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
334 334 else:
335 335 r = super(DisplayObject, self).__repr__()
336 336 return r
337 337
338 338 def _check_data(self):
339 339 """Override in subclasses if there's something to check."""
340 340 pass
341 341
342 342 def _data_and_metadata(self):
343 343 """shortcut for returning metadata with shape information, if defined"""
344 344 if self.metadata:
345 345 return self.data, deepcopy(self.metadata)
346 346 else:
347 347 return self.data
348 348
349 349 def reload(self):
350 350 """Reload the raw data from file or URL."""
351 351 if self.filename is not None:
352 352 encoding = None if "b" in self._read_flags else "utf-8"
353 353 with open(self.filename, self._read_flags, encoding=encoding) as f:
354 354 self.data = f.read()
355 355 elif self.url is not None:
356 356 # Deferred import
357 357 from urllib.request import urlopen
358 358 response = urlopen(self.url)
359 359 data = response.read()
360 360 # extract encoding from header, if there is one:
361 361 encoding = None
362 362 if 'content-type' in response.headers:
363 363 for sub in response.headers['content-type'].split(';'):
364 364 sub = sub.strip()
365 365 if sub.startswith('charset'):
366 366 encoding = sub.split('=')[-1].strip()
367 367 break
368 368 if 'content-encoding' in response.headers:
369 369 # TODO: do deflate?
370 370 if 'gzip' in response.headers['content-encoding']:
371 371 import gzip
372 372 from io import BytesIO
373 373
374 374 # assume utf-8 if encoding is not specified
375 375 with gzip.open(
376 376 BytesIO(data), "rt", encoding=encoding or "utf-8"
377 377 ) as fp:
378 378 encoding = None
379 379 data = fp.read()
380 380
381 381 # decode data, if an encoding was specified
382 382 # We only touch self.data once since
383 383 # subclasses such as SVG have @data.setter methods
384 384 # that transform self.data into ... well svg.
385 385 if encoding:
386 386 self.data = data.decode(encoding, 'replace')
387 387 else:
388 388 self.data = data
389 389
390 390
391 391 class TextDisplayObject(DisplayObject):
392 392 """Create a text display object given raw data.
393 393
394 394 Parameters
395 395 ----------
396 396 data : str or unicode
397 397 The raw data or a URL or file to load the data from.
398 398 url : unicode
399 399 A URL to download the data from.
400 400 filename : unicode
401 401 Path to a local file to load the data from.
402 402 metadata : dict
403 403 Dict of metadata associated to be the object when displayed
404 404 """
405 405 def _check_data(self):
406 406 if self.data is not None and not isinstance(self.data, str):
407 407 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
408 408
409 409 class Pretty(TextDisplayObject):
410 410
411 411 def _repr_pretty_(self, pp, cycle):
412 412 return pp.text(self.data)
413 413
414 414
415 415 class HTML(TextDisplayObject):
416 416
417 417 def __init__(self, data=None, url=None, filename=None, metadata=None):
418 418 def warn():
419 419 if not data:
420 420 return False
421 421
422 422 #
423 423 # Avoid calling lower() on the entire data, because it could be a
424 424 # long string and we're only interested in its beginning and end.
425 425 #
426 426 prefix = data[:10].lower()
427 427 suffix = data[-10:].lower()
428 428 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
429 429
430 430 if warn():
431 431 warnings.warn("Consider using IPython.display.IFrame instead")
432 432 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
433 433
434 434 def _repr_html_(self):
435 435 return self._data_and_metadata()
436 436
437 437 def __html__(self):
438 438 """
439 439 This method exists to inform other HTML-using modules (e.g. Markupsafe,
440 440 htmltag, etc) that this object is HTML and does not need things like
441 441 special characters (<>&) escaped.
442 442 """
443 443 return self._repr_html_()
444 444
445 445
446 446 class Markdown(TextDisplayObject):
447 447
448 448 def _repr_markdown_(self):
449 449 return self._data_and_metadata()
450 450
451 451
452 452 class Math(TextDisplayObject):
453 453
454 454 def _repr_latex_(self):
455 455 s = r"$\displaystyle %s$" % self.data.strip('$')
456 456 if self.metadata:
457 457 return s, deepcopy(self.metadata)
458 458 else:
459 459 return s
460 460
461 461
462 462 class Latex(TextDisplayObject):
463 463
464 464 def _repr_latex_(self):
465 465 return self._data_and_metadata()
466 466
467 467
468 468 class SVG(DisplayObject):
469 469 """Embed an SVG into the display.
470 470
471 471 Note if you just want to view a svg image via a URL use `:class:Image` with
472 472 a url=URL keyword argument.
473 473 """
474 474
475 475 _read_flags = 'rb'
476 476 # wrap data in a property, which extracts the <svg> tag, discarding
477 477 # document headers
478 478 _data = None
479 479
480 480 @property
481 481 def data(self):
482 482 return self._data
483 483
484 484 @data.setter
485 485 def data(self, svg):
486 486 if svg is None:
487 487 self._data = None
488 488 return
489 489 # parse into dom object
490 490 from xml.dom import minidom
491 491 x = minidom.parseString(svg)
492 492 # get svg tag (should be 1)
493 493 found_svg = x.getElementsByTagName('svg')
494 494 if found_svg:
495 495 svg = found_svg[0].toxml()
496 496 else:
497 497 # fallback on the input, trust the user
498 498 # but this is probably an error.
499 499 pass
500 500 svg = cast_unicode(svg)
501 501 self._data = svg
502 502
503 503 def _repr_svg_(self):
504 504 return self._data_and_metadata()
505 505
506 506 class ProgressBar(DisplayObject):
507 507 """Progressbar supports displaying a progressbar like element
508 508 """
509 509 def __init__(self, total):
510 510 """Creates a new progressbar
511 511
512 512 Parameters
513 513 ----------
514 514 total : int
515 515 maximum size of the progressbar
516 516 """
517 517 self.total = total
518 518 self._progress = 0
519 519 self.html_width = '60ex'
520 520 self.text_width = 60
521 521 self._display_id = hexlify(os.urandom(8)).decode('ascii')
522 522
523 523 def __repr__(self):
524 524 fraction = self.progress / self.total
525 525 filled = '=' * int(fraction * self.text_width)
526 526 rest = ' ' * (self.text_width - len(filled))
527 527 return '[{}{}] {}/{}'.format(
528 528 filled, rest,
529 529 self.progress, self.total,
530 530 )
531 531
532 532 def _repr_html_(self):
533 533 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
534 534 self.html_width, self.total, self.progress)
535 535
536 536 def display(self):
537 537 display_functions.display(self, display_id=self._display_id)
538 538
539 539 def update(self):
540 540 display_functions.display(self, display_id=self._display_id, update=True)
541 541
542 542 @property
543 543 def progress(self):
544 544 return self._progress
545 545
546 546 @progress.setter
547 547 def progress(self, value):
548 548 self._progress = value
549 549 self.update()
550 550
551 551 def __iter__(self):
552 552 self.display()
553 553 self._progress = -1 # First iteration is 0
554 554 return self
555 555
556 556 def __next__(self):
557 557 """Returns current value and increments display by one."""
558 558 self.progress += 1
559 559 if self.progress < self.total:
560 560 return self.progress
561 561 else:
562 562 raise StopIteration()
563 563
564 564 class JSON(DisplayObject):
565 565 """JSON expects a JSON-able dict or list
566 566
567 567 not an already-serialized JSON string.
568 568
569 569 Scalar types (None, number, string) are not allowed, only dict or list containers.
570 570 """
571 571 # wrap data in a property, which warns about passing already-serialized JSON
572 572 _data = None
573 573 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
574 574 """Create a JSON display object given raw data.
575 575
576 576 Parameters
577 577 ----------
578 578 data : dict or list
579 579 JSON data to display. Not an already-serialized JSON string.
580 580 Scalar types (None, number, string) are not allowed, only dict
581 581 or list containers.
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 expanded : boolean
587 587 Metadata to control whether a JSON display component is expanded.
588 588 metadata : dict
589 589 Specify extra metadata to attach to the json display object.
590 590 root : str
591 591 The name of the root element of the JSON tree
592 592 """
593 593 self.metadata = {
594 594 'expanded': expanded,
595 595 'root': root,
596 596 }
597 597 if metadata:
598 598 self.metadata.update(metadata)
599 599 if kwargs:
600 600 self.metadata.update(kwargs)
601 601 super(JSON, self).__init__(data=data, url=url, filename=filename)
602 602
603 603 def _check_data(self):
604 604 if self.data is not None and not isinstance(self.data, (dict, list)):
605 605 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
606 606
607 607 @property
608 608 def data(self):
609 609 return self._data
610 610
611 611 @data.setter
612 612 def data(self, data):
613 613 if isinstance(data, (Path, PurePath)):
614 614 data = str(data)
615 615
616 616 if isinstance(data, str):
617 617 if self.filename is None and self.url is None:
618 618 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
619 619 data = json.loads(data)
620 620 self._data = data
621 621
622 622 def _data_and_metadata(self):
623 623 return self.data, self.metadata
624 624
625 625 def _repr_json_(self):
626 626 return self._data_and_metadata()
627 627
628 628
629 629 _css_t = """var link = document.createElement("link");
630 630 link.rel = "stylesheet";
631 631 link.type = "text/css";
632 632 link.href = "%s";
633 633 document.head.appendChild(link);
634 634 """
635 635
636 636 _lib_t1 = """new Promise(function(resolve, reject) {
637 637 var script = document.createElement("script");
638 638 script.onload = resolve;
639 639 script.onerror = reject;
640 640 script.src = "%s";
641 641 document.head.appendChild(script);
642 642 }).then(() => {
643 643 """
644 644
645 645 _lib_t2 = """
646 646 });"""
647 647
648 648 class GeoJSON(JSON):
649 649 """GeoJSON expects JSON-able dict
650 650
651 651 not an already-serialized JSON string.
652 652
653 653 Scalar types (None, number, string) are not allowed, only dict containers.
654 654 """
655 655
656 656 def __init__(self, *args, **kwargs):
657 657 """Create a GeoJSON display object given raw data.
658 658
659 659 Parameters
660 660 ----------
661 661 data : dict or list
662 662 VegaLite data. Not an already-serialized JSON string.
663 663 Scalar types (None, number, string) are not allowed, only dict
664 664 or list containers.
665 665 url_template : string
666 666 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
667 667 layer_options : dict
668 668 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
669 669 url : unicode
670 670 A URL to download the data from.
671 671 filename : unicode
672 672 Path to a local file to load the data from.
673 673 metadata : dict
674 674 Specify extra metadata to attach to the json display object.
675 675
676 676 Examples
677 677 --------
678 678 The following will display an interactive map of Mars with a point of
679 679 interest on frontend that do support GeoJSON display.
680 680
681 681 >>> from IPython.display import GeoJSON
682 682
683 683 >>> GeoJSON(data={
684 684 ... "type": "Feature",
685 685 ... "geometry": {
686 686 ... "type": "Point",
687 687 ... "coordinates": [-81.327, 296.038]
688 688 ... }
689 689 ... },
690 690 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
691 691 ... layer_options={
692 692 ... "basemap_id": "celestia_mars-shaded-16k_global",
693 693 ... "attribution" : "Celestia/praesepe",
694 694 ... "minZoom" : 0,
695 695 ... "maxZoom" : 18,
696 696 ... })
697 697 <IPython.core.display.GeoJSON object>
698 698
699 699 In the terminal IPython, you will only see the text representation of
700 700 the GeoJSON object.
701 701
702 702 """
703 703
704 704 super(GeoJSON, self).__init__(*args, **kwargs)
705 705
706 706
707 707 def _ipython_display_(self):
708 708 bundle = {
709 709 'application/geo+json': self.data,
710 710 'text/plain': '<IPython.display.GeoJSON object>'
711 711 }
712 712 metadata = {
713 713 'application/geo+json': self.metadata
714 714 }
715 715 display_functions.display(bundle, metadata=metadata, raw=True)
716 716
717 717 class Javascript(TextDisplayObject):
718 718
719 719 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
720 720 """Create a Javascript display object given raw data.
721 721
722 722 When this object is returned by an expression or passed to the
723 723 display function, it will result in the data being displayed
724 724 in the frontend. If the data is a URL, the data will first be
725 725 downloaded and then displayed.
726 726
727 727 In the Notebook, the containing element will be available as `element`,
728 728 and jQuery will be available. Content appended to `element` will be
729 729 visible in the output area.
730 730
731 731 Parameters
732 732 ----------
733 733 data : unicode, str or bytes
734 734 The Javascript source code or a URL to download it from.
735 735 url : unicode
736 736 A URL to download the data from.
737 737 filename : unicode
738 738 Path to a local file to load the data from.
739 739 lib : list or str
740 740 A sequence of Javascript library URLs to load asynchronously before
741 741 running the source code. The full URLs of the libraries should
742 742 be given. A single Javascript library URL can also be given as a
743 743 string.
744 744 css : list or str
745 745 A sequence of css files to load before running the source code.
746 746 The full URLs of the css files should be given. A single css URL
747 747 can also be given as a string.
748 748 """
749 749 if isinstance(lib, str):
750 750 lib = [lib]
751 751 elif lib is None:
752 752 lib = []
753 753 if isinstance(css, str):
754 754 css = [css]
755 755 elif css is None:
756 756 css = []
757 757 if not isinstance(lib, (list,tuple)):
758 758 raise TypeError('expected sequence, got: %r' % lib)
759 759 if not isinstance(css, (list,tuple)):
760 760 raise TypeError('expected sequence, got: %r' % css)
761 761 self.lib = lib
762 762 self.css = css
763 763 super(Javascript, self).__init__(data=data, url=url, filename=filename)
764 764
765 765 def _repr_javascript_(self):
766 766 r = ''
767 767 for c in self.css:
768 768 r += _css_t % c
769 769 for l in self.lib:
770 770 r += _lib_t1 % l
771 771 r += self.data
772 772 r += _lib_t2*len(self.lib)
773 773 return r
774 774
775 775 # constants for identifying png/jpeg data
776 776 _PNG = b'\x89PNG\r\n\x1a\n'
777 777 _JPEG = b'\xff\xd8'
778 778
779 779 def _pngxy(data):
780 780 """read the (width, height) from a PNG header"""
781 781 ihdr = data.index(b'IHDR')
782 782 # next 8 bytes are width/height
783 783 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
784 784
785 785 def _jpegxy(data):
786 786 """read the (width, height) from a JPEG header"""
787 787 # adapted from http://www.64lines.com/jpeg-width-height
788 788
789 789 idx = 4
790 790 while True:
791 791 block_size = struct.unpack('>H', data[idx:idx+2])[0]
792 792 idx = idx + block_size
793 793 if data[idx:idx+2] == b'\xFF\xC0':
794 794 # found Start of Frame
795 795 iSOF = idx
796 796 break
797 797 else:
798 798 # read another block
799 799 idx += 2
800 800
801 801 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
802 802 return w, h
803 803
804 804 def _gifxy(data):
805 805 """read the (width, height) from a GIF header"""
806 806 return struct.unpack('<HH', data[6:10])
807 807
808 808
809 809 class Image(DisplayObject):
810 810
811 811 _read_flags = 'rb'
812 812 _FMT_JPEG = u'jpeg'
813 813 _FMT_PNG = u'png'
814 814 _FMT_GIF = u'gif'
815 815 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
816 816 _MIMETYPES = {
817 817 _FMT_PNG: 'image/png',
818 818 _FMT_JPEG: 'image/jpeg',
819 819 _FMT_GIF: 'image/gif',
820 820 }
821 821
822 822 def __init__(
823 823 self,
824 824 data=None,
825 825 url=None,
826 826 filename=None,
827 827 format=None,
828 828 embed=None,
829 829 width=None,
830 830 height=None,
831 831 retina=False,
832 832 unconfined=False,
833 833 metadata=None,
834 834 alt=None,
835 835 ):
836 836 """Create a PNG/JPEG/GIF image object given raw data.
837 837
838 838 When this object is returned by an input cell or passed to the
839 839 display function, it will result in the image being displayed
840 840 in the frontend.
841 841
842 842 Parameters
843 843 ----------
844 844 data : unicode, str or bytes
845 845 The raw image data or a URL or filename to load the data from.
846 846 This always results in embedded image data.
847 847
848 848 url : unicode
849 849 A URL to download the data from. If you specify `url=`,
850 850 the image data will not be embedded unless you also specify `embed=True`.
851 851
852 852 filename : unicode
853 853 Path to a local file to load the data from.
854 854 Images from a file are always embedded.
855 855
856 856 format : unicode
857 857 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
858 858 for format will be inferred from the filename extension.
859 859
860 860 embed : bool
861 861 Should the image data be embedded using a data URI (True) or be
862 862 loaded using an <img> tag. Set this to True if you want the image
863 863 to be viewable later with no internet connection in the notebook.
864 864
865 865 Default is `True`, unless the keyword argument `url` is set, then
866 866 default value is `False`.
867 867
868 868 Note that QtConsole is not able to display images if `embed` is set to `False`
869 869
870 870 width : int
871 871 Width in pixels to which to constrain the image in html
872 872
873 873 height : int
874 874 Height in pixels to which to constrain the image in html
875 875
876 876 retina : bool
877 877 Automatically set the width and height to half of the measured
878 878 width and height.
879 879 This only works for embedded images because it reads the width/height
880 880 from image data.
881 881 For non-embedded images, you can just set the desired display width
882 882 and height directly.
883 883
884 884 unconfined : bool
885 885 Set unconfined=True to disable max-width confinement of the image.
886 886
887 887 metadata : dict
888 888 Specify extra metadata to attach to the image.
889 889
890 890 alt : unicode
891 891 Alternative text for the image, for use by screen readers.
892 892
893 893 Examples
894 894 --------
895 895 embedded image data, works in qtconsole and notebook
896 896 when passed positionally, the first arg can be any of raw image data,
897 897 a URL, or a filename from which to load image data.
898 898 The result is always embedding image data for inline images.
899 899
900 900 >>> Image('https://www.google.fr/images/srpr/logo3w.png') # doctest: +SKIP
901 901 <IPython.core.display.Image object>
902 902
903 903 >>> Image('/path/to/image.jpg')
904 904 <IPython.core.display.Image object>
905 905
906 906 >>> Image(b'RAW_PNG_DATA...')
907 907 <IPython.core.display.Image object>
908 908
909 909 Specifying Image(url=...) does not embed the image data,
910 910 it only generates ``<img>`` tag with a link to the source.
911 911 This will not work in the qtconsole or offline.
912 912
913 913 >>> Image(url='https://www.google.fr/images/srpr/logo3w.png')
914 914 <IPython.core.display.Image object>
915 915
916 916 """
917 917 if isinstance(data, (Path, PurePath)):
918 918 data = str(data)
919 919
920 920 if filename is not None:
921 921 ext = self._find_ext(filename)
922 922 elif url is not None:
923 923 ext = self._find_ext(url)
924 924 elif data is None:
925 925 raise ValueError("No image data found. Expecting filename, url, or data.")
926 926 elif isinstance(data, str) and (
927 927 data.startswith('http') or _safe_exists(data)
928 928 ):
929 929 ext = self._find_ext(data)
930 930 else:
931 931 ext = None
932 932
933 933 if format is None:
934 934 if ext is not None:
935 935 if ext == u'jpg' or ext == u'jpeg':
936 936 format = self._FMT_JPEG
937 937 elif ext == u'png':
938 938 format = self._FMT_PNG
939 939 elif ext == u'gif':
940 940 format = self._FMT_GIF
941 941 else:
942 942 format = ext.lower()
943 943 elif isinstance(data, bytes):
944 944 # infer image type from image data header,
945 945 # only if format has not been specified.
946 946 if data[:2] == _JPEG:
947 947 format = self._FMT_JPEG
948 948
949 949 # failed to detect format, default png
950 950 if format is None:
951 951 format = self._FMT_PNG
952 952
953 953 if format.lower() == 'jpg':
954 954 # jpg->jpeg
955 955 format = self._FMT_JPEG
956 956
957 957 self.format = format.lower()
958 958 self.embed = embed if embed is not None else (url is None)
959 959
960 960 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
961 961 raise ValueError("Cannot embed the '%s' image format" % (self.format))
962 962 if self.embed:
963 963 self._mimetype = self._MIMETYPES.get(self.format)
964 964
965 965 self.width = width
966 966 self.height = height
967 967 self.retina = retina
968 968 self.unconfined = unconfined
969 969 self.alt = alt
970 970 super(Image, self).__init__(data=data, url=url, filename=filename,
971 971 metadata=metadata)
972 972
973 973 if self.width is None and self.metadata.get('width', {}):
974 974 self.width = metadata['width']
975 975
976 976 if self.height is None and self.metadata.get('height', {}):
977 977 self.height = metadata['height']
978 978
979 979 if self.alt is None and self.metadata.get("alt", {}):
980 980 self.alt = metadata["alt"]
981 981
982 982 if retina:
983 983 self._retina_shape()
984 984
985 985
986 986 def _retina_shape(self):
987 987 """load pixel-doubled width and height from image data"""
988 988 if not self.embed:
989 989 return
990 990 if self.format == self._FMT_PNG:
991 991 w, h = _pngxy(self.data)
992 992 elif self.format == self._FMT_JPEG:
993 993 w, h = _jpegxy(self.data)
994 994 elif self.format == self._FMT_GIF:
995 995 w, h = _gifxy(self.data)
996 996 else:
997 997 # retina only supports png
998 998 return
999 999 self.width = w // 2
1000 1000 self.height = h // 2
1001 1001
1002 1002 def reload(self):
1003 1003 """Reload the raw data from file or URL."""
1004 1004 if self.embed:
1005 1005 super(Image,self).reload()
1006 1006 if self.retina:
1007 1007 self._retina_shape()
1008 1008
1009 1009 def _repr_html_(self):
1010 1010 if not self.embed:
1011 1011 width = height = klass = alt = ""
1012 1012 if self.width:
1013 1013 width = ' width="%d"' % self.width
1014 1014 if self.height:
1015 1015 height = ' height="%d"' % self.height
1016 1016 if self.unconfined:
1017 1017 klass = ' class="unconfined"'
1018 1018 if self.alt:
1019 1019 alt = ' alt="%s"' % html.escape(self.alt)
1020 1020 return '<img src="{url}"{width}{height}{klass}{alt}/>'.format(
1021 1021 url=self.url,
1022 1022 width=width,
1023 1023 height=height,
1024 1024 klass=klass,
1025 1025 alt=alt,
1026 1026 )
1027 1027
1028 1028 def _repr_mimebundle_(self, include=None, exclude=None):
1029 1029 """Return the image as a mimebundle
1030 1030
1031 1031 Any new mimetype support should be implemented here.
1032 1032 """
1033 1033 if self.embed:
1034 1034 mimetype = self._mimetype
1035 1035 data, metadata = self._data_and_metadata(always_both=True)
1036 1036 if metadata:
1037 1037 metadata = {mimetype: metadata}
1038 1038 return {mimetype: data}, metadata
1039 1039 else:
1040 1040 return {'text/html': self._repr_html_()}
1041 1041
1042 1042 def _data_and_metadata(self, always_both=False):
1043 1043 """shortcut for returning metadata with shape information, if defined"""
1044 1044 try:
1045 b64_data = b2a_base64(self.data).decode('ascii')
1045 b64_data = b2a_base64(self.data, newline=False).decode("ascii")
1046 1046 except TypeError as e:
1047 1047 raise FileNotFoundError(
1048 1048 "No such file or directory: '%s'" % (self.data)) from e
1049 1049 md = {}
1050 1050 if self.metadata:
1051 1051 md.update(self.metadata)
1052 1052 if self.width:
1053 1053 md['width'] = self.width
1054 1054 if self.height:
1055 1055 md['height'] = self.height
1056 1056 if self.unconfined:
1057 1057 md['unconfined'] = self.unconfined
1058 1058 if self.alt:
1059 1059 md["alt"] = self.alt
1060 1060 if md or always_both:
1061 1061 return b64_data, md
1062 1062 else:
1063 1063 return b64_data
1064 1064
1065 1065 def _repr_png_(self):
1066 1066 if self.embed and self.format == self._FMT_PNG:
1067 1067 return self._data_and_metadata()
1068 1068
1069 1069 def _repr_jpeg_(self):
1070 1070 if self.embed and self.format == self._FMT_JPEG:
1071 1071 return self._data_and_metadata()
1072 1072
1073 1073 def _find_ext(self, s):
1074 1074 base, ext = splitext(s)
1075 1075
1076 1076 if not ext:
1077 1077 return base
1078 1078
1079 1079 # `splitext` includes leading period, so we skip it
1080 1080 return ext[1:].lower()
1081 1081
1082 1082
1083 1083 class Video(DisplayObject):
1084 1084
1085 1085 def __init__(self, data=None, url=None, filename=None, embed=False,
1086 1086 mimetype=None, width=None, height=None, html_attributes="controls"):
1087 1087 """Create a video object given raw data or an URL.
1088 1088
1089 1089 When this object is returned by an input cell or passed to the
1090 1090 display function, it will result in the video being displayed
1091 1091 in the frontend.
1092 1092
1093 1093 Parameters
1094 1094 ----------
1095 1095 data : unicode, str or bytes
1096 1096 The raw video data or a URL or filename to load the data from.
1097 1097 Raw data will require passing ``embed=True``.
1098 1098
1099 1099 url : unicode
1100 1100 A URL for the video. If you specify ``url=``,
1101 1101 the image data will not be embedded.
1102 1102
1103 1103 filename : unicode
1104 1104 Path to a local file containing the video.
1105 1105 Will be interpreted as a local URL unless ``embed=True``.
1106 1106
1107 1107 embed : bool
1108 1108 Should the video be embedded using a data URI (True) or be
1109 1109 loaded using a <video> tag (False).
1110 1110
1111 1111 Since videos are large, embedding them should be avoided, if possible.
1112 1112 You must confirm embedding as your intention by passing ``embed=True``.
1113 1113
1114 1114 Local files can be displayed with URLs without embedding the content, via::
1115 1115
1116 1116 Video('./video.mp4')
1117 1117
1118 1118 mimetype : unicode
1119 1119 Specify the mimetype for embedded videos.
1120 1120 Default will be guessed from file extension, if available.
1121 1121
1122 1122 width : int
1123 1123 Width in pixels to which to constrain the video in HTML.
1124 1124 If not supplied, defaults to the width of the video.
1125 1125
1126 1126 height : int
1127 1127 Height in pixels to which to constrain the video in html.
1128 1128 If not supplied, defaults to the height of the video.
1129 1129
1130 1130 html_attributes : str
1131 1131 Attributes for the HTML ``<video>`` block.
1132 1132 Default: ``"controls"`` to get video controls.
1133 1133 Other examples: ``"controls muted"`` for muted video with controls,
1134 1134 ``"loop autoplay"`` for looping autoplaying video without controls.
1135 1135
1136 1136 Examples
1137 1137 --------
1138 1138 ::
1139 1139
1140 1140 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1141 1141 Video('path/to/video.mp4')
1142 1142 Video('path/to/video.mp4', embed=True)
1143 1143 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1144 1144 Video(b'raw-videodata', embed=True)
1145 1145 """
1146 1146 if isinstance(data, (Path, PurePath)):
1147 1147 data = str(data)
1148 1148
1149 1149 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1150 1150 url = data
1151 1151 data = None
1152 1152 elif data is not None and os.path.exists(data):
1153 1153 filename = data
1154 1154 data = None
1155 1155
1156 1156 if data and not embed:
1157 1157 msg = ''.join([
1158 1158 "To embed videos, you must pass embed=True ",
1159 1159 "(this may make your notebook files huge)\n",
1160 1160 "Consider passing Video(url='...')",
1161 1161 ])
1162 1162 raise ValueError(msg)
1163 1163
1164 1164 self.mimetype = mimetype
1165 1165 self.embed = embed
1166 1166 self.width = width
1167 1167 self.height = height
1168 1168 self.html_attributes = html_attributes
1169 1169 super(Video, self).__init__(data=data, url=url, filename=filename)
1170 1170
1171 1171 def _repr_html_(self):
1172 1172 width = height = ''
1173 1173 if self.width:
1174 1174 width = ' width="%d"' % self.width
1175 1175 if self.height:
1176 1176 height = ' height="%d"' % self.height
1177 1177
1178 1178 # External URLs and potentially local files are not embedded into the
1179 1179 # notebook output.
1180 1180 if not self.embed:
1181 1181 url = self.url if self.url is not None else self.filename
1182 1182 output = """<video src="{0}" {1} {2} {3}>
1183 1183 Your browser does not support the <code>video</code> element.
1184 1184 </video>""".format(url, self.html_attributes, width, height)
1185 1185 return output
1186 1186
1187 1187 # Embedded videos are base64-encoded.
1188 1188 mimetype = self.mimetype
1189 1189 if self.filename is not None:
1190 1190 if not mimetype:
1191 1191 mimetype, _ = mimetypes.guess_type(self.filename)
1192 1192
1193 1193 with open(self.filename, 'rb') as f:
1194 1194 video = f.read()
1195 1195 else:
1196 1196 video = self.data
1197 1197 if isinstance(video, str):
1198 1198 # unicode input is already b64-encoded
1199 1199 b64_video = video
1200 1200 else:
1201 b64_video = b2a_base64(video).decode('ascii').rstrip()
1201 b64_video = b2a_base64(video, newline=False).decode("ascii").rstrip()
1202 1202
1203 1203 output = """<video {0} {1} {2}>
1204 1204 <source src="data:{3};base64,{4}" type="{3}">
1205 1205 Your browser does not support the video tag.
1206 1206 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1207 1207 return output
1208 1208
1209 1209 def reload(self):
1210 1210 # TODO
1211 1211 pass
1212 1212
1213 1213
1214 1214 @skip_doctest
1215 1215 def set_matplotlib_formats(*formats, **kwargs):
1216 1216 """
1217 1217 .. deprecated:: 7.23
1218 1218
1219 1219 use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
1220 1220
1221 1221 Select figure formats for the inline backend. Optionally pass quality for JPEG.
1222 1222
1223 1223 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1224 1224
1225 1225 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1226 1226
1227 1227 To set this in your config files use the following::
1228 1228
1229 1229 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1230 1230 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1231 1231
1232 1232 Parameters
1233 1233 ----------
1234 1234 *formats : strs
1235 1235 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1236 1236 **kwargs
1237 1237 Keyword args will be relayed to ``figure.canvas.print_figure``.
1238 1238 """
1239 1239 warnings.warn(
1240 1240 "`set_matplotlib_formats` is deprecated since IPython 7.23, directly "
1241 1241 "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`",
1242 1242 DeprecationWarning,
1243 1243 stacklevel=2,
1244 1244 )
1245 1245
1246 1246 from matplotlib_inline.backend_inline import (
1247 1247 set_matplotlib_formats as set_matplotlib_formats_orig,
1248 1248 )
1249 1249
1250 1250 set_matplotlib_formats_orig(*formats, **kwargs)
1251 1251
1252 1252 @skip_doctest
1253 1253 def set_matplotlib_close(close=True):
1254 1254 """
1255 1255 .. deprecated:: 7.23
1256 1256
1257 1257 use `matplotlib_inline.backend_inline.set_matplotlib_close()`
1258 1258
1259 1259 Set whether the inline backend closes all figures automatically or not.
1260 1260
1261 1261 By default, the inline backend used in the IPython Notebook will close all
1262 1262 matplotlib figures automatically after each cell is run. This means that
1263 1263 plots in different cells won't interfere. Sometimes, you may want to make
1264 1264 a plot in one cell and then refine it in later cells. This can be accomplished
1265 1265 by::
1266 1266
1267 1267 In [1]: set_matplotlib_close(False)
1268 1268
1269 1269 To set this in your config files use the following::
1270 1270
1271 1271 c.InlineBackend.close_figures = False
1272 1272
1273 1273 Parameters
1274 1274 ----------
1275 1275 close : bool
1276 1276 Should all matplotlib figures be automatically closed after each cell is
1277 1277 run?
1278 1278 """
1279 1279 warnings.warn(
1280 1280 "`set_matplotlib_close` is deprecated since IPython 7.23, directly "
1281 1281 "use `matplotlib_inline.backend_inline.set_matplotlib_close()`",
1282 1282 DeprecationWarning,
1283 1283 stacklevel=2,
1284 1284 )
1285 1285
1286 1286 from matplotlib_inline.backend_inline import (
1287 1287 set_matplotlib_close as set_matplotlib_close_orig,
1288 1288 )
1289 1289
1290 1290 set_matplotlib_close_orig(close)
@@ -1,425 +1,425 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Pylab (matplotlib) support utilities."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 from io import BytesIO
8 8 from binascii import b2a_base64
9 9 from functools import partial
10 10 import warnings
11 11
12 12 from IPython.core.display import _pngxy
13 13 from IPython.utils.decorators import flag_calls
14 14
15 15 # If user specifies a GUI, that dictates the backend, otherwise we read the
16 16 # user's mpl default from the mpl rc structure
17 17 backends = {
18 18 "tk": "TkAgg",
19 19 "gtk": "GTKAgg",
20 20 "gtk3": "GTK3Agg",
21 21 "gtk4": "GTK4Agg",
22 22 "wx": "WXAgg",
23 23 "qt4": "Qt4Agg",
24 24 "qt5": "Qt5Agg",
25 25 "qt6": "QtAgg",
26 26 "qt": "Qt5Agg",
27 27 "osx": "MacOSX",
28 28 "nbagg": "nbAgg",
29 29 "webagg": "WebAgg",
30 30 "notebook": "nbAgg",
31 31 "agg": "agg",
32 32 "svg": "svg",
33 33 "pdf": "pdf",
34 34 "ps": "ps",
35 35 "inline": "module://matplotlib_inline.backend_inline",
36 36 "ipympl": "module://ipympl.backend_nbagg",
37 37 "widget": "module://ipympl.backend_nbagg",
38 38 }
39 39
40 40 # We also need a reverse backends2guis mapping that will properly choose which
41 41 # GUI support to activate based on the desired matplotlib backend. For the
42 42 # most part it's just a reverse of the above dict, but we also need to add a
43 43 # few others that map to the same GUI manually:
44 44 backend2gui = dict(zip(backends.values(), backends.keys()))
45 45 # In the reverse mapping, there are a few extra valid matplotlib backends that
46 46 # map to the same GUI support
47 47 backend2gui["GTK"] = backend2gui["GTKCairo"] = "gtk"
48 48 backend2gui["GTK3Cairo"] = "gtk3"
49 49 backend2gui["GTK4Cairo"] = "gtk4"
50 50 backend2gui["WX"] = "wx"
51 51 backend2gui["CocoaAgg"] = "osx"
52 52 # There needs to be a hysteresis here as the new QtAgg Matplotlib backend
53 53 # supports either Qt5 or Qt6 and the IPython qt event loop support Qt4, Qt5,
54 54 # and Qt6.
55 55 backend2gui["QtAgg"] = "qt"
56 56 backend2gui["Qt4Agg"] = "qt"
57 57 backend2gui["Qt5Agg"] = "qt"
58 58
59 59 # And some backends that don't need GUI integration
60 60 del backend2gui["nbAgg"]
61 61 del backend2gui["agg"]
62 62 del backend2gui["svg"]
63 63 del backend2gui["pdf"]
64 64 del backend2gui["ps"]
65 65 del backend2gui["module://matplotlib_inline.backend_inline"]
66 66 del backend2gui["module://ipympl.backend_nbagg"]
67 67
68 68 #-----------------------------------------------------------------------------
69 69 # Matplotlib utilities
70 70 #-----------------------------------------------------------------------------
71 71
72 72
73 73 def getfigs(*fig_nums):
74 74 """Get a list of matplotlib figures by figure numbers.
75 75
76 76 If no arguments are given, all available figures are returned. If the
77 77 argument list contains references to invalid figures, a warning is printed
78 78 but the function continues pasting further figures.
79 79
80 80 Parameters
81 81 ----------
82 82 figs : tuple
83 83 A tuple of ints giving the figure numbers of the figures to return.
84 84 """
85 85 from matplotlib._pylab_helpers import Gcf
86 86 if not fig_nums:
87 87 fig_managers = Gcf.get_all_fig_managers()
88 88 return [fm.canvas.figure for fm in fig_managers]
89 89 else:
90 90 figs = []
91 91 for num in fig_nums:
92 92 f = Gcf.figs.get(num)
93 93 if f is None:
94 94 print('Warning: figure %s not available.' % num)
95 95 else:
96 96 figs.append(f.canvas.figure)
97 97 return figs
98 98
99 99
100 100 def figsize(sizex, sizey):
101 101 """Set the default figure size to be [sizex, sizey].
102 102
103 103 This is just an easy to remember, convenience wrapper that sets::
104 104
105 105 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
106 106 """
107 107 import matplotlib
108 108 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
109 109
110 110
111 111 def print_figure(fig, fmt="png", bbox_inches="tight", base64=False, **kwargs):
112 112 """Print a figure to an image, and return the resulting file data
113 113
114 114 Returned data will be bytes unless ``fmt='svg'``,
115 115 in which case it will be unicode.
116 116
117 117 Any keyword args are passed to fig.canvas.print_figure,
118 118 such as ``quality`` or ``bbox_inches``.
119 119
120 120 If `base64` is True, return base64-encoded str instead of raw bytes
121 121 for binary-encoded image formats
122 122
123 123 .. versionadded:: 7.29
124 124 base64 argument
125 125 """
126 126 # When there's an empty figure, we shouldn't return anything, otherwise we
127 127 # get big blank areas in the qt console.
128 128 if not fig.axes and not fig.lines:
129 129 return
130 130
131 131 dpi = fig.dpi
132 132 if fmt == 'retina':
133 133 dpi = dpi * 2
134 134 fmt = 'png'
135 135
136 136 # build keyword args
137 137 kw = {
138 138 "format":fmt,
139 139 "facecolor":fig.get_facecolor(),
140 140 "edgecolor":fig.get_edgecolor(),
141 141 "dpi":dpi,
142 142 "bbox_inches":bbox_inches,
143 143 }
144 144 # **kwargs get higher priority
145 145 kw.update(kwargs)
146 146
147 147 bytes_io = BytesIO()
148 148 if fig.canvas is None:
149 149 from matplotlib.backend_bases import FigureCanvasBase
150 150 FigureCanvasBase(fig)
151 151
152 152 fig.canvas.print_figure(bytes_io, **kw)
153 153 data = bytes_io.getvalue()
154 154 if fmt == 'svg':
155 155 data = data.decode('utf-8')
156 156 elif base64:
157 data = b2a_base64(data).decode("ascii")
157 data = b2a_base64(data, newline=False).decode("ascii")
158 158 return data
159 159
160 160 def retina_figure(fig, base64=False, **kwargs):
161 161 """format a figure as a pixel-doubled (retina) PNG
162 162
163 163 If `base64` is True, return base64-encoded str instead of raw bytes
164 164 for binary-encoded image formats
165 165
166 166 .. versionadded:: 7.29
167 167 base64 argument
168 168 """
169 169 pngdata = print_figure(fig, fmt="retina", base64=False, **kwargs)
170 170 # Make sure that retina_figure acts just like print_figure and returns
171 171 # None when the figure is empty.
172 172 if pngdata is None:
173 173 return
174 174 w, h = _pngxy(pngdata)
175 175 metadata = {"width": w//2, "height":h//2}
176 176 if base64:
177 pngdata = b2a_base64(pngdata).decode("ascii")
177 pngdata = b2a_base64(pngdata, newline=False).decode("ascii")
178 178 return pngdata, metadata
179 179
180 180
181 181 # We need a little factory function here to create the closure where
182 182 # safe_execfile can live.
183 183 def mpl_runner(safe_execfile):
184 184 """Factory to return a matplotlib-enabled runner for %run.
185 185
186 186 Parameters
187 187 ----------
188 188 safe_execfile : function
189 189 This must be a function with the same interface as the
190 190 :meth:`safe_execfile` method of IPython.
191 191
192 192 Returns
193 193 -------
194 194 A function suitable for use as the ``runner`` argument of the %run magic
195 195 function.
196 196 """
197 197
198 198 def mpl_execfile(fname,*where,**kw):
199 199 """matplotlib-aware wrapper around safe_execfile.
200 200
201 201 Its interface is identical to that of the :func:`execfile` builtin.
202 202
203 203 This is ultimately a call to execfile(), but wrapped in safeties to
204 204 properly handle interactive rendering."""
205 205
206 206 import matplotlib
207 207 import matplotlib.pyplot as plt
208 208
209 209 #print '*** Matplotlib runner ***' # dbg
210 210 # turn off rendering until end of script
211 211 is_interactive = matplotlib.rcParams['interactive']
212 212 matplotlib.interactive(False)
213 213 safe_execfile(fname,*where,**kw)
214 214 matplotlib.interactive(is_interactive)
215 215 # make rendering call now, if the user tried to do it
216 216 if plt.draw_if_interactive.called:
217 217 plt.draw()
218 218 plt.draw_if_interactive.called = False
219 219
220 220 # re-draw everything that is stale
221 221 try:
222 222 da = plt.draw_all
223 223 except AttributeError:
224 224 pass
225 225 else:
226 226 da()
227 227
228 228 return mpl_execfile
229 229
230 230
231 231 def _reshow_nbagg_figure(fig):
232 232 """reshow an nbagg figure"""
233 233 try:
234 234 reshow = fig.canvas.manager.reshow
235 235 except AttributeError as e:
236 236 raise NotImplementedError() from e
237 237 else:
238 238 reshow()
239 239
240 240
241 241 def select_figure_formats(shell, formats, **kwargs):
242 242 """Select figure formats for the inline backend.
243 243
244 244 Parameters
245 245 ----------
246 246 shell : InteractiveShell
247 247 The main IPython instance.
248 248 formats : str or set
249 249 One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
250 250 **kwargs : any
251 251 Extra keyword arguments to be passed to fig.canvas.print_figure.
252 252 """
253 253 import matplotlib
254 254 from matplotlib.figure import Figure
255 255
256 256 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
257 257 png_formatter = shell.display_formatter.formatters['image/png']
258 258 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
259 259 pdf_formatter = shell.display_formatter.formatters['application/pdf']
260 260
261 261 if isinstance(formats, str):
262 262 formats = {formats}
263 263 # cast in case of list / tuple
264 264 formats = set(formats)
265 265
266 266 [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]
267 267 mplbackend = matplotlib.get_backend().lower()
268 268 if mplbackend == 'nbagg' or mplbackend == 'module://ipympl.backend_nbagg':
269 269 formatter = shell.display_formatter.ipython_display_formatter
270 270 formatter.for_type(Figure, _reshow_nbagg_figure)
271 271
272 272 supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'}
273 273 bad = formats.difference(supported)
274 274 if bad:
275 275 bs = "%s" % ','.join([repr(f) for f in bad])
276 276 gs = "%s" % ','.join([repr(f) for f in supported])
277 277 raise ValueError("supported formats are: %s not %s" % (gs, bs))
278 278
279 279 if "png" in formats:
280 280 png_formatter.for_type(
281 281 Figure, partial(print_figure, fmt="png", base64=True, **kwargs)
282 282 )
283 283 if "retina" in formats or "png2x" in formats:
284 284 png_formatter.for_type(Figure, partial(retina_figure, base64=True, **kwargs))
285 285 if "jpg" in formats or "jpeg" in formats:
286 286 jpg_formatter.for_type(
287 287 Figure, partial(print_figure, fmt="jpg", base64=True, **kwargs)
288 288 )
289 289 if "svg" in formats:
290 290 svg_formatter.for_type(Figure, partial(print_figure, fmt="svg", **kwargs))
291 291 if "pdf" in formats:
292 292 pdf_formatter.for_type(
293 293 Figure, partial(print_figure, fmt="pdf", base64=True, **kwargs)
294 294 )
295 295
296 296 #-----------------------------------------------------------------------------
297 297 # Code for initializing matplotlib and importing pylab
298 298 #-----------------------------------------------------------------------------
299 299
300 300
301 301 def find_gui_and_backend(gui=None, gui_select=None):
302 302 """Given a gui string return the gui and mpl backend.
303 303
304 304 Parameters
305 305 ----------
306 306 gui : str
307 307 Can be one of ('tk','gtk','wx','qt','qt4','inline','agg').
308 308 gui_select : str
309 309 Can be one of ('tk','gtk','wx','qt','qt4','inline').
310 310 This is any gui already selected by the shell.
311 311
312 312 Returns
313 313 -------
314 314 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
315 315 'WXAgg','Qt4Agg','module://matplotlib_inline.backend_inline','agg').
316 316 """
317 317
318 318 import matplotlib
319 319
320 320 if gui and gui != 'auto':
321 321 # select backend based on requested gui
322 322 backend = backends[gui]
323 323 if gui == 'agg':
324 324 gui = None
325 325 else:
326 326 # We need to read the backend from the original data structure, *not*
327 327 # from mpl.rcParams, since a prior invocation of %matplotlib may have
328 328 # overwritten that.
329 329 # WARNING: this assumes matplotlib 1.1 or newer!!
330 330 backend = matplotlib.rcParamsOrig['backend']
331 331 # In this case, we need to find what the appropriate gui selection call
332 332 # should be for IPython, so we can activate inputhook accordingly
333 333 gui = backend2gui.get(backend, None)
334 334
335 335 # If we have already had a gui active, we need it and inline are the
336 336 # ones allowed.
337 337 if gui_select and gui != gui_select:
338 338 gui = gui_select
339 339 backend = backends[gui]
340 340
341 341 return gui, backend
342 342
343 343
344 344 def activate_matplotlib(backend):
345 345 """Activate the given backend and set interactive to True."""
346 346
347 347 import matplotlib
348 348 matplotlib.interactive(True)
349 349
350 350 # Matplotlib had a bug where even switch_backend could not force
351 351 # the rcParam to update. This needs to be set *before* the module
352 352 # magic of switch_backend().
353 353 matplotlib.rcParams['backend'] = backend
354 354
355 355 # Due to circular imports, pyplot may be only partially initialised
356 356 # when this function runs.
357 357 # So avoid needing matplotlib attribute-lookup to access pyplot.
358 358 from matplotlib import pyplot as plt
359 359
360 360 plt.switch_backend(backend)
361 361
362 362 plt.show._needmain = False
363 363 # We need to detect at runtime whether show() is called by the user.
364 364 # For this, we wrap it into a decorator which adds a 'called' flag.
365 365 plt.draw_if_interactive = flag_calls(plt.draw_if_interactive)
366 366
367 367
368 368 def import_pylab(user_ns, import_all=True):
369 369 """Populate the namespace with pylab-related values.
370 370
371 371 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
372 372
373 373 Also imports a few names from IPython (figsize, display, getfigs)
374 374
375 375 """
376 376
377 377 # Import numpy as np/pyplot as plt are conventions we're trying to
378 378 # somewhat standardize on. Making them available to users by default
379 379 # will greatly help this.
380 380 s = ("import numpy\n"
381 381 "import matplotlib\n"
382 382 "from matplotlib import pylab, mlab, pyplot\n"
383 383 "np = numpy\n"
384 384 "plt = pyplot\n"
385 385 )
386 386 exec(s, user_ns)
387 387
388 388 if import_all:
389 389 s = ("from matplotlib.pylab import *\n"
390 390 "from numpy import *\n")
391 391 exec(s, user_ns)
392 392
393 393 # IPython symbols to add
394 394 user_ns['figsize'] = figsize
395 395 from IPython.display import display
396 396 # Add display and getfigs to the user's namespace
397 397 user_ns['display'] = display
398 398 user_ns['getfigs'] = getfigs
399 399
400 400
401 401 def configure_inline_support(shell, backend):
402 402 """
403 403 .. deprecated:: 7.23
404 404
405 405 use `matplotlib_inline.backend_inline.configure_inline_support()`
406 406
407 407 Configure an IPython shell object for matplotlib use.
408 408
409 409 Parameters
410 410 ----------
411 411 shell : InteractiveShell instance
412 412 backend : matplotlib backend
413 413 """
414 414 warnings.warn(
415 415 "`configure_inline_support` is deprecated since IPython 7.23, directly "
416 416 "use `matplotlib_inline.backend_inline.configure_inline_support()`",
417 417 DeprecationWarning,
418 418 stacklevel=2,
419 419 )
420 420
421 421 from matplotlib_inline.backend_inline import (
422 422 configure_inline_support as configure_inline_support_orig,
423 423 )
424 424
425 425 configure_inline_support_orig(shell, backend)
General Comments 0
You need to be logged in to leave comments. Login now