{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Updatable Displays\n", "\n", "Note: This feature requires notebook >= 5.0 or JupyterLab, and \n", "\n", "\n", "IPython 6 implements a new API as part of the Jupyter Protocol version 5.1 for easily updating displays.\n", "\n", "When you display something, you can now pass a `display_id` argument to attach an id to that output.\n", "\n", "Any future display with the same ID will also update other displays that had the same ID.\n", "\n", "`display` with a `display_id` will return a `DisplayHandle`\n", "object, which gives you easy access to update the output:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from IPython.display import display, update_display" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'z'" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "handle = display('x', display_id='update-me')\n", "handle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we call `handle.display('y')`, we get a new display of 'y',\n", "but in addition to that, we updated the previous display." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'z'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "handle.display('y')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also *just* update the existing displays,\n", "without creating a new display:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": true }, "outputs": [], "source": [ "handle.update('z')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You don't have to generate display_ids yourself,\n", "if you specify `display_id=True`, then a unique ID will be assigned:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'hello'" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "handle = display(\"hello\", display_id=True)\n", "handle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Calling `handle.display(obj)` is the same as calling `display(obj, handle.display_id)`,\n", "so you don't need to use the handle objects if you don't want to:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'z'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "display('x', display_id='here');" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'z'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "display('y', display_id='here');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And just like `display`, there is now `update_display`,\n", "which is what `DisplayHandle.update` calls:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": true }, "outputs": [], "source": [ "update_display('z', display_id='here')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More detailed example\n", "\n", "One of the motivating use cases for this is simple progress bars.\n", "\n", "Here is an example ProgressBar using these APIs:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " 10 / 10\n", " " ], "text/plain": [ "[============================================================] 10/10" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import os\n", "from binascii import hexlify\n", "\n", "class ProgressBar(object):\n", " def __init__(self, capacity):\n", " self.progress = 0\n", " self.capacity = capacity\n", " self.html_width = '60ex'\n", " self.text_width = 60\n", " self._display_id = hexlify(os.urandom(8)).decode('ascii')\n", " \n", " def __repr__(self):\n", " fraction = self.progress / self.capacity\n", " filled = '=' * int(fraction * self.text_width)\n", " rest = ' ' * (self.text_width - len(filled))\n", " return '[{}{}] {}/{}'.format(\n", " filled, rest,\n", " self.progress, self.capacity,\n", " )\n", " \n", " def _repr_html_(self):\n", " return \"\"\"\n", " {progress} / {capacity}\n", " \"\"\".format(\n", " progress=self.progress,\n", " capacity=self.capacity,\n", " width=self.html_width,\n", " )\n", " \n", " def display(self):\n", " display(self, display_id=self._display_id)\n", " \n", " def update(self):\n", " update_display(self, display_id=self._display_id)\n", "\n", "bar = ProgressBar(10)\n", "bar.display()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And the ProgressBar has `.display` and `.update` methods:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " 10 / 10\n", " " ], "text/plain": [ "[============================================================] 10/10" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import time\n", "\n", "bar.display()\n", "\n", "for i in range(11):\n", " bar.progress = i\n", " bar.update()\n", " time.sleep(0.25)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We would encourage any updatable-display objects that track their own display_ids to follow-suit with `.display()` and `.update()` or `.update_display()` methods." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 1 }