Note: This feature requires notebook >= 5.0 or JupyterLab, and
IPython 6 implements a new API as part of the Jupyter Protocol version 5.1 for easily updating displays.
When you display something, you can now pass a display_id
argument to attach an id to that output.
Any future display with the same ID will also update other displays that had the same ID.
display
with a display_id
will return a DisplayHandle
object, which gives you easy access to update the output:
from IPython.display import display, update_display
handle = display('x', display_id='update-me')
handle
'z'
<DisplayHandle display_id=update-me>
When we call handle.display('y')
, we get a new display of 'y',
but in addition to that, we updated the previous display.
handle.display('y')
'z'
We can also just update the existing displays, without creating a new display:
handle.update('z')
You don't have to generate display_ids yourself,
if you specify display_id=True
, then a unique ID will be assigned:
handle = display("hello", display_id=True)
handle
'hello'
<DisplayHandle display_id=07fc47b2ef652ccb70addeee3eb0981a>
Calling handle.display(obj)
is the same as calling display(obj, handle.display_id)
,
so you don't need to use the handle objects if you don't want to:
display('x', display_id='here');
'z'
display('y', display_id='here');
'z'
And just like display
, there is now update_display
,
which is what DisplayHandle.update
calls:
update_display('z', display_id='here')
One of the motivating use cases for this is simple progress bars.
Here is an example ProgressBar using these APIs:
import os
from binascii import hexlify
class ProgressBar(object):
def __init__(self, capacity):
self.progress = 0
self.capacity = capacity
self.html_width = '60ex'
self.text_width = 60
self._display_id = hexlify(os.urandom(8)).decode('ascii')
def __repr__(self):
fraction = self.progress / self.capacity
filled = '=' * int(fraction * self.text_width)
rest = ' ' * (self.text_width - len(filled))
return '[{}{}] {}/{}'.format(
filled, rest,
self.progress, self.capacity,
)
def _repr_html_(self):
return """<progress
value={progress}
max={capacity}
style="width: {width}"/>
{progress} / {capacity}
""".format(
progress=self.progress,
capacity=self.capacity,
width=self.html_width,
)
def display(self):
display(self, display_id=self._display_id)
def update(self):
update_display(self, display_id=self._display_id)
bar = ProgressBar(10)
bar.display()
And the ProgressBar has .display
and .update
methods:
import time
bar.display()
for i in range(11):
bar.progress = i
bar.update()
time.sleep(0.25)
We would encourage any updatable-display objects that track their own display_ids to follow-suit with .display()
and .update()
or .update_display()
methods.