##// END OF EJS Templates
Merge pull request #10813 from henryiii/master...
Matthias Bussonnier -
r23985:36ab971d merge
parent child Browse files
Show More
@@ -1,1399 +1,1412
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.total = total
743 self.total = total
744 self._progress = 0
744 self._progress = 0
745 self.html_width = '60ex'
745 self.html_width = '60ex'
746 self.text_width = 60
746 self.text_width = 60
747 self._display_id = hexlify(os.urandom(8)).decode('ascii')
747 self._display_id = hexlify(os.urandom(8)).decode('ascii')
748
748
749 def __repr__(self):
749 def __repr__(self):
750 fraction = self.progress / self.total
750 fraction = self.progress / self.total
751 filled = '=' * int(fraction * self.text_width)
751 filled = '=' * int(fraction * self.text_width)
752 rest = ' ' * (self.text_width - len(filled))
752 rest = ' ' * (self.text_width - len(filled))
753 return '[{}{}] {}/{}'.format(
753 return '[{}{}] {}/{}'.format(
754 filled, rest,
754 filled, rest,
755 self.progress, self.total,
755 self.progress, self.total,
756 )
756 )
757
757
758 def _repr_html_(self):
758 def _repr_html_(self):
759 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
759 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
760 self.html_width, self.total, self.progress)
760 self.html_width, self.total, self.progress)
761
761
762 def display(self):
762 def display(self):
763 display(self, display_id=self._display_id)
763 display(self, display_id=self._display_id)
764
764
765 def update(self):
765 def update(self):
766 display(self, display_id=self._display_id, update=True)
766 display(self, display_id=self._display_id, update=True)
767
767
768 @property
768 @property
769 def progress(self):
769 def progress(self):
770 return self._progress
770 return self._progress
771
771
772 @progress.setter
772 @progress.setter
773 def progress(self, value):
773 def progress(self, value):
774 self._progress = value
774 self._progress = value
775 self.update()
775 self.update()
776
776
777 def __iter__(self):
778 self.display()
779 self._progress = -1 # First iteration is 0
780 return self
781
782 def __next__(self):
783 """Returns current value and increments display by one."""
784 self.progress += 1
785 if self.progress < self.total:
786 return self.progress
787 else:
788 raise StopIteration()
789
777 class JSON(DisplayObject):
790 class JSON(DisplayObject):
778 """JSON expects a JSON-able dict or list
791 """JSON expects a JSON-able dict or list
779
792
780 not an already-serialized JSON string.
793 not an already-serialized JSON string.
781
794
782 Scalar types (None, number, string) are not allowed, only dict or list containers.
795 Scalar types (None, number, string) are not allowed, only dict or list containers.
783 """
796 """
784 # wrap data in a property, which warns about passing already-serialized JSON
797 # wrap data in a property, which warns about passing already-serialized JSON
785 _data = None
798 _data = None
786 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, **kwargs):
799 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, **kwargs):
787 """Create a JSON display object given raw data.
800 """Create a JSON display object given raw data.
788
801
789 Parameters
802 Parameters
790 ----------
803 ----------
791 data : dict or list
804 data : dict or list
792 JSON data to display. Not an already-serialized JSON string.
805 JSON data to display. Not an already-serialized JSON string.
793 Scalar types (None, number, string) are not allowed, only dict
806 Scalar types (None, number, string) are not allowed, only dict
794 or list containers.
807 or list containers.
795 url : unicode
808 url : unicode
796 A URL to download the data from.
809 A URL to download the data from.
797 filename : unicode
810 filename : unicode
798 Path to a local file to load the data from.
811 Path to a local file to load the data from.
799 expanded : boolean
812 expanded : boolean
800 Metadata to control whether a JSON display component is expanded.
813 Metadata to control whether a JSON display component is expanded.
801 metadata: dict
814 metadata: dict
802 Specify extra metadata to attach to the json display object.
815 Specify extra metadata to attach to the json display object.
803 """
816 """
804 self.metadata = {'expanded': expanded}
817 self.metadata = {'expanded': expanded}
805 if metadata:
818 if metadata:
806 self.metadata.update(metadata)
819 self.metadata.update(metadata)
807 if kwargs:
820 if kwargs:
808 self.metadata.update(kwargs)
821 self.metadata.update(kwargs)
809 super(JSON, self).__init__(data=data, url=url, filename=filename)
822 super(JSON, self).__init__(data=data, url=url, filename=filename)
810
823
811 def _check_data(self):
824 def _check_data(self):
812 if self.data is not None and not isinstance(self.data, (dict, list)):
825 if self.data is not None and not isinstance(self.data, (dict, list)):
813 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
826 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
814
827
815 @property
828 @property
816 def data(self):
829 def data(self):
817 return self._data
830 return self._data
818
831
819 @data.setter
832 @data.setter
820 def data(self, data):
833 def data(self, data):
821 if isinstance(data, str):
834 if isinstance(data, str):
822 if getattr(self, 'filename', None) is None:
835 if getattr(self, 'filename', None) is None:
823 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
836 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
824 data = json.loads(data)
837 data = json.loads(data)
825 self._data = data
838 self._data = data
826
839
827 def _data_and_metadata(self):
840 def _data_and_metadata(self):
828 return self.data, self.metadata
841 return self.data, self.metadata
829
842
830 def _repr_json_(self):
843 def _repr_json_(self):
831 return self._data_and_metadata()
844 return self._data_and_metadata()
832
845
833 _css_t = """$("head").append($("<link/>").attr({
846 _css_t = """$("head").append($("<link/>").attr({
834 rel: "stylesheet",
847 rel: "stylesheet",
835 type: "text/css",
848 type: "text/css",
836 href: "%s"
849 href: "%s"
837 }));
850 }));
838 """
851 """
839
852
840 _lib_t1 = """$.getScript("%s", function () {
853 _lib_t1 = """$.getScript("%s", function () {
841 """
854 """
842 _lib_t2 = """});
855 _lib_t2 = """});
843 """
856 """
844
857
845 class GeoJSON(JSON):
858 class GeoJSON(JSON):
846 """GeoJSON expects JSON-able dict
859 """GeoJSON expects JSON-able dict
847
860
848 not an already-serialized JSON string.
861 not an already-serialized JSON string.
849
862
850 Scalar types (None, number, string) are not allowed, only dict containers.
863 Scalar types (None, number, string) are not allowed, only dict containers.
851 """
864 """
852
865
853 def __init__(self, *args, **kwargs):
866 def __init__(self, *args, **kwargs):
854 """Create a GeoJSON display object given raw data.
867 """Create a GeoJSON display object given raw data.
855
868
856 Parameters
869 Parameters
857 ----------
870 ----------
858 data : dict or list
871 data : dict or list
859 VegaLite data. Not an already-serialized JSON string.
872 VegaLite data. Not an already-serialized JSON string.
860 Scalar types (None, number, string) are not allowed, only dict
873 Scalar types (None, number, string) are not allowed, only dict
861 or list containers.
874 or list containers.
862 url_template : string
875 url_template : string
863 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
876 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
864 layer_options : dict
877 layer_options : dict
865 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
878 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
866 url : unicode
879 url : unicode
867 A URL to download the data from.
880 A URL to download the data from.
868 filename : unicode
881 filename : unicode
869 Path to a local file to load the data from.
882 Path to a local file to load the data from.
870 metadata: dict
883 metadata: dict
871 Specify extra metadata to attach to the json display object.
884 Specify extra metadata to attach to the json display object.
872
885
873 Examples
886 Examples
874 --------
887 --------
875
888
876 The following will display an interactive map of Mars with a point of
889 The following will display an interactive map of Mars with a point of
877 interest on frontend that do support GeoJSON display.
890 interest on frontend that do support GeoJSON display.
878
891
879 >>> from IPython.display import GeoJSON
892 >>> from IPython.display import GeoJSON
880
893
881 >>> GeoJSON(data={
894 >>> GeoJSON(data={
882 ... "type": "Feature",
895 ... "type": "Feature",
883 ... "geometry": {
896 ... "geometry": {
884 ... "type": "Point",
897 ... "type": "Point",
885 ... "coordinates": [-81.327, 296.038]
898 ... "coordinates": [-81.327, 296.038]
886 ... }
899 ... }
887 ... },
900 ... },
888 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
901 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
889 ... layer_options={
902 ... layer_options={
890 ... "basemap_id": "celestia_mars-shaded-16k_global",
903 ... "basemap_id": "celestia_mars-shaded-16k_global",
891 ... "attribution" : "Celestia/praesepe",
904 ... "attribution" : "Celestia/praesepe",
892 ... "minZoom" : 0,
905 ... "minZoom" : 0,
893 ... "maxZoom" : 18,
906 ... "maxZoom" : 18,
894 ... })
907 ... })
895 <IPython.core.display.GeoJSON object>
908 <IPython.core.display.GeoJSON object>
896
909
897 In the terminal IPython, you will only see the text representation of
910 In the terminal IPython, you will only see the text representation of
898 the GeoJSON object.
911 the GeoJSON object.
899
912
900 """
913 """
901
914
902 super(GeoJSON, self).__init__(*args, **kwargs)
915 super(GeoJSON, self).__init__(*args, **kwargs)
903
916
904
917
905 def _ipython_display_(self):
918 def _ipython_display_(self):
906 bundle = {
919 bundle = {
907 'application/geo+json': self.data,
920 'application/geo+json': self.data,
908 'text/plain': '<IPython.display.GeoJSON object>'
921 'text/plain': '<IPython.display.GeoJSON object>'
909 }
922 }
910 metadata = {
923 metadata = {
911 'application/geo+json': self.metadata
924 'application/geo+json': self.metadata
912 }
925 }
913 display(bundle, metadata=metadata, raw=True)
926 display(bundle, metadata=metadata, raw=True)
914
927
915 class Javascript(TextDisplayObject):
928 class Javascript(TextDisplayObject):
916
929
917 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
930 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
918 """Create a Javascript display object given raw data.
931 """Create a Javascript display object given raw data.
919
932
920 When this object is returned by an expression or passed to the
933 When this object is returned by an expression or passed to the
921 display function, it will result in the data being displayed
934 display function, it will result in the data being displayed
922 in the frontend. If the data is a URL, the data will first be
935 in the frontend. If the data is a URL, the data will first be
923 downloaded and then displayed.
936 downloaded and then displayed.
924
937
925 In the Notebook, the containing element will be available as `element`,
938 In the Notebook, the containing element will be available as `element`,
926 and jQuery will be available. Content appended to `element` will be
939 and jQuery will be available. Content appended to `element` will be
927 visible in the output area.
940 visible in the output area.
928
941
929 Parameters
942 Parameters
930 ----------
943 ----------
931 data : unicode, str or bytes
944 data : unicode, str or bytes
932 The Javascript source code or a URL to download it from.
945 The Javascript source code or a URL to download it from.
933 url : unicode
946 url : unicode
934 A URL to download the data from.
947 A URL to download the data from.
935 filename : unicode
948 filename : unicode
936 Path to a local file to load the data from.
949 Path to a local file to load the data from.
937 lib : list or str
950 lib : list or str
938 A sequence of Javascript library URLs to load asynchronously before
951 A sequence of Javascript library URLs to load asynchronously before
939 running the source code. The full URLs of the libraries should
952 running the source code. The full URLs of the libraries should
940 be given. A single Javascript library URL can also be given as a
953 be given. A single Javascript library URL can also be given as a
941 string.
954 string.
942 css: : list or str
955 css: : list or str
943 A sequence of css files to load before running the source code.
956 A sequence of css files to load before running the source code.
944 The full URLs of the css files should be given. A single css URL
957 The full URLs of the css files should be given. A single css URL
945 can also be given as a string.
958 can also be given as a string.
946 """
959 """
947 if isinstance(lib, str):
960 if isinstance(lib, str):
948 lib = [lib]
961 lib = [lib]
949 elif lib is None:
962 elif lib is None:
950 lib = []
963 lib = []
951 if isinstance(css, str):
964 if isinstance(css, str):
952 css = [css]
965 css = [css]
953 elif css is None:
966 elif css is None:
954 css = []
967 css = []
955 if not isinstance(lib, (list,tuple)):
968 if not isinstance(lib, (list,tuple)):
956 raise TypeError('expected sequence, got: %r' % lib)
969 raise TypeError('expected sequence, got: %r' % lib)
957 if not isinstance(css, (list,tuple)):
970 if not isinstance(css, (list,tuple)):
958 raise TypeError('expected sequence, got: %r' % css)
971 raise TypeError('expected sequence, got: %r' % css)
959 self.lib = lib
972 self.lib = lib
960 self.css = css
973 self.css = css
961 super(Javascript, self).__init__(data=data, url=url, filename=filename)
974 super(Javascript, self).__init__(data=data, url=url, filename=filename)
962
975
963 def _repr_javascript_(self):
976 def _repr_javascript_(self):
964 r = ''
977 r = ''
965 for c in self.css:
978 for c in self.css:
966 r += _css_t % c
979 r += _css_t % c
967 for l in self.lib:
980 for l in self.lib:
968 r += _lib_t1 % l
981 r += _lib_t1 % l
969 r += self.data
982 r += self.data
970 r += _lib_t2*len(self.lib)
983 r += _lib_t2*len(self.lib)
971 return r
984 return r
972
985
973 # constants for identifying png/jpeg data
986 # constants for identifying png/jpeg data
974 _PNG = b'\x89PNG\r\n\x1a\n'
987 _PNG = b'\x89PNG\r\n\x1a\n'
975 _JPEG = b'\xff\xd8'
988 _JPEG = b'\xff\xd8'
976
989
977 def _pngxy(data):
990 def _pngxy(data):
978 """read the (width, height) from a PNG header"""
991 """read the (width, height) from a PNG header"""
979 ihdr = data.index(b'IHDR')
992 ihdr = data.index(b'IHDR')
980 # next 8 bytes are width/height
993 # next 8 bytes are width/height
981 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
994 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
982
995
983 def _jpegxy(data):
996 def _jpegxy(data):
984 """read the (width, height) from a JPEG header"""
997 """read the (width, height) from a JPEG header"""
985 # adapted from http://www.64lines.com/jpeg-width-height
998 # adapted from http://www.64lines.com/jpeg-width-height
986
999
987 idx = 4
1000 idx = 4
988 while True:
1001 while True:
989 block_size = struct.unpack('>H', data[idx:idx+2])[0]
1002 block_size = struct.unpack('>H', data[idx:idx+2])[0]
990 idx = idx + block_size
1003 idx = idx + block_size
991 if data[idx:idx+2] == b'\xFF\xC0':
1004 if data[idx:idx+2] == b'\xFF\xC0':
992 # found Start of Frame
1005 # found Start of Frame
993 iSOF = idx
1006 iSOF = idx
994 break
1007 break
995 else:
1008 else:
996 # read another block
1009 # read another block
997 idx += 2
1010 idx += 2
998
1011
999 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
1012 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
1000 return w, h
1013 return w, h
1001
1014
1002 def _gifxy(data):
1015 def _gifxy(data):
1003 """read the (width, height) from a GIF header"""
1016 """read the (width, height) from a GIF header"""
1004 return struct.unpack('<HH', data[6:10])
1017 return struct.unpack('<HH', data[6:10])
1005
1018
1006
1019
1007 class Image(DisplayObject):
1020 class Image(DisplayObject):
1008
1021
1009 _read_flags = 'rb'
1022 _read_flags = 'rb'
1010 _FMT_JPEG = u'jpeg'
1023 _FMT_JPEG = u'jpeg'
1011 _FMT_PNG = u'png'
1024 _FMT_PNG = u'png'
1012 _FMT_GIF = u'gif'
1025 _FMT_GIF = u'gif'
1013 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
1026 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
1014 _MIMETYPES = {
1027 _MIMETYPES = {
1015 _FMT_PNG: 'image/png',
1028 _FMT_PNG: 'image/png',
1016 _FMT_JPEG: 'image/jpeg',
1029 _FMT_JPEG: 'image/jpeg',
1017 _FMT_GIF: 'image/gif',
1030 _FMT_GIF: 'image/gif',
1018 }
1031 }
1019
1032
1020 def __init__(self, data=None, url=None, filename=None, format=None,
1033 def __init__(self, data=None, url=None, filename=None, format=None,
1021 embed=None, width=None, height=None, retina=False,
1034 embed=None, width=None, height=None, retina=False,
1022 unconfined=False, metadata=None):
1035 unconfined=False, metadata=None):
1023 """Create a PNG/JPEG/GIF image object given raw data.
1036 """Create a PNG/JPEG/GIF image object given raw data.
1024
1037
1025 When this object is returned by an input cell or passed to the
1038 When this object is returned by an input cell or passed to the
1026 display function, it will result in the image being displayed
1039 display function, it will result in the image being displayed
1027 in the frontend.
1040 in the frontend.
1028
1041
1029 Parameters
1042 Parameters
1030 ----------
1043 ----------
1031 data : unicode, str or bytes
1044 data : unicode, str or bytes
1032 The raw image data or a URL or filename to load the data from.
1045 The raw image data or a URL or filename to load the data from.
1033 This always results in embedded image data.
1046 This always results in embedded image data.
1034 url : unicode
1047 url : unicode
1035 A URL to download the data from. If you specify `url=`,
1048 A URL to download the data from. If you specify `url=`,
1036 the image data will not be embedded unless you also specify `embed=True`.
1049 the image data will not be embedded unless you also specify `embed=True`.
1037 filename : unicode
1050 filename : unicode
1038 Path to a local file to load the data from.
1051 Path to a local file to load the data from.
1039 Images from a file are always embedded.
1052 Images from a file are always embedded.
1040 format : unicode
1053 format : unicode
1041 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
1054 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
1042 for format will be inferred from the filename extension.
1055 for format will be inferred from the filename extension.
1043 embed : bool
1056 embed : bool
1044 Should the image data be embedded using a data URI (True) or be
1057 Should the image data be embedded using a data URI (True) or be
1045 loaded using an <img> tag. Set this to True if you want the image
1058 loaded using an <img> tag. Set this to True if you want the image
1046 to be viewable later with no internet connection in the notebook.
1059 to be viewable later with no internet connection in the notebook.
1047
1060
1048 Default is `True`, unless the keyword argument `url` is set, then
1061 Default is `True`, unless the keyword argument `url` is set, then
1049 default value is `False`.
1062 default value is `False`.
1050
1063
1051 Note that QtConsole is not able to display images if `embed` is set to `False`
1064 Note that QtConsole is not able to display images if `embed` is set to `False`
1052 width : int
1065 width : int
1053 Width in pixels to which to constrain the image in html
1066 Width in pixels to which to constrain the image in html
1054 height : int
1067 height : int
1055 Height in pixels to which to constrain the image in html
1068 Height in pixels to which to constrain the image in html
1056 retina : bool
1069 retina : bool
1057 Automatically set the width and height to half of the measured
1070 Automatically set the width and height to half of the measured
1058 width and height.
1071 width and height.
1059 This only works for embedded images because it reads the width/height
1072 This only works for embedded images because it reads the width/height
1060 from image data.
1073 from image data.
1061 For non-embedded images, you can just set the desired display width
1074 For non-embedded images, you can just set the desired display width
1062 and height directly.
1075 and height directly.
1063 unconfined: bool
1076 unconfined: bool
1064 Set unconfined=True to disable max-width confinement of the image.
1077 Set unconfined=True to disable max-width confinement of the image.
1065 metadata: dict
1078 metadata: dict
1066 Specify extra metadata to attach to the image.
1079 Specify extra metadata to attach to the image.
1067
1080
1068 Examples
1081 Examples
1069 --------
1082 --------
1070 # embedded image data, works in qtconsole and notebook
1083 # embedded image data, works in qtconsole and notebook
1071 # when passed positionally, the first arg can be any of raw image data,
1084 # when passed positionally, the first arg can be any of raw image data,
1072 # a URL, or a filename from which to load image data.
1085 # a URL, or a filename from which to load image data.
1073 # The result is always embedding image data for inline images.
1086 # The result is always embedding image data for inline images.
1074 Image('http://www.google.fr/images/srpr/logo3w.png')
1087 Image('http://www.google.fr/images/srpr/logo3w.png')
1075 Image('/path/to/image.jpg')
1088 Image('/path/to/image.jpg')
1076 Image(b'RAW_PNG_DATA...')
1089 Image(b'RAW_PNG_DATA...')
1077
1090
1078 # Specifying Image(url=...) does not embed the image data,
1091 # Specifying Image(url=...) does not embed the image data,
1079 # it only generates `<img>` tag with a link to the source.
1092 # it only generates `<img>` tag with a link to the source.
1080 # This will not work in the qtconsole or offline.
1093 # This will not work in the qtconsole or offline.
1081 Image(url='http://www.google.fr/images/srpr/logo3w.png')
1094 Image(url='http://www.google.fr/images/srpr/logo3w.png')
1082
1095
1083 """
1096 """
1084 if filename is not None:
1097 if filename is not None:
1085 ext = self._find_ext(filename)
1098 ext = self._find_ext(filename)
1086 elif url is not None:
1099 elif url is not None:
1087 ext = self._find_ext(url)
1100 ext = self._find_ext(url)
1088 elif data is None:
1101 elif data is None:
1089 raise ValueError("No image data found. Expecting filename, url, or data.")
1102 raise ValueError("No image data found. Expecting filename, url, or data.")
1090 elif isinstance(data, str) and (
1103 elif isinstance(data, str) and (
1091 data.startswith('http') or _safe_exists(data)
1104 data.startswith('http') or _safe_exists(data)
1092 ):
1105 ):
1093 ext = self._find_ext(data)
1106 ext = self._find_ext(data)
1094 else:
1107 else:
1095 ext = None
1108 ext = None
1096
1109
1097 if format is None:
1110 if format is None:
1098 if ext is not None:
1111 if ext is not None:
1099 if ext == u'jpg' or ext == u'jpeg':
1112 if ext == u'jpg' or ext == u'jpeg':
1100 format = self._FMT_JPEG
1113 format = self._FMT_JPEG
1101 elif ext == u'png':
1114 elif ext == u'png':
1102 format = self._FMT_PNG
1115 format = self._FMT_PNG
1103 elif ext == u'gif':
1116 elif ext == u'gif':
1104 format = self._FMT_GIF
1117 format = self._FMT_GIF
1105 else:
1118 else:
1106 format = ext.lower()
1119 format = ext.lower()
1107 elif isinstance(data, bytes):
1120 elif isinstance(data, bytes):
1108 # infer image type from image data header,
1121 # infer image type from image data header,
1109 # only if format has not been specified.
1122 # only if format has not been specified.
1110 if data[:2] == _JPEG:
1123 if data[:2] == _JPEG:
1111 format = self._FMT_JPEG
1124 format = self._FMT_JPEG
1112
1125
1113 # failed to detect format, default png
1126 # failed to detect format, default png
1114 if format is None:
1127 if format is None:
1115 format = self._FMT_PNG
1128 format = self._FMT_PNG
1116
1129
1117 if format.lower() == 'jpg':
1130 if format.lower() == 'jpg':
1118 # jpg->jpeg
1131 # jpg->jpeg
1119 format = self._FMT_JPEG
1132 format = self._FMT_JPEG
1120
1133
1121 self.format = format.lower()
1134 self.format = format.lower()
1122 self.embed = embed if embed is not None else (url is None)
1135 self.embed = embed if embed is not None else (url is None)
1123
1136
1124 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
1137 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
1125 raise ValueError("Cannot embed the '%s' image format" % (self.format))
1138 raise ValueError("Cannot embed the '%s' image format" % (self.format))
1126 if self.embed:
1139 if self.embed:
1127 self._mimetype = self._MIMETYPES.get(self.format)
1140 self._mimetype = self._MIMETYPES.get(self.format)
1128
1141
1129 self.width = width
1142 self.width = width
1130 self.height = height
1143 self.height = height
1131 self.retina = retina
1144 self.retina = retina
1132 self.unconfined = unconfined
1145 self.unconfined = unconfined
1133 super(Image, self).__init__(data=data, url=url, filename=filename,
1146 super(Image, self).__init__(data=data, url=url, filename=filename,
1134 metadata=metadata)
1147 metadata=metadata)
1135
1148
1136 if self.width is None and self.metadata.get('width', {}):
1149 if self.width is None and self.metadata.get('width', {}):
1137 self.width = metadata['width']
1150 self.width = metadata['width']
1138
1151
1139 if self.height is None and self.metadata.get('height', {}):
1152 if self.height is None and self.metadata.get('height', {}):
1140 self.height = metadata['height']
1153 self.height = metadata['height']
1141
1154
1142 if retina:
1155 if retina:
1143 self._retina_shape()
1156 self._retina_shape()
1144
1157
1145
1158
1146 def _retina_shape(self):
1159 def _retina_shape(self):
1147 """load pixel-doubled width and height from image data"""
1160 """load pixel-doubled width and height from image data"""
1148 if not self.embed:
1161 if not self.embed:
1149 return
1162 return
1150 if self.format == self._FMT_PNG:
1163 if self.format == self._FMT_PNG:
1151 w, h = _pngxy(self.data)
1164 w, h = _pngxy(self.data)
1152 elif self.format == self._FMT_JPEG:
1165 elif self.format == self._FMT_JPEG:
1153 w, h = _jpegxy(self.data)
1166 w, h = _jpegxy(self.data)
1154 elif self.format == self._FMT_GIF:
1167 elif self.format == self._FMT_GIF:
1155 w, h = _gifxy(self.data)
1168 w, h = _gifxy(self.data)
1156 else:
1169 else:
1157 # retina only supports png
1170 # retina only supports png
1158 return
1171 return
1159 self.width = w // 2
1172 self.width = w // 2
1160 self.height = h // 2
1173 self.height = h // 2
1161
1174
1162 def reload(self):
1175 def reload(self):
1163 """Reload the raw data from file or URL."""
1176 """Reload the raw data from file or URL."""
1164 if self.embed:
1177 if self.embed:
1165 super(Image,self).reload()
1178 super(Image,self).reload()
1166 if self.retina:
1179 if self.retina:
1167 self._retina_shape()
1180 self._retina_shape()
1168
1181
1169 def _repr_html_(self):
1182 def _repr_html_(self):
1170 if not self.embed:
1183 if not self.embed:
1171 width = height = klass = ''
1184 width = height = klass = ''
1172 if self.width:
1185 if self.width:
1173 width = ' width="%d"' % self.width
1186 width = ' width="%d"' % self.width
1174 if self.height:
1187 if self.height:
1175 height = ' height="%d"' % self.height
1188 height = ' height="%d"' % self.height
1176 if self.unconfined:
1189 if self.unconfined:
1177 klass = ' class="unconfined"'
1190 klass = ' class="unconfined"'
1178 return u'<img src="{url}"{width}{height}{klass}/>'.format(
1191 return u'<img src="{url}"{width}{height}{klass}/>'.format(
1179 url=self.url,
1192 url=self.url,
1180 width=width,
1193 width=width,
1181 height=height,
1194 height=height,
1182 klass=klass,
1195 klass=klass,
1183 )
1196 )
1184
1197
1185 def _repr_mimebundle_(self, include=None, exclude=None):
1198 def _repr_mimebundle_(self, include=None, exclude=None):
1186 """Return the image as a mimebundle
1199 """Return the image as a mimebundle
1187
1200
1188 Any new mimetype support should be implemented here.
1201 Any new mimetype support should be implemented here.
1189 """
1202 """
1190 if self.embed:
1203 if self.embed:
1191 mimetype = self._mimetype
1204 mimetype = self._mimetype
1192 data, metadata = self._data_and_metadata(always_both=True)
1205 data, metadata = self._data_and_metadata(always_both=True)
1193 if metadata:
1206 if metadata:
1194 metadata = {mimetype: metadata}
1207 metadata = {mimetype: metadata}
1195 return {mimetype: data}, metadata
1208 return {mimetype: data}, metadata
1196 else:
1209 else:
1197 return {'text/html': self._repr_html_()}
1210 return {'text/html': self._repr_html_()}
1198
1211
1199 def _data_and_metadata(self, always_both=False):
1212 def _data_and_metadata(self, always_both=False):
1200 """shortcut for returning metadata with shape information, if defined"""
1213 """shortcut for returning metadata with shape information, if defined"""
1201 b64_data = b2a_base64(self.data).decode('ascii')
1214 b64_data = b2a_base64(self.data).decode('ascii')
1202 md = {}
1215 md = {}
1203 if self.metadata:
1216 if self.metadata:
1204 md.update(self.metadata)
1217 md.update(self.metadata)
1205 if self.width:
1218 if self.width:
1206 md['width'] = self.width
1219 md['width'] = self.width
1207 if self.height:
1220 if self.height:
1208 md['height'] = self.height
1221 md['height'] = self.height
1209 if self.unconfined:
1222 if self.unconfined:
1210 md['unconfined'] = self.unconfined
1223 md['unconfined'] = self.unconfined
1211 if md or always_both:
1224 if md or always_both:
1212 return b64_data, md
1225 return b64_data, md
1213 else:
1226 else:
1214 return b64_data
1227 return b64_data
1215
1228
1216 def _repr_png_(self):
1229 def _repr_png_(self):
1217 if self.embed and self.format == self._FMT_PNG:
1230 if self.embed and self.format == self._FMT_PNG:
1218 return self._data_and_metadata()
1231 return self._data_and_metadata()
1219
1232
1220 def _repr_jpeg_(self):
1233 def _repr_jpeg_(self):
1221 if self.embed and self.format == self._FMT_JPEG:
1234 if self.embed and self.format == self._FMT_JPEG:
1222 return self._data_and_metadata()
1235 return self._data_and_metadata()
1223
1236
1224 def _find_ext(self, s):
1237 def _find_ext(self, s):
1225 return s.split('.')[-1].lower()
1238 return s.split('.')[-1].lower()
1226
1239
1227
1240
1228 class Video(DisplayObject):
1241 class Video(DisplayObject):
1229
1242
1230 def __init__(self, data=None, url=None, filename=None, embed=False, mimetype=None):
1243 def __init__(self, data=None, url=None, filename=None, embed=False, mimetype=None):
1231 """Create a video object given raw data or an URL.
1244 """Create a video object given raw data or an URL.
1232
1245
1233 When this object is returned by an input cell or passed to the
1246 When this object is returned by an input cell or passed to the
1234 display function, it will result in the video being displayed
1247 display function, it will result in the video being displayed
1235 in the frontend.
1248 in the frontend.
1236
1249
1237 Parameters
1250 Parameters
1238 ----------
1251 ----------
1239 data : unicode, str or bytes
1252 data : unicode, str or bytes
1240 The raw video data or a URL or filename to load the data from.
1253 The raw video data or a URL or filename to load the data from.
1241 Raw data will require passing `embed=True`.
1254 Raw data will require passing `embed=True`.
1242 url : unicode
1255 url : unicode
1243 A URL for the video. If you specify `url=`,
1256 A URL for the video. If you specify `url=`,
1244 the image data will not be embedded.
1257 the image data will not be embedded.
1245 filename : unicode
1258 filename : unicode
1246 Path to a local file containing the video.
1259 Path to a local file containing the video.
1247 Will be interpreted as a local URL unless `embed=True`.
1260 Will be interpreted as a local URL unless `embed=True`.
1248 embed : bool
1261 embed : bool
1249 Should the video be embedded using a data URI (True) or be
1262 Should the video be embedded using a data URI (True) or be
1250 loaded using a <video> tag (False).
1263 loaded using a <video> tag (False).
1251
1264
1252 Since videos are large, embedding them should be avoided, if possible.
1265 Since videos are large, embedding them should be avoided, if possible.
1253 You must confirm embedding as your intention by passing `embed=True`.
1266 You must confirm embedding as your intention by passing `embed=True`.
1254
1267
1255 Local files can be displayed with URLs without embedding the content, via::
1268 Local files can be displayed with URLs without embedding the content, via::
1256
1269
1257 Video('./video.mp4')
1270 Video('./video.mp4')
1258
1271
1259 mimetype: unicode
1272 mimetype: unicode
1260 Specify the mimetype for embedded videos.
1273 Specify the mimetype for embedded videos.
1261 Default will be guessed from file extension, if available.
1274 Default will be guessed from file extension, if available.
1262
1275
1263 Examples
1276 Examples
1264 --------
1277 --------
1265
1278
1266 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1279 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1267 Video('path/to/video.mp4')
1280 Video('path/to/video.mp4')
1268 Video('path/to/video.mp4', embed=True)
1281 Video('path/to/video.mp4', embed=True)
1269 Video(b'raw-videodata', embed=True)
1282 Video(b'raw-videodata', embed=True)
1270 """
1283 """
1271 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1284 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1272 url = data
1285 url = data
1273 data = None
1286 data = None
1274 elif os.path.exists(data):
1287 elif os.path.exists(data):
1275 filename = data
1288 filename = data
1276 data = None
1289 data = None
1277
1290
1278 if data and not embed:
1291 if data and not embed:
1279 msg = ''.join([
1292 msg = ''.join([
1280 "To embed videos, you must pass embed=True ",
1293 "To embed videos, you must pass embed=True ",
1281 "(this may make your notebook files huge)\n",
1294 "(this may make your notebook files huge)\n",
1282 "Consider passing Video(url='...')",
1295 "Consider passing Video(url='...')",
1283 ])
1296 ])
1284 raise ValueError(msg)
1297 raise ValueError(msg)
1285
1298
1286 self.mimetype = mimetype
1299 self.mimetype = mimetype
1287 self.embed = embed
1300 self.embed = embed
1288 super(Video, self).__init__(data=data, url=url, filename=filename)
1301 super(Video, self).__init__(data=data, url=url, filename=filename)
1289
1302
1290 def _repr_html_(self):
1303 def _repr_html_(self):
1291 # External URLs and potentially local files are not embedded into the
1304 # External URLs and potentially local files are not embedded into the
1292 # notebook output.
1305 # notebook output.
1293 if not self.embed:
1306 if not self.embed:
1294 url = self.url if self.url is not None else self.filename
1307 url = self.url if self.url is not None else self.filename
1295 output = """<video src="{0}" controls>
1308 output = """<video src="{0}" controls>
1296 Your browser does not support the <code>video</code> element.
1309 Your browser does not support the <code>video</code> element.
1297 </video>""".format(url)
1310 </video>""".format(url)
1298 return output
1311 return output
1299
1312
1300 # Embedded videos are base64-encoded.
1313 # Embedded videos are base64-encoded.
1301 mimetype = self.mimetype
1314 mimetype = self.mimetype
1302 if self.filename is not None:
1315 if self.filename is not None:
1303 if not mimetype:
1316 if not mimetype:
1304 mimetype, _ = mimetypes.guess_type(self.filename)
1317 mimetype, _ = mimetypes.guess_type(self.filename)
1305
1318
1306 with open(self.filename, 'rb') as f:
1319 with open(self.filename, 'rb') as f:
1307 video = f.read()
1320 video = f.read()
1308 else:
1321 else:
1309 video = self.data
1322 video = self.data
1310 if isinstance(video, str):
1323 if isinstance(video, str):
1311 # unicode input is already b64-encoded
1324 # unicode input is already b64-encoded
1312 b64_video = video
1325 b64_video = video
1313 else:
1326 else:
1314 b64_video = b2a_base64(video).decode('ascii').rstrip()
1327 b64_video = b2a_base64(video).decode('ascii').rstrip()
1315
1328
1316 output = """<video controls>
1329 output = """<video controls>
1317 <source src="data:{0};base64,{1}" type="{0}">
1330 <source src="data:{0};base64,{1}" type="{0}">
1318 Your browser does not support the video tag.
1331 Your browser does not support the video tag.
1319 </video>""".format(mimetype, b64_video)
1332 </video>""".format(mimetype, b64_video)
1320 return output
1333 return output
1321
1334
1322 def reload(self):
1335 def reload(self):
1323 # TODO
1336 # TODO
1324 pass
1337 pass
1325
1338
1326
1339
1327 def clear_output(wait=False):
1340 def clear_output(wait=False):
1328 """Clear the output of the current cell receiving output.
1341 """Clear the output of the current cell receiving output.
1329
1342
1330 Parameters
1343 Parameters
1331 ----------
1344 ----------
1332 wait : bool [default: false]
1345 wait : bool [default: false]
1333 Wait to clear the output until new output is available to replace it."""
1346 Wait to clear the output until new output is available to replace it."""
1334 from IPython.core.interactiveshell import InteractiveShell
1347 from IPython.core.interactiveshell import InteractiveShell
1335 if InteractiveShell.initialized():
1348 if InteractiveShell.initialized():
1336 InteractiveShell.instance().display_pub.clear_output(wait)
1349 InteractiveShell.instance().display_pub.clear_output(wait)
1337 else:
1350 else:
1338 print('\033[2K\r', end='')
1351 print('\033[2K\r', end='')
1339 sys.stdout.flush()
1352 sys.stdout.flush()
1340 print('\033[2K\r', end='')
1353 print('\033[2K\r', end='')
1341 sys.stderr.flush()
1354 sys.stderr.flush()
1342
1355
1343
1356
1344 @skip_doctest
1357 @skip_doctest
1345 def set_matplotlib_formats(*formats, **kwargs):
1358 def set_matplotlib_formats(*formats, **kwargs):
1346 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1359 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1347
1360
1348 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1361 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1349
1362
1350 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1363 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1351
1364
1352 To set this in your config files use the following::
1365 To set this in your config files use the following::
1353
1366
1354 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1367 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1355 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1368 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1356
1369
1357 Parameters
1370 Parameters
1358 ----------
1371 ----------
1359 *formats : strs
1372 *formats : strs
1360 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1373 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1361 **kwargs :
1374 **kwargs :
1362 Keyword args will be relayed to ``figure.canvas.print_figure``.
1375 Keyword args will be relayed to ``figure.canvas.print_figure``.
1363 """
1376 """
1364 from IPython.core.interactiveshell import InteractiveShell
1377 from IPython.core.interactiveshell import InteractiveShell
1365 from IPython.core.pylabtools import select_figure_formats
1378 from IPython.core.pylabtools import select_figure_formats
1366 # build kwargs, starting with InlineBackend config
1379 # build kwargs, starting with InlineBackend config
1367 kw = {}
1380 kw = {}
1368 from ipykernel.pylab.config import InlineBackend
1381 from ipykernel.pylab.config import InlineBackend
1369 cfg = InlineBackend.instance()
1382 cfg = InlineBackend.instance()
1370 kw.update(cfg.print_figure_kwargs)
1383 kw.update(cfg.print_figure_kwargs)
1371 kw.update(**kwargs)
1384 kw.update(**kwargs)
1372 shell = InteractiveShell.instance()
1385 shell = InteractiveShell.instance()
1373 select_figure_formats(shell, formats, **kw)
1386 select_figure_formats(shell, formats, **kw)
1374
1387
1375 @skip_doctest
1388 @skip_doctest
1376 def set_matplotlib_close(close=True):
1389 def set_matplotlib_close(close=True):
1377 """Set whether the inline backend closes all figures automatically or not.
1390 """Set whether the inline backend closes all figures automatically or not.
1378
1391
1379 By default, the inline backend used in the IPython Notebook will close all
1392 By default, the inline backend used in the IPython Notebook will close all
1380 matplotlib figures automatically after each cell is run. This means that
1393 matplotlib figures automatically after each cell is run. This means that
1381 plots in different cells won't interfere. Sometimes, you may want to make
1394 plots in different cells won't interfere. Sometimes, you may want to make
1382 a plot in one cell and then refine it in later cells. This can be accomplished
1395 a plot in one cell and then refine it in later cells. This can be accomplished
1383 by::
1396 by::
1384
1397
1385 In [1]: set_matplotlib_close(False)
1398 In [1]: set_matplotlib_close(False)
1386
1399
1387 To set this in your config files use the following::
1400 To set this in your config files use the following::
1388
1401
1389 c.InlineBackend.close_figures = False
1402 c.InlineBackend.close_figures = False
1390
1403
1391 Parameters
1404 Parameters
1392 ----------
1405 ----------
1393 close : bool
1406 close : bool
1394 Should all matplotlib figures be automatically closed after each cell is
1407 Should all matplotlib figures be automatically closed after each cell is
1395 run?
1408 run?
1396 """
1409 """
1397 from ipykernel.pylab.config import InlineBackend
1410 from ipykernel.pylab.config import InlineBackend
1398 cfg = InlineBackend.instance()
1411 cfg = InlineBackend.instance()
1399 cfg.close_figures = close
1412 cfg.close_figures = close
@@ -1,380 +1,389
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.io import capture_output
14 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
15 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
15 from IPython import paths as ipath
16 from IPython import paths as ipath
16 from IPython.testing.tools import AssertPrints, AssertNotPrints
17 from IPython.testing.tools import AssertPrints, AssertNotPrints
17
18
18 import IPython.testing.decorators as dec
19 import IPython.testing.decorators as dec
19
20
20 def test_image_size():
21 def test_image_size():
21 """Simple test for display.Image(args, width=x,height=y)"""
22 """Simple test for display.Image(args, width=x,height=y)"""
22 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
23 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
23 img = display.Image(url=thisurl, width=200, height=200)
24 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_())
25 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})
26 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_())
27 nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_())
27 img = display.Image(url=thisurl, width=200)
28 img = display.Image(url=thisurl, width=200)
28 nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_())
29 nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_())
29 img = display.Image(url=thisurl)
30 img = display.Image(url=thisurl)
30 nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_())
31 nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_())
31 img = display.Image(url=thisurl, unconfined=True)
32 img = display.Image(url=thisurl, unconfined=True)
32 nt.assert_equal(u'<img src="%s" class="unconfined"/>' % (thisurl), img._repr_html_())
33 nt.assert_equal(u'<img src="%s" class="unconfined"/>' % (thisurl), img._repr_html_())
33
34
34
35
35 def test_image_mimes():
36 def test_image_mimes():
36 fmt = get_ipython().display_formatter.format
37 fmt = get_ipython().display_formatter.format
37 for format in display.Image._ACCEPTABLE_EMBEDDINGS:
38 for format in display.Image._ACCEPTABLE_EMBEDDINGS:
38 mime = display.Image._MIMETYPES[format]
39 mime = display.Image._MIMETYPES[format]
39 img = display.Image(b'garbage', format=format)
40 img = display.Image(b'garbage', format=format)
40 data, metadata = fmt(img)
41 data, metadata = fmt(img)
41 nt.assert_equal(sorted(data), sorted([mime, 'text/plain']))
42 nt.assert_equal(sorted(data), sorted([mime, 'text/plain']))
42
43
43
44
44 def test_geojson():
45 def test_geojson():
45
46
46 gj = display.GeoJSON(data={
47 gj = display.GeoJSON(data={
47 "type": "Feature",
48 "type": "Feature",
48 "geometry": {
49 "geometry": {
49 "type": "Point",
50 "type": "Point",
50 "coordinates": [-81.327, 296.038]
51 "coordinates": [-81.327, 296.038]
51 },
52 },
52 "properties": {
53 "properties": {
53 "name": "Inca City"
54 "name": "Inca City"
54 }
55 }
55 },
56 },
56 url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
57 url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
57 layer_options={
58 layer_options={
58 "basemap_id": "celestia_mars-shaded-16k_global",
59 "basemap_id": "celestia_mars-shaded-16k_global",
59 "attribution": "Celestia/praesepe",
60 "attribution": "Celestia/praesepe",
60 "minZoom": 0,
61 "minZoom": 0,
61 "maxZoom": 18,
62 "maxZoom": 18,
62 })
63 })
63 nt.assert_equal(u'<IPython.core.display.GeoJSON object>', str(gj))
64 nt.assert_equal(u'<IPython.core.display.GeoJSON object>', str(gj))
64
65
65 def test_retina_png():
66 def test_retina_png():
66 here = os.path.dirname(__file__)
67 here = os.path.dirname(__file__)
67 img = display.Image(os.path.join(here, "2x2.png"), retina=True)
68 img = display.Image(os.path.join(here, "2x2.png"), retina=True)
68 nt.assert_equal(img.height, 1)
69 nt.assert_equal(img.height, 1)
69 nt.assert_equal(img.width, 1)
70 nt.assert_equal(img.width, 1)
70 data, md = img._repr_png_()
71 data, md = img._repr_png_()
71 nt.assert_equal(md['width'], 1)
72 nt.assert_equal(md['width'], 1)
72 nt.assert_equal(md['height'], 1)
73 nt.assert_equal(md['height'], 1)
73
74
74 def test_retina_jpeg():
75 def test_retina_jpeg():
75 here = os.path.dirname(__file__)
76 here = os.path.dirname(__file__)
76 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
77 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
77 nt.assert_equal(img.height, 1)
78 nt.assert_equal(img.height, 1)
78 nt.assert_equal(img.width, 1)
79 nt.assert_equal(img.width, 1)
79 data, md = img._repr_jpeg_()
80 data, md = img._repr_jpeg_()
80 nt.assert_equal(md['width'], 1)
81 nt.assert_equal(md['width'], 1)
81 nt.assert_equal(md['height'], 1)
82 nt.assert_equal(md['height'], 1)
82
83
83 def test_base64image():
84 def test_base64image():
84 display.Image("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AAAAACAAHiIbwzAAAAAElFTkSuQmCC")
85 display.Image("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AAAAACAAHiIbwzAAAAAElFTkSuQmCC")
85
86
86 def test_image_filename_defaults():
87 def test_image_filename_defaults():
87 '''test format constraint, and validity of jpeg and png'''
88 '''test format constraint, and validity of jpeg and png'''
88 tpath = ipath.get_ipython_package_dir()
89 tpath = ipath.get_ipython_package_dir()
89 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.zip'),
90 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.zip'),
90 embed=True)
91 embed=True)
91 nt.assert_raises(ValueError, display.Image)
92 nt.assert_raises(ValueError, display.Image)
92 nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
93 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
94 # check boths paths to allow packages to test at build and install time
94 imgfile = os.path.join(tpath, 'core/tests/2x2.png')
95 imgfile = os.path.join(tpath, 'core/tests/2x2.png')
95 img = display.Image(filename=imgfile)
96 img = display.Image(filename=imgfile)
96 nt.assert_equal('png', img.format)
97 nt.assert_equal('png', img.format)
97 nt.assert_is_not_none(img._repr_png_())
98 nt.assert_is_not_none(img._repr_png_())
98 img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False)
99 img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False)
99 nt.assert_equal('jpeg', img.format)
100 nt.assert_equal('jpeg', img.format)
100 nt.assert_is_none(img._repr_jpeg_())
101 nt.assert_is_none(img._repr_jpeg_())
101
102
102 def _get_inline_config():
103 def _get_inline_config():
103 from ipykernel.pylab.config import InlineBackend
104 from ipykernel.pylab.config import InlineBackend
104 return InlineBackend.instance()
105 return InlineBackend.instance()
105
106
106 @dec.skip_without('matplotlib')
107 @dec.skip_without('matplotlib')
107 def test_set_matplotlib_close():
108 def test_set_matplotlib_close():
108 cfg = _get_inline_config()
109 cfg = _get_inline_config()
109 cfg.close_figures = False
110 cfg.close_figures = False
110 display.set_matplotlib_close()
111 display.set_matplotlib_close()
111 assert cfg.close_figures
112 assert cfg.close_figures
112 display.set_matplotlib_close(False)
113 display.set_matplotlib_close(False)
113 assert not cfg.close_figures
114 assert not cfg.close_figures
114
115
115 _fmt_mime_map = {
116 _fmt_mime_map = {
116 'png': 'image/png',
117 'png': 'image/png',
117 'jpeg': 'image/jpeg',
118 'jpeg': 'image/jpeg',
118 'pdf': 'application/pdf',
119 'pdf': 'application/pdf',
119 'retina': 'image/png',
120 'retina': 'image/png',
120 'svg': 'image/svg+xml',
121 'svg': 'image/svg+xml',
121 }
122 }
122
123
123 @dec.skip_without('matplotlib')
124 @dec.skip_without('matplotlib')
124 def test_set_matplotlib_formats():
125 def test_set_matplotlib_formats():
125 from matplotlib.figure import Figure
126 from matplotlib.figure import Figure
126 formatters = get_ipython().display_formatter.formatters
127 formatters = get_ipython().display_formatter.formatters
127 for formats in [
128 for formats in [
128 ('png',),
129 ('png',),
129 ('pdf', 'svg'),
130 ('pdf', 'svg'),
130 ('jpeg', 'retina', 'png'),
131 ('jpeg', 'retina', 'png'),
131 (),
132 (),
132 ]:
133 ]:
133 active_mimes = {_fmt_mime_map[fmt] for fmt in formats}
134 active_mimes = {_fmt_mime_map[fmt] for fmt in formats}
134 display.set_matplotlib_formats(*formats)
135 display.set_matplotlib_formats(*formats)
135 for mime, f in formatters.items():
136 for mime, f in formatters.items():
136 if mime in active_mimes:
137 if mime in active_mimes:
137 nt.assert_in(Figure, f)
138 nt.assert_in(Figure, f)
138 else:
139 else:
139 nt.assert_not_in(Figure, f)
140 nt.assert_not_in(Figure, f)
140
141
141 @dec.skip_without('matplotlib')
142 @dec.skip_without('matplotlib')
142 def test_set_matplotlib_formats_kwargs():
143 def test_set_matplotlib_formats_kwargs():
143 from matplotlib.figure import Figure
144 from matplotlib.figure import Figure
144 ip = get_ipython()
145 ip = get_ipython()
145 cfg = _get_inline_config()
146 cfg = _get_inline_config()
146 cfg.print_figure_kwargs.update(dict(foo='bar'))
147 cfg.print_figure_kwargs.update(dict(foo='bar'))
147 kwargs = dict(quality=10)
148 kwargs = dict(quality=10)
148 display.set_matplotlib_formats('png', **kwargs)
149 display.set_matplotlib_formats('png', **kwargs)
149 formatter = ip.display_formatter.formatters['image/png']
150 formatter = ip.display_formatter.formatters['image/png']
150 f = formatter.lookup_by_type(Figure)
151 f = formatter.lookup_by_type(Figure)
151 cell = f.__closure__[0].cell_contents
152 cell = f.__closure__[0].cell_contents
152 expected = kwargs
153 expected = kwargs
153 expected.update(cfg.print_figure_kwargs)
154 expected.update(cfg.print_figure_kwargs)
154 nt.assert_equal(cell, expected)
155 nt.assert_equal(cell, expected)
155
156
156 def test_display_available():
157 def test_display_available():
157 """
158 """
158 Test that display is available without import
159 Test that display is available without import
159
160
160 We don't really care if it's in builtin or anything else, but it should
161 We don't really care if it's in builtin or anything else, but it should
161 always be available.
162 always be available.
162 """
163 """
163 ip = get_ipython()
164 ip = get_ipython()
164 with AssertNotPrints('NameError'):
165 with AssertNotPrints('NameError'):
165 ip.run_cell('display')
166 ip.run_cell('display')
166 try:
167 try:
167 ip.run_cell('del display')
168 ip.run_cell('del display')
168 except NameError:
169 except NameError:
169 pass # it's ok, it might be in builtins
170 pass # it's ok, it might be in builtins
170 # even if deleted it should be back
171 # even if deleted it should be back
171 with AssertNotPrints('NameError'):
172 with AssertNotPrints('NameError'):
172 ip.run_cell('display')
173 ip.run_cell('display')
173
174
174 def test_textdisplayobj_pretty_repr():
175 def test_textdisplayobj_pretty_repr():
175 p = display.Pretty("This is a simple test")
176 p = display.Pretty("This is a simple test")
176 nt.assert_equal(repr(p), '<IPython.core.display.Pretty object>')
177 nt.assert_equal(repr(p), '<IPython.core.display.Pretty object>')
177 nt.assert_equal(p.data, 'This is a simple test')
178 nt.assert_equal(p.data, 'This is a simple test')
178
179
179 p._show_mem_addr = True
180 p._show_mem_addr = True
180 nt.assert_equal(repr(p), object.__repr__(p))
181 nt.assert_equal(repr(p), object.__repr__(p))
181
182
182 def test_displayobject_repr():
183 def test_displayobject_repr():
183 h = display.HTML('<br />')
184 h = display.HTML('<br />')
184 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
185 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
185 h._show_mem_addr = True
186 h._show_mem_addr = True
186 nt.assert_equal(repr(h), object.__repr__(h))
187 nt.assert_equal(repr(h), object.__repr__(h))
187 h._show_mem_addr = False
188 h._show_mem_addr = False
188 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
189 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
189
190
190 j = display.Javascript('')
191 j = display.Javascript('')
191 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
192 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
192 j._show_mem_addr = True
193 j._show_mem_addr = True
193 nt.assert_equal(repr(j), object.__repr__(j))
194 nt.assert_equal(repr(j), object.__repr__(j))
194 j._show_mem_addr = False
195 j._show_mem_addr = False
195 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
196 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
196
197
197 def test_progress():
198 def test_progress():
198 p = display.ProgressBar(10)
199 p = display.ProgressBar(10)
199 nt.assert_true('0/10' in repr(p))
200 nt.assert_in('0/10',repr(p))
200 p.html_width = '100%'
201 p.html_width = '100%'
201 p.progress = 5
202 p.progress = 5
202 nt.assert_equal(p._repr_html_(), "<progress style='width:100%' max='10' value='5'></progress>")
203 nt.assert_equal(p._repr_html_(), "<progress style='width:100%' max='10' value='5'></progress>")
203
204
205 def test_progress_iter():
206 with capture_output(display=False) as captured:
207 for i in display.ProgressBar(5):
208 out = captured.stdout
209 nt.assert_in('{0}/5'.format(i), out)
210 out = captured.stdout
211 nt.assert_in('5/5', out)
212
204 def test_json():
213 def test_json():
205 d = {'a': 5}
214 d = {'a': 5}
206 lis = [d]
215 lis = [d]
207 md = {'expanded': False}
216 md = {'expanded': False}
208 md2 = {'expanded': True}
217 md2 = {'expanded': True}
209 j = display.JSON(d)
218 j = display.JSON(d)
210 j2 = display.JSON(d, expanded=True)
219 j2 = display.JSON(d, expanded=True)
211 nt.assert_equal(j._repr_json_(), (d, md))
220 nt.assert_equal(j._repr_json_(), (d, md))
212 nt.assert_equal(j2._repr_json_(), (d, md2))
221 nt.assert_equal(j2._repr_json_(), (d, md2))
213
222
214 with warnings.catch_warnings(record=True) as w:
223 with warnings.catch_warnings(record=True) as w:
215 warnings.simplefilter("always")
224 warnings.simplefilter("always")
216 j = display.JSON(json.dumps(d))
225 j = display.JSON(json.dumps(d))
217 nt.assert_equal(len(w), 1)
226 nt.assert_equal(len(w), 1)
218 nt.assert_equal(j._repr_json_(), (d, md))
227 nt.assert_equal(j._repr_json_(), (d, md))
219 nt.assert_equal(j2._repr_json_(), (d, md2))
228 nt.assert_equal(j2._repr_json_(), (d, md2))
220
229
221 j = display.JSON(lis)
230 j = display.JSON(lis)
222 j2 = display.JSON(lis, expanded=True)
231 j2 = display.JSON(lis, expanded=True)
223 nt.assert_equal(j._repr_json_(), (lis, md))
232 nt.assert_equal(j._repr_json_(), (lis, md))
224 nt.assert_equal(j2._repr_json_(), (lis, md2))
233 nt.assert_equal(j2._repr_json_(), (lis, md2))
225
234
226 with warnings.catch_warnings(record=True) as w:
235 with warnings.catch_warnings(record=True) as w:
227 warnings.simplefilter("always")
236 warnings.simplefilter("always")
228 j = display.JSON(json.dumps(lis))
237 j = display.JSON(json.dumps(lis))
229 nt.assert_equal(len(w), 1)
238 nt.assert_equal(len(w), 1)
230 nt.assert_equal(j._repr_json_(), (lis, md))
239 nt.assert_equal(j._repr_json_(), (lis, md))
231 nt.assert_equal(j2._repr_json_(), (lis, md2))
240 nt.assert_equal(j2._repr_json_(), (lis, md2))
232
241
233 def test_video_embedding():
242 def test_video_embedding():
234 """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash"""
243 """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash"""
235 v = display.Video("http://ignored")
244 v = display.Video("http://ignored")
236 assert not v.embed
245 assert not v.embed
237 html = v._repr_html_()
246 html = v._repr_html_()
238 nt.assert_not_in('src="data:', html)
247 nt.assert_not_in('src="data:', html)
239 nt.assert_in('src="http://ignored"', html)
248 nt.assert_in('src="http://ignored"', html)
240
249
241 with nt.assert_raises(ValueError):
250 with nt.assert_raises(ValueError):
242 v = display.Video(b'abc')
251 v = display.Video(b'abc')
243
252
244 with NamedFileInTemporaryDirectory('test.mp4') as f:
253 with NamedFileInTemporaryDirectory('test.mp4') as f:
245 f.write(b'abc')
254 f.write(b'abc')
246 f.close()
255 f.close()
247
256
248 v = display.Video(f.name)
257 v = display.Video(f.name)
249 assert not v.embed
258 assert not v.embed
250 html = v._repr_html_()
259 html = v._repr_html_()
251 nt.assert_not_in('src="data:', html)
260 nt.assert_not_in('src="data:', html)
252
261
253 v = display.Video(f.name, embed=True)
262 v = display.Video(f.name, embed=True)
254 html = v._repr_html_()
263 html = v._repr_html_()
255 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
264 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
256
265
257 v = display.Video(f.name, embed=True, mimetype='video/other')
266 v = display.Video(f.name, embed=True, mimetype='video/other')
258 html = v._repr_html_()
267 html = v._repr_html_()
259 nt.assert_in('src="data:video/other;base64,YWJj"',html)
268 nt.assert_in('src="data:video/other;base64,YWJj"',html)
260
269
261 v = display.Video(b'abc', embed=True, mimetype='video/mp4')
270 v = display.Video(b'abc', embed=True, mimetype='video/mp4')
262 html = v._repr_html_()
271 html = v._repr_html_()
263 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
272 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
264
273
265 v = display.Video(u'YWJj', embed=True, mimetype='video/xyz')
274 v = display.Video(u'YWJj', embed=True, mimetype='video/xyz')
266 html = v._repr_html_()
275 html = v._repr_html_()
267 nt.assert_in('src="data:video/xyz;base64,YWJj"',html)
276 nt.assert_in('src="data:video/xyz;base64,YWJj"',html)
268
277
269
278
270 def test_display_id():
279 def test_display_id():
271 ip = get_ipython()
280 ip = get_ipython()
272 with mock.patch.object(ip.display_pub, 'publish') as pub:
281 with mock.patch.object(ip.display_pub, 'publish') as pub:
273 handle = display.display('x')
282 handle = display.display('x')
274 nt.assert_is(handle, None)
283 nt.assert_is(handle, None)
275 handle = display.display('y', display_id='secret')
284 handle = display.display('y', display_id='secret')
276 nt.assert_is_instance(handle, display.DisplayHandle)
285 nt.assert_is_instance(handle, display.DisplayHandle)
277 handle2 = display.display('z', display_id=True)
286 handle2 = display.display('z', display_id=True)
278 nt.assert_is_instance(handle2, display.DisplayHandle)
287 nt.assert_is_instance(handle2, display.DisplayHandle)
279 nt.assert_not_equal(handle.display_id, handle2.display_id)
288 nt.assert_not_equal(handle.display_id, handle2.display_id)
280
289
281 nt.assert_equal(pub.call_count, 3)
290 nt.assert_equal(pub.call_count, 3)
282 args, kwargs = pub.call_args_list[0]
291 args, kwargs = pub.call_args_list[0]
283 nt.assert_equal(args, ())
292 nt.assert_equal(args, ())
284 nt.assert_equal(kwargs, {
293 nt.assert_equal(kwargs, {
285 'data': {
294 'data': {
286 'text/plain': repr('x')
295 'text/plain': repr('x')
287 },
296 },
288 'metadata': {},
297 'metadata': {},
289 })
298 })
290 args, kwargs = pub.call_args_list[1]
299 args, kwargs = pub.call_args_list[1]
291 nt.assert_equal(args, ())
300 nt.assert_equal(args, ())
292 nt.assert_equal(kwargs, {
301 nt.assert_equal(kwargs, {
293 'data': {
302 'data': {
294 'text/plain': repr('y')
303 'text/plain': repr('y')
295 },
304 },
296 'metadata': {},
305 'metadata': {},
297 'transient': {
306 'transient': {
298 'display_id': handle.display_id,
307 'display_id': handle.display_id,
299 },
308 },
300 })
309 })
301 args, kwargs = pub.call_args_list[2]
310 args, kwargs = pub.call_args_list[2]
302 nt.assert_equal(args, ())
311 nt.assert_equal(args, ())
303 nt.assert_equal(kwargs, {
312 nt.assert_equal(kwargs, {
304 'data': {
313 'data': {
305 'text/plain': repr('z')
314 'text/plain': repr('z')
306 },
315 },
307 'metadata': {},
316 'metadata': {},
308 'transient': {
317 'transient': {
309 'display_id': handle2.display_id,
318 'display_id': handle2.display_id,
310 },
319 },
311 })
320 })
312
321
313
322
314 def test_update_display():
323 def test_update_display():
315 ip = get_ipython()
324 ip = get_ipython()
316 with mock.patch.object(ip.display_pub, 'publish') as pub:
325 with mock.patch.object(ip.display_pub, 'publish') as pub:
317 with nt.assert_raises(TypeError):
326 with nt.assert_raises(TypeError):
318 display.update_display('x')
327 display.update_display('x')
319 display.update_display('x', display_id='1')
328 display.update_display('x', display_id='1')
320 display.update_display('y', display_id='2')
329 display.update_display('y', display_id='2')
321 args, kwargs = pub.call_args_list[0]
330 args, kwargs = pub.call_args_list[0]
322 nt.assert_equal(args, ())
331 nt.assert_equal(args, ())
323 nt.assert_equal(kwargs, {
332 nt.assert_equal(kwargs, {
324 'data': {
333 'data': {
325 'text/plain': repr('x')
334 'text/plain': repr('x')
326 },
335 },
327 'metadata': {},
336 'metadata': {},
328 'transient': {
337 'transient': {
329 'display_id': '1',
338 'display_id': '1',
330 },
339 },
331 'update': True,
340 'update': True,
332 })
341 })
333 args, kwargs = pub.call_args_list[1]
342 args, kwargs = pub.call_args_list[1]
334 nt.assert_equal(args, ())
343 nt.assert_equal(args, ())
335 nt.assert_equal(kwargs, {
344 nt.assert_equal(kwargs, {
336 'data': {
345 'data': {
337 'text/plain': repr('y')
346 'text/plain': repr('y')
338 },
347 },
339 'metadata': {},
348 'metadata': {},
340 'transient': {
349 'transient': {
341 'display_id': '2',
350 'display_id': '2',
342 },
351 },
343 'update': True,
352 'update': True,
344 })
353 })
345
354
346
355
347 def test_display_handle():
356 def test_display_handle():
348 ip = get_ipython()
357 ip = get_ipython()
349 handle = display.DisplayHandle()
358 handle = display.DisplayHandle()
350 nt.assert_is_instance(handle.display_id, str)
359 nt.assert_is_instance(handle.display_id, str)
351 handle = display.DisplayHandle('my-id')
360 handle = display.DisplayHandle('my-id')
352 nt.assert_equal(handle.display_id, 'my-id')
361 nt.assert_equal(handle.display_id, 'my-id')
353 with mock.patch.object(ip.display_pub, 'publish') as pub:
362 with mock.patch.object(ip.display_pub, 'publish') as pub:
354 handle.display('x')
363 handle.display('x')
355 handle.update('y')
364 handle.update('y')
356
365
357 args, kwargs = pub.call_args_list[0]
366 args, kwargs = pub.call_args_list[0]
358 nt.assert_equal(args, ())
367 nt.assert_equal(args, ())
359 nt.assert_equal(kwargs, {
368 nt.assert_equal(kwargs, {
360 'data': {
369 'data': {
361 'text/plain': repr('x')
370 'text/plain': repr('x')
362 },
371 },
363 'metadata': {},
372 'metadata': {},
364 'transient': {
373 'transient': {
365 'display_id': handle.display_id,
374 'display_id': handle.display_id,
366 }
375 }
367 })
376 })
368 args, kwargs = pub.call_args_list[1]
377 args, kwargs = pub.call_args_list[1]
369 nt.assert_equal(args, ())
378 nt.assert_equal(args, ())
370 nt.assert_equal(kwargs, {
379 nt.assert_equal(kwargs, {
371 'data': {
380 'data': {
372 'text/plain': repr('y')
381 'text/plain': repr('y')
373 },
382 },
374 'metadata': {},
383 'metadata': {},
375 'transient': {
384 'transient': {
376 'display_id': handle.display_id,
385 'display_id': handle.display_id,
377 },
386 },
378 'update': True,
387 'update': True,
379 })
388 })
380
389
@@ -1,313 +1,337
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": 1,
25 "metadata": {
25 "metadata": {},
26 "collapsed": true
27 },
28 "outputs": [],
26 "outputs": [],
29 "source": [
27 "source": [
30 "from IPython.display import display, update_display"
28 "from IPython.display import display, update_display"
31 ]
29 ]
32 },
30 },
33 {
31 {
34 "cell_type": "code",
32 "cell_type": "code",
35 "execution_count": 13,
33 "execution_count": 2,
36 "metadata": {},
34 "metadata": {},
37 "outputs": [
35 "outputs": [
38 {
36 {
39 "data": {
37 "data": {
40 "text/plain": [
38 "text/plain": [
41 "'z'"
39 "'z'"
42 ]
40 ]
43 },
41 },
44 "metadata": {},
42 "metadata": {},
45 "output_type": "display_data"
43 "output_type": "display_data"
46 },
44 },
47 {
45 {
48 "data": {
46 "data": {
49 "text/plain": [
47 "text/plain": [
50 "<DisplayHandle display_id=update-me>"
48 "<DisplayHandle display_id=update-me>"
51 ]
49 ]
52 },
50 },
53 "execution_count": 13,
51 "execution_count": 2,
54 "metadata": {},
52 "metadata": {},
55 "output_type": "execute_result"
53 "output_type": "execute_result"
56 }
54 }
57 ],
55 ],
58 "source": [
56 "source": [
59 "handle = display('x', display_id='update-me')\n",
57 "handle = display('x', display_id='update-me')\n",
60 "handle"
58 "handle"
61 ]
59 ]
62 },
60 },
63 {
61 {
64 "cell_type": "markdown",
62 "cell_type": "markdown",
65 "metadata": {},
63 "metadata": {},
66 "source": [
64 "source": [
67 "When we call `handle.display('y')`, we get a new display of 'y',\n",
65 "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."
66 "but in addition to that, we updated the previous display."
69 ]
67 ]
70 },
68 },
71 {
69 {
72 "cell_type": "code",
70 "cell_type": "code",
73 "execution_count": 14,
71 "execution_count": 3,
74 "metadata": {},
72 "metadata": {},
75 "outputs": [
73 "outputs": [
76 {
74 {
77 "data": {
75 "data": {
78 "text/plain": [
76 "text/plain": [
79 "'z'"
77 "'z'"
80 ]
78 ]
81 },
79 },
82 "metadata": {},
80 "metadata": {},
83 "output_type": "display_data"
81 "output_type": "display_data"
84 }
82 }
85 ],
83 ],
86 "source": [
84 "source": [
87 "handle.display('y')"
85 "handle.display('y')"
88 ]
86 ]
89 },
87 },
90 {
88 {
91 "cell_type": "markdown",
89 "cell_type": "markdown",
92 "metadata": {},
90 "metadata": {},
93 "source": [
91 "source": [
94 "We can also *just* update the existing displays,\n",
92 "We can also *just* update the existing displays,\n",
95 "without creating a new display:"
93 "without creating a new display:"
96 ]
94 ]
97 },
95 },
98 {
96 {
99 "cell_type": "code",
97 "cell_type": "code",
100 "execution_count": 15,
98 "execution_count": 4,
101 "metadata": {
99 "metadata": {},
102 "collapsed": true
103 },
104 "outputs": [],
100 "outputs": [],
105 "source": [
101 "source": [
106 "handle.update('z')"
102 "handle.update('z')"
107 ]
103 ]
108 },
104 },
109 {
105 {
110 "cell_type": "markdown",
106 "cell_type": "markdown",
111 "metadata": {},
107 "metadata": {},
112 "source": [
108 "source": [
113 "You don't have to generate display_ids yourself,\n",
109 "You don't have to generate display_ids yourself,\n",
114 "if you specify `display_id=True`, then a unique ID will be assigned:"
110 "if you specify `display_id=True`, then a unique ID will be assigned:"
115 ]
111 ]
116 },
112 },
117 {
113 {
118 "cell_type": "code",
114 "cell_type": "code",
119 "execution_count": 16,
115 "execution_count": 5,
120 "metadata": {},
116 "metadata": {},
121 "outputs": [
117 "outputs": [
122 {
118 {
123 "data": {
119 "data": {
124 "text/plain": [
120 "text/plain": [
125 "'hello'"
121 "'hello'"
126 ]
122 ]
127 },
123 },
128 "metadata": {},
124 "metadata": {},
129 "output_type": "display_data"
125 "output_type": "display_data"
130 },
126 },
131 {
127 {
132 "data": {
128 "data": {
133 "text/plain": [
129 "text/plain": [
134 "<DisplayHandle display_id=07fc47b2ef652ccb70addeee3eb0981a>"
130 "<DisplayHandle display_id=118a56127f42348f8893440da7181c57>"
135 ]
131 ]
136 },
132 },
137 "execution_count": 16,
133 "execution_count": 5,
138 "metadata": {},
134 "metadata": {},
139 "output_type": "execute_result"
135 "output_type": "execute_result"
140 }
136 }
141 ],
137 ],
142 "source": [
138 "source": [
143 "handle = display(\"hello\", display_id=True)\n",
139 "handle = display(\"hello\", display_id=True)\n",
144 "handle"
140 "handle"
145 ]
141 ]
146 },
142 },
147 {
143 {
148 "cell_type": "markdown",
144 "cell_type": "markdown",
149 "metadata": {},
145 "metadata": {},
150 "source": [
146 "source": [
151 "Calling `handle.display(obj)` is the same as calling `display(obj, handle.display_id)`,\n",
147 "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:"
148 "so you don't need to use the handle objects if you don't want to:"
153 ]
149 ]
154 },
150 },
155 {
151 {
156 "cell_type": "code",
152 "cell_type": "code",
157 "execution_count": 17,
153 "execution_count": 6,
158 "metadata": {},
154 "metadata": {},
159 "outputs": [
155 "outputs": [
160 {
156 {
161 "data": {
157 "data": {
162 "text/plain": [
158 "text/plain": [
163 "'z'"
159 "'z'"
164 ]
160 ]
165 },
161 },
166 "metadata": {},
162 "metadata": {},
167 "output_type": "display_data"
163 "output_type": "display_data"
168 }
164 }
169 ],
165 ],
170 "source": [
166 "source": [
171 "display('x', display_id='here');"
167 "display('x', display_id='here');"
172 ]
168 ]
173 },
169 },
174 {
170 {
175 "cell_type": "code",
171 "cell_type": "code",
176 "execution_count": 18,
172 "execution_count": 7,
177 "metadata": {},
173 "metadata": {},
178 "outputs": [
174 "outputs": [
179 {
175 {
180 "data": {
176 "data": {
181 "text/plain": [
177 "text/plain": [
182 "'z'"
178 "'z'"
183 ]
179 ]
184 },
180 },
185 "metadata": {},
181 "metadata": {},
186 "output_type": "display_data"
182 "output_type": "display_data"
187 }
183 }
188 ],
184 ],
189 "source": [
185 "source": [
190 "display('y', display_id='here');"
186 "display('y', display_id='here');"
191 ]
187 ]
192 },
188 },
193 {
189 {
194 "cell_type": "markdown",
190 "cell_type": "markdown",
195 "metadata": {},
191 "metadata": {},
196 "source": [
192 "source": [
197 "And just like `display`, there is now `update_display`,\n",
193 "And just like `display`, there is now `update_display`,\n",
198 "which is what `DisplayHandle.update` calls:"
194 "which is what `DisplayHandle.update` calls:"
199 ]
195 ]
200 },
196 },
201 {
197 {
202 "cell_type": "code",
198 "cell_type": "code",
203 "execution_count": 19,
199 "execution_count": 8,
204 "metadata": {
200 "metadata": {},
205 "collapsed": true
206 },
207 "outputs": [],
201 "outputs": [],
208 "source": [
202 "source": [
209 "update_display('z', display_id='here')"
203 "update_display('z', display_id='here')"
210 ]
204 ]
211 },
205 },
212 {
206 {
213 "cell_type": "markdown",
207 "cell_type": "markdown",
214 "metadata": {},
208 "metadata": {},
215 "source": [
209 "source": [
216 "## More detailed example\n",
210 "## More detailed example\n",
217 "\n",
211 "\n",
218 "One of the motivating use cases for this is simple progress bars.\n",
212 "One of the motivating use cases for this is simple progress bars.\n",
219 "\n",
213 "\n",
220 "Here is an example ProgressBar using these APIs:"
214 "Here is an example ProgressBar using these APIs:"
221 ]
215 ]
222 },
216 },
223 {
217 {
224 "cell_type": "code",
218 "cell_type": "code",
225 "execution_count": 14,
219 "execution_count": 9,
226 "metadata": {},
220 "metadata": {},
227 "outputs": [
221 "outputs": [
228 {
222 {
229 "data": {
223 "data": {
230 "text/html": [
224 "text/html": [
231 "<progress style='width:100%' max='10' value='10'></progress>"
225 "<progress style='width:60ex' max='10' value='10'></progress>"
232 ],
226 ],
233 "text/plain": [
227 "text/plain": [
234 "<IPython.core.display.ProgressBar object>"
228 "[============================================================] 10/10"
235 ]
229 ]
236 },
230 },
237 "metadata": {},
231 "metadata": {},
238 "output_type": "display_data"
232 "output_type": "display_data"
239 }
233 }
240 ],
234 ],
241 "source": [
235 "source": [
242 "from IPython.display import ProgressBar\n",
236 "from IPython.display import ProgressBar\n",
243 "\n",
237 "\n",
244 "bar = ProgressBar(10)\n",
238 "bar = ProgressBar(10)\n",
245 "bar.display()"
239 "bar.display()"
246 ]
240 ]
247 },
241 },
248 {
242 {
249 "cell_type": "markdown",
243 "cell_type": "markdown",
250 "metadata": {},
244 "metadata": {},
251 "source": [
245 "source": [
252 "And the ProgressBar has `.display` and `.update` methods:"
246 "And the ProgressBar has `.display` and `.update` methods:"
253 ]
247 ]
254 },
248 },
255 {
249 {
256 "cell_type": "code",
250 "cell_type": "code",
257 "execution_count": 15,
251 "execution_count": 10,
258 "metadata": {},
252 "metadata": {},
259 "outputs": [
253 "outputs": [
260 {
254 {
261 "data": {
255 "data": {
262 "text/html": [
256 "text/html": [
263 "<progress style='width:100%' max='10' value='10'></progress>"
257 "<progress style='width:60ex' max='10' value='10'></progress>"
264 ],
258 ],
265 "text/plain": [
259 "text/plain": [
266 "<IPython.core.display.ProgressBar object>"
260 "[============================================================] 10/10"
267 ]
261 ]
268 },
262 },
269 "metadata": {},
263 "metadata": {},
270 "output_type": "display_data"
264 "output_type": "display_data"
271 }
265 }
272 ],
266 ],
273 "source": [
267 "source": [
274 "import time\n",
268 "import time\n",
275 "\n",
269 "\n",
276 "bar.display()\n",
270 "bar.display()\n",
277 "\n",
271 "\n",
278 "for i in range(11):\n",
272 "for i in range(11):\n",
279 " bar.progress = i\n",
273 " bar.progress = i\n",
280 " bar.update()\n",
274 " bar.update()\n",
281 " time.sleep(0.25)"
275 " time.sleep(0.25)"
282 ]
276 ]
283 },
277 },
284 {
278 {
285 "cell_type": "markdown",
279 "cell_type": "markdown",
286 "metadata": {},
280 "metadata": {},
287 "source": [
281 "source": [
282 "The ProgressBar also has an update built into iteration:"
283 ]
284 },
285 {
286 "cell_type": "code",
287 "execution_count": 11,
288 "metadata": {},
289 "outputs": [
290 {
291 "data": {
292 "text/html": [
293 "<progress style='width:60ex' max='10' value='10'></progress>"
294 ],
295 "text/plain": [
296 "[============================================================] 10/10"
297 ]
298 },
299 "metadata": {},
300 "output_type": "display_data"
301 }
302 ],
303 "source": [
304 "for i in ProgressBar(10):\n",
305 " time.sleep(0.25)"
306 ]
307 },
308 {
309 "cell_type": "markdown",
310 "metadata": {},
311 "source": [
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."
312 "We would encourage any updatable-display objects that track their own display_ids to follow-suit with `.display()` and `.update()` or `.update_display()` methods."
289 ]
313 ]
290 }
314 }
291 ],
315 ],
292 "metadata": {
316 "metadata": {
293 "kernelspec": {
317 "kernelspec": {
294 "display_name": "Python 3",
318 "display_name": "Python 3",
295 "language": "python",
319 "language": "python",
296 "name": "python3"
320 "name": "python3"
297 },
321 },
298 "language_info": {
322 "language_info": {
299 "codemirror_mode": {
323 "codemirror_mode": {
300 "name": "ipython",
324 "name": "ipython",
301 "version": 3
325 "version": 3
302 },
326 },
303 "file_extension": ".py",
327 "file_extension": ".py",
304 "mimetype": "text/x-python",
328 "mimetype": "text/x-python",
305 "name": "python",
329 "name": "python",
306 "nbconvert_exporter": "python",
330 "nbconvert_exporter": "python",
307 "pygments_lexer": "ipython3",
331 "pygments_lexer": "ipython3",
308 "version": "3.6.2"
332 "version": "3.6.2"
309 }
333 }
310 },
334 },
311 "nbformat": 4,
335 "nbformat": 4,
312 "nbformat_minor": 1
336 "nbformat_minor": 1
313 }
337 }
General Comments 0
You need to be logged in to leave comments. Login now