| """Top-level display functions for displaying object in different formats.""" |
|
|
| |
| |
|
|
|
|
| from binascii import b2a_base64, hexlify |
| import html |
| import json |
| import mimetypes |
| import os |
| import struct |
| import warnings |
| from copy import deepcopy |
| from os.path import splitext |
| from pathlib import Path, PurePath |
|
|
| from typing import Optional |
|
|
| from IPython.testing.skipdoctest import skip_doctest |
| from . import display_functions |
|
|
|
|
| __all__ = [ |
| "display_pretty", |
| "display_html", |
| "display_markdown", |
| "display_svg", |
| "display_png", |
| "display_jpeg", |
| "display_webp", |
| "display_latex", |
| "display_json", |
| "display_javascript", |
| "display_pdf", |
| "DisplayObject", |
| "TextDisplayObject", |
| "Pretty", |
| "HTML", |
| "Markdown", |
| "Math", |
| "Latex", |
| "SVG", |
| "ProgressBar", |
| "JSON", |
| "GeoJSON", |
| "Javascript", |
| "Image", |
| "Video", |
| ] |
|
|
| |
| |
| |
|
|
| def _safe_exists(path): |
| """Check path, but don't let exceptions raise""" |
| try: |
| return os.path.exists(path) |
| except Exception: |
| return False |
|
|
|
|
| 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') |
| *objs : object |
| 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) |
| Metadata to be associated with the specific mimetype output. |
| """ |
| if metadata: |
| metadata = {mimetype: metadata} |
| if raw: |
| |
| objs = [ {mimetype: obj} for obj in objs ] |
| display_functions.display(*objs, raw=raw, metadata=metadata, include=[mimetype]) |
|
|
| |
| |
| |
|
|
|
|
| def display_pretty(*objs, **kwargs): |
| """Display the pretty (default) representation of an object. |
| |
| Parameters |
| ---------- |
| *objs : object |
| 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) |
| Metadata to be associated with the specific mimetype output. |
| """ |
| _display_mimetype('text/plain', objs, **kwargs) |
|
|
|
|
| def display_html(*objs, **kwargs): |
| """Display the HTML representation of an object. |
| |
| Note: If raw=False and the object does not have a HTML |
| representation, no HTML will be shown. |
| |
| Parameters |
| ---------- |
| *objs : object |
| The Python objects to display, or if raw=True raw HTML 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/html', objs, **kwargs) |
|
|
|
|
| def display_markdown(*objs, **kwargs): |
| """Displays the Markdown representation of an object. |
| |
| Parameters |
| ---------- |
| *objs : object |
| 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) |
|
|
|
|
| def display_svg(*objs, **kwargs): |
| """Display the SVG representation of an object. |
| |
| Parameters |
| ---------- |
| *objs : object |
| 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] |
| metadata : dict (optional) |
| Metadata to be associated with the specific mimetype output. |
| """ |
| _display_mimetype('image/svg+xml', objs, **kwargs) |
|
|
|
|
| def display_png(*objs, **kwargs): |
| """Display the PNG representation of an object. |
| |
| Parameters |
| ---------- |
| *objs : object |
| 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] |
| metadata : dict (optional) |
| Metadata to be associated with the specific mimetype output. |
| """ |
| _display_mimetype('image/png', objs, **kwargs) |
|
|
|
|
| def display_jpeg(*objs, **kwargs): |
| """Display the JPEG representation of an object. |
| |
| Parameters |
| ---------- |
| *objs : object |
| 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] |
| metadata : dict (optional) |
| Metadata to be associated with the specific mimetype output. |
| """ |
| _display_mimetype('image/jpeg', objs, **kwargs) |
|
|
|
|
| def display_webp(*objs, **kwargs): |
| """Display the WEBP representation of an object. |
| |
| Parameters |
| ---------- |
| *objs : object |
| 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] |
| metadata : dict (optional) |
| Metadata to be associated with the specific mimetype output. |
| """ |
| _display_mimetype("image/webp", objs, **kwargs) |
|
|
|
|
| def display_latex(*objs, **kwargs): |
| """Display the LaTeX representation of an object. |
| |
| Parameters |
| ---------- |
| *objs : object |
| 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] |
| metadata : dict (optional) |
| Metadata to be associated with the specific mimetype output. |
| """ |
| _display_mimetype('text/latex', objs, **kwargs) |
|
|
|
|
| def display_json(*objs, **kwargs): |
| """Display the JSON representation of an object. |
| |
| Note that not many frontends support displaying JSON. |
| |
| Parameters |
| ---------- |
| *objs : object |
| 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] |
| metadata : dict (optional) |
| Metadata to be associated with the specific mimetype output. |
| """ |
| _display_mimetype('application/json', objs, **kwargs) |
|
|
|
|
| def display_javascript(*objs, **kwargs): |
| """Display the Javascript representation of an object. |
| |
| Parameters |
| ---------- |
| *objs : object |
| 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/javascript', objs, **kwargs) |
|
|
|
|
| def display_pdf(*objs, **kwargs): |
| """Display the PDF representation of an object. |
| |
| Parameters |
| ---------- |
| *objs : object |
| 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) |
|
|
|
|
| |
| |
| |
|
|
|
|
| class DisplayObject: |
| """An object that wraps data to be displayed.""" |
|
|
| _read_flags = 'r' |
| _show_mem_addr = False |
| metadata = None |
|
|
| def __init__(self, data=None, url=None, filename=None, metadata=None): |
| """Create a 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. 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 |
| and then displayed. |
| |
| Parameters |
| ---------- |
| data : unicode, str or bytes |
| The raw data or a URL or file to load the data from |
| url : unicode |
| A URL to download the data from. |
| filename : unicode |
| Path to a local file to load the data from. |
| metadata : dict |
| Dict of metadata associated to be the object when displayed |
| """ |
| if isinstance(data, (Path, PurePath)): |
| data = str(data) |
|
|
| if data is not None and isinstance(data, str): |
| 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 |
|
|
| self.url = url |
| self.filename = filename |
| |
| |
| |
| self.data = data |
|
|
| if metadata is not None: |
| self.metadata = metadata |
| elif self.metadata is None: |
| self.metadata = {} |
|
|
| self.reload() |
| self._check_data() |
|
|
| 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 |
|
|
| def _check_data(self): |
| """Override in subclasses if there's something to check.""" |
| pass |
|
|
| 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 |
|
|
| def reload(self): |
| """Reload the raw data from file or URL.""" |
| if self.filename is not None: |
| encoding = None if "b" in self._read_flags else "utf-8" |
| with open(self.filename, self._read_flags, encoding=encoding) as f: |
| self.data = f.read() |
| elif self.url is not None: |
| |
| from urllib.request import urlopen |
| response = urlopen(self.url) |
| data = response.read() |
| |
| encoding = None |
| if 'content-type' in response.headers: |
| for sub in response.headers['content-type'].split(';'): |
| sub = sub.strip() |
| if sub.startswith('charset'): |
| encoding = sub.split('=')[-1].strip() |
| break |
| if 'content-encoding' in response.headers: |
| |
| if 'gzip' in response.headers['content-encoding']: |
| import gzip |
| from io import BytesIO |
|
|
| |
| with gzip.open( |
| BytesIO(data), "rt", encoding=encoding or "utf-8" |
| ) as fp: |
| encoding = None |
| data = fp.read() |
|
|
| |
| |
| |
| |
| if encoding: |
| self.data = data.decode(encoding, 'replace') |
| else: |
| self.data = data |
|
|
|
|
| class TextDisplayObject(DisplayObject): |
| """Create a text display object given raw data. |
| |
| Parameters |
| ---------- |
| data : str or unicode |
| The raw data or a URL or file to load the data from. |
| url : unicode |
| A URL to download the data from. |
| filename : unicode |
| Path to a local file to load the data from. |
| metadata : dict |
| Dict of metadata associated to be the object when displayed |
| """ |
| def _check_data(self): |
| if self.data is not None and not isinstance(self.data, str): |
| raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data)) |
|
|
| class Pretty(TextDisplayObject): |
|
|
| def _repr_pretty_(self, pp, cycle): |
| return pp.text(self.data) |
|
|
|
|
| class HTML(TextDisplayObject): |
|
|
| def __init__(self, data=None, url=None, filename=None, metadata=None): |
| def warn(): |
| if not data: |
| return False |
|
|
| |
| |
| |
| |
| prefix = data[:10].lower() |
| suffix = data[-10:].lower() |
| return prefix.startswith("<iframe ") and suffix.endswith("</iframe>") |
|
|
| if warn(): |
| warnings.warn("Consider using IPython.display.IFrame instead") |
| super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata) |
|
|
| def _repr_html_(self): |
| return self._data_and_metadata() |
|
|
| 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_() |
|
|
|
|
| class Markdown(TextDisplayObject): |
|
|
| def _repr_markdown_(self): |
| return self._data_and_metadata() |
|
|
|
|
| class Math(TextDisplayObject): |
|
|
| def _repr_latex_(self): |
| s = r"$\displaystyle %s$" % self.data.strip('$') |
| if self.metadata: |
| return s, deepcopy(self.metadata) |
| else: |
| return s |
|
|
|
|
| class Latex(TextDisplayObject): |
|
|
| def _repr_latex_(self): |
| return self._data_and_metadata() |
|
|
|
|
| class SVG(DisplayObject): |
| """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. |
| """ |
|
|
| _read_flags = 'rb' |
| |
| |
| _data: Optional[str] = None |
|
|
| @property |
| def data(self): |
| return self._data |
|
|
| @data.setter |
| def data(self, svg): |
| if svg is None: |
| self._data = None |
| return |
| |
| from xml.dom import minidom |
| x = minidom.parseString(svg) |
| |
| found_svg = x.getElementsByTagName('svg') |
| if found_svg: |
| svg = found_svg[0].toxml() |
| else: |
| |
| |
| pass |
| if isinstance(svg, bytes): |
| self._data = svg.decode(errors="replace") |
| else: |
| self._data = svg |
|
|
| def _repr_svg_(self): |
| return self._data_and_metadata() |
|
|
| class ProgressBar(DisplayObject): |
| """Progressbar supports displaying a progressbar like element |
| """ |
| def __init__(self, total): |
| """Creates a new progressbar |
| |
| Parameters |
| ---------- |
| total : int |
| maximum size of the progressbar |
| """ |
| self.total = total |
| self._progress = 0 |
| 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, |
| ) |
|
|
| def _repr_html_(self): |
| return "<progress style='width:{}' max='{}' value='{}'></progress>".format( |
| self.html_width, self.total, self.progress) |
|
|
| def display(self): |
| display_functions.display(self, display_id=self._display_id) |
|
|
| def update(self): |
| display_functions.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() |
|
|
| def __iter__(self): |
| self.display() |
| self._progress = -1 |
| 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() |
|
|
| class JSON(DisplayObject): |
| """JSON expects a JSON-able dict or list |
| |
| not an already-serialized JSON string. |
| |
| Scalar types (None, number, string) are not allowed, only dict or list containers. |
| """ |
| |
| _data = None |
| def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs): |
| """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. |
| metadata : dict |
| Specify extra metadata to attach to the json display object. |
| root : str |
| The name of the root element of the JSON tree |
| """ |
| self.metadata = { |
| 'expanded': expanded, |
| 'root': root, |
| } |
| if metadata: |
| self.metadata.update(metadata) |
| if kwargs: |
| self.metadata.update(kwargs) |
| super(JSON, self).__init__(data=data, url=url, filename=filename) |
|
|
| 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 |
|
|
| @data.setter |
| def data(self, data): |
| if isinstance(data, (Path, PurePath)): |
| data = str(data) |
|
|
| if isinstance(data, str): |
| if self.filename is None and self.url is None: |
| warnings.warn("JSON expects JSONable dict or list, not JSON strings") |
| data = json.loads(data) |
| self._data = data |
|
|
| def _data_and_metadata(self): |
| return self.data, self.metadata |
|
|
| def _repr_json_(self): |
| return self._data_and_metadata() |
|
|
|
|
| _css_t = """var link = document.createElement("link"); |
| link.rel = "stylesheet"; |
| link.type = "text/css"; |
| link.href = "%s"; |
| document.head.appendChild(link); |
| """ |
|
|
| _lib_t1 = """new Promise(function(resolve, reject) { |
| var script = document.createElement("script"); |
| script.onload = resolve; |
| script.onerror = reject; |
| script.src = "%s"; |
| document.head.appendChild(script); |
| }).then(() => { |
| """ |
|
|
| _lib_t2 = """ |
| });""" |
|
|
| class GeoJSON(JSON): |
| """GeoJSON expects JSON-able dict |
| |
| not an already-serialized JSON string. |
| |
| Scalar types (None, number, string) are not allowed, only dict containers. |
| """ |
|
|
| def __init__(self, *args, **kwargs): |
| """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. |
| metadata : dict |
| Specify extra metadata to attach to the json display object. |
| |
| 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. |
| |
| """ |
|
|
| super(GeoJSON, self).__init__(*args, **kwargs) |
|
|
|
|
| def _ipython_display_(self): |
| bundle = { |
| 'application/geo+json': self.data, |
| 'text/plain': '<IPython.display.GeoJSON object>' |
| } |
| metadata = { |
| 'application/geo+json': self.metadata |
| } |
| display_functions.display(bundle, metadata=metadata, raw=True) |
|
|
| class Javascript(TextDisplayObject): |
|
|
| 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 |
| downloaded and then displayed. |
| |
| In the Notebook, the containing element will be available as `element`, |
| and jQuery will be available. Content appended to `element` will be |
| visible in the output area. |
| |
| 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. |
| css : list or str |
| A sequence of css files to load before running the source code. |
| The full URLs of the css files should be given. A single css URL |
| can also be given as a string. |
| """ |
| if isinstance(lib, str): |
| lib = [lib] |
| elif lib is None: |
| lib = [] |
| if isinstance(css, str): |
| 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 |
| super(Javascript, self).__init__(data=data, url=url, filename=filename) |
|
|
| def _repr_javascript_(self): |
| r = '' |
| for c in self.css: |
| r += _css_t % c |
| for l in self.lib: |
| r += _lib_t1 % l |
| r += self.data |
| r += _lib_t2*len(self.lib) |
| return r |
|
|
|
|
| |
| _PNG = b"\x89PNG\r\n\x1a\n" |
| _JPEG = b"\xff\xd8" |
| _GIF1 = b"GIF87a" |
| _GIF2 = b"GIF89a" |
| _WEBP = b"WEBP" |
|
|
|
|
| def _pngxy(data): |
| """read the (width, height) from a PNG header""" |
| ihdr = data.index(b'IHDR') |
| |
| return struct.unpack('>ii', data[ihdr+4:ihdr+12]) |
|
|
|
|
| def _jpegxy(data): |
| """read the (width, height) from a JPEG header""" |
| |
|
|
| 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': |
| |
| iSOF = idx |
| break |
| else: |
| |
| idx += 2 |
|
|
| h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9]) |
| return w, h |
|
|
|
|
| def _gifxy(data): |
| """read the (width, height) from a GIF header""" |
| return struct.unpack('<HH', data[6:10]) |
|
|
|
|
| def _webpxy(data): |
| """read the (width, height) from a WEBP header""" |
| if data[12:16] == b"VP8 ": |
| width, height = struct.unpack("<HH", data[24:30]) |
| width = width & 0x3FFF |
| height = height & 0x3FFF |
| return (width, height) |
| elif data[12:16] == b"VP8L": |
| size_info = struct.unpack("<I", data[21:25])[0] |
| width = 1 + ((size_info & 0x3F) << 8) | (size_info >> 24) |
| height = 1 + ( |
| (((size_info >> 8) & 0xF) << 10) |
| | (((size_info >> 14) & 0x3FC) << 2) |
| | ((size_info >> 22) & 0x3) |
| ) |
| return (width, height) |
| else: |
| raise ValueError("Not a valid WEBP header") |
|
|
|
|
| class Image(DisplayObject): |
|
|
| _read_flags = "rb" |
| _FMT_JPEG = "jpeg" |
| _FMT_PNG = "png" |
| _FMT_GIF = "gif" |
| _FMT_WEBP = "webp" |
| _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF, _FMT_WEBP] |
| _MIMETYPES = { |
| _FMT_PNG: "image/png", |
| _FMT_JPEG: "image/jpeg", |
| _FMT_GIF: "image/gif", |
| _FMT_WEBP: "image/webp", |
| } |
|
|
| 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, |
| ): |
| """Create a PNG/JPEG/GIF/WEBP image object given raw data. |
| |
| When this object is returned by an input cell or passed to the |
| display function, it will result in the image being displayed |
| in the frontend. |
| |
| Parameters |
| ---------- |
| data : unicode, str or bytes |
| The raw image data or a URL or filename to load the data from. |
| This always results in embedded image data. |
| |
| url : unicode |
| A URL to download the data from. If you specify `url=`, |
| the image data will not be embedded unless you also specify `embed=True`. |
| |
| filename : unicode |
| Path to a local file to load the data from. |
| Images from a file are always embedded. |
| |
| format : unicode |
| The format of the image data (png/jpeg/jpg/gif/webp). If a filename or URL is given |
| for format will be inferred from the filename extension. |
| |
| embed : bool |
| 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` |
| |
| width : int |
| Width in pixels to which to constrain the image in html |
| |
| height : int |
| Height in pixels to which to constrain the image in html |
| |
| 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. |
| |
| unconfined : bool |
| Set unconfined=True to disable max-width confinement of the image. |
| |
| metadata : dict |
| Specify extra metadata to attach to the image. |
| |
| alt : unicode |
| Alternative text for the image, for use by screen readers. |
| |
| Examples |
| -------- |
| 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('https://www.google.fr/images/srpr/logo3w.png') # doctest: +SKIP |
| <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='https://www.google.fr/images/srpr/logo3w.png') |
| <IPython.core.display.Image object> |
| |
| """ |
| if isinstance(data, (Path, PurePath)): |
| data = str(data) |
|
|
| if filename is not None: |
| ext = self._find_ext(filename) |
| elif url is not None: |
| ext = self._find_ext(url) |
| elif data is None: |
| raise ValueError("No image data found. Expecting filename, url, or data.") |
| elif isinstance(data, str) and ( |
| data.startswith('http') or _safe_exists(data) |
| ): |
| ext = self._find_ext(data) |
| else: |
| ext = None |
|
|
| if format is None: |
| if ext is not None: |
| if ext == u'jpg' or ext == u'jpeg': |
| format = self._FMT_JPEG |
| elif ext == u'png': |
| format = self._FMT_PNG |
| elif ext == u'gif': |
| format = self._FMT_GIF |
| elif ext == "webp": |
| format = self._FMT_WEBP |
| else: |
| format = ext.lower() |
| elif isinstance(data, bytes): |
| |
| |
| if data[:2] == _JPEG: |
| format = self._FMT_JPEG |
| elif data[:8] == _PNG: |
| format = self._FMT_PNG |
| elif data[8:12] == _WEBP: |
| format = self._FMT_WEBP |
| elif data[:6] == _GIF1 or data[:6] == _GIF2: |
| format = self._FMT_GIF |
|
|
| |
| if format is None: |
| format = self._FMT_PNG |
|
|
| if format.lower() == 'jpg': |
| |
| format = self._FMT_JPEG |
|
|
| self.format = format.lower() |
| self.embed = embed if embed is not None else (url is None) |
|
|
| if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS: |
| raise ValueError("Cannot embed the '%s' image format" % (self.format)) |
| if self.embed: |
| self._mimetype = self._MIMETYPES.get(self.format) |
|
|
| self.width = width |
| self.height = height |
| self.retina = retina |
| self.unconfined = unconfined |
| self.alt = alt |
| super(Image, self).__init__(data=data, url=url, filename=filename, |
| 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'] |
|
|
| if self.alt is None and self.metadata.get("alt", {}): |
| self.alt = metadata["alt"] |
|
|
| if retina: |
| self._retina_shape() |
|
|
|
|
| def _retina_shape(self): |
| """load pixel-doubled width and height from image data""" |
| if not self.embed: |
| return |
| if self.format == self._FMT_PNG: |
| w, h = _pngxy(self.data) |
| elif self.format == self._FMT_JPEG: |
| w, h = _jpegxy(self.data) |
| elif self.format == self._FMT_GIF: |
| w, h = _gifxy(self.data) |
| else: |
| |
| return |
| self.width = w // 2 |
| self.height = h // 2 |
|
|
| def reload(self): |
| """Reload the raw data from file or URL.""" |
| if self.embed: |
| super(Image,self).reload() |
| if self.retina: |
| self._retina_shape() |
|
|
| def _repr_html_(self): |
| if not self.embed: |
| width = height = klass = alt = "" |
| if self.width: |
| width = ' width="%d"' % self.width |
| if self.height: |
| height = ' height="%d"' % self.height |
| if self.unconfined: |
| klass = ' class="unconfined"' |
| if self.alt: |
| alt = ' alt="%s"' % html.escape(self.alt) |
| return '<img src="{url}"{width}{height}{klass}{alt}/>'.format( |
| url=self.url, |
| width=width, |
| height=height, |
| klass=klass, |
| alt=alt, |
| ) |
|
|
| 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): |
| """shortcut for returning metadata with shape information, if defined""" |
| try: |
| b64_data = b2a_base64(self.data, newline=False).decode("ascii") |
| except TypeError as e: |
| raise FileNotFoundError( |
| "No such file or directory: '%s'" % (self.data)) from e |
| md = {} |
| if self.metadata: |
| md.update(self.metadata) |
| if self.width: |
| md['width'] = self.width |
| if self.height: |
| md['height'] = self.height |
| if self.unconfined: |
| md['unconfined'] = self.unconfined |
| if self.alt: |
| md["alt"] = self.alt |
| if md or always_both: |
| return b64_data, md |
| else: |
| return b64_data |
|
|
| def _repr_png_(self): |
| if self.embed and self.format == self._FMT_PNG: |
| return self._data_and_metadata() |
|
|
| def _repr_jpeg_(self): |
| if self.embed and self.format == self._FMT_JPEG: |
| return self._data_and_metadata() |
|
|
| def _find_ext(self, s): |
| base, ext = splitext(s) |
|
|
| if not ext: |
| return base |
|
|
| |
| return ext[1:].lower() |
|
|
|
|
| class Video(DisplayObject): |
|
|
| def __init__(self, data=None, url=None, filename=None, embed=False, |
| mimetype=None, width=None, height=None, html_attributes="controls"): |
| """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 |
| The raw video data or a URL or filename to load the data from. |
| Raw data will require passing ``embed=True``. |
| |
| url : unicode |
| A URL for the video. If you specify ``url=``, |
| the image data will not be embedded. |
| |
| filename : unicode |
| Path to a local file containing the video. |
| Will be interpreted as a local URL unless ``embed=True``. |
| |
| embed : bool |
| Should the video be embedded using a data URI (True) or be |
| loaded using a <video> tag (False). |
| |
| Since videos are large, embedding them should be avoided, if possible. |
| You must confirm embedding as your intention by passing ``embed=True``. |
| |
| Local files can be displayed with URLs without embedding the content, via:: |
| |
| Video('./video.mp4') |
| |
| mimetype : unicode |
| Specify the mimetype for embedded videos. |
| Default will be guessed from file extension, if available. |
| |
| 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. |
| |
| html_attributes : str |
| 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. |
| |
| Examples |
| -------- |
| :: |
| |
| 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) |
| """ |
| if isinstance(data, (Path, PurePath)): |
| data = str(data) |
|
|
| if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')): |
| url = data |
| data = None |
| elif data is not None and os.path.exists(data): |
| filename = data |
| data = None |
|
|
| 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) |
|
|
| self.mimetype = mimetype |
| self.embed = embed |
| self.width = width |
| self.height = height |
| self.html_attributes = html_attributes |
| super(Video, self).__init__(data=data, url=url, filename=filename) |
|
|
| def _repr_html_(self): |
| width = height = '' |
| if self.width: |
| width = ' width="%d"' % self.width |
| if self.height: |
| height = ' height="%d"' % self.height |
|
|
| |
| |
| if not self.embed: |
| url = self.url if self.url is not None else self.filename |
| output = """<video src="{0}" {1} {2} {3}> |
| Your browser does not support the <code>video</code> element. |
| </video>""".format(url, self.html_attributes, width, height) |
| return output |
|
|
| |
| mimetype = self.mimetype |
| if self.filename is not None: |
| if not mimetype: |
| mimetype, _ = mimetypes.guess_type(self.filename) |
|
|
| with open(self.filename, 'rb') as f: |
| video = f.read() |
| else: |
| video = self.data |
| if isinstance(video, str): |
| |
| b64_video = video |
| else: |
| b64_video = b2a_base64(video, newline=False).decode("ascii").rstrip() |
|
|
| output = """<video {0} {1} {2}> |
| <source src="data:{3};base64,{4}" type="{3}"> |
| Your browser does not support the video tag. |
| </video>""".format(self.html_attributes, width, height, mimetype, b64_video) |
| return output |
|
|
| def reload(self): |
| |
| pass |
|
|