<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - Olivier Coudray</title><link href="https://jupyter.org/blog/" rel="alternate"/><link href="https://jupyter.org/blog/feeds/author-olivier-coudray.atom.xml" rel="self"/><id>https://jupyter.org/blog/</id><updated>2018-03-05T12:12:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>Authoring Custom Jupyter Widgets</title><link href="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/" rel="alternate"/><published>2018-03-05T12:12:00+00:00</published><updated>2018-03-05T12:12:00+00:00</updated><author><name>Olivier Borderies</name></author><id>tag:jupyter.org,2018-03-05:/blog/posts/2018/authoring-custom-jupyter-widgets/</id><summary type="html">&lt;p&gt;A Hands-On Guide&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="https://jupyter.org/widgets"&gt;Jupyter interactive widgets&lt;/a&gt; enhance the notebook experience by allowing users to create graphical user interfaces. They enable richer interaction with the data and computing resources.&lt;/p&gt;
&lt;p&gt;While the base &lt;a href="https://github.com/jupyter-widgets/ipywidgets"&gt;ipywidgets&lt;/a&gt; library comes with a &lt;a href="https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html"&gt;number of controls&lt;/a&gt; such as sliders, buttons, and dropdowns, it is in fact much more than a collection of basic controls: it is the foundation of a framework upon which one can build arbitrarily complex interactions.&lt;/p&gt;
&lt;p&gt;Examples of custom widget libraries built upon the foundational package are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/bloomberg/bqplot"&gt;bqplot&lt;/a&gt;, a d3-Jupyter bridge, and a 2-D plotting library following the constructs of the Grammar of Graphics,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ellisonbg/ipyleaflet"&gt;ipyleaflet&lt;/a&gt;, a leaflet-Jupyter bridge enabling maps visualization in the Jupyter notebook,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jovyan/pythreejs"&gt;pythreejs&lt;/a&gt;, a 3-D visualization library bringing the functionalities of Three.js into the Jupyter notebook,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/maartenbreddels/ipyvolume"&gt;ipyvolume&lt;/a&gt;, a 3-D plotting library also based on Three.js enabling volume rendering, quiver plots and much more.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="jupyter-widgets-a-bridge-between-two-continents"&gt;Jupyter Widgets, a Bridge Between Two Continents&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Jupyter widgets provide a means to bridge the kernel and the rich ecosystem of JavaScript visualization libraries for the web browser. It is an amazing opportunity for scientific developers to use all these resources in their language of choice.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This article is meant to serve as a guide for developers interested in authoring a custom widget library and bridge the gap between the basic examples of the official documentation and fully-fledged visualization libraries like the ones listed above.&lt;/p&gt;
&lt;p&gt;A number of resources are provided alongside the article, including the complete source code of the examples, notebooks, and Binder links.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In this post, we focus on the &lt;strong&gt;Python&lt;/strong&gt; back-end, even though &lt;a href="https://github.com/jupyter/jupyter/wiki/Jupyter-kernels"&gt;dozens of Jupyter kernels&lt;/a&gt; exist. The Python kernel is the reference implementation of the Jupyter protocol and remains the most featureful. We should also mention QuantStack’s &lt;a href="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/"&gt;Xeus&lt;/a&gt;, a native C++ implementation of Jupyter kernel protocol, which supports interactive widgets. Xeus is used as the foundation for the support of Jupyter widgets for the R and C++ kernels.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;
&lt;img alt="Xeus: C++ implementation of Jupyter kernel protocol" src="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/images/001-1_7cPVSk9PpumGA6vM79kHwQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Xeus: C++ implementation of Jupyter kernel protocol&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="a-hands-on-guide"&gt;A Hands-On Guide&lt;/h2&gt;
&lt;p&gt;While the Jupyter community is thriving, and we start seeing a growth in the number of custom widget libraries as well. Although the learning curve from the examples of the &lt;a href="https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Custom.html"&gt;official Jupyter documentation&lt;/a&gt; to authoring state-of-the-art libraries like the ones listed above is steep and may be intimidating.&lt;/p&gt;
&lt;p&gt;We started on this path a few months ago and benefited from guidance from core Jupyter developers along the way. Now, we would like to share the lessons learned. We hope this will help turn this mountain trail into a new silk road!&lt;/p&gt;
&lt;p&gt;Let us start with the ‘Hello World’ example widget from the ipywidgets documentation, before moving on to more advanced use cases.&lt;/p&gt;
&lt;h2 id="1-improving-on-the-hello-world-example-from-the-documentation"&gt;1 — Improving on the Hello-World Example from the Documentation&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;This section is derived from the &lt;a href="http://ipywidgets.readthedocs.io"&gt;ipywidgets documentation&lt;/a&gt;. The code snippets are CC-0 licensed.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The idea behind Jupyter widgets is to enable a bi-directional communication channel between the kernel (back-end) and the JavaScript front-end. In Python, widgets are special objects which are automatically synchronized with a counterpart object in the JavaScript front-end. In the back-end, the change events are handled by the &lt;a href="http://traitlets.readthedocs.io/en/stable/"&gt;traitlets&lt;/a&gt; package, which implements the observer pattern, while on the JavaScript side, this is done with the &lt;a href="http://backbonejs.org/"&gt;Backbone.js&lt;/a&gt; library.&lt;/p&gt;
&lt;p&gt;The front-end implementation follows the &lt;a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller"&gt;MVC (Model View Controller) pattern&lt;/a&gt;. This allows the rendering of the same widget in multiple cell outputs, where all views share the same model, analogous to printing out a string variable multiple times.&lt;/p&gt;
&lt;p&gt;The official documentation includes a &lt;a href="https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Custom.html"&gt;Hello-World example widget&lt;/a&gt;, which offers an example of synchronization from Python to JavaScript, but not the other way around. Our &lt;a href="https://github.com/PierreMarion23/jupyter-widget-hello-world-binder"&gt;jupyter-widget-hello-world-binder&lt;/a&gt; example provides a slightly more advanced version of it which demonstrates the bi-directional communication between the Python kernel and the JavaScript front-end. You can experiment with this widget on Binder or simply check out the example notebook with nbviewer.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/PierreMarion23/jupyter-widget-hello-world-binder/master?filepath=hello_world.ipynb"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/images/002-1_x6oPwz8nMiy4YIgEpczQHg.webp" alt="Launch Binder" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://nbviewer.jupyter.org/github/PierreMarion23/jupyter-widget-hello-world-binder/blob/master/hello_world.ipynb"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/images/003-1_lK1Zi8_qorxl5ZrL_ZMc7w.webp" alt="Render with nbviewer" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let us briefly present the main aspects of the implementation.&lt;/p&gt;
&lt;p&gt;The JavaScript front-end defines a custom view by extending the base &lt;code&gt;DOMWidgetView&lt;/code&gt; class from the base package:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HelloView&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;widgets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DOMWidgetView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value_changed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;change:value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value_changed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;value_changed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textContent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The Python back-end extends the corresponding base class from the &lt;code&gt;ipywidgets&lt;/code&gt; package:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;HelloWidget&lt;/span&gt;(&lt;span class="n"&gt;widgets&lt;/span&gt;.&lt;span class="n"&gt;DOMWidget&lt;/span&gt;):
    &lt;span class="n"&gt;_view_name&lt;/span&gt; = &lt;span class="n"&gt;Unicode&lt;/span&gt;(&lt;span class="s"&gt;&amp;#39;HelloView&amp;#39;&lt;/span&gt;).&lt;span class="n"&gt;tag&lt;/span&gt;(&lt;span class="n"&gt;sync&lt;/span&gt;=&lt;span class="nb"&gt;True&lt;/span&gt;)
    &lt;span class="n"&gt;_view_module&lt;/span&gt; = &lt;span class="n"&gt;Unicode&lt;/span&gt;(&lt;span class="s"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;).&lt;span class="n"&gt;tag&lt;/span&gt;(&lt;span class="n"&gt;sync&lt;/span&gt;=&lt;span class="nb"&gt;True&lt;/span&gt;)
    &lt;span class="n"&gt;_view_module_version&lt;/span&gt; = &lt;span class="n"&gt;Unicode&lt;/span&gt;(&lt;span class="s"&gt;&amp;#39;0.1.0&amp;#39;&lt;/span&gt;).&lt;span class="n"&gt;tag&lt;/span&gt;(&lt;span class="n"&gt;sync&lt;/span&gt;=&lt;span class="nb"&gt;True&lt;/span&gt;)

    &lt;span class="nb"&gt;value&lt;/span&gt; = &lt;span class="n"&gt;Unicode&lt;/span&gt;(&lt;span class="s"&gt;&amp;#39;Hello World!&amp;#39;&lt;/span&gt;).&lt;span class="n"&gt;tag&lt;/span&gt;(&lt;span class="n"&gt;sync&lt;/span&gt;=&lt;span class="nb"&gt;True&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;value&lt;/code&gt; attribute get synchronized between the back-end and the front-end.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;value_changed&lt;/code&gt; callback, which changes the HTML representation of the widget is attached to changes of the value property with the line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;change:value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value_changed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the &lt;a href="https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Custom.html"&gt;Hello-World&lt;/a&gt; example from the documentation, there is no way to change the JavaScript &lt;code&gt;value&lt;/code&gt; from the notebook front-end. We have added this feature in order to illustrate the bi-directional synchronization between JavaScript and Python. The idea is to trigger an event in the browser which will update the JavaScript model and then automatically - that’s the magic of ipywidgets - the Python back-end. These additional lines trigger the model update:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// update the JavasScript model&lt;/span&gt;
&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;formElement&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// sync with Python&lt;/span&gt;
&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;touch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Conversely, when changing the value from the Python kernel the corresponding JavaScript value gets updated, as explained above.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href="https://github.com/PierreMarion23/jupyter-widget-hello-world-binder"&gt;jupyter-widget-hello-world-binder&lt;/a&gt; repo for more information. The notebook includes additional details and comments.&lt;/p&gt;
&lt;h2 id="2-first-example-involving-bi-directional-communication"&gt;2 — First Example Involving Bi-Directional Communication&lt;/h2&gt;
&lt;p&gt;In the previous section, widgets were entirely defined in the notebook. We now show how to move the implementation outside of the notebook document and produce a proper installable package.&lt;/p&gt;
&lt;h3 id="21-first-widget"&gt;2.1 — First Widget&lt;/h3&gt;
&lt;p&gt;To help you create your own custom widget, the Jupyter team provides a &lt;a href="https://github.com/jupyter-widgets/widget-cookiecutter"&gt;cookiecutter&lt;/a&gt; template, producing a custom Jupyter widget library containing all the boilerplate for packaging. The cookiecutter is initialized with the hello-world widget from the documentation.&lt;/p&gt;
&lt;p&gt;We used the widget cookiecutter to put the hello-world widget presented in the previous section into a well-organized GitHub repository. It contains&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the Python part in the &lt;code&gt;first_widget&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;the JavaScript part in the &lt;code&gt;js&lt;/code&gt; folder.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;README&lt;/code&gt; provides detailed instructions to install the package, together with information about how to enable the Jupyter extension, and tips for custom widget authors. It delves into the technical details a bit more than this overview blog post.&lt;/p&gt;
&lt;p&gt;Now that the basics of two-way synchronization are covered, you can &lt;strong&gt;create arbitrarily complex widgets!&lt;/strong&gt; The key is to identify the data you would like to synchronize between the front-end and the back-end. Then you can set up the events that will update this data when a change is detected using the building blocks above.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Now you can &lt;strong&gt;‘widget-ify’ any JavaScript library!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="22-barebones-setuppy"&gt;2.2 — Barebones &lt;code&gt;setup.py&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://github.com/jupyter-widgets/widget-cookiecutter/blob/master/%7B%7Bcookiecutter.github_project_name%7D%7D/setup.py"&gt;setup.py&lt;/a&gt; described in the official documentation tries to automate many of the build steps, at the cost of readability. Fortunately, packaging Jupyter widgets will work with the bare-bones &lt;code&gt;setup.py&lt;/code&gt; we provide.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The gain is a clearer, lighter (~100 less lines) &lt;code&gt;setup.py&lt;/code&gt;, giving you a better understanding of what is happening.&lt;/li&gt;
&lt;li&gt;The drawback is that you need one extra step to install the widget from source.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;#&lt;/span&gt; Example: d3-slider
$ git clone https://gitlab.com/oscar6echo/jupyter-widget-d3-slider.git
$ cd js
$ npm install
$ cd ..
$ pip install -e .

&lt;span class="gh"&gt;#&lt;/span&gt; Extra line `jupyter nbextension install`
$ jupyter nbextension install --py --symlink --sys-prefix jupyter_widget_d3_slider
$ jupyter nbextension enable --py --sys-prefix jupyter_widget_d3_slider
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;More importantly we thought that it was clearer to keep the build steps of the Python and JavaScript packages separated. Both build processes are well documented, making the steps easier to follow. Finally, the inclusion of compiled JavaScript bundles in the Python package, is more explicit.&lt;/p&gt;
&lt;h3 id="23-side-note-naming-conventions"&gt;2.3 — Side Note: Naming Conventions&lt;/h3&gt;
&lt;p&gt;Naming conventions for Python packages are covered by &lt;a href="https://www.python.org/dev/peps/pep-0008/#package-and-module-names"&gt;PEP8&lt;/a&gt;. They can be a bit tricky in the case of 2-words names like “first-widget”. Where to use underscore (_) or hyphen (-) ? Typically “-” is used in GitHub repositories, and URLs and JavaScript while any folder or file in a Python module can only contain “_”. In the case of a Jupyter widget there is an extra attention point: in the setup.py file, the &lt;code&gt;data_files&lt;/code&gt; argument in the &lt;code&gt;setup&lt;/code&gt; function is a list of tuples. For each, the first element (representing a path in the filesystem) contains “-” as it relates to JavaScript code while the paths in the second contain “_” as they represent paths in the Python package. For a full example see the &lt;a href="https://github.com/ocoudray/FirstWidget"&gt;first-widget&lt;/a&gt; repo and the detailed README.&lt;/p&gt;
&lt;h2 id="3-increasingly-complex-widgets"&gt;3 — Increasingly Complex Widgets&lt;/h2&gt;
&lt;p&gt;We made the following three widgets, gradually adding complexity. The first two examples are meant as educational examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/oscar6echo/jupyter-widget-d3-slider/"&gt;d3-slider&lt;/a&gt;, a d3.js-based slider,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ocoudray/jupyter-drawing-pad"&gt;drawing-pad&lt;/a&gt;, a 2-D drawing pad,&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We hope that the last example will become more than a demonstration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/PierreMarion23/ipypivot"&gt;ipypivot&lt;/a&gt;, a visual Pivot Table UI&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In each case, the GitHub repository includes a demo notebook and the required boilerplate for &lt;a href="https://mybinder.org/"&gt;Binder&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="31-d3-slider"&gt;3.1 — d3-slider&lt;/h3&gt;
&lt;p&gt;This &lt;a href="https://gitlab.com/oscar6echo/jupyter-widget-d3-slider/"&gt;custom d3-slider widget&lt;/a&gt; wraps a &lt;a href="https://bl.ocks.org/mbostock/6452972"&gt;simple custom slider&lt;/a&gt; based on the fantastic &lt;a href="https://d3js.org/"&gt;d3.js library&lt;/a&gt;. You can run and try it on the &lt;a href="https://github.com/ocoudray/jupyter-d3-slider-binder"&gt;Binder repo&lt;/a&gt; or watch it on nbviewer.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/ocoudray/jupyter-d3-slider-binder/master?filepath=Slider_d3_demo.ipynb"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/images/002-1_x6oPwz8nMiy4YIgEpczQHg.webp" alt="Launch Binder" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://nbviewer.jupyter.org/urls/gitlab.com/oscar6echo/jupyter-widget-d3-slider/raw/master/notebooks/demo_d3_slider.ipynb"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/images/003-1_lK1Zi8_qorxl5ZrL_ZMc7w.webp" alt="Render with nbviewer" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="d3-slider widget" src="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/images/004-1_W8oXVTdQ4na6qbkOSpo88w.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;d3-slider widget&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To install:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;jupyter_widget_d3_slider
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="32-drawing-pad"&gt;3.2 — drawing-pad&lt;/h3&gt;
&lt;p&gt;This &lt;a href="https://github.com/ocoudray/jupyter-drawing-pad"&gt;small drawing pad app&lt;/a&gt;, is inspired from &lt;a href="https://codepen.io/anon/pen/aLYeNB"&gt;this codepen&lt;/a&gt;. You can run and try it on the &lt;a href="https://github.com/PierreMarion23/jupyter-widget-drawing-pad-binder"&gt;Binder repo&lt;/a&gt; or watch it on nbviewer.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/PierreMarion23/jupyter-widget-drawing-pad-binder/master?filepath=Demo_drawing_pad.ipynb"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/images/002-1_x6oPwz8nMiy4YIgEpczQHg.webp" alt="Launch Binder" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://nbviewer.jupyter.org/github/ocoudray/jupyter-drawing-pad/blob/master/Example/Demo_drawing_pad.ipynb"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/images/003-1_lK1Zi8_qorxl5ZrL_ZMc7w.webp" alt="Render with nbviewer" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="drawing pad widget" src="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/images/005-1_32qRaKxSbihtMaetq6jFog.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;drawing pad widget&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To install:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;jupyter-drawing-pad
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="33-ipypivot"&gt;3.3 — ipypivot&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://github.com/PierreMarion23/pivot-table-widget"&gt;ipypivot&lt;/a&gt; widget, wraps the convenient &lt;a href="https://github.com/nicolaskruchten/pivottable"&gt;PivotTable.js library&lt;/a&gt;. You can run and try it on the &lt;a href="https://github.com/PierreMarion23/ipypivot-binder"&gt;binder repo&lt;/a&gt; or watch it on nbviewer.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/PierreMarion23/ipypivot-binder/master?filepath=demo_pivot_table.ipynb"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/images/002-1_x6oPwz8nMiy4YIgEpczQHg.webp" alt="Launch Binder" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://nbviewer.jupyter.org/github/PierreMarion23/ipypivot/blob/master/notebooks/demo_ipypivot.ipynb"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/images/003-1_lK1Zi8_qorxl5ZrL_ZMc7w.webp" alt="Render with nbviewer" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="ipypivot widget" src="https://jupyter.org/blog/posts/2018/authoring-custom-jupyter-widgets/images/006-1_FnkBH8yA-PfCNCxXA2PNIw.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;ipypivot widget&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To install:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;ipypivot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;conda&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;conda-forge&lt;span class="w"&gt; &lt;/span&gt;ipypivot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;NOTE: The PivotUI widget is a &lt;a href="https://github.com/PierreMarion23/ipypivot/blob/master/js/lib/widget_pivotui_box.js"&gt;combo of a custom widget and core widgets&lt;/a&gt;. This modular approach is more flexible (and looks nicer) but the same features can be made in a &lt;a href="https://github.com/PierreMarion23/ipypivot/blob/alt/js/lib/widget_pivotui.js"&gt;single custom widget&lt;/a&gt; containing extra buttons and display fields. The &lt;a href="https://github.com/PierreMarion23/ipypivot/tree/alt"&gt;&lt;code&gt;alt&lt;/code&gt; branch&lt;/a&gt; of the repo contains this version.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="34-including-javascript-callbacks"&gt;3.4 — Including JavaScript Callbacks&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://github.com/PierreMarion23/pivot-table-widget"&gt;ipypivot&lt;/a&gt; widget is an example of a widget that transparently wraps a JavaScript library. Transparent in the sense that &lt;strong&gt;all parameters&lt;/strong&gt; of the JavaScript API are exposed, &lt;strong&gt;including functions&lt;/strong&gt;, which are exposed in the form of strings on the Python side. In Python, JavaScript functions can only be strings, so there is an &lt;code&gt;eval()&lt;/code&gt; to convert them to actual JavaScript functions.&lt;/p&gt;
&lt;p&gt;The benefit is that all the functionalities of the JS libraries are exposed to the Python users with a very thin API. Developers can ‘widget-ify’ a large array of interesting libraries, thereby boosting the productivity of a Jupyter notebook user.&lt;/p&gt;
&lt;p&gt;The downside is naturally the security concerns of enabling arbitrary JavaScript code to be injected by the notebook users. It is less a concern in the context of notebooks being shared within a small team of coworkers.&lt;/p&gt;
&lt;p&gt;We are currently exploring means to execute the user-provided arbitrary JavaScript function in a sandboxed fashion, for example using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe"&gt;iframe &lt;code&gt;srcdoc&lt;/code&gt; field&lt;/a&gt; (Cf. this &lt;a href="https://github.com/oscar6echo/notebook-image-tabs"&gt;repo&lt;/a&gt; for an example, though not in a Jupyter widget context) and messages can be sent back and forth between the main page and an iframe with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage"&gt;Window.postMessage&lt;/a&gt; function (see this &lt;a href="https://gist.github.com/pbojinov/8965299"&gt;gist&lt;/a&gt; for a bare bones example).&lt;/p&gt;
&lt;h3 id="35-enabling-jupyter-widgets-by-default"&gt;3.5 — Enabling Jupyter Widgets By Default&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;jupyter nbextension enable&lt;/code&gt; command, arguably cumbersome, has become unnecessary as Jupyter widgets can be enabled by default from notebook version 5.3 (included). See this &lt;a href="https://github.com/jupyter/notebook/pull/3116"&gt;PR&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In order to be future-proof, all the widgets in this article include the file which triggers this ‘automatic enable’, and require notebook &amp;gt;= 5.3. Thus the pip-installation of our widgets is a one-line command. However, in dev mode, you still need to install and enable the notebook example (see section 3.1 for an example).&lt;/p&gt;
&lt;p&gt;If you are working with an older version of notebook (run &lt;code&gt;jupyter notebook --version&lt;/code&gt; to check), you will have to run the following command after pip-installing a widget:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;jupyter&lt;span class="w"&gt; &lt;/span&gt;nbextension&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--py&lt;span class="w"&gt; &lt;/span&gt;--sys-prefix&lt;span class="w"&gt; &lt;/span&gt;name_of_the_widget
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="4-packaging-and-publishing"&gt;4 — Packaging and Publishing&lt;/h2&gt;
&lt;h3 id="41-pypi-and-npm"&gt;4.1 — PyPI and npm&lt;/h3&gt;
&lt;p&gt;If you have followed the “best practices” so far, packaging shouldn’t be an issue. Indeed, the &lt;a href="https://github.com/jupyter-widgets/widget-cookiecutter"&gt;cookiecutter&lt;/a&gt; provides a template of an easily-packageable widget.&lt;/p&gt;
&lt;p&gt;Once your package is ready, you can publish it on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pypi.python.org/pypi"&gt;PyPI&lt;/a&gt; for the package to be pip-installable&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/"&gt;npmjs&lt;/a&gt; for the JavaScript extension, which is necessary to use the widget as a standalone application (outside of the notebook), render it with nbviewer, and also in the JupyterLab context.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To do so, you can follow &lt;a href="https://github.com/ocoudray/first-widget#4---publish-on-pypi-and-npm"&gt;these instructions&lt;/a&gt; in the documentation of &lt;a href="https://github.com/ocoudray/first-widget"&gt;first-widget&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="42-conda-forge"&gt;4.2 — conda-forge&lt;/h3&gt;
&lt;p&gt;Conda has several advantages over pip:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is a general-purpose package manager, which allows for non-python dependencies.&lt;/li&gt;
&lt;li&gt;Unlike pip, it also has a real dependency solver, which prevents breaking your environments when updating a single package.&lt;/li&gt;
&lt;li&gt;It allows creating virtual environments to isolate your projects.&lt;/li&gt;
&lt;li&gt;It allows for one-line installation of Jupyter extension, including the enabling of the extension as a “post-link” script (temporary advantage: see the previous section).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Conda packages are available on different channels. The default channel is administrated by Anaconda Inc. The usually recommended channel to upload open source projects is conda-forge, as &lt;a href="https://www.anaconda.com/blog/developer-blog/anaconda-build-migration-conda-forge/"&gt;this article&lt;/a&gt; from Anaconda announces. To add this channel to your conda configuration, run the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda config --add channels conda-forge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Several steps are needed to publish a package on conda forge, using a so-called ‘recipe’:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;writing the recipe, which describes how to build the package along with the dependencies required for building and running it&lt;/li&gt;
&lt;li&gt;testing the recipe&lt;/li&gt;
&lt;li&gt;publishing the package on the conda-forge GitHub by forking their &lt;a href="https://github.com/conda-forge/staged-recipes"&gt;staged-recipes repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;maintaining the package&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href="https://conda.io/docs/user-guide/tasks/build-packages/index.html"&gt;conda doc&lt;/a&gt; and the &lt;a href="https://conda-forge.org/docs/"&gt;conda-forge doc&lt;/a&gt; are very clear and give you much more detailed information about this subject. If you do not want to read the full doc, and jump straight to the necessary information for publishing a new package, you may want to have a look at our &lt;a href="https://github.com/ocoudray/first-widget"&gt;first-widget repo&lt;/a&gt;. The README contains a &lt;a href="https://github.com/ocoudray/first-widget#5---publish-on-conda-forge"&gt;section&lt;/a&gt; describing the publishing process for conda-forge.&lt;/p&gt;
&lt;h3 id="43-automatic-push-script"&gt;4.3 — Automatic Push Script&lt;/h3&gt;
&lt;p&gt;The sequence of steps to update the version of a Jupyter widget and do all the pushing to the various repositories is quite long.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;run npm prepare to build the js in folder &lt;code&gt;static/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Update version in &lt;code&gt;__meta__.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;push package to &lt;a href="https://pypi.python.org/pypi"&gt;pypi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;compute package sha256&lt;/li&gt;
&lt;li&gt;tag repo with version&lt;/li&gt;
&lt;li&gt;push to &lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt; / &lt;a href="https://gitlab.com/"&gt;GitLab&lt;/a&gt; / &lt;a href="https://bitbucket.org/"&gt;Bitbucket&lt;/a&gt; including tag&lt;/li&gt;
&lt;li&gt;push js to &lt;a href="https://www.npmjs.com"&gt;npmjs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;push to conda-forge (which includes sha256 hash)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to automate the process we advise to have a look at Maarten Breddels’ &lt;a href="https://github.com/maartenbreddels/releash"&gt;releash package&lt;/a&gt; (release with relish :-) ), and how it is used in the context of &lt;a href="https://github.com/maartenbreddels/ipyvolume/blob/master/.releash.py"&gt;ipyvolume&lt;/a&gt; and &lt;a href="https://github.com/QuantStack/ipysheet/blob/master/.releash.py"&gt;ipysheet&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="44-binder-and-nbviewer"&gt;4.4 — Binder and nbviewer&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://nbviewer.org/"&gt;nbviewer&lt;/a&gt; and &lt;a href="http://mybinder.org/"&gt;Binder&lt;/a&gt; are two fantastic tools to share both static and live notebooks.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;nbviewer&lt;/strong&gt; requires a URL to a valid notebook JSON file — typically hosted on GitHub/GitLab. &lt;a href="https://ipywidgets.readthedocs.io/en/latest/embedding.html"&gt;To make widgets render in nbviewer&lt;/a&gt;, you need (1) to make the JavaScript package for your widget available on npm, (2) to run the notebook with the corresponding JavaScript extension (with the same version), and (3) to save the notebook widget state (in the ‘Widgets’ tab) before pushing it to GitHub / GitLab.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Binder&lt;/strong&gt; requires a URL to a GitHub repository containing notebooks and a manifest of the dependencies required to run this notebook, which is used to produce a Docker image including all the resources to run the notebook. Check out the &lt;a href="http://mybinder.readthedocs.io/en/latest/faq.html"&gt;mybinder.org documentation&lt;/a&gt; to know how exactly to make use of it. Another resource is the &lt;a href="https://binderhub.readthedocs.io/en/latest/"&gt;BinderHub documentation&lt;/a&gt; if you want to host your own deployment of Binder. Either way we also highly recommend the article &lt;a href="https://jupyter.org/blog/posts/2017/binder-2-0-a-tech-guide-2017/"&gt;Binder 2.0, a Tech Guide&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="5-conclusion"&gt;5 — Conclusion&lt;/h2&gt;
&lt;p&gt;Hopefully you will have learned something reading this article. We believe in the potential of Jupyter widgets and hope that this intermediate-level article will help getting more people involved in the development of the ecosystem.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: This article only covers the case of the classic Jupyter notebook. The integration with JupyterLab will be covered in a future article!&lt;/p&gt;
&lt;p&gt;If you find bugs or are interested in improving the example widgets presented here, please do not hesitate contact the authors or open a pull request!&lt;/p&gt;
&lt;h2 id="about-the-authors"&gt;About the Authors&lt;/h2&gt;
&lt;p&gt;Alphabetical order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Olivier Borderies, Société Générale&lt;/li&gt;
&lt;li&gt;Olivier Coudray, Student at École Polytechnique&lt;/li&gt;
&lt;li&gt;Pierre Marion, Student at École Polytechnique&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The software presented in this post was built upon the work of a large number of people including the &lt;strong&gt;Jupyter&lt;/strong&gt; team. We are especially grateful to &lt;a href="https://twitter.com/SylvainCorlay"&gt;Sylvain Corlay&lt;/a&gt;, &lt;a href="https://twitter.com/jason_grout"&gt;Jason Grout&lt;/a&gt;, &lt;a href="https://twitter.com/ivanov"&gt;Paul Ivanov&lt;/a&gt;, and &lt;a href="https://twitter.com/steve_silvester"&gt;Steven Silvester&lt;/a&gt; from the Jupyter Steering Council, as well as &lt;a href="https://twitter.com/maartenbreddels"&gt;Maarten Breddels&lt;/a&gt; and &lt;a href="https://twitter.com/pascalbugnion"&gt;Pascal Bugnion&lt;/a&gt;.&lt;/p&gt;
</content><category term="widgets"/></entry></feed>