display.py
1256 lines
| 40.8 KiB
| text/x-python
|
PythonLexer
Brian Granger
|
r3278 | # -*- coding: utf-8 -*- | ||
Min RK
|
r19557 | """Top-level display functions for displaying object in different formats.""" | ||
Brian Granger
|
r3278 | |||
Min RK
|
r19557 | # Copyright (c) IPython Development Team. | ||
# Distributed under the terms of the Modified BSD License. | ||||
Brian Granger
|
r3278 | |||
MinRK
|
r6422 | |||
martinRenou
|
r25612 | from binascii import b2a_base64, hexlify | ||
Pete Blois
|
r26382 | import html | ||
Min RK
|
r19557 | import json | ||
import mimetypes | ||||
MinRK
|
r10050 | import os | ||
MinRK
|
r10803 | import struct | ||
Min RK
|
r19557 | import warnings | ||
M Pacer
|
r23746 | from copy import deepcopy | ||
Alok Singh
|
r25057 | from os.path import splitext | ||
Alok Singh
|
r25061 | from pathlib import Path, PurePath | ||
MinRK
|
r10050 | |||
Srinivas Reddy Thatiparthy
|
r23666 | from IPython.utils.py3compat import cast_unicode | ||
Brian E. Granger
|
r15126 | from IPython.testing.skipdoctest import skip_doctest | ||
Matthias Bussonnier
|
r25632 | from . import display_functions | ||
MinRK
|
r10449 | |||
Matthias Bussonnier
|
r25632 | |||
__all__ = ['display_pretty', 'display_html', 'display_markdown', | ||||
martinRenou
|
r25612 | 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json', | ||
'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject', | ||||
'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON', | ||||
Matthias Bussonnier
|
r25632 | 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats', | ||
'set_matplotlib_close', | ||||
martinRenou
|
r25612 | 'Video'] | ||
Thomas Kluyver
|
r17121 | |||
Matthias Bussonnier
|
r25632 | _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"] | ||
__all__ = __all__ + _deprecated_names | ||||
# ----- warn to import from IPython.display ----- | ||||
from warnings import warn | ||||
def __getattr__(name): | ||||
if name in _deprecated_names: | ||||
warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2) | ||||
return getattr(display_functions, name) | ||||
if name in globals().keys(): | ||||
return globals()[name] | ||||
else: | ||||
raise AttributeError(f"module {__name__} has no attribute {name}") | ||||
MinRK
|
r10449 | |||
Thomas Kluyver
|
r17121 | |||
Brian Granger
|
r3278 | #----------------------------------------------------------------------------- | ||
MinRK
|
r10050 | # utility functions | ||
#----------------------------------------------------------------------------- | ||||
def _safe_exists(path): | ||||
Kyle Kelley
|
r10213 | """Check path, but don't let exceptions raise""" | ||
MinRK
|
r10050 | try: | ||
return os.path.exists(path) | ||||
except Exception: | ||||
return False | ||||
MinRK
|
r10448 | |||
MinRK
|
r10449 | def _display_mimetype(mimetype, objs, raw=False, metadata=None): | ||
"""internal implementation of all display_foo methods | ||||
Parameters | ||||
---------- | ||||
mimetype : str | ||||
The mimetype to be published (e.g. 'image/png') | ||||
luttik
|
r25584 | *objs : object | ||
MinRK
|
r10449 | The Python objects to display, or if raw=True raw text data to | ||
display. | ||||
raw : bool | ||||
Are the data objects raw data or Python objects that need to be | ||||
formatted before display? [default: False] | ||||
metadata : dict (optional) | ||||
MinRK
|
r10450 | Metadata to be associated with the specific mimetype output. | ||
MinRK
|
r10449 | """ | ||
MinRK
|
r10450 | if metadata: | ||
metadata = {mimetype: metadata} | ||||
MinRK
|
r10449 | if raw: | ||
MinRK
|
r10450 | # turn list of pngdata into list of { 'image/png': pngdata } | ||
objs = [ {mimetype: obj} for obj in objs ] | ||||
display(*objs, raw=raw, metadata=metadata, include=[mimetype]) | ||||
MinRK
|
r10050 | #----------------------------------------------------------------------------- | ||
Brian Granger
|
r3278 | # Main functions | ||
#----------------------------------------------------------------------------- | ||||
MinRK
|
r10448 | |||
def display_pretty(*objs, **kwargs): | ||||
"""Display the pretty (default) representation of an object. | ||||
Parameters | ||||
---------- | ||||
luttik
|
r25584 | *objs : object | ||
MinRK
|
r10448 | The Python objects to display, or if raw=True raw text data to | ||
display. | ||||
raw : bool | ||||
Are the data objects raw data or Python objects that need to be | ||||
formatted before display? [default: False] | ||||
metadata : dict (optional) | ||||
MinRK
|
r10450 | Metadata to be associated with the specific mimetype output. | ||
MinRK
|
r10448 | """ | ||
_display_mimetype('text/plain', objs, **kwargs) | ||||
Brian Granger
|
r3279 | |||
Brian E. Granger
|
r4526 | def display_html(*objs, **kwargs): | ||
Brian Granger
|
r3278 | """Display the HTML representation of an object. | ||
Adam Eury
|
r22947 | |||
Peter Waller
|
r21995 | Note: If raw=False and the object does not have a HTML | ||
representation, no HTML will be shown. | ||||
Brian Granger
|
r3278 | |||
Parameters | ||||
---------- | ||||
luttik
|
r25584 | *objs : object | ||
Brian E. Granger
|
r4528 | The Python objects to display, or if raw=True raw HTML data to | ||
Brian E. Granger
|
r4526 | display. | ||
raw : bool | ||||
Are the data objects raw data or Python objects that need to be | ||||
formatted before display? [default: False] | ||||
MinRK
|
r10448 | metadata : dict (optional) | ||
MinRK
|
r10450 | Metadata to be associated with the specific mimetype output. | ||
Brian E. Granger
|
r4526 | """ | ||
MinRK
|
r10448 | _display_mimetype('text/html', objs, **kwargs) | ||
Brian Granger
|
r3278 | |||
Andrew Jesaitis
|
r16364 | def display_markdown(*objs, **kwargs): | ||
"""Displays the Markdown representation of an object. | ||||
Parameters | ||||
---------- | ||||
luttik
|
r25584 | *objs : object | ||
Andrew Jesaitis
|
r16364 | The Python objects to display, or if raw=True raw markdown data to | ||
display. | ||||
raw : bool | ||||
Are the data objects raw data or Python objects that need to be | ||||
formatted before display? [default: False] | ||||
metadata : dict (optional) | ||||
Metadata to be associated with the specific mimetype output. | ||||
""" | ||||
_display_mimetype('text/markdown', objs, **kwargs) | ||||
Brian E. Granger
|
r4526 | def display_svg(*objs, **kwargs): | ||
Brian Granger
|
r3278 | """Display the SVG representation of an object. | ||
Parameters | ||||
---------- | ||||
luttik
|
r25584 | *objs : object | ||
Brian E. Granger
|
r4526 | The Python objects to display, or if raw=True raw svg data to | ||
display. | ||||
raw : bool | ||||
Are the data objects raw data or Python objects that need to be | ||||
formatted before display? [default: False] | ||||
MinRK
|
r10448 | metadata : dict (optional) | ||
MinRK
|
r10450 | Metadata to be associated with the specific mimetype output. | ||
Brian Granger
|
r3278 | """ | ||
MinRK
|
r10448 | _display_mimetype('image/svg+xml', objs, **kwargs) | ||
Brian Granger
|
r3278 | |||
Brian E. Granger
|
r4526 | def display_png(*objs, **kwargs): | ||
Brian Granger
|
r3278 | """Display the PNG representation of an object. | ||
Parameters | ||||
---------- | ||||
luttik
|
r25584 | *objs : object | ||
Brian E. Granger
|
r4526 | The Python objects to display, or if raw=True raw png data to | ||
display. | ||||
raw : bool | ||||
Are the data objects raw data or Python objects that need to be | ||||
formatted before display? [default: False] | ||||
MinRK
|
r10448 | metadata : dict (optional) | ||
MinRK
|
r10450 | Metadata to be associated with the specific mimetype output. | ||
Brian Granger
|
r3278 | """ | ||
MinRK
|
r10448 | _display_mimetype('image/png', objs, **kwargs) | ||
Brian Granger
|
r3278 | |||
Brian E. Granger
|
r4528 | def display_jpeg(*objs, **kwargs): | ||
"""Display the JPEG representation of an object. | ||||
Parameters | ||||
---------- | ||||
luttik
|
r25584 | *objs : object | ||
Brian E. Granger
|
r4528 | The Python objects to display, or if raw=True raw JPEG data to | ||
display. | ||||
raw : bool | ||||
Are the data objects raw data or Python objects that need to be | ||||
formatted before display? [default: False] | ||||
MinRK
|
r10448 | metadata : dict (optional) | ||
MinRK
|
r10450 | Metadata to be associated with the specific mimetype output. | ||
Brian E. Granger
|
r4528 | """ | ||
MinRK
|
r10448 | _display_mimetype('image/jpeg', objs, **kwargs) | ||
Brian E. Granger
|
r4528 | |||
Brian E. Granger
|
r4526 | def display_latex(*objs, **kwargs): | ||
Brian Granger
|
r3278 | """Display the LaTeX representation of an object. | ||
Parameters | ||||
---------- | ||||
luttik
|
r25584 | *objs : object | ||
Brian E. Granger
|
r4526 | The Python objects to display, or if raw=True raw latex data to | ||
display. | ||||
raw : bool | ||||
Are the data objects raw data or Python objects that need to be | ||||
formatted before display? [default: False] | ||||
MinRK
|
r10448 | metadata : dict (optional) | ||
MinRK
|
r10450 | Metadata to be associated with the specific mimetype output. | ||
Brian Granger
|
r3278 | """ | ||
MinRK
|
r10448 | _display_mimetype('text/latex', objs, **kwargs) | ||
Brian Granger
|
r3278 | |||
Brian E. Granger
|
r4526 | def display_json(*objs, **kwargs): | ||
Brian Granger
|
r3278 | """Display the JSON representation of an object. | ||
Kyle Kelley
|
r10212 | |||
Thomas Kluyver
|
r8086 | Note that not many frontends support displaying JSON. | ||
Brian Granger
|
r3278 | |||
Parameters | ||||
---------- | ||||
luttik
|
r25584 | *objs : object | ||
Brian E. Granger
|
r4526 | The Python objects to display, or if raw=True raw json data to | ||
display. | ||||
raw : bool | ||||
Are the data objects raw data or Python objects that need to be | ||||
formatted before display? [default: False] | ||||
MinRK
|
r10448 | metadata : dict (optional) | ||
MinRK
|
r10450 | Metadata to be associated with the specific mimetype output. | ||
Brian Granger
|
r3278 | """ | ||
MinRK
|
r10448 | _display_mimetype('application/json', objs, **kwargs) | ||
Brian Granger
|
r3278 | |||
Brian E. Granger
|
r4526 | def display_javascript(*objs, **kwargs): | ||
Brian Granger
|
r3878 | """Display the Javascript representation of an object. | ||
Brian Granger
|
r3278 | |||
Brian Granger
|
r3878 | Parameters | ||
---------- | ||||
luttik
|
r25584 | *objs : object | ||
Brian E. Granger
|
r4526 | The Python objects to display, or if raw=True raw javascript data to | ||
display. | ||||
raw : bool | ||||
Are the data objects raw data or Python objects that need to be | ||||
formatted before display? [default: False] | ||||
MinRK
|
r10448 | metadata : dict (optional) | ||
MinRK
|
r10450 | Metadata to be associated with the specific mimetype output. | ||
Brian Granger
|
r3878 | """ | ||
MinRK
|
r10448 | _display_mimetype('application/javascript', objs, **kwargs) | ||
Brian E. Granger
|
r4526 | |||
Brian E. Granger
|
r15121 | |||
def display_pdf(*objs, **kwargs): | ||||
"""Display the PDF representation of an object. | ||||
Parameters | ||||
---------- | ||||
luttik
|
r25584 | *objs : object | ||
Brian E. Granger
|
r15121 | The Python objects to display, or if raw=True raw javascript data to | ||
display. | ||||
raw : bool | ||||
Are the data objects raw data or Python objects that need to be | ||||
formatted before display? [default: False] | ||||
metadata : dict (optional) | ||||
Metadata to be associated with the specific mimetype output. | ||||
""" | ||||
_display_mimetype('application/pdf', objs, **kwargs) | ||||
Brian E. Granger
|
r4526 | #----------------------------------------------------------------------------- | ||
# Smart classes | ||||
#----------------------------------------------------------------------------- | ||||
class DisplayObject(object): | ||||
"""An object that wraps data to be displayed.""" | ||||
Brian E. Granger
|
r4528 | _read_flags = 'r' | ||
Jessica B. Hamrick
|
r16396 | _show_mem_addr = False | ||
M Pacer
|
r23746 | metadata = None | ||
Brian E. Granger
|
r4528 | |||
M Pacer
|
r23746 | def __init__(self, data=None, url=None, filename=None, metadata=None): | ||
Brian E. Granger
|
r4528 | """Create a display object given raw data. | ||
Brian E. Granger
|
r4526 | |||
When this object is returned by an expression or passed to the | ||||
display function, it will result in the data being displayed | ||||
in the frontend. The MIME type of the data should match the | ||||
subclasses used, so the Png subclass should be used for 'image/png' | ||||
data. If the data is a URL, the data will first be downloaded | ||||
Bernardo B. Marques
|
r4872 | and then displayed. If | ||
Brian E. Granger
|
r4526 | |||
Parameters | ||||
---------- | ||||
data : unicode, str or bytes | ||||
MinRK
|
r10050 | The raw data or a URL or file to load the data from | ||
Brian E. Granger
|
r4528 | url : unicode | ||
A URL to download the data from. | ||||
filename : unicode | ||||
Path to a local file to load the data from. | ||||
M Pacer
|
r23746 | metadata : dict | ||
Dict of metadata associated to be the object when displayed | ||||
Brian E. Granger
|
r4526 | """ | ||
Alok Singh
|
r25061 | if isinstance(data, (Path, PurePath)): | ||
data = str(data) | ||||
Srinivas Reddy Thatiparthy
|
r23037 | if data is not None and isinstance(data, str): | ||
MinRK
|
r10050 | if data.startswith('http') and url is None: | ||
url = data | ||||
filename = None | ||||
data = None | ||||
elif _safe_exists(data) and filename is None: | ||||
url = None | ||||
filename = data | ||||
data = None | ||||
Kyle Kelley
|
r10212 | |||
MinRK
|
r10050 | self.url = url | ||
Srinivas Reddy Thatiparthy
|
r23044 | self.filename = filename | ||
Ian Castleden
|
r25614 | # because of @data.setter methods in | ||
# subclasses ensure url and filename are set | ||||
# before assigning to self.data | ||||
self.data = data | ||||
Kyle Kelley
|
r10212 | |||
M Pacer
|
r23746 | if metadata is not None: | ||
self.metadata = metadata | ||||
elif self.metadata is None: | ||||
self.metadata = {} | ||||
Brian E. Granger
|
r4528 | self.reload() | ||
MinRK
|
r14153 | self._check_data() | ||
Jessica B. Hamrick
|
r16396 | |||
def __repr__(self): | ||||
if not self._show_mem_addr: | ||||
cls = self.__class__ | ||||
r = "<%s.%s object>" % (cls.__module__, cls.__name__) | ||||
else: | ||||
r = super(DisplayObject, self).__repr__() | ||||
return r | ||||
MinRK
|
r14153 | def _check_data(self): | ||
"""Override in subclasses if there's something to check.""" | ||||
pass | ||||
Brian E. Granger
|
r4528 | |||
M Pacer
|
r23746 | def _data_and_metadata(self): | ||
"""shortcut for returning metadata with shape information, if defined""" | ||||
if self.metadata: | ||||
return self.data, deepcopy(self.metadata) | ||||
else: | ||||
return self.data | ||||
Brian E. Granger
|
r4528 | def reload(self): | ||
"""Reload the raw data from file or URL.""" | ||||
if self.filename is not None: | ||||
with open(self.filename, self._read_flags) as f: | ||||
self.data = f.read() | ||||
elif self.url is not None: | ||||
Ian Castleden
|
r25614 | # Deferred import | ||
from urllib.request import urlopen | ||||
response = urlopen(self.url) | ||||
data = response.read() | ||||
# extract encoding from header, if there is one: | ||||
encoding = None | ||||
Ian Castleden
|
r25615 | if 'content-type' in response.headers: | ||
MinRK
|
r4684 | for sub in response.headers['content-type'].split(';'): | ||
sub = sub.strip() | ||||
if sub.startswith('charset'): | ||||
encoding = sub.split('=')[-1].strip() | ||||
break | ||||
Ian Castleden
|
r25615 | if 'content-encoding' in response.headers: | ||
# TODO: do deflate? | ||||
if 'gzip' in response.headers['content-encoding']: | ||||
import gzip | ||||
from io import BytesIO | ||||
with gzip.open(BytesIO(data), 'rt', encoding=encoding) as fp: | ||||
encoding = None | ||||
data = fp.read() | ||||
Pete Blois
|
r26382 | |||
Ian Castleden
|
r25614 | # decode data, if an encoding was specified | ||
# We only touch self.data once since | ||||
# subclasses such as SVG have @data.setter methods | ||||
# that transform self.data into ... well svg. | ||||
if encoding: | ||||
self.data = data.decode(encoding, 'replace') | ||||
else: | ||||
self.data = data | ||||
Brian E. Granger
|
r4526 | |||
MinRK
|
r14153 | class TextDisplayObject(DisplayObject): | ||
"""Validate that display data is text""" | ||||
def _check_data(self): | ||||
Srinivas Reddy Thatiparthy
|
r23037 | if self.data is not None and not isinstance(self.data, str): | ||
MinRK
|
r14159 | raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data)) | ||
MinRK
|
r14153 | |||
class Pretty(TextDisplayObject): | ||||
Brian E. Granger
|
r4526 | |||
ryan thielke
|
r23729 | def _repr_pretty_(self, pp, cycle): | ||
return pp.text(self.data) | ||||
Brian E. Granger
|
r4526 | |||
MinRK
|
r14153 | class HTML(TextDisplayObject): | ||
Brian E. Granger
|
r4526 | |||
Michael Penkov
|
r24619 | def __init__(self, data=None, url=None, filename=None, metadata=None): | ||
Michael Penkov
|
r24654 | def warn(): | ||
if not data: | ||||
return False | ||||
# | ||||
# Avoid calling lower() on the entire data, because it could be a | ||||
# long string and we're only interested in its beginning and end. | ||||
# | ||||
prefix = data[:10].lower() | ||||
suffix = data[-10:].lower() | ||||
return prefix.startswith("<iframe ") and suffix.endswith("</iframe>") | ||||
if warn(): | ||||
Michael Penkov
|
r24619 | warnings.warn("Consider using IPython.display.IFrame instead") | ||
super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata) | ||||
Brian E. Granger
|
r4526 | def _repr_html_(self): | ||
Thomas Kluyver
|
r24301 | return self._data_and_metadata() | ||
Brian E. Granger
|
r4526 | |||
Dan McDougall
|
r10490 | def __html__(self): | ||
""" | ||||
This method exists to inform other HTML-using modules (e.g. Markupsafe, | ||||
htmltag, etc) that this object is HTML and does not need things like | ||||
special characters (<>&) escaped. | ||||
""" | ||||
return self._repr_html_() | ||||
Brian E. Granger
|
r4526 | |||
Andrew Jesaitis
|
r16364 | class Markdown(TextDisplayObject): | ||
def _repr_markdown_(self): | ||||
Thomas Kluyver
|
r24301 | return self._data_and_metadata() | ||
Andrew Jesaitis
|
r16364 | |||
MinRK
|
r14153 | class Math(TextDisplayObject): | ||
Brian E. Granger
|
r4526 | |||
def _repr_latex_(self): | ||||
Matthias Bussonnier
|
r24780 | s = r"$\displaystyle %s$" % self.data.strip('$') | ||
Thomas Kluyver
|
r24301 | if self.metadata: | ||
return s, deepcopy(self.metadata) | ||||
else: | ||||
return s | ||||
Brian Granger
|
r6065 | |||
MinRK
|
r14153 | class Latex(TextDisplayObject): | ||
Brian Granger
|
r6065 | |||
def _repr_latex_(self): | ||||
Thomas Kluyver
|
r24301 | return self._data_and_metadata() | ||
Brian E. Granger
|
r4526 | |||
Brian E. Granger
|
r4528 | class SVG(DisplayObject): | ||
Ian Castleden
|
r25616 | """Embed an SVG into the display. | ||
Note if you just want to view a svg image via a URL use `:class:Image` with | ||||
a url=URL keyword argument. | ||||
""" | ||||
Kyle Kelley
|
r10212 | |||
Srinivas Reddy Thatiparthy
|
r23026 | _read_flags = 'rb' | ||
MinRK
|
r5671 | # wrap data in a property, which extracts the <svg> tag, discarding | ||
# document headers | ||||
_data = None | ||||
Kyle Kelley
|
r10212 | |||
MinRK
|
r5671 | @property | ||
def data(self): | ||||
return self._data | ||||
Kyle Kelley
|
r10212 | |||
MinRK
|
r5671 | @data.setter | ||
def data(self, svg): | ||||
if svg is None: | ||||
self._data = None | ||||
return | ||||
# parse into dom object | ||||
Thomas Kluyver
|
r9390 | from xml.dom import minidom | ||
MinRK
|
r5671 | x = minidom.parseString(svg) | ||
# get svg tag (should be 1) | ||||
found_svg = x.getElementsByTagName('svg') | ||||
if found_svg: | ||||
svg = found_svg[0].toxml() | ||||
else: | ||||
# fallback on the input, trust the user | ||||
# but this is probably an error. | ||||
pass | ||||
MinRK
|
r11535 | svg = cast_unicode(svg) | ||
MinRK
|
r5671 | self._data = svg | ||
martinRenou
|
r25612 | |||
Brian E. Granger
|
r4526 | def _repr_svg_(self): | ||
M Pacer
|
r23746 | return self._data_and_metadata() | ||
Brian E. Granger
|
r4526 | |||
Marius van Niekerk
|
r23856 | class ProgressBar(DisplayObject): | ||
martinRenou
|
r25612 | """Progressbar supports displaying a progressbar like element | ||
Marius van Niekerk
|
r23854 | """ | ||
def __init__(self, total): | ||||
"""Creates a new progressbar | ||||
martinRenou
|
r25612 | |||
Marius van Niekerk
|
r23854 | Parameters | ||
---------- | ||||
total : int | ||||
Marius van Niekerk
|
r23859 | maximum size of the progressbar | ||
Marius van Niekerk
|
r23854 | """ | ||
self.total = total | ||||
self._progress = 0 | ||||
Marius van Niekerk
|
r23860 | self.html_width = '60ex' | ||
self.text_width = 60 | ||||
self._display_id = hexlify(os.urandom(8)).decode('ascii') | ||||
def __repr__(self): | ||||
fraction = self.progress / self.total | ||||
filled = '=' * int(fraction * self.text_width) | ||||
rest = ' ' * (self.text_width - len(filled)) | ||||
return '[{}{}] {}/{}'.format( | ||||
filled, rest, | ||||
self.progress, self.total, | ||||
) | ||||
Marius van Niekerk
|
r23854 | |||
def _repr_html_(self): | ||||
Marius van Niekerk
|
r23860 | return "<progress style='width:{}' max='{}' value='{}'></progress>".format( | ||
self.html_width, self.total, self.progress) | ||||
Marius van Niekerk
|
r23854 | |||
def display(self): | ||||
display(self, display_id=self._display_id) | ||||
def update(self): | ||||
display(self, display_id=self._display_id, update=True) | ||||
@property | ||||
def progress(self): | ||||
return self._progress | ||||
@progress.setter | ||||
def progress(self, value): | ||||
self._progress = value | ||||
self.update() | ||||
Brian E. Granger
|
r4526 | |||
Henry Fredrick Schreiner
|
r23943 | def __iter__(self): | ||
self.display() | ||||
self._progress = -1 # First iteration is 0 | ||||
return self | ||||
def __next__(self): | ||||
"""Returns current value and increments display by one.""" | ||||
self.progress += 1 | ||||
if self.progress < self.total: | ||||
return self.progress | ||||
else: | ||||
raise StopIteration() | ||||
Min RK
|
r19557 | class JSON(DisplayObject): | ||
"""JSON expects a JSON-able dict or list | ||||
Adam Eury
|
r22947 | |||
Min RK
|
r19557 | not an already-serialized JSON string. | ||
Adam Eury
|
r22947 | |||
Min RK
|
r19557 | Scalar types (None, number, string) are not allowed, only dict or list containers. | ||
""" | ||||
# wrap data in a property, which warns about passing already-serialized JSON | ||||
_data = None | ||||
Dave Hirschfeld
|
r24412 | def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs): | ||
Adam Eury
|
r22947 | """Create a JSON display object given raw data. | ||
Parameters | ||||
---------- | ||||
data : dict or list | ||||
JSON data to display. Not an already-serialized JSON string. | ||||
Scalar types (None, number, string) are not allowed, only dict | ||||
or list containers. | ||||
url : unicode | ||||
A URL to download the data from. | ||||
filename : unicode | ||||
Path to a local file to load the data from. | ||||
expanded : boolean | ||||
Metadata to control whether a JSON display component is expanded. | ||||
Matthias Bussonnier
|
r26294 | metadata : dict | ||
Adam Eury
|
r22947 | Specify extra metadata to attach to the json display object. | ||
Dave Hirschfeld
|
r24412 | root : str | ||
martinRenou
|
r25612 | The name of the root element of the JSON tree | ||
Adam Eury
|
r22947 | """ | ||
Dave Hirschfeld
|
r24412 | self.metadata = { | ||
'expanded': expanded, | ||||
'root': root, | ||||
} | ||||
Grant Nestor
|
r23501 | if metadata: | ||
self.metadata.update(metadata) | ||||
Grant Nestor
|
r23503 | if kwargs: | ||
self.metadata.update(kwargs) | ||||
Adam Eury
|
r22947 | super(JSON, self).__init__(data=data, url=url, filename=filename) | ||
Min RK
|
r19557 | def _check_data(self): | ||
if self.data is not None and not isinstance(self.data, (dict, list)): | ||||
raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data)) | ||||
@property | ||||
def data(self): | ||||
return self._data | ||||
Adam Eury
|
r22947 | |||
Min RK
|
r19557 | @data.setter | ||
def data(self, data): | ||||
Alok Singh
|
r25061 | if isinstance(data, (Path, PurePath)): | ||
data = str(data) | ||||
Srinivas Reddy Thatiparthy
|
r23037 | if isinstance(data, str): | ||
Ian Castleden
|
r25614 | if self.filename is None and self.url is None: | ||
Grant Nestor
|
r23393 | warnings.warn("JSON expects JSONable dict or list, not JSON strings") | ||
Min RK
|
r19557 | data = json.loads(data) | ||
self._data = data | ||||
Brian E. Granger
|
r4526 | |||
Adam Eury
|
r22947 | def _data_and_metadata(self): | ||
Grant Nestor
|
r23501 | return self.data, self.metadata | ||
Adam Eury
|
r22947 | |||
Brian E. Granger
|
r4526 | def _repr_json_(self): | ||
Adam Eury
|
r22947 | return self._data_and_metadata() | ||
Brian E. Granger
|
r4526 | |||
Grant Nestor
|
r24470 | _css_t = """var link = document.createElement("link"); | ||
link.ref = "stylesheet"; | ||||
link.type = "text/css"; | ||||
link.href = "%s"; | ||||
document.head.appendChild(link); | ||||
Brian Granger
|
r6120 | """ | ||
Grant Nestor
|
r24479 | _lib_t1 = """new Promise(function(resolve, reject) { | ||
var script = document.createElement("script"); | ||||
script.onload = resolve; | ||||
script.onerror = reject; | ||||
Grant Nestor
|
r24470 | script.src = "%s"; | ||
Grant Nestor
|
r24479 | document.head.appendChild(script); | ||
}).then(() => { | ||||
Brian Granger
|
r6120 | """ | ||
Brian E. Granger
|
r4526 | |||
Grant Nestor
|
r24479 | _lib_t2 = """ | ||
});""" | ||||
Grant Nestor
|
r24470 | |||
Grant Nestor
|
r23501 | class GeoJSON(JSON): | ||
Grant Nestor
|
r23404 | """GeoJSON expects JSON-able dict | ||
Grant Nestor
|
r23312 | |||
Grant Nestor
|
r23404 | not an already-serialized JSON string. | ||
Scalar types (None, number, string) are not allowed, only dict containers. | ||||
""" | ||||
martinRenou
|
r25612 | |||
Grant Nestor
|
r23501 | def __init__(self, *args, **kwargs): | ||
Grant Nestor
|
r23404 | """Create a GeoJSON display object given raw data. | ||
Parameters | ||||
---------- | ||||
data : dict or list | ||||
VegaLite data. Not an already-serialized JSON string. | ||||
Scalar types (None, number, string) are not allowed, only dict | ||||
or list containers. | ||||
url_template : string | ||||
Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template | ||||
layer_options : dict | ||||
Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options | ||||
url : unicode | ||||
A URL to download the data from. | ||||
filename : unicode | ||||
Path to a local file to load the data from. | ||||
Matthias Bussonnier
|
r26294 | metadata : dict | ||
Grant Nestor
|
r23404 | Specify extra metadata to attach to the json display object. | ||
Matthias Bussonnier
|
r23407 | |||
Examples | ||||
-------- | ||||
The following will display an interactive map of Mars with a point of | ||||
interest on frontend that do support GeoJSON display. | ||||
>>> from IPython.display import GeoJSON | ||||
>>> GeoJSON(data={ | ||||
... "type": "Feature", | ||||
... "geometry": { | ||||
... "type": "Point", | ||||
... "coordinates": [-81.327, 296.038] | ||||
... } | ||||
... }, | ||||
... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png", | ||||
... layer_options={ | ||||
... "basemap_id": "celestia_mars-shaded-16k_global", | ||||
... "attribution" : "Celestia/praesepe", | ||||
... "minZoom" : 0, | ||||
... "maxZoom" : 18, | ||||
... }) | ||||
<IPython.core.display.GeoJSON object> | ||||
In the terminal IPython, you will only see the text representation of | ||||
the GeoJSON object. | ||||
Grant Nestor
|
r23404 | """ | ||
martinRenou
|
r25612 | |||
Grant Nestor
|
r23501 | super(GeoJSON, self).__init__(*args, **kwargs) | ||
Grant Nestor
|
r23312 | |||
def _ipython_display_(self): | ||||
bundle = { | ||||
'application/geo+json': self.data, | ||||
Grant Nestor
|
r23501 | 'text/plain': '<IPython.display.GeoJSON object>' | ||
Grant Nestor
|
r23312 | } | ||
Grant Nestor
|
r23404 | metadata = { | ||
Grant Nestor
|
r23501 | 'application/geo+json': self.metadata | ||
Grant Nestor
|
r23404 | } | ||
display(bundle, metadata=metadata, raw=True) | ||||
Grant Nestor
|
r23312 | |||
MinRK
|
r14153 | class Javascript(TextDisplayObject): | ||
Brian E. Granger
|
r4526 | |||
Brian Granger
|
r6120 | def __init__(self, data=None, url=None, filename=None, lib=None, css=None): | ||
"""Create a Javascript display object given raw data. | ||||
When this object is returned by an expression or passed to the | ||||
display function, it will result in the data being displayed | ||||
in the frontend. If the data is a URL, the data will first be | ||||
Kyle Kelley
|
r10212 | downloaded and then displayed. | ||
MinRK
|
r6425 | In the Notebook, the containing element will be available as `element`, | ||
Andrew Payne
|
r17002 | and jQuery will be available. Content appended to `element` will be | ||
Adam Eury
|
r22947 | visible in the output area. | ||
Brian Granger
|
r6120 | |||
Parameters | ||||
---------- | ||||
data : unicode, str or bytes | ||||
The Javascript source code or a URL to download it from. | ||||
url : unicode | ||||
A URL to download the data from. | ||||
filename : unicode | ||||
Path to a local file to load the data from. | ||||
lib : list or str | ||||
A sequence of Javascript library URLs to load asynchronously before | ||||
running the source code. The full URLs of the libraries should | ||||
be given. A single Javascript library URL can also be given as a | ||||
string. | ||||
Matthias Bussonnier
|
r26294 | css : list or str | ||
Brian Granger
|
r6120 | A sequence of css files to load before running the source code. | ||
Kyle Kelley
|
r10212 | The full URLs of the css files should be given. A single css URL | ||
Brian Granger
|
r6120 | can also be given as a string. | ||
""" | ||||
Srinivas Reddy Thatiparthy
|
r23037 | if isinstance(lib, str): | ||
Brian Granger
|
r6120 | lib = [lib] | ||
elif lib is None: | ||||
lib = [] | ||||
Srinivas Reddy Thatiparthy
|
r23037 | if isinstance(css, str): | ||
Brian Granger
|
r6120 | css = [css] | ||
elif css is None: | ||||
css = [] | ||||
if not isinstance(lib, (list,tuple)): | ||||
raise TypeError('expected sequence, got: %r' % lib) | ||||
if not isinstance(css, (list,tuple)): | ||||
raise TypeError('expected sequence, got: %r' % css) | ||||
self.lib = lib | ||||
self.css = css | ||||
Grant Nestor
|
r23816 | super(Javascript, self).__init__(data=data, url=url, filename=filename) | ||
Brian Granger
|
r6120 | |||
Brian E. Granger
|
r4526 | def _repr_javascript_(self): | ||
Brian Granger
|
r6120 | r = '' | ||
for c in self.css: | ||||
Matthias Bussonnier
|
r23524 | r += _css_t % c | ||
Brian Granger
|
r6120 | for l in self.lib: | ||
Matthias Bussonnier
|
r23524 | r += _lib_t1 % l | ||
Brian Granger
|
r6120 | r += self.data | ||
Matthias Bussonnier
|
r23524 | r += _lib_t2*len(self.lib) | ||
Brian Granger
|
r6120 | return r | ||
Brian E. Granger
|
r4526 | |||
MinRK
|
r10049 | # constants for identifying png/jpeg data | ||
_PNG = b'\x89PNG\r\n\x1a\n' | ||||
_JPEG = b'\xff\xd8' | ||||
Brian E. Granger
|
r4526 | |||
MinRK
|
r10803 | def _pngxy(data): | ||
"""read the (width, height) from a PNG header""" | ||||
ihdr = data.index(b'IHDR') | ||||
# next 8 bytes are width/height | ||||
Grant Nestor
|
r23821 | return struct.unpack('>ii', data[ihdr+4:ihdr+12]) | ||
MinRK
|
r10803 | |||
def _jpegxy(data): | ||||
"""read the (width, height) from a JPEG header""" | ||||
# adapted from http://www.64lines.com/jpeg-width-height | ||||
Adam Eury
|
r22947 | |||
MinRK
|
r10803 | idx = 4 | ||
while True: | ||||
block_size = struct.unpack('>H', data[idx:idx+2])[0] | ||||
idx = idx + block_size | ||||
if data[idx:idx+2] == b'\xFF\xC0': | ||||
# found Start of Frame | ||||
iSOF = idx | ||||
break | ||||
else: | ||||
# read another block | ||||
idx += 2 | ||||
Min RK
|
r23851 | h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9]) | ||
return w, h | ||||
Min RK
|
r23849 | |||
Grant Nestor
|
r23821 | def _gifxy(data): | ||
"""read the (width, height) from a GIF header""" | ||||
return struct.unpack('<HH', data[6:10]) | ||||
MinRK
|
r10803 | |||
Min RK
|
r23849 | |||
Brian E. Granger
|
r4528 | class Image(DisplayObject): | ||
_read_flags = 'rb' | ||||
Jerry Fowler
|
r8077 | _FMT_JPEG = u'jpeg' | ||
_FMT_PNG = u'png' | ||||
Grant Nestor
|
r23814 | _FMT_GIF = u'gif' | ||
_ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF] | ||||
Min RK
|
r23849 | _MIMETYPES = { | ||
_FMT_PNG: 'image/png', | ||||
_FMT_JPEG: 'image/jpeg', | ||||
_FMT_GIF: 'image/gif', | ||||
} | ||||
Brian E. Granger
|
r4528 | |||
Pete Blois
|
r26410 | def __init__( | ||
self, | ||||
data=None, | ||||
url=None, | ||||
filename=None, | ||||
format=None, | ||||
embed=None, | ||||
width=None, | ||||
height=None, | ||||
retina=False, | ||||
unconfined=False, | ||||
metadata=None, | ||||
alt=None, | ||||
): | ||||
Grant Nestor
|
r23814 | """Create a PNG/JPEG/GIF image object given raw data. | ||
Brian E. Granger
|
r4528 | |||
Andrew Mark
|
r12557 | When this object is returned by an input cell or passed to the | ||
Brian E. Granger
|
r4528 | display function, it will result in the image being displayed | ||
in the frontend. | ||||
Parameters | ||||
---------- | ||||
data : unicode, str or bytes | ||||
MinRK
|
r10619 | The raw image data or a URL or filename to load the data from. | ||
This always results in embedded image data. | ||||
Brian E. Granger
|
r4528 | url : unicode | ||
MinRK
|
r10619 | A URL to download the data from. If you specify `url=`, | ||
the image data will not be embedded unless you also specify `embed=True`. | ||||
Brian E. Granger
|
r4528 | filename : unicode | ||
Path to a local file to load the data from. | ||||
MinRK
|
r10619 | Images from a file are always embedded. | ||
Brian E. Granger
|
r4528 | format : unicode | ||
Grant Nestor
|
r23814 | The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given | ||
Brian E. Granger
|
r4528 | for format will be inferred from the filename extension. | ||
embed : bool | ||||
Matthias BUSSONNIER
|
r6529 | Should the image data be embedded using a data URI (True) or be | ||
loaded using an <img> tag. Set this to True if you want the image | ||||
to be viewable later with no internet connection in the notebook. | ||||
Default is `True`, unless the keyword argument `url` is set, then | ||||
default value is `False`. | ||||
Note that QtConsole is not able to display images if `embed` is set to `False` | ||||
Jerry Fowler
|
r8077 | width : int | ||
nvdv
|
r22394 | Width in pixels to which to constrain the image in html | ||
Jerry Fowler
|
r8077 | height : int | ||
nvdv
|
r22394 | Height in pixels to which to constrain the image in html | ||
MinRK
|
r10803 | retina : bool | ||
Automatically set the width and height to half of the measured | ||||
width and height. | ||||
This only works for embedded images because it reads the width/height | ||||
from image data. | ||||
For non-embedded images, you can just set the desired display width | ||||
and height directly. | ||||
Matthias Bussonnier
|
r26294 | unconfined : bool | ||
Min RK
|
r20986 | Set unconfined=True to disable max-width confinement of the image. | ||
Matthias Bussonnier
|
r26294 | metadata : dict | ||
Min RK
|
r20986 | Specify extra metadata to attach to the image. | ||
Pete Blois
|
r26382 | alt : unicode | ||
Alternative text for the image, for use by screen readers. | ||||
Matthias BUSSONNIER
|
r6529 | |||
Examples | ||||
-------- | ||||
Matthias Bussonnier
|
r26292 | embedded image data, works in qtconsole and notebook | ||
when passed positionally, the first arg can be any of raw image data, | ||||
a URL, or a filename from which to load image data. | ||||
The result is always embedding image data for inline images. | ||||
>>> Image('http://www.google.fr/images/srpr/logo3w.png') | ||||
<IPython.core.display.Image object> | ||||
>>> Image('/path/to/image.jpg') | ||||
<IPython.core.display.Image object> | ||||
>>> Image(b'RAW_PNG_DATA...') | ||||
<IPython.core.display.Image object> | ||||
Specifying Image(url=...) does not embed the image data, | ||||
it only generates ``<img>`` tag with a link to the source. | ||||
This will not work in the qtconsole or offline. | ||||
>>> Image(url='http://www.google.fr/images/srpr/logo3w.png') | ||||
Matthias Bussonnier
|
r26294 | <IPython.core.display.Image object> | ||
Matthias BUSSONNIER
|
r6529 | |||
Brian E. Granger
|
r4528 | """ | ||
Alok Singh
|
r25061 | if isinstance(data, (Path, PurePath)): | ||
data = str(data) | ||||
Brian E. Granger
|
r4528 | if filename is not None: | ||
ext = self._find_ext(filename) | ||||
elif url is not None: | ||||
ext = self._find_ext(url) | ||||
Jerry Fowler
|
r8077 | elif data is None: | ||
raise ValueError("No image data found. Expecting filename, url, or data.") | ||||
Srinivas Reddy Thatiparthy
|
r23037 | elif isinstance(data, str) and ( | ||
MinRK
|
r10050 | data.startswith('http') or _safe_exists(data) | ||
): | ||||
Brian E. Granger
|
r4528 | ext = self._find_ext(data) | ||
else: | ||||
ext = None | ||||
Jerry Fowler
|
r8077 | |||
Min RK
|
r21818 | if format is None: | ||
if ext is not None: | ||||
if ext == u'jpg' or ext == u'jpeg': | ||||
format = self._FMT_JPEG | ||||
Thomas Kluyver
|
r23944 | elif ext == u'png': | ||
Min RK
|
r21818 | format = self._FMT_PNG | ||
Thomas Kluyver
|
r23944 | elif ext == u'gif': | ||
Grant Nestor
|
r23818 | format = self._FMT_GIF | ||
Min RK
|
r21818 | else: | ||
format = ext.lower() | ||||
elif isinstance(data, bytes): | ||||
# infer image type from image data header, | ||||
# only if format has not been specified. | ||||
if data[:2] == _JPEG: | ||||
format = self._FMT_JPEG | ||||
# failed to detect format, default png | ||||
if format is None: | ||||
Grant Nestor
|
r23814 | format = self._FMT_PNG | ||
Jerry Fowler
|
r8077 | |||
Matthias Bussonnier
|
r22013 | if format.lower() == 'jpg': | ||
# jpg->jpeg | ||||
format = self._FMT_JPEG | ||||
Min RK
|
r23849 | |||
Srinivas Reddy Thatiparthy
|
r23044 | self.format = format.lower() | ||
Matthias BUSSONNIER
|
r6529 | self.embed = embed if embed is not None else (url is None) | ||
Jerry Fowler
|
r8077 | |||
if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS: | ||||
raise ValueError("Cannot embed the '%s' image format" % (self.format)) | ||||
Min RK
|
r23849 | if self.embed: | ||
self._mimetype = self._MIMETYPES.get(self.format) | ||||
Jerry Fowler
|
r8077 | self.width = width | ||
self.height = height | ||||
MinRK
|
r10803 | self.retina = retina | ||
Min RK
|
r20986 | self.unconfined = unconfined | ||
Pete Blois
|
r26382 | self.alt = alt | ||
martinRenou
|
r25612 | super(Image, self).__init__(data=data, url=url, filename=filename, | ||
M Pacer
|
r23746 | metadata=metadata) | ||
if self.width is None and self.metadata.get('width', {}): | ||||
self.width = metadata['width'] | ||||
if self.height is None and self.metadata.get('height', {}): | ||||
self.height = metadata['height'] | ||||
Adam Eury
|
r22947 | |||
Pete Blois
|
r26410 | if self.alt is None and self.metadata.get("alt", {}): | ||
self.alt = metadata["alt"] | ||||
Pete Blois
|
r26382 | |||
MinRK
|
r10803 | if retina: | ||
self._retina_shape() | ||||
Adam Eury
|
r22947 | |||
Min RK
|
r23849 | |||
MinRK
|
r10803 | def _retina_shape(self): | ||
"""load pixel-doubled width and height from image data""" | ||||
if not self.embed: | ||||
return | ||||
Grant Nestor
|
r23814 | if self.format == self._FMT_PNG: | ||
MinRK
|
r10803 | w, h = _pngxy(self.data) | ||
Grant Nestor
|
r23814 | elif self.format == self._FMT_JPEG: | ||
MinRK
|
r10803 | w, h = _jpegxy(self.data) | ||
Grant Nestor
|
r23818 | elif self.format == self._FMT_GIF: | ||
Grant Nestor
|
r23821 | w, h = _gifxy(self.data) | ||
MinRK
|
r10803 | else: | ||
# retina only supports png | ||||
return | ||||
self.width = w // 2 | ||||
self.height = h // 2 | ||||
Brian E. Granger
|
r4528 | |||
def reload(self): | ||||
"""Reload the raw data from file or URL.""" | ||||
if self.embed: | ||||
super(Image,self).reload() | ||||
MinRK
|
r10803 | if self.retina: | ||
self._retina_shape() | ||||
Brian E. Granger
|
r4528 | |||
def _repr_html_(self): | ||||
if not self.embed: | ||||
Pete Blois
|
r26410 | width = height = klass = alt = "" | ||
Jerry Fowler
|
r8077 | if self.width: | ||
width = ' width="%d"' % self.width | ||||
if self.height: | ||||
height = ' height="%d"' % self.height | ||||
Min RK
|
r20986 | if self.unconfined: | ||
klass = ' class="unconfined"' | ||||
Pete Blois
|
r26382 | if self.alt: | ||
alt = ' alt="%s"' % html.escape(self.alt) | ||||
Pete Blois
|
r26410 | return '<img src="{url}"{width}{height}{klass}{alt}/>'.format( | ||
Min RK
|
r20986 | url=self.url, | ||
width=width, | ||||
height=height, | ||||
klass=klass, | ||||
Pete Blois
|
r26382 | alt=alt, | ||
Min RK
|
r20986 | ) | ||
Dan McDougall
|
r10490 | |||
Min RK
|
r23849 | def _repr_mimebundle_(self, include=None, exclude=None): | ||
"""Return the image as a mimebundle | ||||
Any new mimetype support should be implemented here. | ||||
""" | ||||
if self.embed: | ||||
mimetype = self._mimetype | ||||
data, metadata = self._data_and_metadata(always_both=True) | ||||
if metadata: | ||||
metadata = {mimetype: metadata} | ||||
return {mimetype: data}, metadata | ||||
else: | ||||
return {'text/html': self._repr_html_()} | ||||
def _data_and_metadata(self, always_both=False): | ||||
MinRK
|
r10444 | """shortcut for returning metadata with shape information, if defined""" | ||
Nick Tallant
|
r24989 | try: | ||
b64_data = b2a_base64(self.data).decode('ascii') | ||||
Ram Rachum
|
r25833 | except TypeError as e: | ||
Nick Tallant
|
r24989 | raise FileNotFoundError( | ||
Ram Rachum
|
r25833 | "No such file or directory: '%s'" % (self.data)) from e | ||
MinRK
|
r10444 | md = {} | ||
M Pacer
|
r23746 | if self.metadata: | ||
md.update(self.metadata) | ||||
MinRK
|
r10444 | if self.width: | ||
md['width'] = self.width | ||||
if self.height: | ||||
md['height'] = self.height | ||||
Min RK
|
r20986 | if self.unconfined: | ||
md['unconfined'] = self.unconfined | ||||
Pete Blois
|
r26382 | if self.alt: | ||
Pete Blois
|
r26410 | md["alt"] = self.alt | ||
Min RK
|
r23849 | if md or always_both: | ||
Min RK
|
r23835 | return b64_data, md | ||
MinRK
|
r10444 | else: | ||
Min RK
|
r23835 | return b64_data | ||
Brian E. Granger
|
r4528 | |||
def _repr_png_(self): | ||||
Nick Tallant
|
r24990 | if self.embed and self.format == self._FMT_PNG: | ||
return self._data_and_metadata() | ||||
Brian E. Granger
|
r4528 | |||
def _repr_jpeg_(self): | ||||
Nick Tallant
|
r24990 | if self.embed and self.format == self._FMT_JPEG: | ||
return self._data_and_metadata() | ||||
Brian E. Granger
|
r4528 | |||
def _find_ext(self, s): | ||||
Alok Singh
|
r25057 | base, ext = splitext(s) | ||
if not ext: | ||||
return base | ||||
# `splitext` includes leading period, so we skip it | ||||
return ext[1:].lower() | ||||
Brian Granger
|
r5080 | |||
Min RK
|
r23849 | |||
Daniel Wehner
|
r17001 | class Video(DisplayObject): | ||
Dominic Kuang
|
r24623 | def __init__(self, data=None, url=None, filename=None, embed=False, | ||
Matthieu Ancellin
|
r25603 | mimetype=None, width=None, height=None, html_attributes="controls"): | ||
Daniel Wehner
|
r17001 | """Create a video object given raw data or an URL. | ||
When this object is returned by an input cell or passed to the | ||||
display function, it will result in the video being displayed | ||||
in the frontend. | ||||
Parameters | ||||
---------- | ||||
data : unicode, str or bytes | ||||
Min RK
|
r22116 | The raw video data or a URL or filename to load the data from. | ||
Matthias Bussonnier
|
r26333 | Raw data will require passing ``embed=True``. | ||
Daniel Wehner
|
r17001 | url : unicode | ||
Matthias Bussonnier
|
r26333 | A URL for the video. If you specify ``url=``, | ||
Min RK
|
r22116 | the image data will not be embedded. | ||
Daniel Wehner
|
r17001 | filename : unicode | ||
Min RK
|
r22116 | Path to a local file containing the video. | ||
Matthias Bussonnier
|
r26333 | Will be interpreted as a local URL unless ``embed=True``. | ||
Daniel Wehner
|
r17001 | embed : bool | ||
Min RK
|
r22116 | Should the video be embedded using a data URI (True) or be | ||
loaded using a <video> tag (False). | ||||
Daniel Wehner
|
r17001 | |||
Min RK
|
r22116 | Since videos are large, embedding them should be avoided, if possible. | ||
Matthias Bussonnier
|
r26333 | You must confirm embedding as your intention by passing ``embed=True``. | ||
Min RK
|
r22116 | |||
Local files can be displayed with URLs without embedding the content, via:: | ||||
Video('./video.mp4') | ||||
Matthias Bussonnier
|
r26294 | mimetype : unicode | ||
Min RK
|
r22116 | Specify the mimetype for embedded videos. | ||
Default will be guessed from file extension, if available. | ||||
Dominic Kuang
|
r24623 | width : int | ||
Width in pixels to which to constrain the video in HTML. | ||||
If not supplied, defaults to the width of the video. | ||||
height : int | ||||
Height in pixels to which to constrain the video in html. | ||||
If not supplied, defaults to the height of the video. | ||||
Matthieu Ancellin
|
r25603 | html_attributes : str | ||
Matthias Bussonnier
|
r26333 | Attributes for the HTML ``<video>`` block. | ||
Default: ``"controls"`` to get video controls. | ||||
Other examples: ``"controls muted"`` for muted video with controls, | ||||
``"loop autoplay"`` for looping autoplaying video without controls. | ||||
Min RK
|
r22115 | |||
Daniel Wehner
|
r17001 | Examples | ||
-------- | ||||
Matthieu Ancellin
|
r25604 | :: | ||
Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4') | ||||
Video('path/to/video.mp4') | ||||
Video('path/to/video.mp4', embed=True) | ||||
Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay") | ||||
Video(b'raw-videodata', embed=True) | ||||
Daniel Wehner
|
r17001 | """ | ||
Alok Singh
|
r25061 | if isinstance(data, (Path, PurePath)): | ||
data = str(data) | ||||
Srinivas Reddy Thatiparthy
|
r23037 | if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')): | ||
Daniel Wehner
|
r17001 | url = data | ||
data = None | ||||
juacrumar
|
r26060 | elif data is not None and os.path.exists(data): | ||
Daniel Wehner
|
r17001 | filename = data | ||
data = None | ||||
Adam Eury
|
r22947 | |||
Min RK
|
r22116 | if data and not embed: | ||
msg = ''.join([ | ||||
"To embed videos, you must pass embed=True ", | ||||
"(this may make your notebook files huge)\n", | ||||
"Consider passing Video(url='...')", | ||||
]) | ||||
raise ValueError(msg) | ||||
Daniel Wehner
|
r17001 | |||
self.mimetype = mimetype | ||||
Min RK
|
r22116 | self.embed = embed | ||
Dominic Kuang
|
r24623 | self.width = width | ||
self.height = height | ||||
Matthieu Ancellin
|
r25603 | self.html_attributes = html_attributes | ||
Daniel Wehner
|
r17001 | super(Video, self).__init__(data=data, url=url, filename=filename) | ||
def _repr_html_(self): | ||||
Dominic Kuang
|
r24623 | width = height = '' | ||
if self.width: | ||||
width = ' width="%d"' % self.width | ||||
if self.height: | ||||
height = ' height="%d"' % self.height | ||||
Daniel Wehner
|
r17001 | # External URLs and potentially local files are not embedded into the | ||
# notebook output. | ||||
if not self.embed: | ||||
url = self.url if self.url is not None else self.filename | ||||
Matthieu Ancellin
|
r25603 | output = """<video src="{0}" {1} {2} {3}> | ||
Daniel Wehner
|
r17001 | Your browser does not support the <code>video</code> element. | ||
Matthieu Ancellin
|
r25603 | </video>""".format(url, self.html_attributes, width, height) | ||
Daniel Wehner
|
r17001 | return output | ||
Adam Eury
|
r22947 | |||
Min RK
|
r22116 | # Embedded videos are base64-encoded. | ||
Min RK
|
r22115 | mimetype = self.mimetype | ||
Daniel Wehner
|
r17001 | if self.filename is not None: | ||
Min RK
|
r22115 | if not mimetype: | ||
mimetype, _ = mimetypes.guess_type(self.filename) | ||||
Adam Eury
|
r22947 | |||
Min RK
|
r22115 | with open(self.filename, 'rb') as f: | ||
video = f.read() | ||||
else: | ||||
video = self.data | ||||
Srinivas Reddy Thatiparthy
|
r23044 | if isinstance(video, str): | ||
Min RK
|
r22115 | # unicode input is already b64-encoded | ||
b64_video = video | ||||
Daniel Wehner
|
r17001 | else: | ||
Min RK
|
r23835 | b64_video = b2a_base64(video).decode('ascii').rstrip() | ||
Adam Eury
|
r22947 | |||
Matthieu Ancellin
|
r25603 | output = """<video {0} {1} {2}> | ||
<source src="data:{3};base64,{4}" type="{3}"> | ||||
Daniel Wehner
|
r17001 | Your browser does not support the video tag. | ||
Matthieu Ancellin
|
r25603 | </video>""".format(self.html_attributes, width, height, mimetype, b64_video) | ||
Daniel Wehner
|
r17001 | return output | ||
def reload(self): | ||||
# TODO | ||||
pass | ||||
Brian Granger
|
r5080 | |||
Brian E. Granger
|
r15126 | @skip_doctest | ||
def set_matplotlib_formats(*formats, **kwargs): | ||||
martinRenou
|
r26464 | """ | ||
Matthias Bussonnier
|
r26474 | .. deprecated:: 7.23 | ||
use `matplotlib_inline.backend_inline.set_matplotlib_formats()` | ||||
martinRenou
|
r26464 | |||
Select figure formats for the inline backend. Optionally pass quality for JPEG. | ||||
Brian E. Granger
|
r15126 | |||
For example, this enables PNG and JPEG output with a JPEG quality of 90%:: | ||||
In [1]: set_matplotlib_formats('png', 'jpeg', quality=90) | ||||
To set this in your config files use the following:: | ||||
Adam Eury
|
r22947 | |||
MinRK
|
r15393 | c.InlineBackend.figure_formats = {'png', 'jpeg'} | ||
c.InlineBackend.print_figure_kwargs.update({'quality' : 90}) | ||||
Brian E. Granger
|
r15124 | |||
Parameters | ||||
Brian E. Granger
|
r15126 | ---------- | ||
MinRK
|
r15393 | *formats : strs | ||
One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'. | ||||
Matthias Bussonnier
|
r26294 | **kwargs | ||
MinRK
|
r15393 | Keyword args will be relayed to ``figure.canvas.print_figure``. | ||
Brian E. Granger
|
r15124 | """ | ||
martinRenou
|
r26464 | warnings.warn( | ||
Matthias Bussonnier
|
r26474 | "`set_matplotlib_formats` is deprecated since IPython 7.23, directly " | ||
"use `matplotlib_inline.backend_inline.set_matplotlib_formats()`", | ||||
martinRenou
|
r26464 | DeprecationWarning, | ||
stacklevel=2, | ||||
) | ||||
from matplotlib_inline.backend_inline import ( | ||||
set_matplotlib_formats as set_matplotlib_formats_orig, | ||||
) | ||||
set_matplotlib_formats_orig(*formats, **kwargs) | ||||
Brian E. Granger
|
r15124 | |||
Brian E. Granger
|
r15126 | @skip_doctest | ||
MinRK
|
r15395 | def set_matplotlib_close(close=True): | ||
martinRenou
|
r26464 | """ | ||
Matthias Bussonnier
|
r26474 | .. deprecated:: 7.23 | ||
use `matplotlib_inline.backend_inline.set_matplotlib_close()` | ||||
martinRenou
|
r26464 | |||
Set whether the inline backend closes all figures automatically or not. | ||||
Adam Eury
|
r22947 | |||
Brian E. Granger
|
r15126 | By default, the inline backend used in the IPython Notebook will close all | ||
matplotlib figures automatically after each cell is run. This means that | ||||
plots in different cells won't interfere. Sometimes, you may want to make | ||||
a plot in one cell and then refine it in later cells. This can be accomplished | ||||
by:: | ||||
Adam Eury
|
r22947 | |||
Brian E. Granger
|
r15126 | In [1]: set_matplotlib_close(False) | ||
Adam Eury
|
r22947 | |||
Brian E. Granger
|
r15126 | To set this in your config files use the following:: | ||
Adam Eury
|
r22947 | |||
Brian E. Granger
|
r15126 | c.InlineBackend.close_figures = False | ||
Adam Eury
|
r22947 | |||
Brian E. Granger
|
r15126 | Parameters | ||
---------- | ||||
close : bool | ||||
Should all matplotlib figures be automatically closed after each cell is | ||||
run? | ||||
""" | ||||
martinRenou
|
r26464 | warnings.warn( | ||
Matthias Bussonnier
|
r26474 | "`set_matplotlib_close` is deprecated since IPython 7.23, directly " | ||
"use `matplotlib_inline.backend_inline.set_matplotlib_close()`", | ||||
martinRenou
|
r26464 | DeprecationWarning, | ||
stacklevel=2, | ||||
) | ||||
from matplotlib_inline.backend_inline import ( | ||||
set_matplotlib_close as set_matplotlib_close_orig, | ||||
) | ||||
set_matplotlib_close_orig(close) | ||||