#!/usr/bin/env python # coding: utf-8 # # Updatable Displays # # 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: # In[10]: from IPython.display import display, update_display # In[13]: handle = display('x', display_id='update-me') handle # When we call `handle.display('y')`, we get a new display of 'y', # but in addition to that, we updated the previous display. # In[14]: handle.display('y') # We can also *just* update the existing displays, # without creating a new display: # In[15]: 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: # In[16]: handle = display("hello", display_id=True) handle # 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: # In[17]: display('x', display_id='here'); # In[18]: display('y', display_id='here'); # And just like `display`, there is now `update_display`, # which is what `DisplayHandle.update` calls: # In[19]: update_display('z', display_id='here') # ## More detailed example # # One of the motivating use cases for this is simple progress bars. # # Here is an example ProgressBar using these APIs: # In[35]: 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} / {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: # In[36]: 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.