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