<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - widgets</title><link href="https://jasongrout.github.io/medium-archive/pelican/" rel="alternate"/><link href="https://jasongrout.github.io/medium-archive/pelican/feeds/tag-widgets.atom.xml" rel="self"/><id>https://jasongrout.github.io/medium-archive/pelican/</id><updated>2024-08-22T15:01:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>ipydatagrid is now part of Project Jupyter</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2024/ipydatagrid-is-now-part-of-project-jupyter/" rel="alternate"/><published>2024-08-22T15:01:00+00:00</published><updated>2024-08-22T15:01:00+00:00</updated><author><name>Sylvain Corlay</name></author><id>tag:jasongrout.github.io,2024-08-22:/medium-archive/pelican/posts/2024/ipydatagrid-is-now-part-of-project-jupyter/</id><summary type="html">&lt;p&gt;Today, we are proud to announce that the ipydatagrid open source project has been incorporated into Project Jupyter as part of the Jupyter…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Today, we are proud to announce that the &lt;a href="https://github.com/jupyter-widgets/ipydatagrid"&gt;ipydatagrid&lt;/a&gt; open source project has been incorporated into Project Jupyter as part of the Jupyter Widgets subproject.&lt;/p&gt;
&lt;h2 id="what-is-ipydatagrid"&gt;What is ipydatagrid?&lt;/h2&gt;
&lt;p&gt;ipydatagrid is a fast data grid widget for Jupyter Notebooks and JupyterLab. Since its inception in 2019, it has been developed as an open source project in Bloomberg’s GitHub organization.&lt;/p&gt;
&lt;p&gt;It offers a high-performance fully-featured DataGrid interface, fully integrated with ipywidgets. Built upon the Lumino datagrid, which also powers the JupyterLab CSV viewer, ipydatagrid provides users with a robust and versatile tool for data visualization and manipulation.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/ipydatagrid-is-now-part-of-project-jupyter/images/001-0_9xQd6YxFsBDFPyxw.mp4" alt="Screencast of ipydatagrid in action, showcasing multiple cell renderers with conditional rendering, and filtering of data." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="key-features"&gt;Key Features&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Users can customize the way data is represented in their grid using a variety of renderers.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/ipydatagrid-is-now-part-of-project-jupyter/images/002-0_bNrtqVrcwR1dpX9O.mp4" alt="Screencast of ipydatagrid showcasing advanced rendering capabilities." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ipydatagrid includes a sophisticated selection model with two-way data binding.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/ipydatagrid-is-now-part-of-project-jupyter/images/003-0_wKlCImr0LtGDpeDz.mp4" alt="Screencast of ipydatagrid showcasing the advanced selection model with bi-directional bindings." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It enables conditional formatting powered by&lt;/strong&gt; &lt;a href="https://vega.github.io/vega/docs/expressions/"&gt;&lt;strong&gt;Vega Expressions&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/ipydatagrid-is-now-part-of-project-jupyter/images/004-0_U5H7uyoSLf0pZm6q.gif" alt="Screenshot of ipydatagrid showcasing the use of Vega expression for conditional formatting." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="the-transfer-to-project-jupyter"&gt;The transfer to Project Jupyter&lt;/h2&gt;
&lt;p&gt;We would like to extend our gratitude to the developers and contributors who have significantly improved the ipydatagrid project over the years: Itay Dafna, Martin Renou, Mehmet Bektas, Kaia Young, Bernát Gábor, Vasilis Themelis, Supriya K., Greg Mooney, Ian Thomas, Olly Hensby, and John Gunstone. Special thanks to Chris Colbert, the creator of the Lumino Datagrid, which forms the foundation of ipydatagrid’s front-end.&lt;/p&gt;
&lt;h2 id="bloombergs-contribution-to-project-jupyter"&gt;Bloomberg’s contribution to Project Jupyter&lt;/h2&gt;
&lt;p&gt;The transfer of ipydatagrid to Project Jupyter is just one of many contributions that Bloomberg has made to Project Jupyter. As a long-standing sponsor, Bloomberg has been instrumental in the development and success of Jupyter. Home to core maintainers and a major funder of JupyterLab since its inception, Bloomberg has also sponsored all editions of JupyterCon. The sustained support by Bloomberg has been crucial to Jupyter’s growth and impact.&lt;/p&gt;
&lt;p&gt;We are excited about the future of ipydatagrid within Project Jupyter, and look forward to continued innovation and collaboration within the broader community.&lt;/p&gt;
&lt;p&gt;— on behalf of the Jupyter Widgets Council, Sylvain Corlay&lt;/p&gt;
</content><category term="visualization"/><category term="widgets"/></entry><entry><title>Make your Pandas or Polars DataFrames Interactive with ITables 2.0</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2024/make-your-pandas-or-polars-dataframes-interactive-with/" rel="alternate"/><published>2024-03-19T18:39:00+00:00</published><updated>2024-03-19T18:39:00+00:00</updated><author><name>Marc Wouts</name></author><id>tag:jasongrout.github.io,2024-03-19:/medium-archive/pelican/posts/2024/make-your-pandas-or-polars-dataframes-interactive-with/</id><summary type="html">&lt;p&gt;ITables, or Interactive Tables, is a MIT-licensed Python package that renders Python DataFrames using the DataTables JavaScript library…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;ITables, or Interactive Tables, is a MIT-licensed Python package that renders Python DataFrames using the &lt;a href="https://datatables.net/"&gt;DataTables&lt;/a&gt; JavaScript library. ITables 2.0, that I have just released, adds support for the DataTables Extensions. In this post we review the functionalities brought by this release.&lt;/p&gt;
&lt;h2 id="how-to-use-itables-and-what-you-get"&gt;How to use ITables, and what you get&lt;/h2&gt;
&lt;p&gt;ITables can be installed using either pip or conda:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;itables
&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;conda&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;itables
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ITables is essentially a Python wrapper for &lt;a href="https://datatables.net/"&gt;DataTables&lt;/a&gt;. We have managed to keep its dependencies to a minimum: ITables only requires &lt;code&gt;IPython&lt;/code&gt;, &lt;code&gt;pandas&lt;/code&gt; and &lt;code&gt;numpy&lt;/code&gt;, which you must already have if you use Pandas in Jupyter (add &lt;code&gt;polars&lt;/code&gt; and &lt;code&gt;pyarrow&lt;/code&gt; if you wish to use ITables with Polars DataFrames).&lt;/p&gt;
&lt;p&gt;To use ITables in your notebook, run this code snippet:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;itables&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;init_notebook_mode&lt;/span&gt;

&lt;span class="n"&gt;init_notebook_mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_interactive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After that, every Pandas or Polars DataFrame will be displayed using the &lt;a href="https://datatables.net/"&gt;DataTables&lt;/a&gt; library. With DataTables, you get an easier and more complete access to your data. You can expand the table, explore the various pages, sort the data or even search through it, without having to go back to the Python prompt.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A Pandas DataFrame rendered with ITables" src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/make-your-pandas-or-polars-dataframes-interactive-with/images/001-1_kPeW6sSz3obRCxM50U6QAA.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;A Pandas DataFrame rendered with ITables&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To render only specific tables as interactive DataTables, or pass arguments to the DataTable constructor, you can use the &lt;code&gt;show&lt;/code&gt; function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;itables&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="a-brief-history-of-itables"&gt;A brief history of ITables&lt;/h2&gt;
&lt;p&gt;I started the ITables project back in 2019. The full changelog is available &lt;a href="https://mwouts.github.io/itables/changelog.html"&gt;here&lt;/a&gt;, but let me highlight a few important milestones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;v1.0 (June 2022): Offline mode&lt;/li&gt;
&lt;li&gt;v1.2 (Aug 2022): In addition to Jupyter Lab, Notebook, Book, HTML export, VS Code, PyCharm, Google Colab, RISE presentations, Voilà applications, ITables works in &lt;a href="https://mwouts.github.io/itables/supported_editors.html#using-itables-in-shiny"&gt;Shiny&lt;/a&gt; applications&lt;/li&gt;
&lt;li&gt;v1.3.5 (Nov 2022): Large tables are fast too&lt;/li&gt;
&lt;li&gt;v1.5 (March 2023): Polars DataFrames are supported&lt;/li&gt;
&lt;li&gt;v1.6 (Sep 2023): &lt;a href="https://mwouts.github.io/itables/pandas_style.html"&gt;Pandas Style&lt;/a&gt; objects can be displayed with ITables&lt;/li&gt;
&lt;li&gt;v1.7 (Feb 2024): ITables works with &lt;a href="https://mwouts.github.io/itables/quarto.html"&gt;Quarto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;v2.0 (March 2024): ITables support the &lt;a href="https://mwouts.github.io/itables/extensions.html"&gt;DataTables Extensions&lt;/a&gt;!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It took me much research (and two years I am afraid) to reach v2 and the DataTables Extensions. But we’re finally there! Let’s review what these extensions mean to your data workflow.&lt;/p&gt;
&lt;h2 id="my-favorite-datatables-extensions"&gt;My favorite DataTables Extensions&lt;/h2&gt;
&lt;h3 id="download-your-data"&gt;Download your data!&lt;/h3&gt;
&lt;p&gt;If you think of a Jupyter Notebook, it might sound strange that we are considering downloading the table data, since we have it already in the Python session. Actually, ITables does not stop at the Notebook. The interactive DataTables continue to be interactive even if you export the notebook to an HTML document with e.g. Jupyter &lt;a href="https://nbconvert.readthedocs.io"&gt;nbconvert&lt;/a&gt; (&lt;code&gt;jupyter nbconvert --to html&lt;/code&gt;). The are also interactive in a &lt;a href="https://github.com/jupyterlab-contrib/rise"&gt;RISE&lt;/a&gt; or &lt;a href="https://quarto.org/docs/presentations/"&gt;Quarto&lt;/a&gt; presentation. ITables also works in &lt;a href="https://jupyterbook.org/"&gt;Jupyter Book&lt;/a&gt; (which we use for the ITables &lt;a href="https://mwouts.github.io/itables"&gt;documentation&lt;/a&gt;), and in interactive applications build with e.g. &lt;a href="https://voila.readthedocs.io"&gt;Voilà&lt;/a&gt; or &lt;a href="https://mwouts.github.io/itables/supported_editors.html#using-itables-in-shiny"&gt;Shiny&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hopefully it’s clearer now why we might want to make the data downloadable! And the good news is that, with the &lt;a href="https://mwouts.github.io/itables/extensions.html#buttons"&gt;Buttons&lt;/a&gt; extension of DataTables, is it as easy as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;show&lt;span class="o"&gt;(&lt;/span&gt;df,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;buttons&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;copyHtml5&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;csvHtml5&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;excelHtml5&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="The Copy/CSV/Excel buttons" src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/make-your-pandas-or-polars-dataframes-interactive-with/images/002-1_zuo-oFLtcb1Yw05oGRUTNg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The Copy/CSV/Excel &lt;a href="https://mwouts.github.io/itables/extensions.html#buttons"&gt;buttons&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="cascade-search"&gt;Cascade search&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://mwouts.github.io/itables/extensions.html#searchpanes"&gt;SearchPanes&lt;/a&gt; extension lets you proceed to a quick and visual search through the columns that have repeated values:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The SearchPanes extension" src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/make-your-pandas-or-polars-dataframes-interactive-with/images/003-1_pMZabsjGnznieY-wjpn8HQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The &lt;a href="https://mwouts.github.io/itables/extensions.html#searchpanes"&gt;SearchPanes&lt;/a&gt; extension&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="search-builder"&gt;Search builder&lt;/h3&gt;
&lt;p&gt;I find the &lt;a href="https://mwouts.github.io/itables/extensions.html#searchbuilder"&gt;SearchBuilder&lt;/a&gt; extension very useful. Also, I love the option to set a pre-defined search and display only the part of the dataset we want to focus on.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The SearchBuilder extension" src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/make-your-pandas-or-polars-dataframes-interactive-with/images/004-1_U3_wuju-3UVQRWRxHo2Gmw.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The &lt;a href="https://mwouts.github.io/itables/extensions.html#searchbuilder"&gt;SearchBuilder&lt;/a&gt; extension&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="downsampling"&gt;Downsampling&lt;/h2&gt;
&lt;p&gt;Last but not least, I need to tell you about ITables’ &lt;a href="https://mwouts.github.io/itables/downsampling.html"&gt;down-sampling&lt;/a&gt; mechanism. By default, only a subset of the table with an estimated size of not more than 64kB (and not more than 200 columns) is displayed. You can change this with e.g.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;itables.options&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;opt&lt;/span&gt;

&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;131072&lt;/span&gt;
&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxColumns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can tell whether your table was down-sampled by looking at the table summary at the bottom right of your table.&lt;/p&gt;
&lt;p&gt;Please keep the following in mind: when down-sampling occurs, only a fraction of the data is passed on to DataTables, and hence the search or data export functions will only have access to that partial dataset.&lt;/p&gt;
&lt;p&gt;Down-sampling is what makes ITables fast. Think twice before setting &lt;code&gt;opt.maxBytes&lt;/code&gt; to a large number or to &lt;code&gt;0&lt;/code&gt;, as this might well freeze your notebook. Displaying a 1G DataFrame will make your notebook at least as big (probably bigger as the data is exported to JSON), and it’s not clear that your browser will support this.&lt;/p&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;ITables would not exist without the beautiful &lt;a href="https://datatables.net/"&gt;DataTables&lt;/a&gt; JavaScript library. The DataTables library (MIT license) is authored and maintained by &lt;a href="https://github.com/AllanJard"&gt;Allan Jardine&lt;/a&gt;. DataTables has a great documentation, many examples, and also a useful forum. Allan also provided precious advice on how to bundle the extensions with ITables.&lt;/p&gt;
&lt;p&gt;The point in using DataTables in the context of data exploration and analysis had been demonstrated prior to ITables by the &lt;a href="https://rstudio.github.io/DT/"&gt;DT&lt;/a&gt; package for R.&lt;/p&gt;
&lt;p&gt;Last but not least, ITables owes a lot to my brother &lt;a href="https://fwouts.com/"&gt;François Wouts&lt;/a&gt;. François patiently helped me find a way to provide DataTables and its extensions that would be compatible with all the &lt;a href="https://mwouts.github.io/itables/supported_editors.html"&gt;editors&lt;/a&gt; and rendering contexts that we want to support in ITables.&lt;/p&gt;
</content><category term="widgets"/></entry><entry><title>anywidget: Jupyter Widgets Made Easy</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2023/anywidget-jupyter-widgets-made-easy/" rel="alternate"/><published>2023-07-11T23:09:00+00:00</published><updated>2023-07-11T23:09:00+00:00</updated><author><name>Trevor Manz</name></author><id>tag:jasongrout.github.io,2023-07-11:/medium-archive/pelican/posts/2023/anywidget-jupyter-widgets-made-easy/</id><summary type="html">&lt;p&gt;anywidget is a Python library that makes it simple and enjoyable to create custom Jupyter Widgets that run in classic Jupyter notebooks…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="https://github.com/manzt/anywidget"&gt;&lt;em&gt;&lt;strong&gt;anywidget&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt; &lt;em&gt;is a Python library that makes it simple and enjoyable to create custom Jupyter Widgets that run in classic Jupyter notebooks, JupyterLite, JupyterLab, Google Colab, VS Code, and more. It focuses on:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;Prototyping&lt;/strong&gt;&lt;/em&gt;*: Create custom widgets right within your notebook.*&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;Portability&lt;/strong&gt;&lt;/em&gt;*: Share widgets as Python scripts or pip-installable packages.*&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;Productivity&lt;/strong&gt;&lt;/em&gt;: &lt;em&gt;Enjoy a modern front-end developer experience that supports real-time updates and removes manual project setup and other boilerplate.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
&lt;img alt="Realt-time widget developement in JupyterLab with anywidget. Changes to JavaScript source code are immediately reflected in the front end without requiring a page reload or clearing model state." src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/anywidget-jupyter-widgets-made-easy/images/001-1_VCG-57YX8IrfTyeby3Ybjg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;strong&gt;anywidget&lt;/strong&gt; enables real-time widget development entirely from within JupyterLab&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;Skip ahead to see an&lt;/em&gt; &lt;a href="#d575"&gt;&lt;em&gt;example&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="why-making-widgets-is-hard"&gt;Why making widgets is hard&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Basics.html"&gt;Jupyter Widgets&lt;/a&gt; enrich notebooks with interactive JavaScript-based views and controls for Python objects in the Jupyter kernel. They enable a wide range of users, from students to professionals, to tailor their programming environment with custom or ready-made tools to interact with their programs and explore data. Consider a machine learning researcher adjusting model parameters with a slider or a computational biologist navigating a genome browser programmatically.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A Jupyter Widget is comprised of two components: a JavaScript front-end and a Python object in the Jupyter kernel." src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/anywidget-jupyter-widgets-made-easy/images/002-0_k0vqkYqq7F0MaF7q.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Jupyter Widgets link JavaScript-based views and controls with objects in the Jupyter kernel&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;A useful feature of widgets is that they may be packaged and distributed as pip-installable modules, offering reusable components to address both general and specialized needs. For example, the &lt;a href="https://ipywidgets.readthedocs.io/en/stable/"&gt;ipywidgets&lt;/a&gt; project provides basic elements like form controls and layout containers, while numerous community projects offer custom widgets for domain-specific tasks (e.g., &lt;a href="https://github.com/bqplot/bqplot"&gt;bqplot&lt;/a&gt;, &lt;a href="https://ipyvolume.readthedocs.io/en/latest/"&gt;ipyvolume&lt;/a&gt;, &lt;a href="https://ipyleaflet.readthedocs.io/en/latest/"&gt;ipyleaflet&lt;/a&gt;).&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Side-by-Side comparison of two large genomic interaction matrices in Jupyter notebook using the interactive HiGlass browser." src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/anywidget-jupyter-widgets-made-easy/images/003-1_vSfRgAQN2UnfDlYmhjruSg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Comparing two massive genomic contract matrices within Jupyter using a custom widget from &lt;a href="https://github.com/higlass/higlass-python"&gt;higlass-python&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;However, the growing number of Jupyter environments supporting &lt;code&gt;.ipynb&lt;/code&gt; files complicates custom widget creation and sharing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Developers must individually adapt front-end code to meet subtle, often undocumented, requirements of each notebook platform.&lt;/li&gt;
&lt;li&gt;Basic prototyping involves setting up a local Python package and manually installing extensions.&lt;/li&gt;
&lt;li&gt;Proper widget distribution requires deep understanding of both Python and JavaScript packaging.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ve expanded on these challenges &lt;a href="https://anywidget.dev/blog/introducing-anywidget/#the-multi-platform-problem"&gt;previously&lt;/a&gt;, but to put it simply: traditional widget development has a steep learning curve and maintenance is both error-prone and tedious.&lt;/p&gt;
&lt;h2 id="a-universal-widget-adapter"&gt;A universal widget adapter&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;anywidget&lt;/strong&gt; introduces a fresh approach for creating and sharing custom widgets. It lets you avoid traditional development complexities, simplifying the process and making it easier than ever to start building widgets.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Development without anywidget" src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/anywidget-jupyter-widgets-made-easy/images/004-0_W8GArznb6ZRmcxQ9.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Development &lt;strong&gt;without&lt;/strong&gt; anywidget&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;anywidget&lt;/strong&gt; is &lt;em&gt;&lt;strong&gt;not&lt;/strong&gt;&lt;/em&gt; a new framework, but rather a compatibility layer around traditional Jupyter Widgets. It utilizes the standard module system now found in web browsers to let widget developers write front-end code that executes universally. Think of &lt;strong&gt;anywidget&lt;/strong&gt; as an adapter that runs your widget’s JavaScript across various notebook environments.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Development with anywidget" src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/anywidget-jupyter-widgets-made-easy/images/005-0_be82R4Wuyn-zpc_7.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Development &lt;strong&gt;with&lt;/strong&gt; anywidget&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;With &lt;strong&gt;anywidget,&lt;/strong&gt; a custom widget consists of two components: a Python class and an &lt;a href="https://nodejs.org/api/esm.html#modules-ecmascript-modules"&gt;ECMAScript module&lt;/a&gt; (ESM) — or web-standard JavaScript. You just write ESM and &lt;strong&gt;anywidget&lt;/strong&gt; handles the platform-specifics quirks.&lt;/p&gt;
&lt;p&gt;It takes less than 20 lines of code to &lt;a href="https://github.com/jupyter-widgets/widget-cookiecutter/tree/master/%7B%7Bcookiecutter.github_project_name%7D%7D"&gt;recreate&lt;/a&gt; a simple “Hello World” widget.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;anywidget&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;traitlets&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anywidget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AnyWidget&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;_esm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;    export function render({ model, el }) {&lt;/span&gt;
&lt;span class="s2"&gt;        el.classList.add(&amp;quot;custom-widget&amp;quot;);&lt;/span&gt;
&lt;span class="s2"&gt;        function valueChanged() {&lt;/span&gt;
&lt;span class="s2"&gt;            el.textContent = model.get(&amp;quot;value&amp;quot;);&lt;/span&gt;
&lt;span class="s2"&gt;        }&lt;/span&gt;
&lt;span class="s2"&gt;        valueChanged();&lt;/span&gt;
&lt;span class="s2"&gt;        model.on(&amp;quot;change:value&amp;quot;, valueChanged);&lt;/span&gt;
&lt;span class="s2"&gt;    }&lt;/span&gt;
&lt;span class="s2"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_css&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;    .custom-widget {&lt;/span&gt;
&lt;span class="s2"&gt;        background-color: lightseagreen;&lt;/span&gt;
&lt;span class="s2"&gt;        padding: 0px 2px;&lt;/span&gt;
&lt;span class="s2"&gt;    }&lt;/span&gt;
&lt;span class="s2"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;traitlets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Hello World&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can &lt;strong&gt;copy and paste&lt;/strong&gt; this code directly into Jupyter notebooks, JupyterLite, JupyterLab, Google Colab, or VS Code and it just works.&lt;/p&gt;
&lt;p&gt;No installation, build configuration, or bundlers.&lt;/p&gt;
&lt;p&gt;By comparison, creating an identical &lt;code&gt;ExampleWidget&lt;/code&gt; the traditional way involves forking a Python repo template (including ~50 files), building JavaScript source code with Node.js and webpack, and manually installing the local extensions in classic notebooks or JupyterLab (not compatible with Google Colab or VS Code).&lt;/p&gt;
&lt;h2 id="a-realistic-example"&gt;A realistic example&lt;/h2&gt;
&lt;p&gt;This tutorial demonstrates using &lt;strong&gt;anywidget&lt;/strong&gt; to address a &lt;a href="https://github.com/altair-viz/altair/issues/1153"&gt;long-standing issue&lt;/a&gt; in the &lt;a href="https://github.com/altair-viz/altair"&gt;Altair&lt;/a&gt;: retrieving data from a brush selection back into Python. You can either follow along here or execute the notebook yourself &lt;a href="https://colab.research.google.com/gist/manzt/2b83d3461c40ac7b43fde29515cb8077/extending_altair_with_anywidget.ipynb"&gt;in Colab&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We’ll begin with an interactive scatterplot example from the Altair &lt;a href="https://github.com/altair-viz/altair#example"&gt;docs&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;altair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;alt&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;vega_datasets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;

&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cars&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;brush&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selection_interval&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark_point&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Horsepower&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Miles_per_Gallon&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Origin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;lightgray&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;brush&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;bars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark_bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Origin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Origin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;count(Origin)&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;brush&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;bars&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/anywidget-jupyter-widgets-made-easy/images/006-1_KIB7LU5YpN90zEh0-Czilg.mp4" alt="An interactive scatterplot displaying cars. Miles per gallon by horsepower. Individual points (cars) are colored by their country of origin." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Notice how the brush selection on the scatter plot filters the data in the linked bar chart. Neat, but unfortunately we are unable inspect the selected points because the selection is processed in JavaScript, and Altair lacks a mechanism to communicate back to Python.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Sounds like a job for a widget!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Behind the scenes, Altair produces JSON that follows the &lt;a href="https://vega.github.io/vega-lite/"&gt;Vega-Lite&lt;/a&gt; visualization grammar. With &lt;strong&gt;anywidget&lt;/strong&gt;, we can create a custom widget to render this validated JSON independently and additionally relay the JavaScript-based selections back to Python.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;anywidget&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;traitlets&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ChartWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anywidget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AnyWidget&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;_esm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;    import embed from &amp;quot;https://cdn.jsdelivr.net/npm/vega-embed@6/+esm&amp;quot;;&lt;/span&gt;
&lt;span class="s2"&gt;    &lt;/span&gt;
&lt;span class="s2"&gt;    export async function render({ model, el }) {&lt;/span&gt;
&lt;span class="s2"&gt;        let spec = JSON.parse(model.get(&amp;quot;spec&amp;quot;));&lt;/span&gt;
&lt;span class="s2"&gt;        let api = await embed(el, spec);&lt;/span&gt;
&lt;span class="s2"&gt;        api.view.addSignalListener(spec.params[0].name, (_, update) =&amp;gt; {&lt;/span&gt;
&lt;span class="s2"&gt;            console.log(update);&lt;/span&gt;
&lt;span class="s2"&gt;            model.set(&amp;quot;selection&amp;quot;, update);&lt;/span&gt;
&lt;span class="s2"&gt;            model.save_changes();&lt;/span&gt;
&lt;span class="s2"&gt;        });&lt;/span&gt;
&lt;span class="s2"&gt;    }&lt;/span&gt;
&lt;span class="s2"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;spec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;traitlets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unicode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;selection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;traitlets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What’s happening here? Our custom &lt;code&gt;ChartWidget&lt;/code&gt; is defined by subclassing &lt;code&gt;anywidget.AnyWidget&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;_esm&lt;/code&gt; (line 6) defines the ECMAScript module for the widget. It imports necessary rendering dependencies (&lt;a href="https://github.com/vega/vega-embed"&gt;vega-embed&lt;/a&gt;) and exports &lt;code&gt;render&lt;/code&gt;: a function to displays the Vega-Lite chart and (importantly) links the JavaScript-based selection to Python.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;spec&lt;/code&gt; and &lt;code&gt;selection&lt;/code&gt; (lines 19–20) define stateful properties via &lt;a href="https://github.com/ipython/traitlets"&gt;traitlets&lt;/a&gt; that are accessible by both client JavaScript and Python. These represent the Vega-Lite JSON specification and the JavaScript-based brush selection.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now our ESM takes care of rendering instead of Altair.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;chart_widget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChartWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;bars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# Prints updates log console (JupyterLab: View &amp;gt; Show Log Console)&lt;/span&gt;
&lt;span class="n"&gt;chart_widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;selection&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;chart_widget&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/anywidget-jupyter-widgets-made-easy/images/007-1_OsSPlHcgfeF8ehIPGtFmpw.mp4" alt="The same scatterplot as before except the JavaScript-based brush selection is printed to the (Python) Jupyter console any time it changes." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The original cross-filtering behavior stays the same, but now we have access to the JavaScript selection &lt;em&gt;in Python&lt;/em&gt; via &lt;code&gt;chart_widget.selection&lt;/code&gt;. The Python callback (line 4) prints the synchronized selection in the JupyterLab log console any time it changes.&lt;/p&gt;
&lt;p&gt;Finally, we can present this data more effectively using a second widget that displays our selection as &lt;code&gt;pd.DataFrame&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ipywidgets&lt;/span&gt;

&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ipywidgets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clear_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;
    &lt;span class="n"&gt;selection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;selection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;chart_widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;on_change&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;selection&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;ipywidgets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VBox&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;chart_widget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/anywidget-jupyter-widgets-made-easy/images/008-1_0vn1t-SlN8LvAKLrVAiHoA.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;on_change&lt;/code&gt; callback (line 6) is invoked whenever the &lt;code&gt;chart_widget.selection&lt;/code&gt; changes (line 13). It filters the original data based on the selection bounds and displays the given subset as a table within &lt;code&gt;output&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With just a few lines of code, we enhanced Altair with new functionality using &lt;strong&gt;anywidget&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ChartWidget&lt;/code&gt; can be shared in its current state. However, if we add more features, we could transition the JavaScript code from inline strings to separate files, gradually evolving the widget into a fully-fledged Python package. This incremental development is a feature of &lt;strong&gt;anywidget&lt;/strong&gt;, allowing prototypes to grow into robust tools over time.&lt;/p&gt;
&lt;h2 id="modern-web-development-meets-jupyter"&gt;Modern web development meets Jupyter&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;anywidget&lt;/strong&gt; further embraces modern JavaScript to reduce friction and make developing widgets more accessible and &lt;em&gt;fun.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Because &lt;strong&gt;anywidget&lt;/strong&gt; takes care of all the necessary plumbing, you no longer need to setup up a local Python package or manually install extensions to start building. Instead, you can prototype and share widget ideas directly from notebooks — just like regular Python scripts.&lt;/p&gt;
&lt;p&gt;Since v0.2, &lt;strong&gt;anywidget&lt;/strong&gt; allows you to use a file path to define your widget’s front-end code (i.e., the &lt;code&gt;_esm&lt;/code&gt; and &lt;code&gt;_css&lt;/code&gt; attributes). During development, &lt;strong&gt;anywidget&lt;/strong&gt; will monitor for changes and immediately refresh the UI without requiring a full page reload or resetting widget model state.&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;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;anywidget&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;traitlets&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anywidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AnyWidget&lt;/span&gt;&lt;span class="p"&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;_esm&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="s"&gt;&amp;quot;index.js&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;_css&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="s"&gt;&amp;quot;styles.css&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;value&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;traitlets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Hello World&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This &lt;a href="https://anywidget.dev/blog/anywidget-02/#native-hot-module-replacement-hmr"&gt;feature&lt;/a&gt; has been popularized by modern web frameworks, but &lt;strong&gt;anywidget&lt;/strong&gt; introduces it for the first time to Jupyter. See this real-time development workflow it &lt;a href="https://www.youtube.com/watch?v=600PU6E4Srw&amp;amp;t=305s"&gt;in action&lt;/a&gt; or try it out yourself!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Real-time widget development with anywidget entirely from within JupyterLab. Changes to widget front-end source code are displayed instantly in the UI as changes are made." src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/anywidget-jupyter-widgets-made-easy/images/001-1_VCG-57YX8IrfTyeby3Ybjg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Real-time widget development entirely from within JupyterLab&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="try-it-out"&gt;Try it out!&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;anywidget&lt;/strong&gt; is available on &lt;a href="https://github.com/manzt/anywidget"&gt;GitHub&lt;/a&gt; and &lt;a href="https://pypi.org/project/anywidget/"&gt;PyPI&lt;/a&gt; and may be installed via &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;anywidget[dev]&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I hope using &lt;strong&gt;anywidget&lt;/strong&gt; is simple and enjoyable. I have found it valuable in my work as a biomedical visualization researcher, and it’s been exciting to see the positive reception from the wider Jupyter community.&lt;/p&gt;
&lt;p&gt;Since its release a few months ago, &lt;strong&gt;anywidget&lt;/strong&gt; already been adopted by several notable projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/uwdata/mosaic"&gt;Mosaic&lt;/a&gt;: an extensible framework for linking interactive views to databases for scalable data processing.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/flekschas/jupyter-scatter"&gt;jupyter-scatter&lt;/a&gt;: interactive 2D scatter plots that scale to millions of points and support view linking.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/opengeos/mapwidget"&gt;Mapwidget&lt;/a&gt;: interactive 2D/3D maps using popular JavaScript libraries with bidirectional communication, such as Cesium, Mapbox, MapLibre, Leaflet, and OpenLayers&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/juba/pyobsplot"&gt;pyobsplot&lt;/a&gt;: a Python interface for &lt;a href="https://observablehq.com/plot/"&gt;Observable Plot&lt;/a&gt; that supports Pandas and Polars dataframes.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/widgetti/ipyreact"&gt;ipyreact&lt;/a&gt;: a Python library for authoring Jupyter Widgets with React components.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;… demonstrating its current viability as an alternative to traditional widget development. I am committed to maintaining the simplicity that defines &lt;strong&gt;anywidget&lt;/strong&gt; while exploring ideas to further modernize widgets like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A simple decorator-based API&lt;/li&gt;
&lt;li&gt;Using builtin dataclasses or libraries like &lt;a href="https://pydantic.dev/"&gt;Pydantic&lt;/a&gt; or &lt;a href="https://jcristharif.com/msgspec/"&gt;msgspec&lt;/a&gt; to define widget models and serialization logic&lt;/li&gt;
&lt;li&gt;Handling TypeScript and JSX for front-end code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re curious about custom widgets or facing difficulties in widget development, please give &lt;strong&gt;anywidget&lt;/strong&gt; a try and &lt;a href="https://github.com/manzt/anywidget/issues"&gt;share your experience&lt;/a&gt;. Happy coding!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/manzt/anywidget"&gt;GitHub - anywidget: custom jupyter widgets made easy&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I’d like to extend my gratitude to the numerous contributors to our documentation and specifically &lt;a href="https://github.com/tlambert03"&gt;Talley Lambert&lt;/a&gt; for his work towards modernizing widgets from a Python perspective. He’s played a key role in developing our &lt;a href="https://anywidget.dev/en/experimental/"&gt;&lt;code&gt;experimental&lt;/code&gt;&lt;/a&gt; API, which allows more flexible communication between JavaScript and Python without requiring ipywidgets.&lt;/p&gt;
&lt;/blockquote&gt;
</content><category term="anywidget"/><category term="JavaScript"/><category term="widgets"/></entry><entry><title>Jupyter Community Workshop: The Future of Jupyter Widgets</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-community-workshop-the-future-of-jupyter/" rel="alternate"/><published>2022-09-20T17:02:00+00:00</published><updated>2022-09-21T16:19:00+00:00</updated><author><name>Itay Dafna</name></author><id>tag:jasongrout.github.io,2022-09-20:/medium-archive/pelican/posts/2022/jupyter-community-workshop-the-future-of-jupyter/</id><summary type="html">&lt;p&gt;After a long break from in-person events, we are excited to announce an in-person Jupyter Community Workshop!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-community-workshop-the-future-of-jupyter/images/001-1_16fTregYuv8Vu0T6AUzuVA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;After a long break from in-person events, we are excited to announce an in-person Jupyter Community Workshop!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="/posts/2022/jupyter-community-workshops/"&gt;Jupyter Community Workshops&lt;/a&gt; is a series of community-organized events to tackle challenging development and design projects, growing the community of contributors, and strengthening collaborations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;ipywidgets 8.0&lt;/code&gt; was released earlier this year, and we are now looking at the future of Jupyter Widgets with versions 9 and beyond of &lt;code&gt;ipywidgets&lt;/code&gt;. Some topics we are looking to discuss (non-exhaustive list) include the embedding of widgets in iframes, the next iteration of the Jupyter comm protocol, visual and styling improvements, and the embedding of Jupyter Widgets in contexts outside of Jupyter Notebook/Lab.&lt;/p&gt;
&lt;p&gt;The workshop dates are 18th-21st of October. It will take place in the Bloomberg EMEA headquarters in London. Travel funding assistance is available for attendees from academia and those from groups which are not well-represented within the Jupyter and wider tech community!&lt;/p&gt;
&lt;p&gt;Registration and all other details can be found in the link below: &lt;a href="https://go.bloomberg.com/attend/invite/jupyter-community-workshop-the-future-of-jupyter-widgets/"&gt;Jupyter Community Workshop: The Future of Jupyter Widgets (bloomberg.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We are really excited for this community workshop and hope you will join us!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;We are grateful to Bloomberg for hosting this event. We are also grateful to the sponsors of the Jupyter Community Workshop series, Bloomberg and Amazon AWS.&lt;/em&gt;&lt;/p&gt;
</content><category term="events"/><category term="widgets"/><category term="workshops"/></entry><entry><title>Jupyter Games</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/" rel="alternate"/><published>2021-12-14T19:38:00+00:00</published><updated>2021-12-14T19:38:00+00:00</updated><author><name>Thorsten Beier</name></author><id>tag:jasongrout.github.io,2021-12-14:/medium-archive/pelican/posts/2021/jupyter-games/</id><summary type="html">&lt;p&gt;Ipycanvas + Box2D&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/001-1_gXbeqCDvKyaRySdAX6SnKg.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;
&lt;p&gt;Making their own tiny video games can be a great way for kids to learn programming in a playful matter. While &lt;a href="https://jupyter.org/"&gt;Jupyter&lt;/a&gt; is widely used as a scientific and educational tool, &lt;a href="https://jupyter.org/"&gt;Jupyter&lt;/a&gt; is seldom used as a platform for game development. In this blog post, we show how &lt;a href="https://jupyter.org/"&gt;Jupyter&lt;/a&gt; can be used to develop tiny games based on &lt;a href="https://box2d.org/"&gt;Box2D&lt;/a&gt;. While &lt;a href="https://jupyter.org/"&gt;Jupyter&lt;/a&gt; is language agnostic and kernels exist for many programming languages, we will focus on the &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt; programming language in this blog post.&lt;/p&gt;
&lt;h2 id="mini-games"&gt;Mini-Games&lt;/h2&gt;
&lt;p&gt;When talking about video-games one might think of games such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.epicgames.com/fortnite/de/home"&gt;Fortnite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ea.com/de-de/games/battlefield"&gt;Battlefield&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ageofempires.com/"&gt;Age of Empires&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Games as the ones above are usually developed with an engine like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://unity.com/pages/unity-pro-buy-now?gclid=CjwKCAiA-9uNBhBTEiwAN3IlNHGsvz9U2GgitHO4hm4sVSHaBTi99nBotad_ht7kqABcbL00fYA0wxoC4osQAvD_BwE&amp;amp;gclsrc=aw.ds"&gt;Unity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.unrealengine.com/en-US/"&gt;Unreal Engine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://godotengine.org/"&gt;Godot engine&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not only do these games require complex engines to be developed, but also a whole team developing them and a lot of time and money.&lt;br&gt;
Obviously, these are not the kind of games we can and want to develop in Jupyter. We want tiny games (~ 1000 lines of code) which can be implemented by kids in a matter of a few hours rather than many days, i.e. games like &lt;a href="https://de.wikipedia.org/wiki/Pong"&gt;Pong&lt;/a&gt;, Pinball, and &lt;a href="https://www.angrybirds.com/"&gt;AngryBirds&lt;/a&gt;. In fact, we will focus on a certain class of games: 2D physics-based games, since these can be implemented very easily with the use of a 2D physics simulation engine like &lt;a href="https://box2d.org/"&gt;Box2D&lt;/a&gt; or &lt;a href="https://github.com/slembcke/Chipmunk2D"&gt;Chipmunk2D&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="box2d"&gt;Box2D&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://box2d.org/"&gt;Box2D&lt;/a&gt; is a 2D rigid body simulation library for games. &lt;a href="https://box2d.org/"&gt;Box2D&lt;/a&gt; is best explained by the demo below.&lt;/p&gt;
&lt;figure&gt;
&lt;a href="http://www.iforce2d.net/embox2d/testbed.html"&gt;&lt;img alt="A demo of Box2D compiled to JavaScript with Emscripten. Try it out (only HTTP, no HTTPS 😢 )" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/002-1_-g4lwaLsCuTGDQUR9IBYxA.mp4" loading="lazy" data-body-image=""&gt;&lt;/a&gt;
&lt;figcaption&gt;A demo of &lt;a href="https://box2d.org/"&gt;Box2D&lt;/a&gt; compiled to &lt;a href="https://en.wikipedia.org/wiki/JavaScript"&gt;JavaScript&lt;/a&gt; with &lt;a href="https://emscripten.org/"&gt;Emscripten&lt;/a&gt;. &lt;a href="http://www.iforce2d.net/embox2d/testbed.html"&gt;Try it out&lt;/a&gt; (only HTTP, no HTTPS 😢 )&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href="https://box2d.org/"&gt;Box2D&lt;/a&gt; makes it very easy to implement physics-based games like &lt;a href="https://de.wikipedia.org/wiki/Angry_Birds"&gt;Angry Birds&lt;/a&gt; or &lt;a href="https://store.steampowered.com/app/22000/World_of_Goo/?l=german"&gt;World of Goo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="box2d-debug-draw"&gt;Box2D Debug Draw&lt;/h2&gt;
&lt;h3 id="debugdraw"&gt;DebugDraw&lt;/h3&gt;
&lt;p&gt;Box2D has an abstract class &lt;em&gt;DebugDraw&lt;/em&gt; with a handful of methods to draw simple primitives. The API is given by the following:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Box2D DebugDraw API" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/003-1_WjriahGPOAl9g4GlMRJlVw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Box2D DebugDraw API&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img alt="The ipycanvas based Box2D debug draw in action" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/004-1_63aIvo4SQdeqUbVppf_WUA.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The ipycanvas based &lt;a href="https://box2d.org/"&gt;Box2D&lt;/a&gt; debug draw in action&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Implementing this abstract class allows you to draw the physics for your UI back-end. Box2D is shipped with a &lt;a href="https://www.glfw.org/"&gt;glfw&lt;/a&gt; based DebugDraw implementation. For Python one can implement a &lt;a href="https://www.pygame.org/news"&gt;pygame&lt;/a&gt; or &lt;a href="https://kivy.org/#home"&gt;kivy&lt;/a&gt; based &lt;em&gt;DebugDraw&lt;/em&gt; implementation. For a finished product, one wants to replace the debug-draw-based renderings with custom shiny rendering routines. But using only debug-draw-based renderings is more than enough to quickly test game ideas and play around with &lt;a href="https://box2d.org/"&gt;Box2D&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="liquidfun"&gt;LiquidFun&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://google.github.io/liquidfun/"&gt;LiquidFun&lt;/a&gt; is a 2D rigid-body and fluid simulation library for games written in C++ based upon &lt;a href="http://box2d.org/"&gt;Box2D&lt;/a&gt;. &lt;a href="https://google.github.io/liquidfun/"&gt;LiquidFun&lt;/a&gt; is best explained by the demo below:&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://google.github.io/liquidfun/"&gt;&lt;img alt="A demo of LiquidFun compiled to JavaScript with Emscripten which runs in the browser. Try it out!" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/005-1_uPfz8srjXR24dtFm8wVBQg.mp4" loading="lazy" data-body-image=""&gt;&lt;/a&gt;
&lt;figcaption&gt;A demo of LiquidFun compiled to JavaScript with &lt;a href="https://emscripten.org/"&gt;Emscripten&lt;/a&gt; which runs in the browser. &lt;a href="https://google.github.io/liquidfun/"&gt;Try it out!&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="more-than-just-games"&gt;More than just Games&lt;/h2&gt;
&lt;p&gt;Even though &lt;a href="http://box2d.org/"&gt;Box2D&lt;/a&gt; is advertised as a 2D physics engine for games, &lt;a href="http://box2d.org/"&gt;Box2D&lt;/a&gt; can also be used for educational purposes:&lt;br&gt;
The YouTube channel &lt;a href="https://www.youtube.com/c/iforce2d/videos?view=0&amp;amp;sort=da&amp;amp;flow=grid"&gt;iforce2d&lt;/a&gt; has done some impressive things with &lt;a href="http://box2d.org/"&gt;Box2D&lt;/a&gt; like a Box2D-based &lt;a href="https://youtu.be/8kZRpouZ3OQ?t=1228"&gt;combustion engine&lt;/a&gt; and a &lt;a href="https://youtu.be/zhFVMxus3No?t=155"&gt;wind tunnel&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://youtu.be/8kZRpouZ3OQ?t=1228"&gt;&lt;img alt="Box2D based combustion engine by the youtuber iforce2d" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/006-1_0AABOZ_YoDUqcyOVlCT-SA.mp4" loading="lazy" data-body-image=""&gt;&lt;/a&gt;
&lt;figcaption&gt;Box2D based &lt;a href="https://youtu.be/8kZRpouZ3OQ?t=1228"&gt;combustion engine&lt;/a&gt; by the youtuber &lt;a href="https://www.youtube.com/channel/UCTXOorupCLqqQifs2jbz7rQ"&gt;iforce2d&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img alt="Box2D based wind tunnel by the youtuber iforce2d" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/007-1_rVDzJqp1EHI9UzzgkPzlqw.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Box2D based &lt;a href="https://youtu.be/zhFVMxus3No?t=155"&gt;wind tunnel&lt;/a&gt; by the youtuber &lt;a href="https://www.youtube.com/channel/UCTXOorupCLqqQifs2jbz7rQ"&gt;iforce2d&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="box2d-python"&gt;Box2D Python&lt;/h2&gt;
&lt;p&gt;Box2D has bindings for many languages and can be compiled to &lt;a href="https://webassembly.org/"&gt;Wasm&lt;/a&gt;. Several Box2D demos exist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.iforce2d.net/embox2d/testbed.html"&gt;http://www.iforce2d.net/embox2d/testbed.html&lt;/a&gt; (only HTTP, no HTTPS 😢 )&lt;/li&gt;
&lt;li&gt;&lt;a href="https://birchlabs.co.uk/liquidfun-wasm/"&gt;https://birchlabs.co.uk/liquidfun-wasm/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://google.github.io/liquidfun/"&gt;https://google.github.io/liquidfun/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We are particularly interested in using Box2D / LiquidFun from Python.&lt;br&gt;
Here we have the following possibilities:&lt;/p&gt;
&lt;h3 id="pybox2d"&gt;pybox2d&lt;/h3&gt;
&lt;p&gt;With &lt;a href="https://github.com/pybox2d/pybox2d"&gt;pybox2d&lt;/a&gt;, Box2D has matured and robust &lt;a href="https://github.com/pybox2d/pybox2d"&gt;Python bindings&lt;/a&gt; with extensive &lt;a href="https://github.com/pybox2d/pybox2d/wiki/manual"&gt;documentation&lt;/a&gt;. Unfortunately, &lt;a href="https://github.com/pybox2d/pybox2d"&gt;pybox2d&lt;/a&gt; has a few shortcomings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It only works with an old version of Box2D.&lt;/li&gt;
&lt;li&gt;There is no support for LiquidFun.&lt;/li&gt;
&lt;li&gt;pybox2d is mostly unmaintained.&lt;/li&gt;
&lt;li&gt;The Python bindings are generated with &lt;a href="http://www.swig.org/"&gt;SIWG&lt;/a&gt; (not really a shortcoming, but I personally strongly prefer &lt;a href="https://github.com/pybind/pybind11"&gt;pybind11&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="pyb2d"&gt;pyb2d&lt;/h3&gt;
&lt;p&gt;A disclaimer first, I am the author of the &lt;a href="https://github.com/pyb2d/pyb2d"&gt;pyb2d&lt;/a&gt; Box2D Python bindings. The motivation for creating &lt;a href="https://github.com/pyb2d/pyb2d"&gt;pyb2d&lt;/a&gt; was having Box2D / LiquidFun bindings created with &lt;a href="https://github.com/pybind/pybind11"&gt;pybind11&lt;/a&gt;. While pyb2d works with the brand-new 2.4.1 release of Box2D, has support for LiquidFun and &lt;a href="https://github.com/pybind/pybind11"&gt;pybind11&lt;/a&gt; has been used for generating the Python bindings, there are also some downsides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pyb2d/pyb2d"&gt;pyb2d&lt;/a&gt; has fewer examples compared to &lt;a href="https://github.com/pybox2d/pybox2d"&gt;pybox2d&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pyb2d/pyb2d"&gt;pyb2d&lt;/a&gt; is not as mature and robust as &lt;a href="https://github.com/pybox2d/pybox2d"&gt;pybox2d&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pyb2d/pyb2d"&gt;pyb2d&lt;/a&gt; is not yet as well documented as &lt;a href="https://github.com/pybox2d/pybox2d"&gt;pybox2d&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A particularly useful feature of &lt;a href="https://github.com/pyb2d/pyb2d"&gt;pyb2d&lt;/a&gt; is its &lt;em&gt;BatchDebugDraw&lt;/em&gt; implementation: While we could implement the above mentioned debug draw API directly in Python, this would have a few drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is a certain overhead when calling C++ from Python and vice versa. When our game contains a lot of shapes, for instance circles, we would have to call &lt;em&gt;drawCircle&lt;/em&gt; very often and have a lot of calls from C++ to Python and vice versa.&lt;/li&gt;
&lt;li&gt;Some back-ends provide &lt;em&gt;batch-drawing&lt;/em&gt; capabilities such that we can draw multiple primitives at once. For instance, drawing 100 circles at different locations with a single function call where the function arguments are NumPy nd-arrays with the centers/radii of the circles. Using such a batch API can lead to tremendous speedups.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
&lt;img alt="The python batch debug draw API of pyb2d" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/008-1_He7B_NhepkHeNjPEfziwjw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The python batch debug draw API of pyb2d&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To address these issues pyb2d provides a &lt;em&gt;BatchDebugDraw&lt;/em&gt; implementation where we first collect all the individual calls like &lt;em&gt;drawCircle&lt;/em&gt;, &lt;em&gt;drawSegment, drawPolygon etc.&lt;/em&gt; We store the arguments of these calls in NumPy arrays. After collecting all the shapes we call the Python API with functions like &lt;em&gt;draw_circles, draw_segments, draw_polygons,&lt;/em&gt; etc. where all the shapes are passed to Python in a single function call. On the Python side, one can pass these batched draw instructions to a &lt;em&gt;batch-drawing&lt;/em&gt; API of the UI backed. (Spoiler: We will use the batch drawing API of ipycanvas for the Jupyter pyb2d integration.)&lt;/p&gt;
&lt;h2 id="jupyter-box2d-integration-requirements"&gt;Jupyter Box2D Integration: Requirements&lt;/h2&gt;
&lt;p&gt;To have a platform to develop tiny games from within Jupyter we need at least the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Canvas: We need a surface/canvas on which we can draw the content of the games.&lt;/li&gt;
&lt;li&gt;Input Devices: Games would be boring without any user input. We need access to input devices like the mouse, the keyboard or even game-pads.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ipycanvas-ipywidgets-ipyevents"&gt;Ipycanvas, Ipywidgets, Ipyevents&lt;/h2&gt;
&lt;p&gt;Jupyter has a huge ecosystem of extensions. For the Jupyter Box2D integration, we just need to pick the appropriate Jupyter extensions: &lt;a href="https://ipycanvas.readthedocs.io/en/latest/"&gt;Ipycanvas&lt;/a&gt; gives us access to the &lt;a href="https://developer.mozilla.org/de/docs/Web/API/Canvas_API"&gt;HTML Canvas&lt;/a&gt; from within Python kernels in Jupyter. This serves as the drawable surface for our games. &lt;a href="https://ipywidgets.readthedocs.io/en/latest/"&gt;Ipywidets&lt;/a&gt; and &lt;a href="https://github.com/mwcraig/ipyevents"&gt;Ipyevents&lt;/a&gt; give us access to input devices like game-pads, the keyboard, and the mouse.&lt;/p&gt;
&lt;h2 id="ipycanvas"&gt;Ipycanvas&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/009-1_KlqFuAvQ6Hd9-pPo4hthgA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ipycanvas.readthedocs.io/en/latest/"&gt;Ipycanvas&lt;/a&gt; is a lightweight library developed by &lt;a href="https://twitter.com/martinRenou"&gt;Martin Renou&lt;/a&gt; exposing the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API"&gt;browser’s Canvas API&lt;/a&gt; to Jupyter. It allows drawing simple primitives directly from Python like text, lines, polygons, arcs, images, etc. With &lt;a href="https://ipycanvas.readthedocs.io/en/latest/animations.html"&gt;a few tricks&lt;/a&gt;, ipycanvas is fast enough to draw smooth animations. This even works when the server and the client are not on the same machines (when we run ipycanvas on &lt;a href="https://camo.githubusercontent.com/581c077bdbc6ca6899c86d0acc6145ae85e9d80e6f805a1071793dbe48917982/68747470733a2f2f6d7962696e6465722e6f72672f62616467655f6c6f676f2e737667"&gt;MyBinder&lt;/a&gt; for example).&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://camo.githubusercontent.com/581c077bdbc6ca6899c86d0acc6145ae85e9d80e6f805a1071793dbe48917982/68747470733a2f2f6d7962696e6465722e6f72672f62616467655f6c6f676f2e737667"&gt;&lt;img alt="Conways game of life in ipycanvas, try it out!" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/010-1_v8D4SUV9rkTKQ6nnL6Yfsg.mp4" loading="lazy" data-body-image=""&gt;&lt;/a&gt;
&lt;figcaption&gt;Conways game of life in ipycanvas, &lt;a href="https://camo.githubusercontent.com/581c077bdbc6ca6899c86d0acc6145ae85e9d80e6f805a1071793dbe48917982/68747470733a2f2f6d7962696e6465722e6f72672f62616467655f6c6f676f2e737667"&gt;try it out!&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="an-ipycanvas-based-debugdraw"&gt;An Ipycanvas-based DebugDraw:&lt;/h3&gt;
&lt;p&gt;The first step of integrating pyb2d in Jupyter notebooks is implementing an ipycanvas based &lt;em&gt;DebugDraw&lt;/em&gt;. We recently released a &lt;a href="https://github.com/martinRenou/ipycanvas/releases/tag/0.10.0"&gt;new version&lt;/a&gt; of ipycanvas which provides an extended batch API to draw things very fast. We utilize this batch API when implementing the above mention batch-debug-draw API.&lt;/p&gt;
&lt;h3 id="handling-events-in-jupyter"&gt;Handling events in Jupyter:&lt;/h3&gt;
&lt;p&gt;We can use &lt;a href="https://github.com/mwcraig/ipyevents"&gt;Ipyevents&lt;/a&gt; for the event handling in Jupyter. Obviously, we want to handle the events &lt;em&gt;while&lt;/em&gt; our game is running. To achieve this within a Jupyter-notebook we need to run our game-loop in a dedicated thread and listen for events in the main thread.&lt;/p&gt;
&lt;h3 id="adding-buttons"&gt;Adding Buttons:&lt;/h3&gt;
&lt;p&gt;We want to have a few buttons to &lt;em&gt;start&lt;/em&gt;, &lt;em&gt;pause&lt;/em&gt; and &lt;em&gt;reset&lt;/em&gt; the Box2D based games. We can add these with &lt;a href="https://ipywidgets.readthedocs.io/en/latest/"&gt;ipywidgets&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="putting-it-all-together"&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;With all the above we can put together a pyb2d based Python Box2D integration in Jupyter. As a proof of concept we implemented a few mini-games:&lt;/p&gt;
&lt;h3 id="billiard"&gt;Billiard:&lt;/h3&gt;
&lt;p&gt;A very simple billiard game. &lt;a href="https://mybinder.org/v2/gh/pyb2d/pyb2d/main?urlpath=/lab/tree/examples/jupyter_integration.ipynb"&gt;Try it on binder!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/011-1_ZkV-0GDc_0Kxdbc6CHzB0w.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h3 id="angry-shapes"&gt;Angry Shapes:&lt;/h3&gt;
&lt;p&gt;An &lt;a href="https://www.angrybirds.com/"&gt;Angry Birds&lt;/a&gt;-like game implemented with pyb2d. &lt;a href="https://mybinder.org/v2/gh/pyb2d/pyb2d/main?urlpath=/lab/tree/examples/jupyter_integration.ipynb"&gt;Try it on binder!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/pyb2d/pyb2d/main?urlpath=/lab/tree/examples/jupyter_integration.ipynb"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/012-1_cIqICNLl2CTGoW5vdmyr-w.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="world-of-goo-homage"&gt;World of Goo homage:&lt;/h3&gt;
&lt;p&gt;A &lt;a href="https://store.steampowered.com/app/22000/World_of_Goo/"&gt;World of Goo&lt;/a&gt; homage implemented with pyb2d. &lt;a href="https://mybinder.org/v2/gh/pyb2d/pyb2d/main?urlpath=/lab/tree/examples/jupyter_integration.ipynb"&gt;Try it on binder!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/pyb2d/pyb2d/main?urlpath=/lab/tree/examples/jupyter_integration.ipynb"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/013-1_Mr_2vTFlIfad2Wsbz6gmTQ.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="rocket"&gt;Rocket:&lt;/h3&gt;
&lt;p&gt;Fly a rocket controlled with the keyboard, but avoid the black hole! &lt;a href="https://mybinder.org/v2/gh/pyb2d/pyb2d/main?urlpath=/lab/tree/examples/jupyter_integration.ipynb"&gt;Try it on binder!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/pyb2d/pyb2d/main?urlpath=/lab/tree/examples/jupyter_integration.ipynb"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/014-1_8f05EGZrQyBxZr-Byn_vxg.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="compatibility"&gt;Compatibility&lt;/h2&gt;
&lt;p&gt;All the pyb2d examples shown above can also be run in a &lt;a href="https://www.pygame.org/news"&gt;pygame&lt;/a&gt; window or a &lt;a href="https://kivy.org/#home"&gt;kivy&lt;/a&gt; window since pyb2d also provides back-ends for these. But even when one prefers to experiment with &lt;a href="https://www.pygame.org/news"&gt;pygame&lt;/a&gt;, the Jupyter back-end can still be interesting since one can put the testbed-examples on &lt;a href="https://mybinder.org/"&gt;MyBinder&lt;/a&gt; and have them accessible in a convenient fashion.&lt;/p&gt;
&lt;h2 id="caveats"&gt;Caveats&lt;/h2&gt;
&lt;p&gt;The Jupyter integration of pyb2d gives usable frame rates, even when run through &lt;a href="https://mybinder.org/"&gt;MyBinder&lt;/a&gt;. But nevertheless, a &lt;a href="https://www.pygame.org/news"&gt;pygame&lt;/a&gt; based back-end usually leads to a better frame rate and smoother gameplay.&lt;/p&gt;
&lt;h2 id="outlook-sneak-preview"&gt;Outlook / Sneak preview&lt;/h2&gt;
&lt;p&gt;We currently work on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adding more examples and better documentation to pyb2d.&lt;/li&gt;
&lt;li&gt;Improve the performance of the Jupyter integration.&lt;/li&gt;
&lt;li&gt;Add pyb2d to &lt;a href="https://jupyterlite.readthedocs.io/en/latest/"&gt;JupyterLite&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Implement remote multiplayer-based gaming via the collaborative mode of JupyterLab.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
&lt;img alt="Sneak-preview: Via the collaborative mode of JupyterLab we can connect to the notebook from two machines and play billiard against each other." src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyter-games/images/015-1_9gJsZRQ9CUEd2Uz_I_BBJw.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Sneak-preview: Via the collaborative mode of JupyterLab we can connect to the notebook from two machines and play billiard against each other.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;I would like to thank &lt;a href="https://twitter.com/martinRenou"&gt;Martin Renou&lt;/a&gt; for his help with ipycanvas.&lt;/p&gt;
&lt;h2 id="about-the-author"&gt;About the author&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://quantstack.net/thorsten.html"&gt;Thorsten Beier&lt;/a&gt; is a Scientific Software Engineer at &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt;. Before joining &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt;, he graduated in computer science at the University of Heidelberg and worked at the &lt;a href="https://www.embl.org/"&gt;EMBL&lt;/a&gt;. As an open-source developer, Thorsten worked on a variety of projects, from &lt;a href="https://github.com/DerThorsten/nifty"&gt;nifty&lt;/a&gt; and &lt;a href="https://github.com/ukoethe/vigra"&gt;vigra&lt;/a&gt; in C++ to &lt;a href="https://github.com/inferno-pytorch/inferno"&gt;inferno&lt;/a&gt;, &lt;a href="https://kipoi.org/"&gt;kipoi&lt;/a&gt;, and &lt;a href="https://www.ilastik.org/"&gt;ilastik&lt;/a&gt; in Python.&lt;/p&gt;
</content><category term="education"/><category term="widgets"/></entry><entry><title>Build a Jupyter Widget with React and TypeScript</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/build-a-jupyter-widget-with-react-and-typescript/" rel="alternate"/><published>2021-07-30T19:02:00+00:00</published><updated>2021-08-05T18:00:00+00:00</updated><author><name>John Waidhofer</name></author><id>tag:jasongrout.github.io,2021-07-30:/medium-archive/pelican/posts/2021/build-a-jupyter-widget-with-react-and-typescript/</id><summary type="html">&lt;p&gt;When using a Jupyter Notebook to work on data science projects, I’ve found small user interface abstractions to be very useful. Scrubbing a…&lt;/p&gt;
</summary><content type="html">&lt;figure&gt;
&lt;img alt="Photo by Adi Goldstein on Unsplash" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/build-a-jupyter-widget-with-react-and-typescript/images/001-0_lnZS93JgKwZAZD1m.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Photo by &lt;a href="https://unsplash.com/@adigold1?utm_source=medium&amp;amp;utm_medium=referral"&gt;Adi Goldstein&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When using a Jupyter Notebook to work on data science projects, I’ve found small user interface abstractions to be very useful. Scrubbing a slider or uploading a file can be more intuitive than editing a script and running it manually. This is especially true when a notebook is shared with others, since most people already have a basic knowledge about user interface elements.&lt;/p&gt;
&lt;p&gt;Jupyter Widgets are fantastic tools for simplifying Jupyter Notebook workflows with custom user interfaces. While widgets are versatile and composable, sometimes the default implementations provided by Jupyter don’t have the exact functionality we are looking for. Luckily, we are able to create Custom Widgets to suit our needs using web technologies. To get an idea of how Custom Widgets work, we are going to build a sleek color picker for JupyterLab using React.&lt;/p&gt;
&lt;h2 id="setup"&gt;Setup&lt;/h2&gt;
&lt;p&gt;Before we can start coding, there is some basic boilerplate to download. Start by installing &lt;code&gt;cookiecutter&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install cookiecutter
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then download the React widget boilerplate code.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cookiecutter https://github.com/Waidhoferj/jupyter-widget-react-cookiecutter.git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;cookiecutter&lt;/code&gt; template will guide us through some setup questions. The &lt;code&gt;author_name&lt;/code&gt;, &lt;code&gt;author_email&lt;/code&gt;, and &lt;code&gt;github_project_name&lt;/code&gt; fields are the most important. We can leave the others blank or go with the default values.&lt;/p&gt;
&lt;p&gt;Next, create a development environment using &lt;a href="https://conda.io/projects/conda/en/latest/user-guide/install/index.html"&gt;Anaconda&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda create -n jupyter-react-widget -c conda-forge nodejs yarn python jupyterlab
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda activate jupyter-react-widget
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Every package we install for this project will now be contained in a neat bundle.&lt;/p&gt;
&lt;p&gt;Inside the project directory created by &lt;code&gt;cookiecutter&lt;/code&gt;, run the following script to install the dependencies that we’ll use to build the widget. Then connect our widget environment to JupyterLab:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install -e &amp;quot;.[test, examples]&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;jupyter labextension develop --overwrite .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;yarn run build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Thats it for the setup! Let’s see what the default widget can do.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="interacting-with-the-widget"&gt;Interacting with the Widget&lt;/h2&gt;
&lt;p&gt;To test out the widget, run &lt;code&gt;jlpm watch&lt;/code&gt; in the project directory and open up JupyterLab with &lt;code&gt;jupyter lab&lt;/code&gt; in another terminal window. In JuypterLab, open the notebook file located at &lt;em&gt;&lt;project&gt;/examples/introduction.ipynb&lt;/em&gt;. Run all of the notebook cells to examine the state of the widget. In its current form, the widget displays a greeting alongside an input text field. Typing into the input field will update the target of the greeting. The value of the text box is automatically synced with &lt;code&gt;w.value&lt;/code&gt;, allowing us to access the input’s contents as a Python string. With the default setup working, we can start designing our own custom widget.&lt;/p&gt;
&lt;h2 id="building-a-color-picker"&gt;Building a Color Picker&lt;/h2&gt;
&lt;p&gt;Let’s take a look at the architecture of a Custom Widget. The Jupyter Widget system allows developers to send data between Python and TypeScript using a Model-View-Controller Architecture. The Python Widget Model represents the state of the widget. The TypeScript Controller edits and responds to changes in the Python model based on user input. These updates are rendered in the notebook using React.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Data is passed from the Python model to the TypeScript controller to the React view and back" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/build-a-jupyter-widget-with-react-and-typescript/images/002-1_cEy6OgSDz854q4Z0VJ59aQ.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Data is passed from the Python model to the TypeScript controller to the React view and back&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We can start building our color picker in &lt;em&gt;example.py&lt;/em&gt; by adding a color property to the model.&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="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOMWidget&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;# Your widget state goes here. Make sure to update the corresponding&lt;/span&gt;
    &lt;span class="c1"&gt;# JavaScript widget state (defaultModelProperties) in widget.ts&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Jupyter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Jupyter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The color property is set equal to the &lt;code&gt;Unicode&lt;/code&gt; traitlet, which represents a string of characters. The &lt;code&gt;.tag(sync=True)&lt;/code&gt; method syncs the value of the color property with the TypeScript widget state.&lt;/p&gt;
&lt;p&gt;In &lt;em&gt;widget.ts&lt;/em&gt;, we define the corresponding TypeScript representation of color in the &lt;code&gt;defaultModelProperties&lt;/code&gt; object. This object mirrors the structure of the Python model, allowing us to access the model state in our frontend code.&lt;/p&gt;
&lt;figure&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;defaultModelProperties&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="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figcaption&gt;
&lt;p&gt;Add color to the default model properties object&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Now that we have defined our model, the rest of the work can be done in React. If you’d like to flex your web development skills and design a custom color picker, go for it! Alternatively, we can get a great plug-and-play interface by installing &lt;code&gt;react-colorful&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;jlpm add react-colorful
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create a &lt;em&gt;ColorPicker.tsx&lt;/em&gt; file in the &lt;em&gt;src&lt;/em&gt; folder. We can build out the component in a few lines of code:&lt;/p&gt;
&lt;figure&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&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="nx"&gt;HexColorPicker&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="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;react-colorful&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&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="nx"&gt;useModelState&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="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./hooks/widget-model&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ColorPicker&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="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setColor&lt;/span&gt;&lt;span class="p"&gt;]&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="nx"&gt;useModelState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;color&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;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;HexColorPicker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setColor&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figcaption&gt;
&lt;p&gt;The ColorPicker React component&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Let’s walk through what’s happening here. At the top of the file, we import React, the react-colorful component, and a hook called &lt;code&gt;useModelState&lt;/code&gt;. The &lt;code&gt;useModelState&lt;/code&gt; hook operates like the &lt;a href="https://reactjs.org/docs/hooks-state.html"&gt;&lt;code&gt;useState&lt;/code&gt; hook&lt;/a&gt; but instead of accepting a default value as a parameter, it takes the name of a property from our Python model. The hook will automatically update the React view when the referenced property updates in Python. Also, the React widget can directly update the Python model with the &lt;code&gt;setColor&lt;/code&gt; function. By passing &lt;code&gt;color&lt;/code&gt; and &lt;code&gt;setColor&lt;/code&gt; to the &lt;code&gt;HexColorPicker&lt;/code&gt;, the Python widget model will reflect the chosen color whenever the user makes a selection in the interface.&lt;/p&gt;
&lt;p&gt;To display the color picker in our notebook, add the component to &lt;em&gt;ReactWidget.tsx&lt;/em&gt;, which is the top level React file in the widget system. Import the &lt;code&gt;ColorPicker&lt;/code&gt; component at the top of the file and make it the return value of &lt;code&gt;ReactWidget&lt;/code&gt;. We can delete all of the boilerplate within the &lt;code&gt;ReactWidget&lt;/code&gt; component in the process. Additionally, delete the &lt;code&gt;useModelState&lt;/code&gt; import at the top of the file, since it is no longer needed.&lt;/p&gt;
&lt;figure&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ColorPicker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./ColorPicker&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ReactWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;WidgetProps&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="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ColorPicker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figcaption&gt;
&lt;p&gt;Add ColorPicker to ReactWidget&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To test this out, &lt;a href="https://support.labs.cognitiveclass.ai/knowledgebase/articles/857388-how-to-restart-the-jupyter-kernel"&gt;restart the python kernel&lt;/a&gt; in JupyterLab and refresh the browser window. We will now see a color picker where the input field used to be. Pick a color and print out &lt;code&gt;w.color&lt;/code&gt; to get the hex code in Python. Congrats! You now know how to create Jupyter Widgets using React!&lt;/p&gt;
&lt;p&gt;The principles that we covered here can be applied to create more complex applications. To add additional properties, define them in both &lt;em&gt;example.py&lt;/em&gt; and &lt;em&gt;widget.ts&lt;/em&gt;, then interact with the data via the &lt;code&gt;useModelState&lt;/code&gt; hook in a React component. Go build some amazing widgets!&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Waidhoferj/jupyter-widget-react-cookiecutter#hooks"&gt;Additional React Jupyter Widget Hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Custom.html#Other-traitlet-types"&gt;Data types for the Python Model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Custom.html"&gt;The official IPythonWidget tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="JavaScript"/><category term="widgets"/></entry><entry><title>A Curiously Recurring Widget Library</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/a-curiously-recurring-widget-library/" rel="alternate"/><published>2021-01-27T14:22:00+00:00</published><updated>2021-01-27T21:41:00+00:00</updated><author><name>Sylvain Corlay</name></author><id>tag:jasongrout.github.io,2021-01-27:/medium-archive/pelican/posts/2021/a-curiously-recurring-widget-library/</id><summary type="html">&lt;p&gt;Diving into the implementation of xwidgets&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;em&gt;Diving into the implementation of xwidgets&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Interactive widgets allow Jupyter users to create user interfaces inline in their notebooks, and to turn them into standalone applications with tools such as &lt;a href="https://github.com/voila-dashboards/voila"&gt;Voilà&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Language backends for Jupyter interactive widgets exist in Python (with &lt;a href="https://github.com/jupyter-widgets/ipywidgets"&gt;ipywidgets&lt;/a&gt;), and C++ (with &lt;a href="https://github.com/jupyter-xeus/xwidgets"&gt;xwidgets&lt;/a&gt;, and the &lt;a href="https://github.com/jupyter-xeus/xeus-cling"&gt;xeus-cling&lt;/a&gt; C++ Jupyter kernel), reusing the same frontend implementation of the widgets in JavaScript.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Simple interactive widgets at play in JupyterLab with the xeus-cling C++ kernel" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/a-curiously-recurring-widget-library/images/001-1_b_2s5wfIzrqGapkhBytHSg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Simple interactive widgets at play in JupyterLab with the xeus-cling C++ kernel&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In this article, we dive into some of the C++ techniques used in the implementation of the xwidgets library, including&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;discussions on &lt;strong&gt;value semantics&lt;/strong&gt; and &lt;strong&gt;RAII&lt;/strong&gt; (&lt;em&gt;Resource Acquisition Is Initialization&lt;/em&gt;),&lt;/li&gt;
&lt;li&gt;an original application of &lt;strong&gt;CRTP&lt;/strong&gt; (&lt;em&gt;Curiously Recurring Template Pattern&lt;/em&gt;),&lt;/li&gt;
&lt;li&gt;an original implementation of the &lt;strong&gt;observer&lt;/strong&gt; pattern, xproperty,&lt;/li&gt;
&lt;li&gt;a plea for allowing the &lt;strong&gt;overloading of the dot operator&lt;/strong&gt; in C++, to enable better proxy types.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;For readers interested in knowing more about&lt;/em&gt; &lt;em&gt;&lt;strong&gt;interpreted C++&lt;/strong&gt;&lt;/em&gt;&lt;em&gt;, we recommend the following posts:&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/posts/2017/interactive-workflows-for-c-with-jupyter/"&gt;Interactive workflows for C++ with Jupyter&lt;/a&gt; &lt;em&gt;(Jupyter blog), by Sylvain Corlay, Loic Gouarin, Johan Mabille, and Wolf Vollprecht.&lt;br&gt;
-&lt;/em&gt; &lt;a href="https://blog.llvm.org/posts/2020-12-21-interactive-cpp-for-data-science/"&gt;Interactive C++ for Data Science&lt;/a&gt; &lt;em&gt;(LLVM blog), by Vassil Vassilev, David Lange, Simeon Ehrig, and Sylvain Corlay&lt;br&gt;
-&lt;/em&gt; &lt;a href="/posts/2018/interpreted-c-for-gis-with-jupyter/"&gt;Interpreted C++ for GIS with Jupyter&lt;/a&gt; &lt;em&gt;(Jupyter blog), by Martin Renou&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id="using-the-raii-pattern-for-a-widget-library"&gt;Using the RAII pattern for a widget library&lt;/h2&gt;
&lt;p&gt;Jupyter widgets are special objects that trigger the creation of a counterpart JavaScript model object in the Jupyter frontend upon creation. The state of the object in the backend is synchronized with the state of the JavaScript frontend object. Views of that widget model are instantiated upon display.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The MVC (Model View Controller) architecture of Jupyter widgets, and synchronization with the backend" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/a-curiously-recurring-widget-library/images/002-1_ThTvsqji0l85Pr__5hs9bQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The MVC (Model View Controller) architecture of Jupyter widgets, and synchronization with the backend&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This MVC (Model-View-Controller) architecture for Jupyter interactive widgets allowed us to reuse all of the frontend implementation, by simply providing an alternative backend in C++, implementing the same messaging protocol.&lt;/p&gt;
&lt;p&gt;In order to tie the lifetime of the kernel and frontend objects, we decided to use the &lt;a href="https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization"&gt;RAII (Resource Acquisition Is Initialization)&lt;/a&gt; pattern. RAII is a common programming idiom that consists of tying the lifetime of an object with the holding of a resource. Most typically, the resource is&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;acquired&lt;/strong&gt;&lt;/em&gt; in the &lt;em&gt;&lt;strong&gt;constructor&lt;/strong&gt;&lt;/em&gt; of the object,&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;released&lt;/strong&gt;&lt;/em&gt; in the &lt;em&gt;&lt;strong&gt;destructor&lt;/strong&gt;&lt;/em&gt; of the object.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The RAII pattern is not common in garbage-collected languages because unlike in C++, the time when objects are destroyed is not deterministic. The Python programming language mitigates that issue by introducing&lt;/em&gt; &lt;a href="https://docs.python.org/3/reference/compound_stmts.html#the-with-statement"&gt;&lt;em&gt;context managers&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, often used to&lt;/em&gt; e.g. &lt;em&gt;open and close files.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A consequence of relying on object lifetime for resource management in C++ is to adopt the “&lt;em&gt;&lt;strong&gt;value semantics&lt;/strong&gt;&lt;/em&gt;” for widget instances, instead of “&lt;em&gt;&lt;strong&gt;reference semantics&lt;/strong&gt;&lt;/em&gt;” which is more typical for widget frameworks like Qt.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note: value semantics vs reference semantics&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you are not familiar with the concepts of&lt;/em&gt; &lt;strong&gt;value&lt;/strong&gt; &lt;em&gt;and&lt;/em&gt; &lt;strong&gt;reference&lt;/strong&gt; &lt;strong&gt;semantics&lt;/strong&gt; &lt;em&gt;in C++, I recommend reading the&lt;/em&gt; &lt;a href="https://isocpp.org/wiki/faq/value-vs-ref-semantics"&gt;&lt;em&gt;&lt;strong&gt;excellent FAQ&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt; &lt;em&gt;of isocpp.org.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;To summarize:&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;- with&lt;/em&gt; &lt;em&gt;&lt;strong&gt;value semantics&lt;/strong&gt;&lt;/em&gt;, &lt;em&gt;objects hold actual values, and copying an object copies their attributes. With value semantics, one should provide implementations of&lt;/em&gt; &lt;em&gt;&lt;strong&gt;copy, and move constructors&lt;/strong&gt;&lt;/em&gt;*, as well as* &lt;em&gt;&lt;strong&gt;copy and move assignment operators&lt;/strong&gt;&lt;/em&gt;*. Besides, values should not have virtual methods.*&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;- with&lt;/em&gt; &lt;em&gt;&lt;strong&gt;reference semantics&lt;/strong&gt;&lt;/em&gt;*, objects are manipulated through references (or pointers) and never by value. Reference semantics is typically used for polymorphic programming with* &lt;em&gt;&lt;strong&gt;virtual methods&lt;/strong&gt;&lt;/em&gt;*. With reference semantics, it is recommended to delete copy and move constructors, as well as copy and move assignment operators to avoid accidental copies when passing objects to functions taking arguments by values, causing object slicing. (They can also be made private). Explicit cloning of a reference semantics object is generally allowed via a call to a* &lt;em&gt;&lt;strong&gt;clone&lt;/strong&gt;&lt;/em&gt; &lt;em&gt;virtual method.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In a C++ codebase, the existence of public copy or move constructors alongside virtual methods in the same class is generally a sign of a bad design.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The use of value semantics for xwidgets provides clear lifetime management for associated resources. Careful use of the move semantics provides fine-grained control.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Illustration of the move semantics for xwidgets" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/a-curiously-recurring-widget-library/images/003-1_i1D4_PqaKhej-XRSKkoyBw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Illustration of the move semantics for xwidgets&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Another advantage of using value semantics in xwidgets is that C++ &lt;strong&gt;beginners&lt;/strong&gt; who are typical users of the Jupyter notebook (often used by instructors) can easily manipulate “widgets as values” without having to deal with manual memory allocation etc.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;With value semantics, addressing the lifetime of objects becomes the responsibility of the framework author, in a carefull implementation of (copy and move) constructors, destructors, and (copy and move) assignment operators.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="static-polymorphism-and-the-crtp-pattern"&gt;Static polymorphism and the CRTP pattern&lt;/h2&gt;
&lt;p&gt;While the use of value semantics provides fine-grained control over the lifetime of widgets, it prevents the use of virtual methods in their implementation. Code reuse is achieved with static polymorphism techniques and specifically the CRTP pattern.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern"&gt;Curiously Recurring Template Pattern (CRTP)&lt;/a&gt; is the practice of making a class &lt;code&gt;X&lt;/code&gt; derive from a class template instantiation using &lt;code&gt;X&lt;/code&gt; itself as a template argument.&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="o"&gt;X&lt;/span&gt; : &lt;span class="n"&gt;public&lt;/span&gt; &lt;span class="nb"&gt;base&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;X&lt;/span&gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;CRTP is commonly used by matrix or tensor algebra libraries making use of expression templates (such as&lt;/em&gt; &lt;a href="https://github.com/xtensor-stack/xtensor"&gt;&lt;em&gt;xtensor&lt;/em&gt;&lt;/a&gt;, &lt;a href="http://eigen.tuxfamily.org/index.php?title=Main_Page"&gt;&lt;em&gt;eigen&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, or&lt;/em&gt; &lt;a href="https://github.com/blitzpp/blitz"&gt;&lt;em&gt;blitz&lt;/em&gt;&lt;/a&gt;&lt;em&gt;) to prevent the overhead of virtual function dispatch for operations likely to be performed in a loop, such as element access.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Placing common code in a CRTP base allows code reuse without the overhead of virtual dispatch. However, the template base class is specialized for each final type increasing the resulting &lt;strong&gt;binary size&lt;/strong&gt;. This can be mitigated by factoring as much of the code in a base class that would not be templated by the derived type.&lt;/p&gt;
&lt;h2 id="crtp-in-xwidgets-closing-the-recursion"&gt;CRTP in xwidgets — closing the recursion&lt;/h2&gt;
&lt;p&gt;In order to allow for code reuse without virtual inheritance, xwidgets’ class hierarchy is entirely based on CRTP. Our naming scheme is that CRTP bases, which should not be instantiated directly are prefixed with the letter &lt;code&gt;x&lt;/code&gt; and final concrete widget types are not.&lt;/p&gt;
&lt;p&gt;Upon construction of &lt;em&gt;e.g.&lt;/em&gt; the &lt;strong&gt;&lt;code&gt;button&lt;/code&gt;&lt;/strong&gt; widget, constructors of base types are called in the order of inheritance, which is why we need to establish the connection with the frontend in the constructor of the most derived type, after all attributes have been initialized, and send a message to the frontend with all the values.&lt;/p&gt;
&lt;p&gt;The pattern for creating the most derived type being always the same, we defined the &lt;strong&gt;&lt;code&gt;xmaterialize&lt;/code&gt;&lt;/strong&gt; template class closing the CRTP hierarchy with&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;using button = xmaterialize&amp;lt;xbutton&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;strong&gt;&lt;code&gt;xmaterialize&lt;/code&gt;&lt;/strong&gt; template class is defined as &lt;strong&gt;final&lt;/strong&gt;, to prevent further inheritance. The constructors and assignment operators forward to those of the CRTP bases and implement the RAII pattern.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;P&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;xmaterialize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;final&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="nx"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;xmaterialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;P&lt;/span&gt;&lt;span class="o"&gt;...&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;self_type&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="nx"&gt;xmaterialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;P&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;base_type&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="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;self_type&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;xmaterialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;...&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="nx"&gt;base_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&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;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;//  RAII: create the frontend model.&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;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;inline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xmaterialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;::~&lt;/span&gt;&lt;span class="n"&gt;xmaterialize&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="nf"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;moved_from&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;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// RAII: delete the frontend model.&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;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The full implementation of &lt;strong&gt;&lt;code&gt;xmaterialize&lt;/code&gt;&lt;/strong&gt; (as of xwidgets 0.25) is available &lt;a href="https://raw.githubusercontent.com/jupyter-xeus/xwidgets/0.25.0/include/xwidgets/xmaterialize.hpp"&gt;here&lt;/a&gt;. Beyond the logic described in this section, it also includes the handling of the move semantics and the method chaining API which is the subject of a later section.&lt;/p&gt;
&lt;h2 id="precompilation-and-binary-size-optimization"&gt;&lt;strong&gt;Precompilation and binary size optimization&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Even though xwidgets is fully based on template types, we decided to precompile all final widget types for faster interactive use with the xeus-cling kernel. This is achieved with&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;an &lt;strong&gt;&lt;code&gt;extern&lt;/code&gt;&lt;/strong&gt; declaration in the header (here in &lt;code&gt;xbutton.hpp&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;extern template class xmaterialize&amp;lt;xbutton&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;and in the source file (here in &lt;code&gt;xbutton.cpp&lt;/code&gt;), an instruction for the precompilation&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;template class XWIDGETS_API xmaterialize&amp;lt;xbutton&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Doing this for all widget types of the library initially resulted in a large compiled binary size. Using the &lt;strong&gt;&lt;code&gt;button&lt;/code&gt;&lt;/strong&gt; widget as an example, we see that base types are templated by the final type in the class hierarchy &lt;strong&gt;&lt;code&gt;button -&amp;gt; xbutton&amp;lt;button&amp;gt; -&amp;gt; xwidgets&amp;lt;button&amp;gt; -&amp;gt; xobject&amp;lt;button&amp;gt;&lt;/code&gt;&lt;/strong&gt;, and therefore, their binary representation is duplicated for each final type.&lt;/p&gt;
&lt;p&gt;A strategy for reducing the binary size has been to factor out as much of the logic of &lt;strong&gt;&lt;code&gt;xobject&amp;lt;D&amp;gt;&lt;/code&gt;&lt;/strong&gt; in a non-template base &lt;strong&gt;&lt;code&gt;xcommon&lt;/code&gt;&lt;/strong&gt; improving compilation speed and preventing binary code duplication.&lt;/p&gt;
&lt;h2 id="xproperty-an-implementation-of-the-observer-pattern"&gt;Xproperty: an implementation of the observer pattern&lt;/h2&gt;
&lt;p&gt;In order to update the frontend upon changes of widget properties, xwidgets relies on an implementation of the observer pattern called &lt;a href="https://github.com/jupyter-xeus/xproperty"&gt;&lt;strong&gt;&lt;code&gt;xproperty&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt;. xproperty is to xwidgets what &lt;a href="https://github.com/ipython/traitlets"&gt;traitlets&lt;/a&gt; are to ipywidgets.&lt;/p&gt;
&lt;p&gt;In order to trigger observers and validators in the owner object upon assignment of new values, xproperty relies on the overload of the assignment operator &lt;strong&gt;&lt;code&gt;=&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;O&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;inline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xproperty&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;O&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;::&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Before assigning the new value, invoke validators&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// which may also mutate the value.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;m_value&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;owner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;invoke_validators&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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;/code&gt;&lt;/pre&gt;&lt;/div&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;&lt;span class="c1"&gt;// Call class-level observer.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;&lt;span class="c1"&gt;// Call registered observers for that attribute&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;invoke_observers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;&lt;span class="c1"&gt;// Return the new value &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="method-chaining-for-widget-initialization"&gt;Method chaining for widget initialization&lt;/h2&gt;
&lt;p&gt;Jupyter interactive widgets have many attributes that may be specified at construction time.&lt;/p&gt;
&lt;p&gt;In the Python implementation, this is handled with keyword arguments, but the C++ programming language does not support keyword arguments. There exist various approaches to enable this feature with advanced metaprogramming techniques. In the case of xwidgets, this need is limited to the initialization of xproperty attributes, which allowed us to adopt a more scoped approach: a method chaining API for property initialization:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;slider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;slider&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;double&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="no"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;.min&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="no"&gt;.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;.max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="no"&gt;.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;.description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Another slider&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="na"&gt;.finalize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;this was enabled by overriding the function call operator () on xproperties to pass an intial value (only when the said property is an rvalue). A static &lt;strong&gt;&lt;code&gt;initialize&lt;/code&gt;&lt;/strong&gt; method is used instead of the default constructor to prevent the initialization of the JavaScript counterpart while all attributes may not have been set yet. The frontend counterpart is only acquired with the &lt;strong&gt;&lt;code&gt;finalize()&lt;/code&gt;&lt;/strong&gt; call.&lt;/p&gt;
&lt;h2 id="building-upon-xwidgets"&gt;Building upon xwidgets&lt;/h2&gt;
&lt;p&gt;Jupyter interactive widgets are not limited to the controls available in the core package. In fact, there is a rich ecosystem of widget libraries built upon the core framework: &lt;a href="https://github.com/maartenbreddels/ipyvolume"&gt;ipyvolume&lt;/a&gt; (3-D plotting), &lt;a href="https://github.com/jupyter-widgets/ipyleaflet"&gt;ipyleaflet&lt;/a&gt; (maps visualization), &lt;a href="https://github.com/bqplot/bqplot"&gt;bqplot&lt;/a&gt; (2-D plotting), &lt;a href="https://github.com/QuantStack/ipygany"&gt;ipygany&lt;/a&gt; (3-D mesh visualization), &lt;a href="https://github.com/martinRenou/ipycanvas"&gt;ipycanvas&lt;/a&gt; (generic drawing), &lt;a href="https://github.com/maartenbreddels/ipywebrtc"&gt;ipywebrtc&lt;/a&gt; (streaming video and audio), and many many more.&lt;/p&gt;
&lt;p&gt;For the C++ programming language, we have already provided:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyter-xeus/xleaflet"&gt;xleaflet&lt;/a&gt; (the C++ equivalent to ipyleaflet, reusing the same frontend),&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/QuantStack/xwebrtc"&gt;xwebrtc&lt;/a&gt; (the C++ equivalent to ipywebrtc, reusing the same frontend).&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
&lt;img alt="Screencast of xleaflet in JupyterLab, loading and visualizing a GeoJSON dataset in a C++ notebook." src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/a-curiously-recurring-widget-library/images/004-1_IULQ8LZDnLFMmB1nsbNF0Q.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Screencast of xleaflet in JupyterLab, loading and visualizing a GeoJSON dataset in a C++ notebook.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Potentially, C++ backend to all Jupyter interactive widget packages could be provided, creating a huge opportunity for interactive data visualization in C++.&lt;/p&gt;
&lt;h2 id="c-should-allow-overloading-the-dot-operator"&gt;&lt;strong&gt;C++ should allow overloading the dot operator&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;xwidgets&lt;/strong&gt; and &lt;strong&gt;xproperty&lt;/strong&gt; makes heavy use of value semantics, and proxy objects.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;At the moment, to access an attribute or method of a value held in an xproperty object, we must first call the function call operator () to access the undelying object first, which is cumbersome.&lt;/li&gt;
&lt;li&gt;This is also an issue in other places in the xwidgets stack, when making use of &lt;em&gt;e.g.&lt;/em&gt; reference proxies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Being able to automatically map all methods of the underlying type to be accessible in the xproperty would be incredibly powerful, and remove the need for explicitly accessing the underlying.&lt;/p&gt;
&lt;p&gt;Interestingly, the C++ standard does have the equivalent operator overload when it comes to pointer semantics, with the arrow operator &lt;strong&gt;&lt;code&gt;-&amp;gt;&lt;/code&gt;&lt;/strong&gt;. If overloading the dot operator . was allowed, this could enable this kind of usecase:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;struct V
{
    void f();
};

struct X
{
    V&amp;amp; operator.() { return m_value; }
    V m_value;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;in which case, &lt;strong&gt;&lt;code&gt;X::f&lt;/code&gt;&lt;/strong&gt; would call &lt;strong&gt;&lt;code&gt;V::f&lt;/code&gt;&lt;/strong&gt; .&lt;/p&gt;
&lt;p&gt;Allowing the overloading of the dot operator . would also enable usecases such as &lt;strong&gt;smart references&lt;/strong&gt; (similar to smart pointers, but with value semantics) and fully-fledged &lt;strong&gt;reference proxies&lt;/strong&gt; (such as the return type of &lt;code&gt;operator[]&lt;/code&gt; for &lt;code&gt;std::vector&amp;lt;bool&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id="try-it-online"&gt;Try it online!&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;a href="https://mybinder.org/"&gt;MyBinder&lt;/a&gt;, you can try xwidgets without the need of installing anything on your computer. Just follow this link:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/jupyter-xeus/xwidgets/stable?filepath=notebooks/xwidgets.ipynb"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/a-curiously-recurring-widget-library/images/005-0_TQpdhVZSAnE-XOrm.jpg" alt="&amp;quot;Launch binder&amp;quot; badge" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="about-the-author"&gt;About the author&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://twitter.com/SylvainCorlay"&gt;Sylvain Corlay&lt;/a&gt; is the founder and CEO of &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt;, an open-source software development studio comprising maintainers of key projects of the scientific computing ecosystem.&lt;/p&gt;
&lt;p&gt;As an open-source developer, Sylvain is very active in the Jupyter project, contributing to the &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt; stack, Jupyter interactive widgets, &lt;a href="https://github.com/voila-dashboards/voila"&gt;Voilà dashboards&lt;/a&gt;. He is a member of the Jupyter steering committee and was the vice chair of &lt;a href="https://jupytercon.com/"&gt;JupyterCon 2020&lt;/a&gt;. Sylvain also contributes to the &lt;a href="https://conda-forge.org/"&gt;conda-forge&lt;/a&gt; project and he is the co-creator of the &lt;a href="https://github.com/xtensor-stack/xtensor"&gt;Xtensor&lt;/a&gt; C++ tensor algebra library.&lt;/p&gt;
</content><category term="C++"/><category term="widgets"/></entry><entry><title>Interactive Graph Visualization in Jupyter with ipycytoscape</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2020/interactive-graph-visualization-in-jupyter-with/" rel="alternate"/><published>2020-04-30T16:16:00+00:00</published><updated>2020-07-28T08:34:00+00:00</updated><author><name>Mariana Meireles</name></author><id>tag:jasongrout.github.io,2020-04-30:/medium-archive/pelican/posts/2020/interactive-graph-visualization-in-jupyter-with/</id><summary type="html">&lt;p&gt;The Jupyter widgets ecosystem offers a broad variety of data visualization tools for exploratory analysis in the notebook. However, we…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;The Jupyter widgets ecosystem offers a broad variety of data visualization tools for exploratory analysis in the notebook. However, we lack a good story for exploratory graph visualization.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://cytoscape.org/"&gt;Cytoscape&lt;/a&gt; is an open-source software platform for visualizing complex networks and integrating these with any type of attribute data. While it comes from the computational biology community, cytoscape is fully-fledged general-purpose tool for graph visualization and analytics. It now includes a modern web front-end (&lt;a href="https://js.cytoscape.org/"&gt;Cytoscape.JS&lt;/a&gt;) which is a great candidate for integration with Project Jupyter. This is the &lt;em&gt;raison d’être&lt;/em&gt; of &lt;strong&gt;ipycytoscape&lt;/strong&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Gene visualization in ipycytoscape" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/interactive-graph-visualization-in-jupyter-with/images/001-1_d9UZBB4OB3LXVm7fTHBHaw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Gene visualization in ipycytoscape&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The goal of ipycytoscape is to enable users of well-established libraries of the Python ecosystem like Pandas, NetworkX, and NumPy, to visualize their graph data in the Jupyter notebook, and enable them modify the visual outcome programmatically or graphically with a simple API and user interface.&lt;/p&gt;
&lt;p&gt;Fortunately, Cytoscape offers a broad enough API that allows ipycytoscape to be a tool that can, in fact, be used to solve any type of problem modeled as a graph. Some examples consist in the development of new chemicals to analyze interactions between substances in the pharmaceutic industry, in security systems to create attack graphs that can be useful to show possible vulnerabilities in systems, modeling human behavior to understand people’s interaction with business or even to understand complex phenomena like the current crisis. Currently, there is an effort to make ipycytoscape an accessible tool for researchers that are trying to find ways to mitigate and understand it, there is more information about this initiative on the COVID OSS Help &lt;a href="https://covid-oss-help.org/"&gt;website&lt;/a&gt; and the discussion is happening in this &lt;a href="https://github.com/covid-19-net/covid-19-community/"&gt;repository&lt;/a&gt; if you’re interested in joining it.&lt;/p&gt;
&lt;h2 id="current-state"&gt;Current state&lt;/h2&gt;
&lt;p&gt;IPycytoscape is part of the PLASMA project (aka in French, Plateforme d’eLearning pour l’Analyse de données Scientifiques MAssives). This project aims at creating an interactive tool to teach computational analysis of massive scientific data. Its first instance, PlasmaBio, is designed for the needs of teachers and students of the &lt;a href="http://www.magisteregenet.univ-paris-diderot.fr/"&gt;European Master of Genetics&lt;/a&gt; at &lt;a href="https://u-paris.fr/"&gt;Université de Paris&lt;/a&gt;. PlasmaBio provides an authentic experience of the actual genomic and bioinformatic analyses performed in research labs. For that purpose, a custom &lt;a href="https://github.com/plasmabio/plasmabio"&gt;JupyterHub-based system&lt;/a&gt; to control many different Jupyter instances is being specially developed by &lt;a href="http://twitter.com/jtpio"&gt;Jeremy Tuloup&lt;/a&gt; at &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this first version of ipycytoscape, there are still some limitations to what you may be able to do, but there are also some extents from the Python world that will just work out of the box for you. ipycytoscape offers integration between Pandas DataFrames and NetworkX, meaning that you can have a graph visualization of the data you already have with minimal or none adjustments and just a few lines of code.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Usage with NetworkX and DataFrame" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/interactive-graph-visualization-in-jupyter-with/images/002-1_yRpK3giBa1BLxxnepSE-kg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Usage with NetworkX and DataFrame&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;ipycytoscape supports all of the built-in CytoscapeJS layouts. This includes the &lt;code&gt;cola&lt;/code&gt;, &lt;code&gt;grid&lt;/code&gt;, &lt;code&gt;breadthfirst&lt;/code&gt;, &lt;code&gt;circular&lt;/code&gt;, &lt;code&gt;concentric&lt;/code&gt; and &lt;a href="https://github.com/dagrejs/dagre"&gt;Dagre&lt;/a&gt; layout as well as the &lt;code&gt;random&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;preset&lt;/code&gt; options to build a graph visualization that fits better to your data .Additionally, ipycytoscape also supports the &lt;a href="https://popper.js.org/docs/v2/"&gt;PopperJS&lt;/a&gt; and &lt;a href="https://atomiks.github.io/tippyjs/"&gt;TippyJS&lt;/a&gt; extensions, that allows you to create customizable tips for your nodes and edges .&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="cola, concentric, dagre and grid layouts" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/interactive-graph-visualization-in-jupyter-with/images/003-1__7mOQADG9USBa0AY-RZaBw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;cola, concentric, dagre and grid layouts&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You can also use a variety of labels for a quick visualization of your nodes’ and edges’ contents.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Labels on nodes" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/interactive-graph-visualization-in-jupyter-with/images/004-1_Tx54Yjz5EbF3kUC-u82XaQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Labels on nodes&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Like most Jupyter interactive widgets, ipycytoscape relies on the traitlets library to synchronize data between the back-end and the front-end model.&lt;/p&gt;
&lt;p&gt;Unfortunately, traitlets have a limitation when it comes to container objects and other mutable structures, because synchronization is only triggered upon assignment of the container and not when modifying individual elements.&lt;/p&gt;
&lt;p&gt;To work around this limitation, we make use of the excellent &lt;a href="https://github.com/rmorshea/spectate"&gt;Spectate&lt;/a&gt; library by &lt;a href="https://twitter.com/rmorshea"&gt;Ryan Morshead&lt;/a&gt;, which triggers observers upon individual element changes in containers.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Interaction between ipywidgets and ipycytoscape" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/interactive-graph-visualization-in-jupyter-with/images/005-1_0l66uLHE51IpGlDLsOf2kw.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Interaction between ipywidgets and ipycytoscape&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="try-it-online"&gt;Try it online!&lt;/h3&gt;
&lt;p&gt;You can try it without the need of installing anything on your computer just by clicking on the image below:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/QuantStack/ipycytoscape/stable?filepath=examples"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/interactive-graph-visualization-in-jupyter-with/images/006-0_-Lcpj92fbL3Zr7N3.webp" alt="https://mybinder.org/v2/gh/QuantStack/ipycytoscape/stable?filepath=examples" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="installation"&gt;Installation&lt;/h3&gt;
&lt;p&gt;Note that you first need to have Jupyter installed on your computer. You can install &lt;code&gt;ipycytoscape&lt;/code&gt; using pip:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install ipycytoscape
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or using conda:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda install -c conda-forge ipycytoscape
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you use JupyterLab, you would need to install the JupyterLab extension for ipycanvas (this requires nodejs to be installed):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;jupyter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;labextension&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;@jupyter&lt;/span&gt;&lt;span class="o"&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;jupyterlab&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jupyter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cytoscape&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="about-the-author"&gt;About the author&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/interactive-graph-visualization-in-jupyter-with/images/007-1_S-w69baox7Q1D997a5yKNw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;My name is &lt;a href="https://twitter.com/mari_meir"&gt;Mariana Meireles&lt;/a&gt; and I’m a software developer working for &lt;a href="http://quantstack.net/"&gt;QuantStack&lt;/a&gt;. I care deeply about the impacts that technology has in the world and try my best to be the change I want to see by contributing to open source projects that stand upon libre and diverse standards.&lt;/p&gt;
&lt;p&gt;Prior to QuantStack I worked as a developer on the PySide team at the Qt Company and as a web performance developer at Mozilla.&lt;/p&gt;
&lt;p&gt;Currently I’m working on expanding the Jupyter ecosystem with new libraries and functionalities, like an experimental &lt;a href="https://github.com/jupyter-xeus/xeus-sqlite"&gt;SQLite kernel&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="acknowledgements"&gt;&lt;strong&gt;Acknowledgements&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The development of ipycytoscape at &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt; was funded as part of the &lt;a href="https://twitter.com/PlasmaBio"&gt;PLASMA&lt;/a&gt; project, led by &lt;a href="https://twitter.com/CVandiedonck"&gt;Claire Vandiedonck&lt;/a&gt;, &lt;a href="https://twitter.com/pierrepo"&gt;Pierre Poulain&lt;/a&gt;, and &lt;a href="https://twitter.com/SCaburet"&gt;Sandrine Caburet&lt;/a&gt;, associate professors at Université de Paris.&lt;/p&gt;
&lt;p&gt;Sponsors to the PLASMA initiative include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/iledefrance"&gt;Région Île-de-France&lt;/a&gt;, via the “Trophées franciliens de l’innovation numérique dans le supérieur” (&lt;a href="https://www.iledefrance.fr/trophees-franciliens-de-linnovation-numerique-dans-le-superieur-les-laureats-2018"&gt;EdTech 2018&lt;/a&gt;) grant program,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/Univ_Paris"&gt;Université de Paris&lt;/a&gt;, via the &lt;a href="https://u-paris.fr/en/the-initiative-of-excellence-idex-label/"&gt;Initiative of Excellence (IdEx) Label&lt;/a&gt; and its “inovating teaching” grant program,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/EURGENEPARIS"&gt;EUR G.E.N.E.&lt;/a&gt;, the graduate school on Genetics and Epigenetics,&lt;/li&gt;
&lt;li&gt;the university training “Création, analyse et valorisation de données biologiques omiques” (&lt;a href="https://omics-school.net/"&gt;DU Omiques&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
</content><category term="visualization"/><category term="widgets"/></entry><entry><title>ipycanvas: A Python Canvas for Jupyter</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2019/ipycanvas-a-python-canvas-for-jupyter/" rel="alternate"/><published>2019-10-25T12:48:00+00:00</published><updated>2022-04-08T08:29:00+00:00</updated><author><name>Martin Renou</name></author><id>tag:jasongrout.github.io,2019-10-25:/medium-archive/pelican/posts/2019/ipycanvas-a-python-canvas-for-jupyter/</id><summary type="html">&lt;p&gt;As you may already know, the Jupyter Notebook and JupyterLab are Browser-based applications. Browsers are incredibly powerful, they allow…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/ipycanvas-a-python-canvas-for-jupyter/images/001-1_LHrtcPJMCWVMgsvNR6tR6w.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;As you may already know, the Jupyter Notebook and JupyterLab are Browser-based applications. &lt;strong&gt;Browsers are incredibly powerful&lt;/strong&gt;, they allow you to swap rich and interactive graphical interfaces containing buttons, sliders, maps, 2D and 3D plots and even video games in your webpages!&lt;/p&gt;
&lt;p&gt;All this power is readily made available to the Python ecosystem by &lt;strong&gt;Jupyter interactive widgets&lt;/strong&gt; libraries. Whether you want to create simple controls using &lt;a href="https://github.com/jupyter-widgets/ipywidgets/"&gt;ipywidgets&lt;/a&gt;, display interactive data on a 2D map with &lt;a href="https://github.com/jupyter-widgets/ipyleaflet"&gt;ipyleaflet&lt;/a&gt;, plot 2D data using &lt;a href="https://github.com/bloomberg/bqplot/"&gt;bqplot&lt;/a&gt; or plot volumic data with &lt;a href="https://github.com/maartenbreddels/ipyvolume"&gt;ipyvolume&lt;/a&gt;, all of this is made possible thanks to the &lt;strong&gt;open-source&lt;/strong&gt; community.&lt;/p&gt;
&lt;p&gt;One powerful tool in the Browser is the &lt;strong&gt;HTML5 Canvas&lt;/strong&gt; element, it allows you to draw 2D or 3D graphics on the webpage. There are two available APIs for the Canvas, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API"&gt;Canvas API&lt;/a&gt; which focuses on 2D graphics, and the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API"&gt;WebGL API&lt;/a&gt; which uses hardware acceleration for 3D graphics.&lt;/p&gt;
&lt;p&gt;After some discussions with my work colleague &lt;a href="https://twitter.com/wuoulf"&gt;Wolf Vollprecht&lt;/a&gt;, we came to the conclusion that it would be a great idea to directly expose the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API"&gt;Canvas API&lt;/a&gt; to IPython, without making any modification to it. And that’s how we came up with &lt;a href="https://github.com/martinRenou/ipycanvas"&gt;ipycanvas&lt;/a&gt;!&lt;/p&gt;
&lt;h2 id="ipycanvas-exposing-the-canvas-api-to-ipython"&gt;ipycanvas: Exposing the Canvas API to IPython&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/ipycanvas-a-python-canvas-for-jupyter/images/002-1_-Q6-aW2mJjMfsxmieaGomw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/martinRenou/ipycanvas"&gt;ipycanvas&lt;/a&gt; exposes the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API"&gt;Canvas API&lt;/a&gt; to IPython, making it possible to &lt;strong&gt;draw anything you want on a Jupyter Notebook&lt;/strong&gt; directly in Python! Anything is possible, you can draw custom heatmaps from NumPy arrays, you can implement your own 2D video-game, or you can create yet another IPython plotting library!&lt;/p&gt;
&lt;p&gt;ipycanvas provides a low-level API that allows you to draw simple primitives like lines, polygons, arcs, text, images… Once you’re familiar with the API, you’re only limited by your own imagination!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/ipycanvas-a-python-canvas-for-jupyter/images/003-1_80VJXjNns82TZUcLURNplg.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Draw image from NumPy array (left), implementation of the Game Of Life (right)" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/ipycanvas-a-python-canvas-for-jupyter/images/004-1_jjBIO9JslYo7LfIyTpfjxQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Draw image from NumPy array (left), implementation of the Game Of Life (right)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/ipycanvas-a-python-canvas-for-jupyter/images/005-1_QeQxhuDRL1AwXokdsQ2fhQ.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Draw millions of particles (left), draw custom sprites (right)" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/ipycanvas-a-python-canvas-for-jupyter/images/006-1_6SqrCHH4YsJUY4nrDTU7fw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Draw millions of particles (left), draw custom sprites (right)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/ipycanvas-a-python-canvas-for-jupyter/images/007-1_PtctDM0B6OT604tRFV2WtA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Make your own plotting library for Jupyter fully in Python!" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/ipycanvas-a-python-canvas-for-jupyter/images/008-1_rCvw3tMgRVixKn_wUAHEAQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Make your own plotting library for Jupyter fully in Python!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Using &lt;a href="https://twitter.com/astronomatty"&gt;Matt Craig&lt;/a&gt;’s &lt;a href="https://github.com/mwcraig/ipyevents"&gt;ipyevents&lt;/a&gt; library, you can add mouse and key events to the Canvas and react to user interactions.&lt;/p&gt;
&lt;p&gt;If you have a GamePad around, you can also use the built-in &lt;a href="https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20List.html#Controller"&gt;Controller&lt;/a&gt; widget and make your own video-game in a Jupyter Notebook!&lt;/p&gt;
&lt;h3 id="documentation"&gt;Documentation&lt;/h3&gt;
&lt;p&gt;Check-out the ipycanvas documentation for more information: &lt;a href="https://ipycanvas.readthedocs.io/en/latest/?badge=latest"&gt;ipycanvas.readthedocs.io&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="github-repository"&gt;Github repository&lt;/h3&gt;
&lt;p&gt;Give it a star on Github if you like it! &lt;a href="https://github.com/martinRenou/ipycanvas/"&gt;github.com/martinRenou/ipycanvas&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="try-it-online"&gt;Try it online!&lt;/h3&gt;
&lt;p&gt;You can try it without the need of installing anything on your computer just by clicking on the image below:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/martinRenou/ipycanvas/stable?filepath=examples"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/ipycanvas-a-python-canvas-for-jupyter/images/009-0_gt0KurDRJ50ZIIvf.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="installation"&gt;Installation&lt;/h3&gt;
&lt;p&gt;Note that you first need to have Jupyter installed on your computer. You can install ipycanvas using pip:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pip install ipycanvas&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Or using conda:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;conda install -c conda-forge ipycanvas&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="about-the-author"&gt;About the author&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://twitter.com/martinRenou"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/ipycanvas-a-python-canvas-for-jupyter/images/010-1_GH0Cfo-a2zZuJFrJyKrebA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My name is &lt;a href="https://twitter.com/martinRenou"&gt;Martin Renou&lt;/a&gt;, I am a Scientific Software Engineer at &lt;a href="http://quantstack.net/"&gt;QuantStack&lt;/a&gt;. Before joining &lt;a href="http://quantstack.net/"&gt;QuantStack&lt;/a&gt;, I studied at the aerospace engineering school &lt;a href="https://www.isae-supaero.fr/en"&gt;SUPAERO&lt;/a&gt; in Toulouse, France. I also worked at Logilab in Paris and Enthought in Cambridge, UK. As an open-source developer at QuantStack, I worked on a variety of projects, from &lt;a href="https://github.com/QuantStack/xtensor"&gt;xtensor&lt;/a&gt; and &lt;a href="https://github.com/QuantStack/xeus-python/"&gt;xeus-python&lt;/a&gt; in C++ to &lt;a href="https://github.com/jupyter-widgets/ipyleaflet"&gt;ipyleaflet&lt;/a&gt; and &lt;a href="https://github.com/maartenbreddels/ipywebrtc"&gt;ipywebrtc&lt;/a&gt; in Python and Javascript.&lt;/p&gt;
</content><category term="visualization"/><category term="widgets"/></entry><entry><title>Introducing templates for Jupyter widgets layouts</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2019/introducing-templates-for-jupyter-widget-layouts/" rel="alternate"/><published>2019-07-04T09:41:00+00:00</published><updated>2019-07-04T09:41:00+00:00</updated><author><name>Bartosz Telenczuk</name></author><id>tag:jasongrout.github.io,2019-07-04:/medium-archive/pelican/posts/2019/introducing-templates-for-jupyter-widget-layouts/</id><summary type="html">&lt;p&gt;Creating complex layouts of widgets (button, sliders, maps, graphs etc.) can be cumbersome. New layout templates make this task a breeze.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Notebooks come alive with Jupyter widgets, which allow users to produce interactive GUIs inline in the Jupyter notebook or JupyterLab.&lt;/p&gt;
&lt;p&gt;You can either use them to add a few interactive controls and plots in notebooks or to create fully-fledged applications and interactive dashboards. Both can be built with components from the core &lt;a href="https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20List.html"&gt;built-in widgets&lt;/a&gt; such as buttons, sliders, and dropdowns, or with the rich ecosystem of custom widget libraries that built upon the Jupyter widgets framework, such as interactive maps with &lt;a href="https://github.com/jupyter-widgets/ipyleaflet"&gt;ipyleaflet&lt;/a&gt; or 2-D plots with &lt;a href="https://github.com/bloomberg/bqplot"&gt;bqplot&lt;/a&gt;. You can also combine several types of widgets together to create even richer applications.&lt;/p&gt;
&lt;p&gt;Have you ever tried creating complex widget layouts with multiple widgets placed at specific locations? The preferred approach so far has been to use nested HBox and VBox widgets to compose your layout, which can make creating complex applications a tedious task. We now have a more flexible solution: the &lt;em&gt;layout templates&lt;/em&gt;, which just landed with the latest release of the ipywidgets package.&lt;/p&gt;
&lt;h2 id="the-power-of-css-the-simplicity-of-python"&gt;The power of CSS, the simplicity of Python&lt;/h2&gt;
&lt;p&gt;Layout templates are a set of predefined layouts that allow you to combine multiple widgets on a single screen and arrange them visually. They leverage the powerful &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout"&gt;CSS Grid Layout specification&lt;/a&gt;, which is supported on most current browsers (yes, we are looking at you IE).&lt;/p&gt;
&lt;p&gt;While the CSS Grid properties were first introduced in ipywidgets 7.3, they were tricky to use as they were transparently reflecting the CSS Grid Spec API and required the knowledge of the CSS. The new layout templates of ipywidgets wrap the CSS properties with a pythonic interface and sensible defaults, so they never expose the user to the nasty CSS spec. However, they inherit all the advantages of the Grid being fully responsive (they adapt to the screen size) and super easy to use!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Comparing the Python code with the generated CSS layout." src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/introducing-templates-for-jupyter-widget-layouts/images/001-1_0AvTf0rheCJJeNjOKIw27w.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Comparing the Python code with the generated CSS layout.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="application-like-uis-in-jupyter"&gt;Application-like UIs in Jupyter&lt;/h2&gt;
&lt;figure&gt;
&lt;img alt="AppLayout consists of a header, two side panes, a central pane, and a footer." src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/introducing-templates-for-jupyter-widget-layouts/images/002-1_nJ6g6rhPRdp8xfIq36Z-mw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;strong&gt;AppLayout&lt;/strong&gt; consists of a header, two side panes, a central pane, and a footer.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you want to create a simple application-like layout, you can use &lt;code&gt;AppLayout&lt;/code&gt;, which consists of a header, a footer, two side panes, and a central pane. You can create the layout and populate it with widgets in a single command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;AppLayout(header=header,
          left_sidebar=prev_button,
          center=image,
          right_sidebar=next_button,
          footer=footer,
          grid_gap=&amp;#39;20px&amp;#39;,
          justify_items=&amp;#39;center&amp;#39;,
          align_items=&amp;#39;center&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/introducing-templates-for-jupyter-widget-layouts/images/003-1_YQrJiSx2g6GfhkIqK24RUw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Importantly, if your application does not need all the panes defined by &lt;code&gt;AppLayout&lt;/code&gt;, the layout has also some sensible defaults so that it can automatically merge widget locations that were not assigned.&lt;/p&gt;
&lt;h2 id="widgets-on-a-grid"&gt;Widgets on a grid&lt;/h2&gt;
&lt;figure&gt;
&lt;img alt="GridspecLayout places widgets on a rectangular grid. A single widget can span several rows or columns (or both)." src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/introducing-templates-for-jupyter-widget-layouts/images/004-1_W1gbrgs8aDSs2ZezDQ5v7Q.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;strong&gt;GridspecLayout&lt;/strong&gt; places widgets on a rectangular grid. A single widget can span several rows or columns (or both).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you require more flexibility to arrange widgets, you can also try &lt;code&gt;GridLayout&lt;/code&gt;. First, you define the dimensions of a rectangular grid. Then you can place widgets on the grid either in a single cell of the grid or spanning several rows or columns (or both). This is easily achieved using the same slice-based API that you already use to select items from a NumPy array (or Python lists). If you already know matplotlib’s &lt;code&gt;GridSpec&lt;/code&gt; feature, the syntax may look familiar:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# create a 10x2 grid layout
grid = GridspecLayout(10, 2)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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; fill it in with widgets
grid[:, 0] = map
grid[0, 1] = zoom_slider
grid[1, 1] = basemap_selector
grid[2:, 1] = fig
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# set the widget properties
grid[:, 0].layout.height = &amp;#39;auto&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/introducing-templates-for-jupyter-widget-layouts/images/005-1_Bf8ZF5xTHU68f5OdrPBLtA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="style-me-up"&gt;Style me up&lt;/h2&gt;
&lt;p&gt;The layouts are very configurable and can be easily tuned to the needs of your application. To change the sizes of the layout and the grid intervals, you can use style attributes, such as &lt;code&gt;height&lt;/code&gt; , &lt;code&gt;width&lt;/code&gt;, and &lt;code&gt;gap-size&lt;/code&gt; options:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;AppLayout(grid_gap=&amp;#39;20px&amp;#39;,
          height=&amp;quot;200px&amp;quot;,
          width=&amp;quot;50%&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The size units are directly inherited from the CSS standard. More examples of style attributes can be found in the &lt;a href="https://ipywidgets.readthedocs.io/en/latest/examples/Layout%20Templates.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So please go ahead and install the pre-release of ipywidgets that includes this new feature (&lt;code&gt;pip install --upgrade ipywidgets&lt;/code&gt;) and take the new layout templates for a spin. We are looking forward to your feedback!&lt;/p&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;The author is a seasoned Python developer and a data scientist. He loves contributing to open source software; among others he is the creator and maintainer of the &lt;a href="https://svgutils.readthedocs.io/en/latest/"&gt;svgutils&lt;/a&gt; library.&lt;/p&gt;
&lt;p&gt;The development of the ipywidgets layout templates was kindly supported by &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt;.&lt;/p&gt;
</content><category term="dashboards"/><category term="widgets"/></entry><entry><title>Authoring Custom Jupyter Widgets</title><link href="https://jasongrout.github.io/medium-archive/pelican/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>QuantStack</name></author><id>tag:jasongrout.github.io,2018-03-05:/medium-archive/pelican/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;em&gt;Guest post authored by Olivier Borderies, Olivier Coudray, and Pierre Marion&lt;/em&gt;&lt;/p&gt;
&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;/em&gt; &lt;em&gt;&lt;strong&gt;Python&lt;/strong&gt;&lt;/em&gt; &lt;em&gt;back-end, even though&lt;/em&gt; &lt;a href="https://github.com/jupyter/jupyter/wiki/Jupyter-kernels"&gt;&lt;em&gt;dozens of Jupyter kernels&lt;/em&gt;&lt;/a&gt; &lt;em&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;/em&gt; &lt;a href="/posts/2017/interactive-workflows-for-c-with-jupyter/"&gt;&lt;em&gt;Xeus&lt;/em&gt;&lt;/a&gt;&lt;em&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://jasongrout.github.io/medium-archive/pelican/posts/2018/authoring-custom-jupyter-widgets/images/001-1_7cPVSk9PpumGA6vM79kHwQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;em&gt;Xeus: C++ implementation of Jupyter kernel protocol&lt;/em&gt;&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;/em&gt; &lt;a href="http://ipywidgets.readthedocs.io"&gt;&lt;em&gt;ipywidgets documentation&lt;/em&gt;&lt;/a&gt;&lt;em&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://jasongrout.github.io/medium-archive/pelican/posts/2018/authoring-custom-jupyter-widgets/images/002-1_x6oPwz8nMiy4YIgEpczQHg.webp" alt="" 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://jasongrout.github.io/medium-archive/pelican/posts/2018/authoring-custom-jupyter-widgets/images/003-1_lK1Zi8_qorxl5ZrL_ZMc7w.webp" alt="" 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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    value = Unicode(&amp;#39;Hello World!&amp;#39;).tag(sync=True)
&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;p&gt;&lt;code&gt;this.model.on('change:value', this.value_changed, this);&lt;/code&gt;&lt;/p&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&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;h2 id="21-first-widget"&gt;2.1 — First Widget&lt;/h2&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;h2 id="22-barebones-setuppy"&gt;2.2 — Barebones &lt;code&gt;setup.py&lt;/code&gt;&lt;/h2&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;# Example: d3-slider
$ git clone https://gitlab.com/oscar6echo/jupyter-widget-d3-slider.git
$ cd js
$ npm install
$ cd ..
$ pip install -e .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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; 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;h2 id="23-side-note-naming-conventions"&gt;2.3 — Side Note: Naming Conventions&lt;/h2&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;h2 id="31-d3-slider"&gt;3.1 — d3-slider&lt;/h2&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://jasongrout.github.io/medium-archive/pelican/posts/2018/authoring-custom-jupyter-widgets/images/002-1_x6oPwz8nMiy4YIgEpczQHg.webp" alt="" 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://jasongrout.github.io/medium-archive/pelican/posts/2018/authoring-custom-jupyter-widgets/images/003-1_lK1Zi8_qorxl5ZrL_ZMc7w.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="d3-slider widget" src="https://jasongrout.github.io/medium-archive/pelican/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;h2 id="32-drawing-pad"&gt;3.2 — drawing-pad&lt;/h2&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://jasongrout.github.io/medium-archive/pelican/posts/2018/authoring-custom-jupyter-widgets/images/002-1_x6oPwz8nMiy4YIgEpczQHg.webp" alt="" 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://jasongrout.github.io/medium-archive/pelican/posts/2018/authoring-custom-jupyter-widgets/images/003-1_lK1Zi8_qorxl5ZrL_ZMc7w.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="drawing pad widget" src="https://jasongrout.github.io/medium-archive/pelican/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;h2 id="33-ipypivot"&gt;3.3 — ipypivot&lt;/h2&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://jasongrout.github.io/medium-archive/pelican/posts/2018/authoring-custom-jupyter-widgets/images/002-1_x6oPwz8nMiy4YIgEpczQHg.webp" alt="" 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://jasongrout.github.io/medium-archive/pelican/posts/2018/authoring-custom-jupyter-widgets/images/003-1_lK1Zi8_qorxl5ZrL_ZMc7w.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="ipypivot widget" src="https://jasongrout.github.io/medium-archive/pelican/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;NOTE&lt;em&gt;: The PivotUI widget is a&lt;/em&gt; &lt;a href="https://github.com/PierreMarion23/ipypivot/blob/master/js/lib/widget_pivotui_box.js"&gt;&lt;em&gt;combo of a custom widget and core widgets&lt;/em&gt;&lt;/a&gt;&lt;em&gt;. This modular approach is more flexible (and looks nicer) but the same features can be made in a&lt;/em&gt; &lt;a href="https://github.com/PierreMarion23/ipypivot/blob/alt/js/lib/widget_pivotui.js"&gt;&lt;em&gt;single custom widget&lt;/em&gt;&lt;/a&gt; &lt;em&gt;containing extra buttons and display fields. The&lt;/em&gt; &lt;a href="https://github.com/PierreMarion23/ipypivot/tree/alt"&gt;&lt;em&gt;&lt;code&gt;alt&lt;/code&gt;&lt;/em&gt; &lt;em&gt;branch&lt;/em&gt;&lt;/a&gt; &lt;em&gt;of the repo contains this version.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="34-including-javascript-callbacks"&gt;3.4 — Including JavaScript Callbacks&lt;/h2&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;h2 id="35-enabling-jupyter-widgets-by-default"&gt;3.5 — Enabling Jupyter Widgets By Default&lt;/h2&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;h2 id="41-pypi-and-npm"&gt;4.1 — PyPI and npm&lt;/h2&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;h2 id="42-conda-forge"&gt;4.2 — conda-forge&lt;/h2&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;h2 id="43-automatic-push-script"&gt;4.3 — Automatic Push Script&lt;/h2&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 G&lt;a href="https://github.com/"&gt;itHub&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://gist.github.com/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;h2 id="44-binder-and-nbviewer"&gt;4.4 — Binder and nbviewer&lt;/h2&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="/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><entry><title>Release of ipywidgets 6.0</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2017/ipywidgets-6-release/" rel="alternate"/><published>2017-03-01T15:44:00+00:00</published><updated>2017-08-28T18:33:00+00:00</updated><author><name>Sylvain Corlay</name></author><id>tag:jasongrout.github.io,2017-03-01:/medium-archive/pelican/posts/2017/ipywidgets-6-release/</id><summary type="html">&lt;p&gt;We are pleased to announce the release of ipywidgets 6.0, the Jupyter interactive widget library. Jupyter interactive widgets enable building simple GUIs in the Jupyter notebook. ipywidgets 6.0 is a major release of the project. In this release, we closed 197 issues and 309 pull requests with 732&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We are pleased to announce the release of ipywidgets 6.0, the Jupyter interactive widget library. Jupyter interactive widgets enable building simple GUIs in the Jupyter notebook.&lt;/p&gt;
&lt;p&gt;ipywidgets 6.0 is a major release of the project. In this release, we closed &lt;a href="https://github.com/ipython/ipywidgets/issues?utf8=%E2%9C%93&amp;amp;q=is%3Aissue%20is%3Aclosed%20milestone%3A6.0%20"&gt;197 issues&lt;/a&gt; and &lt;a href="https://github.com/ipython/ipywidgets/pulls?q=is%3Apr+milestone%3A6.0+is%3Aclosed"&gt;309 pull requests&lt;/a&gt; with &lt;a href="https://github.com/ipython/ipywidgets/compare/5.2.2...6.0.0"&gt;732 commits&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="installation"&gt;Installation&lt;/h3&gt;
&lt;p&gt;Using conda:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;ipywidgets&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;conda-forge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using pip:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;ipywidgets
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;widgetsnbextension
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="whats-new-in-ipywidgets-60"&gt;What’s new in ipywidgets 6.0?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Custom widget styling&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In addition to the existing &lt;code&gt;Widget.layout&lt;/code&gt; attribute, which enables the specification of layout-related css properties for the top-most DOM element of a widget, we added a new &lt;code&gt;Widget.style&lt;/code&gt; attribute. This attribute enables custom styling of various widget types (such as &lt;code&gt;Button.style.button_color&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The top-level styling properties that were deprecated in ipywidgets 5.0 have been removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Using widgets outside of the notebook&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Another change in 6.0 is the ability to render Jupyter interactive widgets outside of the notebook:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Widgets can be rendered in Sphinx documentation, either with the &lt;code&gt;jupyter-sphinx&lt;/code&gt; extension or the &lt;code&gt;nbsphinx&lt;/code&gt; notebook converter.&lt;/li&gt;
&lt;li&gt;Widgets now &lt;a href="http://nbviewer.jupyter.org/github/ipython/ipywidgets/blob/6.0.0/docs/source/examples/Widget%20List.ipynb"&gt;render on nbviewer&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Jupyter interactive widgets can be embedded into static web sites, such as &lt;a href="http://jupyter.org/widgets"&gt;http://jupyter.org/widgets&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Custom widget libraries built upon ipywidgets 6.0 can also take advantage of these features.&lt;/p&gt;
&lt;p&gt;This feature required the formal specification of a mime type for Jupyter interactive widgets, which is now contained in the new &lt;code&gt;jupyter-widgets-schema&lt;/code&gt; npm package.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Redesign of the core widgets&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The widgets provided with the base Jupyter widget libraries have gone through a major re-design.&lt;/p&gt;
&lt;p&gt;We also migrated from the &lt;code&gt;less&lt;/code&gt; CSS preprocessor to the use of css variables. In addition to the main benefits of adopting web standards, we take advantage of css variables defined in JupyterLab to style the interactive widgets consistent with the environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration to Typescript&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;jupyter-js-widgets&lt;/code&gt; JavaScript package, which is the front-end component of ipywidgets, has been completely refactored and migrated to the Typescript programming language. We also adopted the &lt;a href="http://phosphorjs.github.io/"&gt;PhosphorJS&lt;/a&gt; JavaScript framework, which is at the foundation of the JupyterLab project, for better layout capability and integration with JupyterLab.&lt;/p&gt;
&lt;h3 id="credits"&gt;Credits&lt;/h3&gt;
&lt;p&gt;This release has been a team effort of a large number of contributors. We would like to thank the following 31 people who contributed, and especially the 14 people who contributed for the first time in this release.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Contributors to this release (alphabetical order):&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Afshin Darian (first contribution)&lt;br&gt;
Adam Chainz (first contribution)&lt;br&gt;
Benjamin Ragan-Kelley&lt;br&gt;
Brian Granger&lt;br&gt;
Cameron Oelsen (first contribution)&lt;br&gt;
Carol Willing&lt;br&gt;
Dave Willmer&lt;br&gt;
denfromufa&lt;br&gt;
Giles Weaver (first contribution)&lt;br&gt;
Gino Bustelo&lt;br&gt;
Grant Nestor (first contribution)&lt;br&gt;
Jason Grout&lt;br&gt;
Javier Pedemonte&lt;br&gt;
Jeff (first contribution)&lt;br&gt;
Jeroen Demeyer&lt;br&gt;
Jonathan Frederic&lt;br&gt;
Justin McCandless (first contribution)&lt;br&gt;
Ludwig Schmidt-Hackenberg (first contribution)&lt;br&gt;
Maarten Breddels (first contribution)&lt;br&gt;
Martin Renou (first contribution)&lt;br&gt;
Matthew Craig&lt;br&gt;
Matthias Bussonnier&lt;br&gt;
Michael Pacer (first contribution)&lt;br&gt;
Oliver Evans (first contribution)&lt;br&gt;
Paul Ivanov&lt;br&gt;
Philipp Rudiger (first contribution)&lt;br&gt;
Srinivas Kumar Sunkara (first contribution)&lt;br&gt;
Steven Silvester&lt;br&gt;
stonebig (first contribution)&lt;br&gt;
Thomas Kluyver&lt;br&gt;
Sylvain Corlay&lt;br&gt;
Yoshiki Vázquez Baeza&lt;/p&gt;
</content><category term="releases"/><category term="widgets"/></entry><entry><title>ipywidget security release</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2016/ipywidget-security-release/" rel="alternate"/><published>2016-09-26T23:37:00+00:00</published><updated>2016-12-15T18:27:00+00:00</updated><author><name>Jupyter Team</name></author><id>tag:jasongrout.github.io,2016-09-26:/medium-archive/pelican/posts/2016/ipywidget-security-release/</id><summary type="html">&lt;p&gt;Hello Jovyan, A version of ipywidget has been released, which fixes important security issues. Please upgrade ipywidgets as soon as you can: $ pip install ipywidgets --upgrade Please do so in all your environments. More details follow. We requested a CVE number and were asked to wait before any public disclosure&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Hello Jovyan,&lt;/p&gt;
&lt;p&gt;A version of ipywidget has been released, which fixes important security issues. Please upgrade ipywidgets as soon as you can:&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;ipywidgets&lt;span class="w"&gt; &lt;/span&gt;--upgrade
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Please do so in all your environments.&lt;/p&gt;
&lt;p&gt;More details follow. We requested a CVE number and were asked to wait before any public disclosure of the vulnerability. As we have now been delaying the disclosure for over a resonable time, and we’re still waiting for the CVE number, we decided to still disclose the vulnerability. This post will be updated once/if a CVE number is made available.&lt;/p&gt;
&lt;p&gt;[Update Dec 15, 2016]&lt;/p&gt;
&lt;p&gt;A CVE number cannot be assigned for lack of sufficient information. No explanation of what more is needed was provided.&lt;/p&gt;
&lt;h1 id="description"&gt;Description&lt;/h1&gt;
&lt;p&gt;ipywidgets version 5.1.5 (widgetsnbextension 1.2.3) fixes a security vulnerability (CVE-PENDING) which affects the usage of ipywidgets in conjunction with the Jupyter Notebook.&lt;/p&gt;
&lt;h2 id="affected-versions"&gt;Affected versions&lt;/h2&gt;
&lt;p&gt;ipywidgets version 5.0.0 ≤ V ≤ 5.1.4 (widgetsnbextension &amp;lt; 1.2.3).&lt;/p&gt;
&lt;p&gt;Only users who installed ipywidgets using pip or from source on the GitHub repository are affected.&lt;/p&gt;
&lt;p&gt;Anaconda users are unaffected because the vulnerable version of ipywidget has never been released to the default conda channel.&lt;/p&gt;
&lt;h2 id="resolution"&gt;Resolution&lt;/h2&gt;
&lt;p&gt;We released ipywidgets version 5.1.5 (widgetsnbextension version 1.2.3).&lt;/p&gt;
&lt;p&gt;You can check whether your system is affected by running the following command from a Python or IPython prompt:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;distutils.version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LooseVersion&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ipywidgets&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;5.0.0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipywidgets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__version__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;5.1.5&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Upgrade ipywidgets to 5.1.5&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If your system is vulnerable, you will see the following output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Upgrade ipywidgets to 5.1.5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If your system is vulnerable please upgrade to ipywidgets version 5.1.5. Use the following command 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;&lt;span class="s2"&gt;&amp;quot;ipywidgets&amp;gt;=5.1.5&amp;quot;&lt;/span&gt;
&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;&lt;span class="s2"&gt;&amp;quot;ipywidgets&amp;gt;=5.1.5&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id="technical-details"&gt;Technical details&lt;/h1&gt;
&lt;p&gt;The vulnerability was discovered following an investigation of a potential vulnerability reported by Brian Granger to the ipython-security mailing list (&lt;code&gt;security@ipython.org&lt;/code&gt;) on May 5.&lt;/p&gt;
&lt;p&gt;The reason for such behavior was determined on May 5 by Matthias Bussonnier.&lt;br&gt;
A fix was proposed written and reviewed, then &lt;a href="https://github.com/ipython/ipywidgets/pull/591"&gt;merged&lt;/a&gt; into the development branch on May 20, and a non vulnerable version released on May 25.&lt;/p&gt;
&lt;p&gt;A widget snapshotting feature introduced in ipywidgets 5.0.0 allowed untrusted javascript code to execute in an untrusted notebook on loading and saving of a notebook. A well crafted notebook could execute arbitrary code with the rights of the current user in the context of the page, the notebook server, and available kernels.&lt;/p&gt;
&lt;p&gt;We recommend immediate upgrade of the ipywidgets package.&lt;/p&gt;
&lt;p&gt;There is no simple configuration option that could mitigate the system for vulnerability. The user must upgrade to ipywidget version 5.1.5 or downgrade to 4.x.&lt;/p&gt;
&lt;h2 id="future-plan"&gt;Future Plan&lt;/h2&gt;
&lt;p&gt;The security issue resulted from the seemingly harmless combination of calls:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;json = cell.get_json()
json = update_json(json)
cell.clear_output()
cell.from_json(json)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;clear_output()&lt;/code&gt; method has as a consequence to mark the cell as trusted (as it has no output that can potentially execute javascript). This is followed by the next call which can trigger JavaScript execution in the page context.&lt;/p&gt;
&lt;p&gt;We plan on improving the notebook API so that &lt;code&gt;clear_output()&lt;/code&gt; does not change the trusted status of a cell (or a notebook), to prevent mistakes like this from having security consequences. This will lead to the slight behavior change that an empty cell with no output can be untrusted.&lt;/p&gt;
&lt;h2 id="doing-better-next-time"&gt;Doing better next time&lt;/h2&gt;
&lt;p&gt;We learned that we are not completely ready for fast release of security fixes. The time from vulnerability discovery to available fix and release could have been better. The announcement was delayed while waiting for a CVE number which is still not there. We will consider a sorter timescale to publication even if we don’t get assigned a CVE number quickly. The standard seem to be 90 days from security vulnerability report, we might end up selecting this as well.&lt;/p&gt;
&lt;p&gt;We encourage users who find possible security issues to notify &lt;code&gt;security@ipython.org&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Thanks!&lt;/p&gt;
</content><category term="releases"/><category term="security"/><category term="widgets"/></entry><entry><title>ipywidgets 5.0</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2016/ipywidgets-5-0/" rel="alternate"/><published>2016-04-23T02:48:00+00:00</published><updated>2017-08-28T18:21:00+00:00</updated><author><name>Project Jupyter</name></author><id>tag:jasongrout.github.io,2016-04-23:/medium-archive/pelican/posts/2016/ipywidgets-5-0/</id><summary type="html">&lt;p&gt;On April 19, 2016, we released ipywidgets 5.0. The ipywidgets package provides interactive HTML &amp;amp; JavaScript widgets (such as sliders, checkboxes, text boxes, charts, and more) for the …&lt;/p&gt;
</summary><content type="html">&lt;p&gt;On April 19, 2016, we released ipywidgets 5.0. The &lt;code&gt;ipywidgets&lt;/code&gt; package provides &lt;a href="https://github.com/ipython/ipywidgets"&gt;interactive HTML &amp;amp; JavaScript widgets&lt;/a&gt; (such as sliders, checkboxes, text boxes, charts, and more) for the Jupyter architecture that combine front-end controls coupled to a Jupyter kernel. It also ships a Python implementation of the kernel-side APIs to be used by the IPython kernel.&lt;/p&gt;
&lt;p&gt;Altogether, 17 authors contributed to 205 pull requests to make this release.&lt;/p&gt;
&lt;p&gt;This release features several major changes and a myriad of smaller changes and bugfixes.&lt;/p&gt;
&lt;p&gt;The main change in this release is the split and refactoring of the old package into two packages: a pure Javascript package, jupyter-js-widgets, published on npm (version 1.0), at &lt;a href="https://www.npmjs.com/package/jupyter-js-widgets"&gt;https://www.npmjs.com/package/jupyter-js-widgets&lt;/a&gt; the Python backend, ipywidgets.&lt;/p&gt;
&lt;p&gt;This separation decouples widget display views from language specific kernel operations of a widget. Kernel authors benefit from being able to more easily implement backends for other language kernels, like R, Julia, Ocaml or Haskell, while at the same time using a stable target version of jupyter-js-widgets. We intend to follow the semantic versioning convention for future releases of jupyter-js-widgets. A runtime compatibility check between frontend and backend is also implemented for user convenience.&lt;/p&gt;
&lt;p&gt;The stabilization of Javascript APIs in this release provides a path for custom widget authors to update efficiently to future versions.&lt;/p&gt;
&lt;p&gt;Another important new feature, use of Jupyter widgets outside of the notebook context, gives new possibilities for information control and display. Live interactive widgets can now be embedded into static web pages or blogs by inserting an html snippet containing the serialized widget state. This also works with custom widget libraries. See &lt;a href="http://jupyter.org/embed-jupyter-widgets.html"&gt;http://jupyter.org/embed-jupyter-widgets.html&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A major change to the Javascript widget code will affect authors of custom Javascript widgets. The Javascript widget code in ipywidgets 5.0 is backward incompatible with previous releases. Three popular custom widget libraries (pythreejs, bqplot, and ipyleaflet) are being released today with the required updates and changes. We can assist authors of Javascript custom widget libraries to make the changes required to migrate their libraries to this new version. Python APIs continue to be backward compatible.&lt;/p&gt;
&lt;p&gt;Smaller changes in this release include the addition of a new ‘selection slider’ and making the layout of interactive widgets on the page much easier.&lt;/p&gt;
&lt;p&gt;A more detailed document on the changes is available here:&lt;a href="https://github.com/ipython/ipywidgets/wiki/ipywidgets-5.0-Release-Document"&gt;https://github.com/ipython/ipywidgets/wiki/ipywidgets-5.0-Release-Document&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Using this release of ipywidgets requires a Jupyter notebook 4.2 installation which uses the notebook’s new nbextension installation and enabling features. If you have an older version of the notebook and you upgrade ipywidgets, the ipywidgets upgrade will also install for you an updated version of the notebook.&lt;/p&gt;
&lt;p&gt;Thank you to the entire Jupyter and IPython team for making this a possibility. An &lt;strong&gt;extra big&lt;/strong&gt; thank you to Sylvain Corlay for working many late hours and weekends to make this happen.&lt;/p&gt;
</content><category term="releases"/><category term="widgets"/></entry></feed>