├── .github └── workflows │ ├── binder-badge.yml │ └── deploy.yml ├── .gitignore ├── .nojekyll ├── LICENSE ├── README.md ├── apt.txt ├── environment.yml ├── install_check.py ├── notebooks ├── 00.00-index.ipynb ├── 01.Introduction │ ├── 01.00-introduction.ipynb │ └── 01.01-overview.ipynb ├── 02.Widget_overview │ ├── 01.Interact │ │ ├── 01.00-Using-Interact.ipynb │ │ ├── 01.01-OPTIONAL-More-About-Interact.ipynb │ │ └── solutions │ │ │ ├── plot-function-interact.py │ │ │ ├── plot-function-mpl-inter.py │ │ │ └── reverse-text.py │ ├── 02.Basics │ │ ├── 02.00-Widget_Basics.ipynb │ │ └── images │ │ │ ├── WidgetModelView.graffle │ │ │ └── WidgetModelView.png │ └── 03.WidgetList │ │ ├── 03.00-widget-list.ipynb │ │ ├── 03.01-more-on-output-widget.ipynb │ │ ├── solutions │ │ ├── bounded-float-text.py │ │ ├── sidecar-triple.py │ │ └── widgets-in-a-box.py │ │ └── widget_org.py ├── 03.Widget_events │ ├── 03.00-Widget_Events.ipynb │ ├── 03.01-OPTIONAL-Widget_Events_2.ipynb │ ├── 03.02-OPTIONAL-Widget_Events_2_--_bad_password_generator,_version_1.ipynb │ ├── 03.03-OPTIONAL-Widget_Events_2_--_Separating_Concerns.ipynb │ ├── 03.04-OPTIONAL-Widget_Events_2--Separating_concerns,_object_oriented.ipynb │ ├── images │ │ └── bad-pass-gen-v1.png │ ├── password_generator_example │ │ ├── gui.py │ │ └── model.py │ └── solutions │ │ ├── bad-pass-pass1-observe.py │ │ ├── bad-pass-pass1-passgen.py │ │ ├── bad-pass-pass1-widgets.py │ │ ├── observe-reverse.py │ │ └── temperature-link.py ├── 04.Widget_styling │ ├── 04.00-Layout-and-Styling-Overview.ipynb │ ├── 04.01-widget-layout-and-styling.ipynb │ ├── 04.03-more-flexbox-layout.ipynb │ ├── 04.04-OPTIONAL-widget-label-styling.ipynb │ ├── 04.05-OPTIONAL-widget-specific-styling.ipynb │ ├── 04.06-OPTIONAL-container-exercises.ipynb │ ├── Table_of_widget_keys_and_style_keys.ipynb │ ├── images │ │ ├── 05-container-exercises-ex4.png │ │ ├── container-exercises-special-chars.png │ │ ├── container-exercises1-final-layout.png │ │ ├── container-exercises2-final-layout.png │ │ ├── flexbox.png │ │ └── pass-gen-demo.gif │ ├── layout_preview.py │ ├── reference_guides │ │ ├── guide-flex-box.ipynb │ │ └── guide-grid-box.ipynb │ └── solutions │ │ ├── applayout-no-sides.py │ │ ├── password-ex2.py │ │ ├── password-ex3i.py │ │ ├── password-ex3ii.py │ │ ├── password-ex4.py │ │ └── slider-bqplot-sliders-app.py ├── 05.Build-complex-libs │ ├── 05.00-Build-complex-libraries.ipynb │ ├── 05.01-Refactoring.ipynb │ └── my_application │ │ ├── __init__.py │ │ ├── application.py │ │ └── chart.py ├── 06.More_widget_libraries │ ├── 06.00-More_widget_libraries.ipynb │ ├── 06.01-ipycanvas.ipynb │ ├── 06.02-ipycytoscape.ipynb │ ├── 06.03-ipydatagrid.ipynb │ ├── 06.04-ipygany.ipynb │ ├── 06.05-other-widget-libraries.ipynb │ ├── extras │ │ ├── PyWWT.ipynb │ │ ├── bqplot.ipynb │ │ ├── extra_examples │ │ │ ├── bqplot_A_Penalized_regression.ipynb │ │ │ ├── bqplot_A_plot_as_a_control_in_a_widget.ipynb │ │ │ ├── flight_sim.ipynb │ │ │ ├── solutions │ │ │ │ └── bqplot-as-control │ │ │ │ │ ├── box-widget.py │ │ │ │ │ └── update_line.py │ │ │ └── vaex.ipynb │ │ ├── ipyleaflet.ipynb │ │ ├── ipympl.ipynb │ │ ├── ipytree.ipynb │ │ ├── ipyvolume.ipynb │ │ ├── ipyvuetify.ipynb │ │ ├── ipywebrtc.ipynb │ │ ├── pythreejs.ipynb │ │ └── solutions │ │ │ └── ipyvuetify │ │ │ └── radio_group.py │ ├── piston.vtu │ └── us-states-density-colored.json ├── 07.Widgets_outside_notebook_context │ ├── 07.00-voila.ipynb │ ├── 07.01-voila-basics.ipynb │ ├── 07.02-voila-vuetify.ipynb │ └── 07.03-jupyterlite.ipynb ├── 08.Ipywidgets_8 │ └── 08.00-ipywidgets_8.ipynb ├── Big.Buck.Bunny.mp4 ├── Flow.svg ├── README.md ├── images │ ├── Binary_Star_Sim.png │ ├── WidgetArch.png │ ├── covid-dash1.png │ ├── covid-dash2.png │ └── plot-as-control.gif ├── invalid_keypress.mp3 ├── reference_guides │ ├── complete-ipywidgets-widget-list.ipynb │ ├── guide-flex-box.ipynb │ ├── guide-grid-box.ipynb │ ├── guide-other.ipynb │ └── images │ │ ├── align-content.svg │ │ ├── align-items.svg │ │ ├── align-self.svg │ │ ├── flex-container.svg │ │ ├── flex-direction1.svg │ │ ├── flex-grow.svg │ │ ├── flex-items.svg │ │ ├── flex-wrap.svg │ │ ├── flexbox.png │ │ ├── grid-area.png │ │ ├── grid-cell.png │ │ ├── grid-justify-self-center.png │ │ ├── grid-justify-self-end.png │ │ ├── grid-justify-self-start.png │ │ ├── grid-justify-self-stretch.png │ │ ├── grid-line.png │ │ ├── grid-start-end-a.png │ │ ├── grid-start-end-b.png │ │ ├── grid-start-end-d.png │ │ ├── grid-track.png │ │ ├── justify-content.svg │ │ └── order-2.svg └── solutions │ └── intro │ └── intro-example.py ├── outline.md ├── postBuild ├── repl └── jupyter-lite.json ├── requirements.txt ├── test_kernel_name.py └── tools ├── README.md ├── add_navigation.py ├── check_load_and_images.py ├── dependency_finder.py ├── execute_notebooks.py ├── generate_contents.py ├── kernel_names.py └── licenses └── LICENSE-CODE /.github/workflows/binder-badge.yml: -------------------------------------------------------------------------------- 1 | #./.github/workflows/binder-badge.yaml 2 | name: Binder Badge 3 | on: 4 | pull_request_target: 5 | types: [opened, edited, reopened] 6 | 7 | jobs: 8 | binder: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | pull-requests: write 12 | steps: 13 | - name: comment on PR with Binder link 14 | uses: actions/github-script@v3 15 | with: 16 | github-token: ${{secrets.GITHUB_TOKEN}} 17 | script: | 18 | var PR_HEAD_USERREPO = process.env.PR_HEAD_USERREPO; 19 | var PR_HEAD_REF = process.env.PR_HEAD_REF; 20 | github.issues.createComment({ 21 | issue_number: context.issue.number, 22 | owner: context.repo.owner, 23 | repo: context.repo.repo, 24 | body: `[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/${PR_HEAD_USERREPO}/${PR_HEAD_REF}?urlpath=%2Flab) :point_left: Launch a binder notebook on branch _${PR_HEAD_USERREPO}/${PR_HEAD_REF}_` 25 | }) 26 | env: 27 | PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} 28 | PR_HEAD_USERREPO: ${{ github.event.pull_request.head.repo.full_name }} 29 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | - name: Setup Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.9' 21 | - name: Install the dependencies 22 | run: | 23 | python -m pip install -r requirements.txt 24 | - name: Build the JupyterLite site 25 | run: | 26 | jupyter lite build --contents notebooks 27 | - name: Upload (dist) 28 | uses: actions/upload-artifact@v2 29 | with: 30 | name: jupyterlite-demo-dist-${{ github.run_number }} 31 | path: ./_output 32 | 33 | deploy: 34 | if: github.ref == 'refs/heads/main' 35 | needs: [build] 36 | runs-on: ubuntu-latest 37 | permissions: 38 | contents: write 39 | 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@v3 43 | - uses: actions/download-artifact@v2 44 | with: 45 | name: jupyterlite-demo-dist-${{ github.run_number }} 46 | path: ./dist 47 | - name: Deploy 48 | uses: JamesIves/github-pages-deploy-action@4.1.3 49 | with: 50 | branch: gh-pages 51 | folder: dist 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ipywidget-example-notebooks 2 | ipywidgets-examples-v2 3 | .ipynb_checkpoints/ 4 | notebooks/__pycache__ 5 | .cache 6 | .pytest_cache 7 | __pycache__ 8 | -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Project Jupyter Contributors 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Jupyter Widget Ecosystem 2 | 3 | ## Tutorial, SciPy 2022 4 | 5 | # https://github.com/jupyter-widgets/tutorial 6 | 7 | # Installation 8 | 9 | **NOTE**: These installation instructions install `ipywidgets` version 7.7. We anticipate that ipywidgets 8 will be released before the tutorial. If it, and all of the packages that we use that are dependent on `ipywidgets` have releases before the tutorial we will also provide ipywidgets 8 installation instructions. 10 | 11 | Either way, we will include some of the new features of ipywidgets 8 in the tutorial. 12 | 13 | ### Installation instructions last updated 2022-07-09 14 | 15 | #### Read this if you set up the tutorial environment before 2022-07-09 16 | 17 | > We have added two packages to the list of requirements on 2022-07-09. To add those packages to the environment you have already 18 | > created, either `conda install -c conda-forge mpl-interactions=0.22.0 orjson=3.7.7` or, if you use pip, 19 | > `pip install mpl-interactions==0.22.0 orjson==3.7.7`. Do not panic if you cannot update your environment; these packages are used in 20 | > only two or three cells. 21 | 22 | We **strongly recommend** using the conda Python distribution. You can install either [miniconda](https://conda.io/miniconda.html) (much smaller, only essential packages) or the full [anaconda distribution](https://www.continuum.io/downloads) (very extensive, but very, very large). 23 | 24 | Almost all of the examples will work in either the regular Jupyter notebook or in JupyterLab. The instructions below assume you will be using JupyterLab. 25 | 26 | *We will spend a few minutes at the beginning of the tutorial pointing out some of the features of JupyterLab from the perspective of people already familiar with Jupyter notebooks.* 27 | 28 | There are also download instructions below for installation using pip, which should work with any Python distribution. 29 | 30 | ## Download this repository 31 | 32 | You can do this with either `git clone https://github.com/jupyter-widgets/tutorial.git` at the command line or by downloading this repostiory as a [Zip file](https://github.com/jupyter-widgets/tutorial/archive/master.zip). 33 | 34 | ## conda installation instructions 35 | 36 | The steps below will get you a working environment. 37 | 38 | ```bash 39 | conda env create -f environment.yml 40 | 41 | conda activate widgets-tutorial-2022 42 | 43 | # Create a kernel for this environment 44 | ipython kernel install --name widgets-tutorial --display-name widgets-tutorial --sys-prefix 45 | ``` 46 | 47 | ### Windows users 48 | The installation instructions were tested on an up-to-date version of Windows 10 Professional. If you encounter any issues on Windows please open an issue or contact us through slack. 49 | 50 | ## pip installation instructions 51 | 52 | If you are not using the anaconda python distribution, please use the instructions below. 53 | 54 | ```bash 55 | pip install -r requirements.txt 56 | 57 | # Create a kernel for this environment 58 | ipython kernel install --name widgets-tutorial --display-name widgets-tutorial --sys-prefix 59 | ``` 60 | 61 | ## Check your installation 62 | 63 | To check your installation, please download the script [install_check.py](https://raw.githubusercontent.com/jupyter-widgets/tutorial/master/install_check.py) and run it: 64 | 65 | ```bash 66 | python install_check.py 67 | ``` 68 | 69 | ## Using binder 70 | 71 | Click the badge below to run the tutorial online: 72 | 73 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyter-widgets/tutorial/main?urlpath=%2Flab%2Ftree%2Fnotebooks) 74 | 75 | Many of the materials work without modification on mybinder.org without needing to install anything on your computer. However, this is *not* the recommended way to do the tutorial. 76 | 77 | 78 | ## Any ipywidgets or custom widgets library question? 79 | 80 | Please join us on the Gitter channel: https://gitter.im/jupyter-widgets/Lobby 81 | 82 | ## Running into trouble? 83 | 84 | Please let us know! You can open an issue on this repository by clicking "Issues" under the repo name on GitHub, then the "New Issue" button in the upper right. 85 | -------------------------------------------------------------------------------- /apt.txt: -------------------------------------------------------------------------------- 1 | xvfb 2 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: widgets-tutorial-2022 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - bqplot=0.12.33 6 | - branca=0.5.0 7 | - ipycanvas=0.12.0 8 | - ipycytoscape=1.3.3 9 | - ipydatagrid=1.1.12 10 | - ipyevents=2.0.1 11 | - ipygany=0.5.0 12 | - ipyleaflet=0.16.0 13 | - ipympl=0.9.1 14 | - ipytree=0.2.1 15 | - ipyvolume=0.6.0a8 16 | - ipyvuetify=1.8.2 17 | - ipywidgets=7.7.0 18 | - jupyterlab=3.4 19 | - mpl-interactions=0.22.0 20 | - numpy 21 | - orjson=3.7.7 # Added 2022 for one of the ipycanvas examples 22 | - pandas 23 | - pip 24 | - python=3.9 25 | - pythreejs=2.3.0 26 | - pyvista=0.34.0 27 | - requests 28 | - scikit-image 29 | - scipy 30 | - sidecar=0.5.1 31 | - voila=0.3.5 32 | - vtk=9.1.0 33 | -------------------------------------------------------------------------------- /install_check.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import logging 3 | from subprocess import check_call, CalledProcessError 4 | 5 | 6 | SUCCESS_PREFIX = '🎉' 7 | FIX_PREFIX = '⚠️⚠️⚠️ ----->' 8 | 9 | tutorial_name = 'Jupyter widget ecosystem' 10 | 11 | requirements = [ 12 | 'notebook', 13 | 'ipywidgets', 14 | 'bqplot', 15 | 'ipyleaflet', 16 | 'ipyvolume', 17 | 'pythreejs', 18 | 'ipytree', 19 | 'ipydatagrid', 20 | 'ipycytoscape', 21 | 'ipygany', 22 | 'jupyterlab', 23 | 'voila', 24 | 'pyvista', 25 | 'mpl_interactions', 26 | 'orjson', # For one of the example in ipycanvas 27 | ] 28 | 29 | import_result = {p: False for p in requirements} 30 | 31 | print("Checking requirements for {}".format(tutorial_name), end='') 32 | 33 | for package in requirements: 34 | try: 35 | __import__(package) 36 | import_result[package] = True 37 | except ImportError: 38 | pass 39 | print('.', end='', flush=True) 40 | 41 | print() 42 | success = all(import_result.values()) 43 | 44 | # List compatible versions for each package 45 | version_check_packages = {'ipywidgets': ['7.7'], 46 | 'jupyterlab': ['3'], 47 | } 48 | 49 | if success: 50 | print(f'\t{SUCCESS_PREFIX} All required packages installed') 51 | else: 52 | print(FIX_PREFIX, 'Please install these missing packages ' 53 | 'for the tutorial "{}":'.format(tutorial_name)) 54 | missing = [k for k, v in import_result.items() if not v] 55 | print('\t' + '\n\t'.join(missing)) 56 | 57 | print('Checking voila version:') 58 | 59 | try: 60 | check_call(['voila', '--version']) 61 | print(f'\t{SUCCESS_PREFIX} Voila is correctly installed') 62 | except CalledProcessError: 63 | print(f'\t {FIX_PREFIX} Voila is not installed! Please install it by running one ' 64 | 'of the following:') 65 | print(' conda install -c conda-forge voila') 66 | print(' pip install voila') 67 | 68 | print('Checking version numbers of these packages: ', 69 | ', '.join(version_check_packages.keys())) 70 | 71 | 72 | def version_checker(package_name, version, nbextension=None): 73 | good_version = any(version.startswith(v) for 74 | v in version_check_packages[package_name]) 75 | if nbextension is None: 76 | nbextension = package_name 77 | if not good_version: 78 | newest = version_check_packages[package_name][-1] 79 | print('\n**** Please upgrade {} to version {} by running:'.format(package_name, 80 | newest)) 81 | print(' conda install {}={} # if you use conda'.format(package_name, newest)) 82 | print(' pip install {}=={}'.format(package_name, newest)) 83 | else: 84 | print(f'\t{SUCCESS_PREFIX} {package_name} version is good!') 85 | 86 | 87 | # Check as many packages as we can... 88 | 89 | 90 | try: 91 | import ipywidgets 92 | except ImportError: 93 | pass 94 | else: 95 | ipywidgets_version = ipywidgets.__version__ 96 | version_checker('ipywidgets', ipywidgets_version) 97 | 98 | try: 99 | import jupyterlab 100 | except ImportError: 101 | pass 102 | else: 103 | jupyterlab_version = jupyterlab.__version__ 104 | version_checker('jupyterlab', jupyterlab_version) 105 | 106 | # Check that the appropriate kernel has been created 107 | 108 | required_kernel = 'widgets-tutorial' 109 | 110 | print('Checking whether kernel {} exists'.format(required_kernel)) 111 | 112 | import jupyter_client 113 | 114 | known_kernels = list(jupyter_client.kernelspec.find_kernel_specs().keys()) 115 | if required_kernel not in known_kernels: 116 | print(FIX_PREFIX, 'Please create custom kernel with: ', 117 | 'ipython kernel install --name widgets-tutorial --display-name widgets-tutorial --sys-prefix') 118 | else: 119 | print(f'\t{SUCCESS_PREFIX} Custom kernel is correctly installed') 120 | 121 | # Check that lab extensions are installed 122 | 123 | print('Checking whether all Jupyter lab extensions are installed') 124 | 125 | lab_extensions = [ 126 | '@jupyter-widgets/jupyterlab-manager', 127 | '@jupyter-widgets/jupyterlab-sidecar', 128 | 'bqplot', 129 | 'jupyter-threejs', 130 | 'jupyter-leaflet', 131 | 'ipyvolume', 132 | 'ipytree', 133 | 'ipycanvas', 134 | 'ipydatagrid', 135 | 'ipygany', 136 | 'jupyter-cytoscape', # for ipycytoscape 137 | 'jupyter-matplotlib', 138 | 'jupyter-vuetify', 139 | ] 140 | 141 | 142 | try: 143 | from jupyterlab.commands import check_extension, AppOptions 144 | from jupyterlab_server.config import get_federated_extensions 145 | except ImportError: 146 | print(FIX_PREFIX, 'Please install jupyterlab before checking extensions.') 147 | else: 148 | missing_extensions = [] 149 | 150 | # Fetch federated extensions 151 | federated_extensions = get_federated_extensions([sys.base_prefix + '/share/jupyter/labextensions']).keys() 152 | 153 | # JupyterLab be quiet 154 | logger = logging.Logger('quiet') 155 | logger.setLevel(logging.CRITICAL) 156 | app_options = AppOptions(logger=logger) 157 | 158 | for extension in lab_extensions: 159 | if not check_extension(extension, app_options=app_options) and extension not in federated_extensions: 160 | missing_extensions.append(extension) 161 | 162 | if missing_extensions: 163 | print(FIX_PREFIX, 'These lab extensions are missing: ', 164 | ', '.join(missing_extensions)) 165 | print(FIX_PREFIX,' Please try to install the following packages with conda or pip: ', 166 | ', '.join(missing_extensions)) 167 | else: 168 | print(f'\t{SUCCESS_PREFIX} All extensions are installed!') 169 | -------------------------------------------------------------------------------- /notebooks/00.00-index.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# The Jupyter Interactive Widget Ecosystem \n", 8 | "## SciPy 2022\n", 9 | "\n", 10 | "### Matt Craig, Itay Dafna, Mariana Meireles, Youness Bennani, Ian Hunt-Isaak\n", 11 | "\n", 12 | "This tutorial will introduce you to the widgets in the Jupyter Notebook, walk through a few approaches to writing widgets, and introduce some relatively new widget packages.\n", 13 | "\n", 14 | "We are using ipywidgets 7.7 and Jupyter Lab 3.4" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "# Acknowledgements\n", 22 | "\n", 23 | "+ Special thanks to the dozens of [ipywidgets developers](https://github.com/jupyter-widgets/ipywidgets/graphs/contributors), including Jonathan Frederic, who wrote much of the code in the early years of ipywidgets, Sylvain Corlay, who kept the project going at a critical time, and Jason Grout, who currently leads the project and has nurtured the growth of the widget ecosystem.\n", 24 | "+ Several of the notebooks in this tutorial were originally developed as part of [ipywidgets](http://ipywidgets.readthedocs.io/en/latest/) by Brian E. Granger ([@ellisonbg](https://github.com/ellisonbg)) and Jonathan Frederic ([@jdfreder](https://github.com/jdfreder)).\n", 25 | "+ Thanks to Doug Redden ([@DougRzz](https://github.com/DougRzz))\n", 26 | "+ Project Jupyter core developer Carol Willing ([@willingc](https://github.com/willingc)) and [Minnesota State University Moorhead](http://physics.mnstate.edu) students Andrew Block ([@ACBlock](https://github.com/ACBlock)) and Jane Glanzer ([@janeglanzer](https://github.com/janeglanzer)) provided very useful feedback on early drafts of this tutorial.\n", 27 | "+ Thanks to [Jason Grout](https://github.com/jasongrout), [Sylvain Corlay](https://github.com/SylvainCorlay) and [Maarten Breddels](https://github.com/maartenbreddels) who also contributed to these tutorial Notebooks." 28 | ] 29 | } 30 | ], 31 | "metadata": { 32 | "kernelspec": { 33 | "display_name": "widgets-tutorial", 34 | "language": "python", 35 | "name": "widgets-tutorial" 36 | }, 37 | "language_info": { 38 | "codemirror_mode": { 39 | "name": "ipython", 40 | "version": 3 41 | }, 42 | "file_extension": ".py", 43 | "mimetype": "text/x-python", 44 | "name": "python", 45 | "nbconvert_exporter": "python", 46 | "pygments_lexer": "ipython3", 47 | "version": "3.8.10" 48 | } 49 | }, 50 | "nbformat": 4, 51 | "nbformat_minor": 4 52 | } 53 | -------------------------------------------------------------------------------- /notebooks/01.Introduction/01.00-introduction.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Jupyter Widgets Tutorial Introduction\n", 8 | "\n", 9 | "## How to ask questions\n", 10 | "\n", 11 | "There is a \"raise hand\" button but instead, please use the chat or the Q/A. Prefer the chat, we will be able to answer your questions and interact in real-time through the chat. If need be we can have the host stop presenting to answer a question that's especially interesting or pertinent. We're not able to answer the Q/A questions unless we stop the flow of the tutorial to answer the question.\n", 12 | "\n", 13 | "## Jupyter lab essentials\n", 14 | "\n", 15 | "+ To open a notebook: double-click it in the file browser.\n", 16 | "+ To hide the file browser: click on the folder icon.\n", 17 | "+ To run a single code cell in the notebook: Push `Shift` and `Enter`, or use the \"play\" button.\n", 18 | "+ To add a new view for an output, right-click on an output and select \"Create new view for output\".\n", 19 | "+ Use tab to autocomplete and shift-tab to show documentation.\n", 20 | "+ Use CTRL + \"/\" to comment/uncomment code" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "## Interactive Jupyter widgets\n", 28 | "\n", 29 | "A Python widget is an object that represents a control on the front end, like a slider. A single control can be displayed multiple times - they all represent the same python object." 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "import ipywidgets as widgets" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "slider = widgets.FloatSlider(\n", 48 | " value=7.5,\n", 49 | " min=5.0,\n", 50 | " max=10.0,\n", 51 | " step=0.1,\n", 52 | " description='Input:',\n", 53 | ")\n", 54 | "\n", 55 | "slider" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "You can trigger actions in the kernel when a control value changes by \"observing\" the value. Here we set a global variable when the slider value changes." 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": { 69 | "scrolled": true 70 | }, 71 | "outputs": [], 72 | "source": [ 73 | "square = slider.value * slider.value\n", 74 | "\n", 75 | "def handle_change(change):\n", 76 | " global square\n", 77 | " square = change.new * change.new\n", 78 | " \n", 79 | "slider.observe(handle_change, 'value')" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": null, 85 | "metadata": {}, 86 | "outputs": [], 87 | "source": [ 88 | "square" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "## `interact`: a shortcut for examining functions\n", 96 | "\n", 97 | "The `interact` function generates widgets based on a function's arguments and displays any output from the function." 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "def f(x):\n", 107 | " print(x * x)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [ 116 | "f(9)" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "import ipywidgets as ipw" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "metadata": {}, 132 | "outputs": [], 133 | "source": [ 134 | "widgets.interact(f, x=(0, 100));" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "## How to load exercise solutions \n", 142 | "\n", 143 | "Some exercises have solutions you can load if you prefer. To do that, uncomment the line with `%load` in it, then run the cell **twice**. The first time loads the solution, the second time runs the solution.\n", 144 | "\n", 145 | "Here is a short exercise to try that with:\n", 146 | "\n", 147 | "**In the cell below, use interact to generate a slider that goes from -10 to 10**" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "metadata": {}, 154 | "outputs": [], 155 | "source": [ 156 | "# %load solutions/intro/intro-example.py" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "## Additional reading\n", 164 | "\n", 165 | "### Behind the scenes\n", 166 | "\n", 167 | "Behind the scenes, even pure Python widgets are composed of two pieces:\n", 168 | "\n", 169 | "+ Python, which runs in the notebook kernel.\n", 170 | "+ JavaScript, which runs in the browser.\n", 171 | "\n", 172 | "When writing your own widgets, that means making a choice: write only in Python or write in both Python and Javascript.\n", 173 | "\n", 174 | "Which to choose depends on what you are trying to accomplish. This tutorial will focus on writing your own widgets in pure Python. An example of a pure-Python package that includes some interesting interfaces is [reducer](http://reducer.readthedocs.io), a package for calibrating astronomical data." 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "metadata": {}, 180 | "source": [ 181 | "### Jupyter widgets as a framework\n", 182 | "\n", 183 | "Jupyter widgets forms a framework for representing python objects interactively. Some large open-source interactive controls based on Jupyter widgets include:\n", 184 | "\n", 185 | " - [bqplot](https://github.com/bqplot/bqplot/blob/master/examples/Index.ipynb) - 2d plotting library in which everything displayed is a widget\n", 186 | " - [ipympl](https://github.com/matplotlib/ipympl) - widget backend for [matplotlib](https://matplotlib.org/3.2.2/contents.html) graphics\n", 187 | " - [pythreejs](https://pythreejs.readthedocs.io/en/stable/index.html) - low-level 3d graphics library\n", 188 | " - [ipyvolume](https://ipyvolume.readthedocs.io/en/latest/) - 3d plotting and volume rendering\n", 189 | " - [ipyleaflet](https://ipyleaflet.readthedocs.io/en/latest/)_ - interactive maps\n", 190 | " - [ipywebrtc](https://github.com/maartenbreddels/ipywebrtc) - video streaming\n", 191 | " - [ipysheet](https://ipysheet.readthedocs.io/en/latest/) - interactive spreadsheets\n", 192 | " - [ipytree](https://github.com/QuantStack/ipytree) - tree for viewing hierarchical material\n", 193 | " - [ipycanvas](https://ipycanvas.readthedocs.io/en/latest/?badge=latest) - interactive drawing in a notebook\n", 194 | " - [ipyevents](https://github.com/mwcraig/ipyevents/blob/master/doc/Widget%20DOM%20Events.ipynb) - capture mouse/keyboard events on a widget\n", 195 | " - ..." 196 | ] 197 | } 198 | ], 199 | "metadata": { 200 | "kernelspec": { 201 | "display_name": "widgets-tutorial", 202 | "language": "python", 203 | "name": "widgets-tutorial" 204 | }, 205 | "language_info": { 206 | "codemirror_mode": { 207 | "name": "ipython", 208 | "version": 3 209 | }, 210 | "file_extension": ".py", 211 | "mimetype": "text/x-python", 212 | "name": "python", 213 | "nbconvert_exporter": "python", 214 | "pygments_lexer": "ipython3", 215 | "version": "3.8.10" 216 | }, 217 | "widgets": { 218 | "state": { 219 | "0cb2d81d826b4e25aba3b1d39711ef3f": { 220 | "views": [ 221 | { 222 | "cell_index": 6 223 | } 224 | ] 225 | }, 226 | "2cfe6521f9834ca69e54518716104cd2": { 227 | "views": [ 228 | { 229 | "cell_index": 8 230 | }, 231 | { 232 | "cell_index": 9 233 | }, 234 | { 235 | "cell_index": 12 236 | } 237 | ] 238 | }, 239 | "889356d59ac543a49da33d134d791c3f": { 240 | "views": [ 241 | { 242 | "cell_index": 11 243 | } 244 | ] 245 | } 246 | }, 247 | "version": "2.0.0-dev" 248 | } 249 | }, 250 | "nbformat": 4, 251 | "nbformat_minor": 4 252 | } 253 | -------------------------------------------------------------------------------- /notebooks/01.Introduction/01.01-overview.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Overview\n", 8 | "\n", 9 | "What you can accomplish with just Python has increased quite a bit in the last years as more sophisticated tools that plug in to the Jupyter widget ecosystem have been written.\n", 10 | "\n", 11 | "One of those tools is [bqplot](https://github.com/bloomberg/bqplot/blob/master/examples/Index.ipynb), which provides a plotting tool in which the plot, and the lines, markers, labels and legend, all act as widgets. bqplot was developed using both Python *and* JavaScript. On the JavaScript side bqplot uses [d3](https://d3js.org/) to do the drawing in the browser. \n", 12 | "\n", 13 | "The widely-used plotting library [matplotlib](https://matplotlib.org/3.2.2/contents.html) also has a widget interface. Use `%matplotlib widget` in the notebook to have interactive plots that are widgets. Look at the documentation for [ipympl](https://github.com/matplotlib/ipympl) for more details on using it as a widget.\n", 14 | "\n", 15 | "Another example is [ipyvolume](https://ipyvolume.readthedocs.io/en/latest/), which does three-dimensional renderings of point or volumetric data in the browser. It has both Python and JavaScript pieces but using it requires only Python.\n", 16 | "\n", 17 | "One last addition is in `ipywidgets` itself: the new `Output` widget can display any content which can be rendered in a Jupyter notebook. That means that anything you can show in a notebook you can include in a widget using only Python." 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "metadata": {}, 23 | "source": [ 24 | "## Example 1: COVID dashboard (pure Python)\n", 25 | "\n", 26 | "+ Dashboard: http://jupyter.mnstate.edu/COVID\n", 27 | "+ Code: https://github.com/JuanCab/COVID_DataViz (see `Dashboard.ipynb`)\n", 28 | "\n", 29 | "Orange boxes are [ipympl](); magenta box is [ipyleaflet](https://ipyleaflet.readthedocs.io/en/latest/); remaining widgets are from [ipywidgets](https://ipywidgets.readthedocs.io/en/stable/).\n", 30 | "\n", 31 | "| | |\n", 32 | "|----|----|\n", 33 | "![Screenshot of COVID dashboard](images/covid-dash1.png) | ![Screenshot of COVID dashboard map](images/covid-dash2.png)" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": {}, 39 | "source": [ 40 | "## Example 2: Binary star simulation (pure Python)\n", 41 | "\n", 42 | "+ Green: [pythreejs](https://github.com/jupyter-widgets/pythreejs)\n", 43 | "+ Blue: [bqplot](https://github.com/bloomberg/bqplot/blob/master/examples/Index.ipynb)\n", 44 | "+ Everything else: [ipywidgets](https://github.com/jupyter-widgets/ipywidgets)\n", 45 | "+ Serving it up to users during development on [mybinder.org](https://mybinder.org/)\n", 46 | "\n", 47 | "![Binary Star Simulator](images/Binary_Star_Sim.png)\n", 48 | "\n", 49 | "### Source for this example (including links to binder): https://github.com/JuanCab/AstroInteractives\n", 50 | "\n", 51 | "[Video](https://youtu.be/kbgST0uifvM)" 52 | ] 53 | } 54 | ], 55 | "metadata": { 56 | "kernelspec": { 57 | "display_name": "widgets-tutorial", 58 | "language": "python", 59 | "name": "widgets-tutorial" 60 | }, 61 | "language_info": { 62 | "codemirror_mode": { 63 | "name": "ipython", 64 | "version": 3 65 | }, 66 | "file_extension": ".py", 67 | "mimetype": "text/x-python", 68 | "name": "python", 69 | "nbconvert_exporter": "python", 70 | "pygments_lexer": "ipython3", 71 | "version": "3.8.10" 72 | } 73 | }, 74 | "nbformat": 4, 75 | "nbformat_minor": 4 76 | } 77 | -------------------------------------------------------------------------------- /notebooks/02.Widget_overview/01.Interact/01.01-OPTIONAL-More-About-Interact.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from ipywidgets import interact, interactive, fixed, interact_manual\n", 10 | "import ipywidgets as widgets" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "# OPTIONAL - More about interact\n", 18 | "\n", 19 | "This notebook describes more ways to autogenerate widgets." 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "## `interactive`\n", 27 | "\n", 28 | "In addition to `interact`, IPython provides another function, `interactive`, that is useful when you want to reuse the widgets that are produced or access the data that is bound to the UI controls.\n", 29 | "\n", 30 | "Note that unlike `interact`, the return value of the function will not be displayed automatically, but you can display a value inside the function with `IPython.display.display`." 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "Here is a function that displays the sum of its two arguments and returns the sum. The `display` line may be omitted if you don't want to show the result of the function." 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "from IPython.display import display\n", 47 | "\n", 48 | "def f(a, b):\n", 49 | " display(a + b)\n", 50 | " return a+b" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "Unlike `interact`, `interactive` returns a `Widget` instance rather than immediately displaying the widget." 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "w = interactive(f, a=10, b=20)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "The widget is an `interactive`, a subclass of `VBox`, which is a container for other widgets." 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "type(w)" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "The children of the `interactive` are two integer-valued sliders and an output widget, produced by the widget abbreviations above." 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "w.children" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "To actually display the widgets, you can use IPython's `display` function." 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [ 114 | "display(w)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "At this point, the UI controls work just like they would if `interact` had been used. You can manipulate them interactively and the function will be called. However, the widget instance returned by `interactive` also gives you access to the current keyword arguments and return value of the underlying Python function. \n", 122 | "\n", 123 | "Here are the current keyword arguments. If you rerun this cell after manipulating the sliders, the values will have changed." 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "w.kwargs" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": {}, 138 | "source": [ 139 | "Here is the current return value of the function." 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": null, 145 | "metadata": {}, 146 | "outputs": [], 147 | "source": [ 148 | "w.result" 149 | ] 150 | }, 151 | { 152 | "cell_type": "markdown", 153 | "metadata": {}, 154 | "source": [ 155 | "## Disabling continuous updates" 156 | ] 157 | }, 158 | { 159 | "cell_type": "markdown", 160 | "metadata": {}, 161 | "source": [ 162 | "When interacting with long running functions, or even with short functions whose results take some to display, realtime feedback is a burden instead of being helpful. You might have noticed the output of some of the widgets above \"flickering\" as you adjusted the controls. By default, `interact` and `interactive` call the function for every update of the widgets value. " 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": {}, 168 | "source": [ 169 | "There are two ways to mitigate this. You can either only execute on demand, or restrict execution to mouse release events." 170 | ] 171 | }, 172 | { 173 | "cell_type": "markdown", 174 | "metadata": {}, 175 | "source": [ 176 | "### `interact_manual`" 177 | ] 178 | }, 179 | { 180 | "cell_type": "markdown", 181 | "metadata": {}, 182 | "source": [ 183 | "The `interact_manual` function provides a variant of interaction that allows you to restrict execution so it is only done on demand. A button is added to the interact controls that allows you to trigger an execute event." 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "execution_count": null, 189 | "metadata": {}, 190 | "outputs": [], 191 | "source": [ 192 | "def slow_function(i):\n", 193 | " \"\"\"\n", 194 | " Sleep for 1 second then print the argument\n", 195 | " \"\"\"\n", 196 | " from time import sleep\n", 197 | " print('Sleeping...')\n", 198 | " sleep(1)\n", 199 | " print(i)\n", 200 | "\n", 201 | "interact_manual(slow_function,i=widgets.FloatSlider(min=1e4, max=1e6, step=1e4));" 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": {}, 207 | "source": [ 208 | "You can do the same thing with `interactive` by using a `dict` as the second argument, as shown below." 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": {}, 215 | "outputs": [], 216 | "source": [ 217 | "foo = interactive(slow_function, {'manual': True}, i=widgets.FloatSlider(min=1e4, max=1e6, step=1e4))\n", 218 | "foo" 219 | ] 220 | }, 221 | { 222 | "cell_type": "markdown", 223 | "metadata": {}, 224 | "source": [ 225 | "### `continuous_update`" 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": {}, 231 | "source": [ 232 | "If you are using slider widgets, you can set the `continuous_update` kwarg to `False`. `continuous_update` is a keyword argument of slider widgets that restricts executions to mouse release events.\n", 233 | "\n", 234 | "In ipywidgets 7, the `Text` and `Textarea` controls also have a `continuous_update` argument.\n", 235 | "\n", 236 | "The first example below provides the `continuous_update` argument when the widget is created." 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": null, 242 | "metadata": {}, 243 | "outputs": [], 244 | "source": [ 245 | "interact(slow_function,i=widgets.FloatSlider(min=1e4, max=1e6, step=5e4, continuous_update=False));" 246 | ] 247 | }, 248 | { 249 | "cell_type": "markdown", 250 | "metadata": {}, 251 | "source": [ 252 | "## More control over the user interface: `interactive_output`\n", 253 | "\n", 254 | "`interactive_output` provides additional flexibility: you can control how the UI elements are laid out.\n", 255 | "\n", 256 | "Unlike `interact`, `interactive`, and `interact_manual`, `interactive_output` does not generate a user interface for the widgets. This is powerful, because it means you can create a widget, put it in a box, and then pass the widget to `interactive_output`, and have control over the widget and its layout." 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": null, 262 | "metadata": {}, 263 | "outputs": [], 264 | "source": [ 265 | "a = widgets.IntSlider()\n", 266 | "b = widgets.IntSlider()\n", 267 | "c = widgets.IntSlider()\n", 268 | "\n", 269 | "# An HBox lays out its children horizontally\n", 270 | "ui = widgets.HBox([a, b, c])\n", 271 | "\n", 272 | "def f(a, b, c):\n", 273 | " # You can use print here instead of display because interactive_output generates a normal notebook \n", 274 | " # output area.\n", 275 | " print((a, b, c))\n", 276 | "\n", 277 | "out = widgets.interactive_output(f, {'a': a, 'b': b, 'c': c})\n", 278 | "\n", 279 | "display(ui, out)" 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "metadata": {}, 285 | "source": [ 286 | "# For more information \n", 287 | "\n", 288 | "For more extended examples of `interact` and `interactive`, see [the example in the ipywidgets source repository](https://github.com/jupyter-widgets/ipywidgets/blob/master/docs/source/examples/Index.ipynb)." 289 | ] 290 | } 291 | ], 292 | "metadata": { 293 | "kernelspec": { 294 | "display_name": "widgets-tutorial", 295 | "language": "python", 296 | "name": "widgets-tutorial" 297 | }, 298 | "language_info": { 299 | "codemirror_mode": { 300 | "name": "ipython", 301 | "version": 3 302 | }, 303 | "file_extension": ".py", 304 | "mimetype": "text/x-python", 305 | "name": "python", 306 | "nbconvert_exporter": "python", 307 | "pygments_lexer": "ipython3", 308 | "version": "3.8.3" 309 | } 310 | }, 311 | "nbformat": 4, 312 | "nbformat_minor": 4 313 | } 314 | -------------------------------------------------------------------------------- /notebooks/02.Widget_overview/01.Interact/solutions/plot-function-interact.py: -------------------------------------------------------------------------------- 1 | # with interact 2 | 3 | fig, ax = plt.subplots() 4 | ax.grid() 5 | line = ax.plot(x, f(x, k, p))[0] 6 | 7 | def plot_f(k, p): 8 | line.set_ydata(f(x, k, p)) 9 | fig.canvas.draw_idle() 10 | 11 | 12 | interact(plot_f, k=(0.5, 2), p=(0, 2 * np.pi)) -------------------------------------------------------------------------------- /notebooks/02.Widget_overview/01.Interact/solutions/plot-function-mpl-inter.py: -------------------------------------------------------------------------------- 1 | fig, ax = plt.subplots() 2 | ax.grid() 3 | ctrls = iplt.plot(x, f, k=(.5, 2), p=(0, 2*np.pi)) -------------------------------------------------------------------------------- /notebooks/02.Widget_overview/01.Interact/solutions/reverse-text.py: -------------------------------------------------------------------------------- 1 | def reverse(x): 2 | return x[::-1] 3 | 4 | interact(reverse, x='Hello') 5 | -------------------------------------------------------------------------------- /notebooks/02.Widget_overview/02.Basics/images/WidgetModelView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/02.Widget_overview/02.Basics/images/WidgetModelView.png -------------------------------------------------------------------------------- /notebooks/02.Widget_overview/03.WidgetList/03.00-widget-list.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Widgets in the core ipywidgets package" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "The package `ipywidgets` provides two things:\n", 15 | "\n", 16 | "+ A communication framework between the front end (your browser) and back end (python or other kernel).\n", 17 | "+ A set of fundamental user interface elements like buttons and checkboxes.\n", 18 | "\n", 19 | "The next couple of cells create a browser of the available elements. To see more detail about any of the elements click on its title. It will be easier to view both the overview and the detail if you have them open in separate tabs." 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "import ipywidgets as widgets\n", 29 | "from widget_org import organized_widgets, list_overview_widget" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "## Instructions\n", 37 | "\n", 38 | "Run the cell below. Click on the name of any widget to see a more detailed example of using the widget." 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "groups = organized_widgets(organize_by='ui')\n", 48 | "help_url_base='../../reference_guides/complete-ipywidgets-widget-list.ipynb'\n", 49 | "list_overview_widget(groups, columns=2, min_width_single_widget=200, help_url_base=help_url_base)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "## Exercises\n", 57 | "\n", 58 | "You may not have time to finish all of these exercises." 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "### 1. Fix the example from the previous notebook\n", 66 | "\n", 67 | "The code below is taken from the previous notebook of this tutorial. \n", 68 | "\n", 69 | "Run the code below then try typing a number larger than 10 or smaller than 5 into the text box." 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "slider = widgets.FloatSlider(\n", 79 | " value=7.5,\n", 80 | " min=5.0,\n", 81 | " max=10.0,\n", 82 | " step=0.1,\n", 83 | " description='Input:',\n", 84 | ")\n", 85 | "\n", 86 | "# Create text box to hold slider value\n", 87 | "text = widgets.FloatText(description='Value')\n", 88 | "\n", 89 | "# Link slider value and text box value\n", 90 | "widgets.link((slider, 'value'), (text, 'value'))\n", 91 | "\n", 92 | "# Put them in a vertical box\n", 93 | "widgets.VBox([slider, text])" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | "Note the slider has the wrong value! The slider has a minimum and maximum value but the text box doesn't.\n", 101 | "\n", 102 | "Replace the `FloatText` in the code above with a text widget that has a minimum and maximum that matches the slider." 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "metadata": {}, 109 | "outputs": [], 110 | "source": [ 111 | "# %load solutions/bounded-float-text.py" 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "metadata": {}, 117 | "source": [ 118 | "## 2. Two widgets in a box and link them\n", 119 | "\n", 120 | "Put two widgets, the `Play` widget and a widget of your choice that can hold an integer, in a horizontal box." 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "# %load solutions/widgets-in-a-box.py" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "Link the values of the two widgets above so that changing the value of one affects the value of the other." 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [] 145 | }, 146 | { 147 | "cell_type": "markdown", 148 | "metadata": {}, 149 | "source": [ 150 | "## 3. Try tabs or accordions\n", 151 | "\n", 152 | "Choose two or more widgets and place them in either different tabs or accordions. Set the name of each tab or accordion to something more meaningful than the default names." 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "Set which tab or accordion is selected by typing the right code in the cell below (hint, look at the `selected_index` attribute)." 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [] 175 | } 176 | ], 177 | "metadata": { 178 | "kernelspec": { 179 | "display_name": "widgets-tutorial", 180 | "language": "python", 181 | "name": "widgets-tutorial" 182 | }, 183 | "language_info": { 184 | "codemirror_mode": { 185 | "name": "ipython", 186 | "version": 3 187 | }, 188 | "file_extension": ".py", 189 | "mimetype": "text/x-python", 190 | "name": "python", 191 | "nbconvert_exporter": "python", 192 | "pygments_lexer": "ipython3", 193 | "version": "3.8.10" 194 | } 195 | }, 196 | "nbformat": 4, 197 | "nbformat_minor": 4 198 | } 199 | -------------------------------------------------------------------------------- /notebooks/02.Widget_overview/03.WidgetList/solutions/bounded-float-text.py: -------------------------------------------------------------------------------- 1 | import ipywidgets as widgets 2 | slider = widgets.FloatSlider( 3 | value=7.5, 4 | min=5.0, 5 | max=10.0, 6 | step=0.1, 7 | description='Input:', 8 | ) 9 | bounded_float_text = widgets.BoundedFloatText( 10 | value=7.5, 11 | min=5, 12 | max=10.0, 13 | step=0.1, 14 | description='Text:', 15 | disabled=False 16 | ) 17 | widgets.link((slider, 'value'), (bounded_float_text, 'value')) 18 | widgets.link((slider, 'min'), (bounded_float_text, 'min')) 19 | widgets.link((slider, 'max'), (bounded_float_text, 'max')) 20 | widgets.VBox([slider, bounded_float_text]) 21 | -------------------------------------------------------------------------------- /notebooks/02.Widget_overview/03.WidgetList/solutions/sidecar-triple.py: -------------------------------------------------------------------------------- 1 | new_side = Sidecar(title="Triple product") 2 | 3 | with new_side: 4 | display(interactive_out_example) 5 | -------------------------------------------------------------------------------- /notebooks/02.Widget_overview/03.WidgetList/solutions/widgets-in-a-box.py: -------------------------------------------------------------------------------- 1 | import ipywidgets as widgets 2 | a = widgets.Play(description='Value A', min=1, max=10, value=5) 3 | b = widgets.IntText(description='Value B') 4 | 5 | vbox = widgets.HBox(children=[a, b]) 6 | vbox 7 | -------------------------------------------------------------------------------- /notebooks/03.Widget_events/03.01-OPTIONAL-Widget_Events_2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# *OPTIONAL* Three approaches to events" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "The next series of notebooks presents three ways of handling widget events. In each of the notebooks we'll construct the same simple password generator.\n", 15 | "\n", 16 | "The first uses a mix of functions and global variables, an approach like that in the notebook introducing widget events.\n", 17 | "\n", 18 | "The second separates the logic of the password generation from the password generation user interface.\n", 19 | "\n", 20 | "The third takes that separation a step further by using a class for the user interface, a class for the logic, and a class to bring them together." 21 | ] 22 | } 23 | ], 24 | "metadata": { 25 | "kernelspec": { 26 | "display_name": "widgets-tutorial", 27 | "language": "python", 28 | "name": "widgets-tutorial" 29 | }, 30 | "language_info": { 31 | "codemirror_mode": { 32 | "name": "ipython", 33 | "version": 3 34 | }, 35 | "file_extension": ".py", 36 | "mimetype": "text/x-python", 37 | "name": "python", 38 | "nbconvert_exporter": "python", 39 | "pygments_lexer": "ipython3", 40 | "version": "3.7.3" 41 | } 42 | }, 43 | "nbformat": 4, 44 | "nbformat_minor": 4 45 | } 46 | -------------------------------------------------------------------------------- /notebooks/03.Widget_events/03.02-OPTIONAL-Widget_Events_2_--_bad_password_generator,_version_1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# *OPTIONAL* Password generator: `observe`\n", 8 | "\n", 9 | "Consider a super-simple (and super-bad) password generator widget: given a password length, represented by a slider in the interface, it constructs a sequence of random letters of that length and displays it. \n", 10 | "\n", 11 | "\n", 12 | "This notebook illustrates how to connect the function that calculates the password to the length slider using `observe` but mixes together the code to calculate the password and the code to handle the events generated by the interface" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "import ipywidgets as widgets" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "## Construct the interface (widget)\n", 29 | "\n", 30 | "The widget should look like this once constructed:\n", 31 | "\n", 32 | "![Password generator](images/bad-pass-gen-v1.png)\n", 33 | "\n", 34 | "Compose the widget out of three basic widgets, one each for the title, the (currently not set) password, and one for the slider. \n", 35 | "\n", 36 | "In the cell below construct each of the basic widgets. " 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": { 43 | "tags": [] 44 | }, 45 | "outputs": [], 46 | "source": [ 47 | "helpful_title = 0 # Replace with something that displays \"Generated password is:\"\n", 48 | "password_text = 0 # Replace with something that displays \"No password set\"\n", 49 | "password_length = 0 # Replace with slider\n" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "Combine these three into a single widget...the output should look like the image above." 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "password_widget = widgets.VBox(children=[helpful_title, password_text, password_length])\n", 66 | "password_widget" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "metadata": { 73 | "tags": [] 74 | }, 75 | "outputs": [], 76 | "source": [ 77 | "# %load solutions/bad-pass-pass1-widgets.py" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "## Calculate the password...\n", 85 | "\n", 86 | "The function below calculates the password and should set the value of the `password_text` widget. The first part has been done, you just need to add the line that sets the widget value." 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": { 93 | "tags": [] 94 | }, 95 | "outputs": [], 96 | "source": [ 97 | "def calculate_password(change):\n", 98 | " import string\n", 99 | " from secrets import choice\n", 100 | " length = change.new\n", 101 | " # Generate a list of random letters of the correct length.\n", 102 | " password = ''.join(choice(string.ascii_letters) for _ in range(length))\n", 103 | " # Add a line below to set the value of the widget password_text\n" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": { 110 | "tags": [] 111 | }, 112 | "outputs": [], 113 | "source": [ 114 | "# %load solutions/bad-pass-pass1-passgen.py" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "## ...and link password to widgets\n", 122 | "\n", 123 | "Fill in the line below. You want `calculate_password` to be called when the value of `password_length` changes. Here is a link to [Widget Events](06-Widget_Events.ipynb) in case you need it." 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": { 130 | "tags": [] 131 | }, 132 | "outputs": [], 133 | "source": [ 134 | "# call calculate_password whenever the password length changes\n" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": null, 140 | "metadata": { 141 | "tags": [] 142 | }, 143 | "outputs": [], 144 | "source": [ 145 | "# %load solutions/bad-pass-pass1-observe.py" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "Now that the connection is made, try moving the slider and you should see the password update." 153 | ] 154 | } 155 | ], 156 | "metadata": { 157 | "kernelspec": { 158 | "display_name": "widgets-tutorial", 159 | "language": "python", 160 | "name": "widgets-tutorial" 161 | }, 162 | "language_info": { 163 | "codemirror_mode": { 164 | "name": "ipython", 165 | "version": 3 166 | }, 167 | "file_extension": ".py", 168 | "mimetype": "text/x-python", 169 | "name": "python", 170 | "nbconvert_exporter": "python", 171 | "pygments_lexer": "ipython3", 172 | "version": "3.8.10" 173 | } 174 | }, 175 | "nbformat": 4, 176 | "nbformat_minor": 4 177 | } 178 | -------------------------------------------------------------------------------- /notebooks/03.Widget_events/03.03-OPTIONAL-Widget_Events_2_--_Separating_Concerns.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# *OPTIONAL* Separating the logic from the widgets\n", 8 | "\n", 9 | "A key principle in designing a graphical user interface is to separate the logic of an application from the graphical widgets the user sees. For example, in the super-simple password generator widget, the basic logic is to construct a sequence of random letters given the length. Let's isolate that logic in a function, without any widgets. This function takes a password length and returns a generated password string." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import ipywidgets as widgets" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "def calculate_password(length):\n", 28 | " import string\n", 29 | " import secrets\n", 30 | " \n", 31 | " # Gaenerate a list of random letters of the correct length.\n", 32 | " password = ''.join(secrets.choice(string.ascii_letters) for _ in range(length))\n", 33 | "\n", 34 | " return password" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "Test out the function a couple times in the cell below with different lengths. Note that unlike our first pass through this, you can test this function without defining any widgets. This means you can write tests for just the logic, use the function as part of a library, etc." 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": null, 47 | "metadata": {}, 48 | "outputs": [], 49 | "source": [ 50 | "calculate_password(10)" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "## The Graphical Controls\n", 58 | "\n", 59 | "The code to build the graphical user interface widgets is the same as the previous iteration." 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "helpful_title = widgets.HTML('Generated password is:')\n", 69 | "password_text = widgets.HTML('No password yet')\n", 70 | "password_text.layout.margin = '0 0 0 20px'\n", 71 | "password_length = widgets.IntSlider(description='Length of password',\n", 72 | " min=8, max=20,\n", 73 | " style={'description_width': 'initial'})\n", 74 | "\n", 75 | "password_widget = widgets.VBox(children=[helpful_title, password_text, password_length])\n", 76 | "password_widget" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "metadata": {}, 83 | "outputs": [], 84 | "source": [] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": {}, 89 | "source": [ 90 | "## Connecting the logic to the widgets\n", 91 | "\n", 92 | "When the slider `password_length` changes, we want to call `calculate_password` to come up with a new password, and set the value of the widget `password` to the return value of the function call.\n", 93 | "\n", 94 | "`update_password` takes the change from the `password_length` as its argument and sets the `password_text` with the result of `calculate_password`." 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "def update_password(change):\n", 104 | " length = int(change.new)\n", 105 | " new_password = calculate_password(length)\n", 106 | " \n", 107 | " # NOTE THE LINE BELOW: it relies on the password widget already being defined.\n", 108 | " password_text.value = new_password\n", 109 | " \n", 110 | "password_length.observe(update_password, names='value')" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "Now that the connection is made, try moving the slider and you should see the password update." 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [ 126 | "password_widget" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "## Benefits of separating concerns\n", 134 | "\n", 135 | "Some advantages of this approach are:\n", 136 | "\n", 137 | "+ Changes in `ipywidgets` only affect your controls setup.\n", 138 | "+ Changes in functional logic only affect your password generation function. If you decide that a password with only letters isn't secure enough and decide to add some numbers and/or special characters, the only code you need to change is in the `calculate_password` function.\n", 139 | "+ You can write unit tests for your `calculate_password` function -- which is where the important work is being done -- without doing in-browser testing of the graphical controls." 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "metadata": {}, 145 | "source": [ 146 | "## Using interact\n", 147 | "\n", 148 | "Note that using interact to build this GUI also emphasizes the separation between the logic and the controls. However, interact also is much more opinionated about how the controls are laid out: controls are in a vbox above the output of the function. Often this is great for a quick initial GUI, but is restrictive for more complex GUIs." 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "from ipywidgets import interact\n", 158 | "from IPython.display import display\n", 159 | "interact(calculate_password, length=(8, 20));" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "We can make the interact a bit nicer by printing the result, rather than just returning the string. This time we use `interact` as a decorator." 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [ 175 | "@interact(length=(8, 20))\n", 176 | "def print_password(length):\n", 177 | " print(calculate_password(length))" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": null, 183 | "metadata": {}, 184 | "outputs": [], 185 | "source": [] 186 | } 187 | ], 188 | "metadata": { 189 | "kernelspec": { 190 | "display_name": "widgets-tutorial", 191 | "language": "python", 192 | "name": "widgets-tutorial" 193 | }, 194 | "language_info": { 195 | "codemirror_mode": { 196 | "name": "ipython", 197 | "version": 3 198 | }, 199 | "file_extension": ".py", 200 | "mimetype": "text/x-python", 201 | "name": "python", 202 | "nbconvert_exporter": "python", 203 | "pygments_lexer": "ipython3", 204 | "version": "3.8.10" 205 | } 206 | }, 207 | "nbformat": 4, 208 | "nbformat_minor": 4 209 | } 210 | -------------------------------------------------------------------------------- /notebooks/03.Widget_events/images/bad-pass-gen-v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/03.Widget_events/images/bad-pass-gen-v1.png -------------------------------------------------------------------------------- /notebooks/03.Widget_events/password_generator_example/gui.py: -------------------------------------------------------------------------------- 1 | from ipywidgets import widgets 2 | from model import PassGen 3 | import traitlets 4 | 5 | class PassGenGUI(widgets.VBox): 6 | def __init__(self): 7 | super(PassGenGUI, self).__init__() 8 | 9 | self._helpful_title = widgets.HTML('Generated password is:') 10 | self._password_text = widgets.HTML('', placeholder='No password generated yet') 11 | self._password_text.layout.margin = '0 0 0 20px' 12 | self._password_length = widgets.IntSlider(description='Length of password', 13 | min=8, max=20, 14 | style={'description_width': 'initial'}) 15 | 16 | self._numbers = widgets.Checkbox(description='Include numbers in password', 17 | style={'description_width': 'initial'}) 18 | 19 | self._label = widgets.Label('Choose special characters to include') 20 | self._toggles = widgets.ToggleButtons(description='', 21 | options=[',./;[', '!@#~%', '^&*()'], 22 | style={'description_width': 'initial'}) 23 | 24 | # Set the margins to control the indentation. 25 | # The order is top right bottom left 26 | self._toggles.layout.margin = '0 0 0 20px' 27 | 28 | children = [self._helpful_title, self._password_text, self._password_length, 29 | self._label, self._toggles, self._numbers] 30 | self.children = children 31 | 32 | self.model = PassGen() 33 | 34 | traitlets.link((self.model, 'password'), (self._password_text, 'value')) 35 | traitlets.link((self.model, 'length'), (self._password_length, 'value')) 36 | traitlets.link((self.model, 'special_character_groups'), (self._toggles, 'value')) 37 | traitlets.link((self.model, 'include_numbers'), (self._numbers, 'value')) 38 | 39 | @property 40 | def value(self): 41 | return self._password_text.value 42 | -------------------------------------------------------------------------------- /notebooks/03.Widget_events/password_generator_example/model.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division 2 | 3 | import string 4 | import random 5 | 6 | import traitlets 7 | 8 | SPECIAL_GROUPS = [',./;[', '!@#~%', '^&*()'] 9 | 10 | 11 | class PassGen(traitlets.HasTraits): 12 | """ 13 | Class to represent state of the password generator and handle generation 14 | of password. 15 | """ 16 | length = traitlets.Integer() 17 | password = traitlets.Unicode("password") 18 | 19 | include_numbers = traitlets.Bool() 20 | special_character_groups = traitlets.Enum(SPECIAL_GROUPS, 21 | default_value=SPECIAL_GROUPS[0]) 22 | 23 | def __init__(self): 24 | super(PassGen, self).__init__() 25 | pass 26 | 27 | @traitlets.observe('length', 'include_numbers', 'special_character_groups') 28 | def generate_password(self, change): 29 | """ 30 | Generate a password of the desired length including the user's chosen 31 | set of special characters and, if desired, including some numerals. 32 | """ 33 | 34 | # Generate an initial password composed only of letters. 35 | new_pass = [] 36 | for _ in range(self.length): 37 | new_pass.append(random.choice(string.ascii_letters)) 38 | 39 | # Generate a list of indices for choosing which characters in the 40 | # initial password to replace, then shuffle it. We'll pop 41 | # elements off the list as we need them. 42 | order_for_replacements = list(range(self.length)) 43 | random.shuffle(order_for_replacements) 44 | 45 | # Replace some of the letters with special characters 46 | n_special = random.randint(1, 3) 47 | for _ in range(n_special): 48 | loc = order_for_replacements.pop(0) 49 | new_pass[loc] = random.choice(self.special_character_groups) 50 | 51 | if self.include_numbers: 52 | # Number of digits to include. 53 | n_digits = random.randint(1, 3) 54 | for _ in range(n_digits): 55 | loc = order_for_replacements.pop(0) 56 | new_pass[loc] = random.choice(string.digits) 57 | 58 | self.password = ''.join(new_pass) 59 | 60 | -------------------------------------------------------------------------------- /notebooks/03.Widget_events/solutions/bad-pass-pass1-observe.py: -------------------------------------------------------------------------------- 1 | password_length.observe(calculate_password, names='value') 2 | -------------------------------------------------------------------------------- /notebooks/03.Widget_events/solutions/bad-pass-pass1-passgen.py: -------------------------------------------------------------------------------- 1 | def calculate_password(change): 2 | import string 3 | from secrets import choice 4 | length = change.new 5 | # Generate a list of random letters of the correct length. 6 | password = ''.join(choice(string.ascii_letters) for _ in range(length)) 7 | # Add a line below to set the value of the widget password_text 8 | password_text.value = password 9 | -------------------------------------------------------------------------------- /notebooks/03.Widget_events/solutions/bad-pass-pass1-widgets.py: -------------------------------------------------------------------------------- 1 | helpful_title = widgets.HTML('Generated password is:') 2 | password_text = widgets.HTML('No password yet', placeholder='No password generated yet') 3 | password_text.layout.margin = '0 0 0 20px' 4 | password_length = widgets.IntSlider(description='Length of password', 5 | min=8, max=20, 6 | style={'description_width': 'initial'}) 7 | 8 | password_widget = widgets.VBox(children=[helpful_title, password_text, password_length]) 9 | password_widget 10 | -------------------------------------------------------------------------------- /notebooks/03.Widget_events/solutions/observe-reverse.py: -------------------------------------------------------------------------------- 1 | text = widgets.Textarea() 2 | output = widgets.Output() 3 | 4 | @output.capture() 5 | def reverse(change): 6 | print(change['new'][::-1]) 7 | 8 | text.observe(reverse, names='value') 9 | 10 | display(text, output) 11 | -------------------------------------------------------------------------------- /notebooks/03.Widget_events/solutions/temperature-link.py: -------------------------------------------------------------------------------- 1 | widgets.link((degree_C, "value"), (degree_F, "value"), transform=(C_to_F, F_to_C)) -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/04.00-Layout-and-Styling-Overview.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Layout and Styling of Jupyter widgets\n", 8 | "\n", 9 | "This section of the tutorial describes \n", 10 | "\n", 11 | "+ How to [lay out and style Jupyter interactive widgets](04.01-widget-layout-and-styling.ipynb) to build rich and *reactive* widget-based applications.\n", 12 | "+ Some [high-level container widgets for laying out widget-based applications](04.01-widget-layout-and-styling.ipynb#Container-Layout-Widgets).\n", 13 | "+ Styling available for [some individual widgets](04.05-OPTIONAL-widget-specific-styling.ipynb) (please read this on your own).\n", 14 | "+ [Layout and styling of widget labels](04.04-OPTIONAL-widget-label-styling.ipynb) (please read this on your own)." 15 | ] 16 | } 17 | ], 18 | "metadata": { 19 | "kernelspec": { 20 | "display_name": "widgets-tutorial", 21 | "language": "python", 22 | "name": "widgets-tutorial" 23 | }, 24 | "language_info": { 25 | "codemirror_mode": { 26 | "name": "ipython", 27 | "version": 3 28 | }, 29 | "file_extension": ".py", 30 | "mimetype": "text/x-python", 31 | "name": "python", 32 | "nbconvert_exporter": "python", 33 | "pygments_lexer": "ipython3", 34 | "version": "3.8.10" 35 | } 36 | }, 37 | "nbformat": 4, 38 | "nbformat_minor": 4 39 | } 40 | -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/04.04-OPTIONAL-widget-label-styling.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# OPTIONAL - Widget label styling" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "### Description" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "You may have noticed that long descriptions are truncated. This is because the description length is, by default, fixed." 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "from ipywidgets import IntSlider\n", 31 | "\n", 32 | "IntSlider(description='A too long description')" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "If you need more flexibility to lay out widgets and descriptions, you can use Label widgets directly." 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "from ipywidgets import HBox, Label\n", 49 | "\n", 50 | "HBox([Label('A too long description'), IntSlider()])" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "You can change the length of the description to fit the description text. However, this will make the widget itself shorter. You can change both by adjusting the description width and the widget width using the widget's style." 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "style = {'description_width': 'initial'}\n", 67 | "IntSlider(description='A too long description', style=style)" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "### Natural sizes, and arrangements using HBox and VBox\n", 75 | "\n", 76 | "Most of the core-widgets have default heights and widths that tile well together. This allows simple layouts based on the `HBox` and `VBox` helper functions to align naturally:" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "metadata": {}, 83 | "outputs": [], 84 | "source": [ 85 | "from ipywidgets import Button, HBox, VBox\n", 86 | "\n", 87 | "words = ['correct', 'horse', 'battery', 'staple']\n", 88 | "items = [Button(description=w) for w in words]\n", 89 | "left_box = VBox([items[0], items[1]])\n", 90 | "right_box = VBox([items[2], items[3]])\n", 91 | "HBox([left_box, right_box])" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": {}, 97 | "source": [ 98 | "### LaTeX" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "Widgets such as sliders and text inputs have a description attribute that can render Latex Equations. The `Label` widget also renders Latex equations." 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [ 114 | "from ipywidgets import IntSlider, Label" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "IntSlider(description=r'\\(\\int_0^t f\\)')" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "Label(value=r'\\(e=mc^2\\)')" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": {}, 138 | "source": [ 139 | "### Number formatting\n", 140 | "\n", 141 | "Sliders have a readout field which can be formatted using Python's [Format Specification Mini-Language](https://docs.python.org/3/library/string.html#format-specification-mini-language). If the space available for the readout is too narrow for the string representation of the slider value, a different styling is applied to show that not all digits are visible." 142 | ] 143 | } 144 | ], 145 | "metadata": { 146 | "kernelspec": { 147 | "display_name": "widgets-tutorial", 148 | "language": "python", 149 | "name": "widgets-tutorial" 150 | }, 151 | "language_info": { 152 | "codemirror_mode": { 153 | "name": "ipython", 154 | "version": 3 155 | }, 156 | "file_extension": ".py", 157 | "mimetype": "text/x-python", 158 | "name": "python", 159 | "nbconvert_exporter": "python", 160 | "pygments_lexer": "ipython3", 161 | "version": "3.7.3" 162 | } 163 | }, 164 | "nbformat": 4, 165 | "nbformat_minor": 2 166 | } 167 | -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/04.05-OPTIONAL-widget-specific-styling.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# OPTIONAL Predefined widget styles\n", 8 | "\n", 9 | "If you wish the styling of widgets to make use of colors and styles defined by the environment (to be consistent with e.g. a notebook theme), some widgets enable choosing in a list of pre-defined styles.\n", 10 | "\n", 11 | "For example, the `Button` widget has a `button_style` attribute that may take 5 different values:\n", 12 | "\n", 13 | " - `'primary'`\n", 14 | " - `'success'`\n", 15 | " - `'info'`\n", 16 | " - `'warning'`\n", 17 | " - `'danger'`\n", 18 | "\n", 19 | "besides the default empty string ''." 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "from ipywidgets import Button, IntSlider\n", 29 | "\n", 30 | "Button(description='Danger Button', button_style='danger')" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "## The `style` attribute\n", 38 | "\n", 39 | "While the `layout` attribute only exposes layout-related CSS properties for the top-level DOM element of widgets, the \n", 40 | "`style` attribute is used to expose non-layout related styling attributes of widgets.\n", 41 | "\n", 42 | "However, the properties of the `style` attribute are specific to each widget type." 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "b1 = Button(description='Custom color')\n", 52 | "b1.style.button_color = 'lightgreen'\n", 53 | "b1" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "You can get a list of the style attributes for a widget with the `keys` property." 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "b1.style.keys" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": {}, 75 | "source": [ 76 | "Just like the `layout` attribute, widget styles can be assigned to other widgets." 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "metadata": {}, 83 | "outputs": [], 84 | "source": [ 85 | "b2 = Button()\n", 86 | "b2.style = b1.style\n", 87 | "b2" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "Widget styling attributes are specific to each widget type." 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "s1 = IntSlider(description='Blue handle')\n", 104 | "s1.style.handle_color = 'lightblue'\n", 105 | "s1" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": {}, 111 | "source": [ 112 | "There is a [list of all style keys](Table%20of%20widget%20keys%20and%20style%20keys.ipynb#Style-keys)." 113 | ] 114 | } 115 | ], 116 | "metadata": { 117 | "kernelspec": { 118 | "display_name": "widgets-tutorial", 119 | "language": "python", 120 | "name": "widgets-tutorial" 121 | }, 122 | "language_info": { 123 | "codemirror_mode": { 124 | "name": "ipython", 125 | "version": 3 126 | }, 127 | "file_extension": ".py", 128 | "mimetype": "text/x-python", 129 | "name": "python", 130 | "nbconvert_exporter": "python", 131 | "pygments_lexer": "ipython3", 132 | "version": "3.8.3" 133 | } 134 | }, 135 | "nbformat": 4, 136 | "nbformat_minor": 4 137 | } 138 | -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/04.06-OPTIONAL-container-exercises.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# *OPTIONAL* widget layout exercises\n", 8 | "\n", 9 | "Earlier notebooks listed the container widgets in ipywidgets and how the widgets contained in them are laid out. As a reminder, the contents of the container are its `children`, a tuple of widgets. The distribution and alignment of the children are determined by the flex-box properties of the `layout` described in [Widget Styling](06-Widget-Styling.ipynb#The-Flexbox-layout).\n", 10 | "\n", 11 | "This set of exercises leads up to a password generator widget that will be completed after discussing widget events. The generator allows the user to set the length of the password, choose a set of special characters to include, and decide whether to include any digits. \n", 12 | "\n", 13 | "**Eventually** the widget will look like this:\n", 14 | "\n", 15 | "\"animated\n", 16 | "\n", 17 | "**At the end of this notebook** we will have laid out the controls shown below. We'll come back to the generator in later notebooks after we have discussed widget events.\n", 18 | "\n", 19 | "\"part\n" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": { 26 | "collapsed": true, 27 | "jupyter": { 28 | "outputs_hidden": true 29 | } 30 | }, 31 | "outputs": [], 32 | "source": [ 33 | "import ipywidgets as widgets" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": {}, 39 | "source": [ 40 | "## 1. Alignment of children\n", 41 | "\n", 42 | "The cell below defines three children that are different sizes and lays them out in a horizontal box. Adjust the two layout properties in the code cell below so that the displayed hbox matches the image below.\n", 43 | "\n", 44 | "![final layout of widgets](images/container-exercises1-final-layout.png)\n", 45 | "\n", 46 | "You may need to look back at the [styling notebook](06-Widget_Styling.ipynb)." 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": { 53 | "collapsed": true, 54 | "jupyter": { 55 | "outputs_hidden": true 56 | } 57 | }, 58 | "outputs": [], 59 | "source": [ 60 | "button = widgets.Button(description='Click me')\n", 61 | "text = widgets.Textarea(description='Words here:', rows=10)\n", 62 | "valid = widgets.Valid(description='check', value=True)\n", 63 | "\n", 64 | "container = widgets.HBox()\n", 65 | "container.children = [button, text, valid]\n", 66 | "container.layout.width = '100%'\n", 67 | "\n", 68 | "# The border is set here just to make it easier to see the position of \n", 69 | "# the children with respect to the box.\n", 70 | "container.layout.border = '2px solid grey'\n", 71 | "container.layout.height = '250px'\n", 72 | "\n", 73 | "# ↓↓↓↓↓↓↓ Adjust these properties ↓↓↓↓↓↓↓↓↓\n", 74 | "container.layout.justify_content = 'flex-start'\n", 75 | "container.layout.align_items = 'flex-start'\n", 76 | "# ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\n", 77 | "\n", 78 | "container" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "## 2. Layout from scratch\n", 86 | "\n", 87 | "Three child widgets are defined in the cell below. Compose them into a vertical box laid out as shown in this image: \n", 88 | "\n", 89 | "![layout of vertical box](images/container-exercises2-final-layout.png)\n", 90 | "\n", 91 | "You should be able to accomplish that layout by setting the appropriate `layout` attribute(s) on `vbox` (don't forget to add the children first).\n", 92 | "\n", 93 | "A box is drawn around the container to make it easier to see where the children are placed" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "metadata": { 100 | "collapsed": true, 101 | "jupyter": { 102 | "outputs_hidden": true 103 | } 104 | }, 105 | "outputs": [], 106 | "source": [ 107 | "# %load solutions/password-ex2.py\n", 108 | "\n", 109 | "numbers = widgets.Checkbox(description='Include numbers in password')\n", 110 | "words = widgets.Label('The generated password is:')\n", 111 | "toggles = widgets.ToggleButtons(description='Type of special characters to include',\n", 112 | " options=[',./;[', '!@#~%', '^&*()'],\n", 113 | " style={'description_width': 'initial'})\n", 114 | "vbox = widgets.VBox()\n", 115 | "\n", 116 | "# The border is set here just to make it easier to see the position of \n", 117 | "# the children with respect to the box.\n", 118 | "vbox.layout.border = '2px solid grey'\n", 119 | "vbox.layout.height = '250px'\n", 120 | "\n", 121 | "# ↓↓↓↓↓↓↓ Insert your layout settings here ↓↓↓↓↓↓↓ \n", 122 | "\n", 123 | "# Don't forget to add the children...\n", 124 | "\n", 125 | "vbox" 126 | ] 127 | }, 128 | { 129 | "cell_type": "markdown", 130 | "metadata": {}, 131 | "source": [ 132 | "## 3. Improve the look of the children\n", 133 | "\n", 134 | "The \"special character\" toggle buttons would really look better if the label was above the buttons, and the checkbox would look better without the whitespace to its left.\n", 135 | "\n", 136 | "### i. A better special character control\n", 137 | "\n", 138 | "In the cell below, construct a widget with the text \"Type of special characters to include\" above the `ToggleButtons`, with all of the content left-aligned, and the toggle buttons slightly indented. \n", 139 | "\n", 140 | "Use the `margin` property of the layout to indent.\n", 141 | "\n", 142 | "It should look like this when you are done:\n", 143 | "\n", 144 | "\n", 145 | "
\n", 146 | "![special characters widget](images/container-exercises-special-chars.png)\n", 147 | "
\n", 148 | "\n", 149 | "This is the second time we've needed a vbox with all the items left-aligned, so let's start out with a `Layout` widget that defines that format" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": null, 155 | "metadata": { 156 | "collapsed": true, 157 | "jupyter": { 158 | "outputs_hidden": true 159 | } 160 | }, 161 | "outputs": [], 162 | "source": [ 163 | "# %load solutions/password-ex3i.py\n", 164 | "\n", 165 | "vbox_left_layout = widgets.Layout(align_items='flex-start')\n", 166 | "\n", 167 | "label = widgets.Label('Choose special characters to include')\n", 168 | "toggles = widgets.ToggleButtons(description='',\n", 169 | " options=[',./;[', '!@#~%', '^&*()'],\n", 170 | " style={'description_width': 'initial'})\n", 171 | "\n", 172 | "# Set the margins to control the indentation. \n", 173 | "# The order is top right bottom left\n", 174 | "toggles.layout.margin = '0 0 0 20px'\n", 175 | "\n", 176 | "better_toggles = widgets.VBox([label, toggles])\n", 177 | "better_toggles.layout = vbox_left_layout\n", 178 | "better_toggles" 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": {}, 184 | "source": [ 185 | "### ii. Checkbox whitespace issues\n", 186 | "\n", 187 | "The checkbox in the example above has unnecessary whitespace to the left of the box. Setting the `description_width` to `initial` removes it, so do that below." 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": null, 193 | "metadata": { 194 | "collapsed": true, 195 | "jupyter": { 196 | "outputs_hidden": true 197 | } 198 | }, 199 | "outputs": [], 200 | "source": [ 201 | "# %load solutions/password-ex3ii.py\n", 202 | "\n", 203 | "numbers = widgets.Checkbox(description='Include numbers in password',\n", 204 | " style={'description_width': 'initial'})\n", 205 | "numbers" 206 | ] 207 | }, 208 | { 209 | "cell_type": "markdown", 210 | "metadata": {}, 211 | "source": [ 212 | "## 4 Put the pieces together\n", 213 | "\n", 214 | "Use your improved toggles and number checkbox to re-do the password generator interface from exercise 2, above.\n", 215 | "\n", 216 | "When you are done it should look like this:\n", 217 | "\n", 218 | "![part of password generator](images/05-container-exercises-ex4.png)\n" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": null, 224 | "metadata": { 225 | "collapsed": true, 226 | "jupyter": { 227 | "outputs_hidden": true 228 | } 229 | }, 230 | "outputs": [], 231 | "source": [ 232 | "# %load solutions/password-ex4.py\n" 233 | ] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "metadata": {}, 238 | "source": [ 239 | "### Alignment of children: additional (optional) challenges\n", 240 | "\n", 241 | "Using only `layout` attributes make the widget above:\n", 242 | "\n", 243 | "+ Display the children in reverse order (**not** by just reversing the list of children).\n", 244 | "+ Display the children vertically instead of horzontally." 245 | ] 246 | } 247 | ], 248 | "metadata": { 249 | "kernelspec": { 250 | "display_name": "widgets-tutorial", 251 | "language": "python", 252 | "name": "widgets-tutorial" 253 | }, 254 | "language_info": { 255 | "codemirror_mode": { 256 | "name": "ipython", 257 | "version": 3 258 | }, 259 | "file_extension": ".py", 260 | "mimetype": "text/x-python", 261 | "name": "python", 262 | "nbconvert_exporter": "python", 263 | "pygments_lexer": "ipython3", 264 | "version": "3.8.10" 265 | } 266 | }, 267 | "nbformat": 4, 268 | "nbformat_minor": 4 269 | } 270 | -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/images/05-container-exercises-ex4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/04.Widget_styling/images/05-container-exercises-ex4.png -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/images/container-exercises-special-chars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/04.Widget_styling/images/container-exercises-special-chars.png -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/images/container-exercises1-final-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/04.Widget_styling/images/container-exercises1-final-layout.png -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/images/container-exercises2-final-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/04.Widget_styling/images/container-exercises2-final-layout.png -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/images/flexbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/04.Widget_styling/images/flexbox.png -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/images/pass-gen-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/04.Widget_styling/images/pass-gen-demo.gif -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/solutions/applayout-no-sides.py: -------------------------------------------------------------------------------- 1 | AppLayout(header=header_button, 2 | left_sidebar=None, 3 | center=center_button, 4 | right_sidebar=None, 5 | footer=footer_button) 6 | -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/solutions/password-ex2.py: -------------------------------------------------------------------------------- 1 | numbers = widgets.Checkbox(description='Include numbers in password') 2 | words = widgets.Label('The generated password is:') 3 | toggles = widgets.ToggleButtons(description='Type of special characters to include', 4 | options=[',./;[', '!@#~%', '^&*()'], 5 | style={'description_width': 'initial'}) 6 | vbox = widgets.VBox() 7 | 8 | # The border is set here just to make it easier to see the position of 9 | # the children with respect to the box. 10 | vbox.layout.border = '2px solid grey' 11 | vbox.layout.height = '250px' 12 | 13 | # Added lines: 14 | vbox.layout.justify_content = 'space-around' 15 | vbox.layout.align_items = 'flex-start' 16 | # Don't forget to add the children... 17 | vbox.children = [words, toggles, numbers] 18 | 19 | vbox 20 | -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/solutions/password-ex3i.py: -------------------------------------------------------------------------------- 1 | vbox_left_layout = widgets.Layout(align_items='flex-start') 2 | 3 | label = widgets.Label('Choose special characters to include') 4 | toggles = widgets.ToggleButtons(description='', 5 | options=[',./;[', '!@#~%', '^&*()'], 6 | style={'description_width': 'initial'}) 7 | 8 | # Set the margins to control the indentation. 9 | # The order is top right bottom left 10 | toggles.layout.margin = '0 0 0 20px' 11 | 12 | better_toggles = widgets.VBox([label, toggles]) 13 | better_toggles.layout = vbox_left_layout 14 | better_toggles 15 | -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/solutions/password-ex3ii.py: -------------------------------------------------------------------------------- 1 | numbers = widgets.Checkbox(description='Include numbers in password', 2 | style={'description_width': 'initial'}) 3 | numbers 4 | -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/solutions/password-ex4.py: -------------------------------------------------------------------------------- 1 | words = widgets.Label('The generated password is:') 2 | 3 | vbox = widgets.VBox() 4 | 5 | # The border is set here just to make it easier to see the position of 6 | # the children with respect to the box. 7 | vbox.layout.border = '2px solid grey' 8 | vbox.layout.height = '250px' 9 | 10 | # Added lines: 11 | vbox.layout.justify_content = 'space-around' 12 | vbox.layout.align_items = 'flex-start' 13 | # Don't forget to add the children... 14 | vbox.children = [words, better_toggles, numbers] 15 | 16 | vbox 17 | -------------------------------------------------------------------------------- /notebooks/04.Widget_styling/solutions/slider-bqplot-sliders-app.py: -------------------------------------------------------------------------------- 1 | container = AppLayout(header=header_button, 2 | left_sidebar=left_button, 3 | center=fig, 4 | right_sidebar=None, 5 | footer=None) 6 | container 7 | -------------------------------------------------------------------------------- /notebooks/05.Build-complex-libs/05.00-Build-complex-libraries.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "f8da2924-07bb-4e1e-9372-6ff2685a5312", 6 | "metadata": {}, 7 | "source": [ 8 | "# Building complex widget libraries" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "e31c1960-d4db-4681-8cbf-5d88b82c5e50", 14 | "metadata": { 15 | "tags": [] 16 | }, 17 | "source": [ 18 | "## The problem\n", 19 | "Often when composing widgets into an application, it becomes cumbersome to find a good way of managing state and keeping track of variables as the application grows. This notebooks aims to provide gentle guidance for managing complex application libraries. Please bear in mind these are merely **suggestions** and are by no means the only, or even best, way of going about this." 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "id": "82d1eef5-52fe-4b37-8c80-cb812cbd4324", 25 | "metadata": {}, 26 | "source": [ 27 | "### The current approach" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "id": "5a7f24bc-4df9-481a-a5c3-380485d5ff35", 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "# Generating data\n", 38 | "import numpy as np\n", 39 | "import pandas as pd\n", 40 | "\n", 41 | "np.random.seed(0)\n", 42 | "p_t, n = 100, 260\n", 43 | "stock_df = pd.DataFrame({f'Stock {i}': p_t + np.round(np.random.standard_normal(n).cumsum(), 2) for i in range(10)})" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "id": "488c6461-0e3c-4234-ad4c-f03ef6115996", 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "# Imports\n", 54 | "from bqplot import LinearScale, Axis, Figure, Lines, CATEGORY10\n", 55 | "from ipywidgets import HBox, VBox, Layout, HTML\n", 56 | "from ipydatagrid import DataGrid\n", 57 | " \n", 58 | "# Setting up the data grid\n", 59 | "stock_grid = DataGrid(stock_df, selection_mode='column')\n", 60 | "\n", 61 | "# Creating the bqplot chart objects\n", 62 | "sc_x = LinearScale()\n", 63 | "sc_y = LinearScale()\n", 64 | "line = Lines(x=[], y=[], labels=['Fake stock price'], display_legend=True,\n", 65 | " scales={'x': sc_x, 'y': sc_y})\n", 66 | "ax_x = Axis(scale=sc_x, label='Index')\n", 67 | "ax_y = Axis(scale=sc_y, orientation='vertical', label='y-value')\n", 68 | "fig = Figure(marks=[line], axes=[ax_x, ax_y], title='Line Chart', layout=Layout(flex='1 1 auto', width='100%'))\n", 69 | "\n", 70 | "# Creating the application title\n", 71 | "app_title = HTML(value=\"

My complex application

Select a column to plot it

\")" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": null, 77 | "id": "4d84c7b3-451c-4c88-b59e-4d0ae3d578e6", 78 | "metadata": {}, 79 | "outputs": [], 80 | "source": [ 81 | "# Define callbacks\n", 82 | "def plot_stock(*args):\n", 83 | " line.y = stock_grid.selected_cell_values\n", 84 | " line.x = range(len(line.y))\n", 85 | " column_index = stock_grid.selections[0]['c1']\n", 86 | " line.labels = [stock_df.columns[column_index]]\n", 87 | " line.colors = [CATEGORY10[np.random.randint(0, len(CATEGORY10)) % len(CATEGORY10)]]\n", 88 | " \n", 89 | "# Event listener for cell click\n", 90 | "stock_grid.observe(plot_stock, names='selections')" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "id": "79e135e3-b37c-457b-a268-3695a3def263", 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "VBox([\n", 101 | " app_title,\n", 102 | " HBox(\n", 103 | " [stock_grid, fig]\n", 104 | " )\n", 105 | "])" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "id": "5c635ec7-c85e-425b-ab5e-f3e4ad937a37", 111 | "metadata": {}, 112 | "source": [ 113 | "### A more structured approach" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "id": "47f6bfa5-b044-4da2-bac8-d3066ac1532d", 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "class Chart:\n", 124 | " def __init__(self, figure_title='Line Chart'):\n", 125 | " self._sc_x = LinearScale()\n", 126 | " self._sc_y = LinearScale()\n", 127 | " self._line = Lines(x=[], y=[], labels=['Fake stock price'], display_legend=True,\n", 128 | " scales={'x': self._sc_x, 'y': self._sc_y})\n", 129 | " self._ax_x = Axis(scale=self._sc_x, label='Index')\n", 130 | " self._ax_y = Axis(scale=self._sc_y, orientation='vertical', label='y-value')\n", 131 | " self._fig = Figure(marks=[self._line], axes=[self._ax_x, self._ax_y], title=figure_title, layout=Layout(flex='1 1 auto', width='100%'))\n", 132 | " \n", 133 | " def get_figure(self):\n", 134 | " return self._fig\n", 135 | " \n", 136 | " def get_line(self):\n", 137 | " return self._line\n", 138 | " \n", 139 | " def set_line(self, x, y, labels, colors):\n", 140 | " self._line.x = x\n", 141 | " self._line.y = y\n", 142 | " self._line.labels = labels\n", 143 | " self._line.colors = colors" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "id": "edaf116c-d873-4a24-ba8c-9536de90e4cc", 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [ 153 | "from IPython.display import display\n", 154 | "\n", 155 | "class MyApplication:\n", 156 | " def __init__(self, data, application_title='My complex application'):\n", 157 | " self.dataframe = data\n", 158 | " self.datagrid = self.process_data(data)\n", 159 | " self.chart = Chart()\n", 160 | " self.app_title = HTML(value=f\"

{application_title}

Select a column to plot it

\")\n", 161 | " self.run_application()\n", 162 | " \n", 163 | " def process_data(self, dataframe):\n", 164 | " return DataGrid(dataframe, selection_mode='column')\n", 165 | " \n", 166 | " def generate_layout(self):\n", 167 | " return VBox([self.app_title, HBox([self.datagrid, self.chart.get_figure()])])\n", 168 | " \n", 169 | " def setup_event_handlers(self):\n", 170 | " self.datagrid.observe(self.plot_stock, names='selections')\n", 171 | " \n", 172 | " def run_application(self):\n", 173 | " self.setup_event_handlers()\n", 174 | " display(self.generate_layout())\n", 175 | " \n", 176 | " # Callbacks section\n", 177 | " def plot_stock(self, *args):\n", 178 | " column_index = self.datagrid.selections[0]['c1']\n", 179 | " line = self.chart.get_line()\n", 180 | " selected_values = self.datagrid.selected_cell_values\n", 181 | " self.chart.set_line(\n", 182 | " range(len(selected_values)), \n", 183 | " selected_values, \n", 184 | " [self.dataframe.columns[column_index]], \n", 185 | " [CATEGORY10[np.random.randint(0, len(CATEGORY10)) % len(CATEGORY10)]]\n", 186 | " )" 187 | ] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": null, 192 | "id": "05fdd2fa-ecac-4b1e-add4-362a8814f491", 193 | "metadata": {}, 194 | "outputs": [], 195 | "source": [ 196 | "app = MyApplication(data=stock_df, application_title=\"An alternative approach\")" 197 | ] 198 | } 199 | ], 200 | "metadata": { 201 | "kernelspec": { 202 | "display_name": "widgets-tutorial", 203 | "language": "python", 204 | "name": "widgets-tutorial" 205 | }, 206 | "language_info": { 207 | "codemirror_mode": { 208 | "name": "ipython", 209 | "version": 3 210 | }, 211 | "file_extension": ".py", 212 | "mimetype": "text/x-python", 213 | "name": "python", 214 | "nbconvert_exporter": "python", 215 | "pygments_lexer": "ipython3", 216 | "version": "3.9.13" 217 | } 218 | }, 219 | "nbformat": 4, 220 | "nbformat_minor": 5 221 | } 222 | -------------------------------------------------------------------------------- /notebooks/05.Build-complex-libs/05.01-Refactoring.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "413439cf-360f-435b-acfe-4b1cc9042773", 6 | "metadata": {}, 7 | "source": [ 8 | "# Refactoring the class-based implementation" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": null, 14 | "id": "36cd43c4-9df2-40cf-a4f0-66e660af7035", 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "# Generating data\n", 19 | "import numpy as np\n", 20 | "import pandas as pd\n", 21 | "\n", 22 | "np.random.seed(0)\n", 23 | "p_t, n = 100, 260\n", 24 | "stock_df = pd.DataFrame({f'Stock {i}': p_t + np.round(np.random.standard_normal(n).cumsum(), 2) for i in range(10)})" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "id": "bde0b4dd-96bf-441c-a7b1-3e4ab37cd7c5", 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "from my_application import MyApplication" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "id": "e04c0045-f815-4c7b-a0a4-dee9c004275a", 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "app = MyApplication(stock_df)" 45 | ] 46 | } 47 | ], 48 | "metadata": { 49 | "kernelspec": { 50 | "display_name": "widgets-tutorial", 51 | "language": "python", 52 | "name": "widgets-tutorial" 53 | }, 54 | "language_info": { 55 | "codemirror_mode": { 56 | "name": "ipython", 57 | "version": 3 58 | }, 59 | "file_extension": ".py", 60 | "mimetype": "text/x-python", 61 | "name": "python", 62 | "nbconvert_exporter": "python", 63 | "pygments_lexer": "ipython3", 64 | "version": "3.9.13" 65 | } 66 | }, 67 | "nbformat": 4, 68 | "nbformat_minor": 5 69 | } 70 | -------------------------------------------------------------------------------- /notebooks/05.Build-complex-libs/my_application/__init__.py: -------------------------------------------------------------------------------- 1 | from .application import MyApplication 2 | 3 | __all__ = ["MyApplication"] -------------------------------------------------------------------------------- /notebooks/05.Build-complex-libs/my_application/application.py: -------------------------------------------------------------------------------- 1 | from IPython.display import display 2 | from ipywidgets import HBox, VBox, HTML 3 | from ipydatagrid import DataGrid 4 | from bqplot import CATEGORY10 5 | from .chart import Chart 6 | import numpy as np 7 | 8 | class MyApplication: 9 | def __init__(self, data, application_title='My complex application'): 10 | self.dataframe = data 11 | self.datagrid = self.process_data(data) 12 | self.chart = Chart() 13 | self.app_title = HTML(value=f"

{application_title}

Select a column to plot it

") 14 | self.run_application() 15 | 16 | def process_data(self, dataframe): 17 | return DataGrid(dataframe, selection_mode='column') 18 | 19 | def generate_layout(self): 20 | return VBox([self.app_title, HBox([self.datagrid, self.chart.get_figure()])]) 21 | 22 | def setup_event_handlers(self): 23 | self.datagrid.observe(self.plot_stock, names='selections') 24 | 25 | def run_application(self): 26 | self.setup_event_handlers() 27 | display(self.generate_layout()) 28 | 29 | # Callbacks section 30 | def plot_stock(self, *args): 31 | column_index = self.datagrid.selections[0]['c1'] 32 | line = self.chart.get_line() 33 | selected_values = self.datagrid.selected_cell_values 34 | self.chart.set_line( 35 | range(len(selected_values)), 36 | selected_values, 37 | [self.dataframe.columns[column_index]], 38 | [CATEGORY10[np.random.randint(0, len(CATEGORY10)) % len(CATEGORY10)]] 39 | ) -------------------------------------------------------------------------------- /notebooks/05.Build-complex-libs/my_application/chart.py: -------------------------------------------------------------------------------- 1 | from bqplot import LinearScale, Axis, Figure, Lines, CATEGORY10 2 | from ipywidgets import Layout 3 | 4 | class Chart: 5 | def __init__(self, figure_title='Line Chart'): 6 | self._sc_x = LinearScale() 7 | self._sc_y = LinearScale() 8 | self._line = Lines(x=[], y=[], labels=['Fake stock price'], display_legend=True, 9 | scales={'x': self._sc_x, 'y': self._sc_y}) 10 | self._ax_x = Axis(scale=self._sc_x, label='Index') 11 | self._ax_y = Axis(scale=self._sc_y, orientation='vertical', label='y-value') 12 | self._fig = Figure(marks=[self._line], axes=[self._ax_x, self._ax_y], title=figure_title, layout=Layout(flex='1 1 auto', width='100%')) 13 | 14 | def get_figure(self): 15 | return self._fig 16 | 17 | def get_line(self): 18 | return self._line 19 | 20 | def set_line(self, x, y, labels, colors): 21 | self._line.x = x 22 | self._line.y = y 23 | self._line.labels = labels 24 | self._line.colors = colors -------------------------------------------------------------------------------- /notebooks/06.More_widget_libraries/06.00-More_widget_libraries.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# More widget libraries" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "There are several widget libraries beyond the core `ipywidgets`. The libraries include plotting tools (bqplot and ipyvolume), an interface to three dimensional renderings (pythreejs), mapping (ipyleaflet) and more.\n", 15 | "\n", 16 | "### Author your own widgets library\n", 17 | "\n", 18 | " * [ipywidgets: Building a Custom Widget - Email Widget](https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Custom.html)\n", 19 | " * [medium: Authoring Custom Jupyter Widgets](https://blog.jupyter.org/authoring-custom-jupyter-widgets-2884a462e724)\n", 20 | " * cookiecutters:\n", 21 | " * https://github.com/jupyter-widgets/widget-cookiecutter\n", 22 | " * https://github.com/jupyter-widgets/widget-ts-cookiecutter\n", 23 | " \n", 24 | "### Widgets libraries presented in this tutorial\n", 25 | "\n", 26 | "- [ipycanvas](07.01-ipycanvas.ipynb)\n", 27 | "- [ipycytoscape](07.02-ipycytoscape.ipynb)\n", 28 | "- [ipydatagrid](07.03-ipydatagrid.ipynb)\n", 29 | "- [ipygany](07.04-ipygany.ipynb)\n", 30 | "- [ipympl](07.05-ipympl.ipynb)\n", 31 | "\n", 32 | "### Optional\n", 33 | "\n", 34 | "You can find more widgets libraries that are optional to this tutorial in the [widgets_libraries](widgets_libraries/) directory." 35 | ] 36 | } 37 | ], 38 | "metadata": { 39 | "kernelspec": { 40 | "display_name": "widgets-tutorial", 41 | "language": "python", 42 | "name": "widgets-tutorial" 43 | }, 44 | "language_info": { 45 | "codemirror_mode": { 46 | "name": "ipython", 47 | "version": 3 48 | }, 49 | "file_extension": ".py", 50 | "mimetype": "text/x-python", 51 | "name": "python", 52 | "nbconvert_exporter": "python", 53 | "pygments_lexer": "ipython3", 54 | "version": "3.9.13" 55 | } 56 | }, 57 | "nbformat": 4, 58 | "nbformat_minor": 4 59 | } 60 | -------------------------------------------------------------------------------- /notebooks/06.More_widget_libraries/06.04-ipygany.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "8cddcdc1", 6 | "metadata": {}, 7 | "source": [ 8 | "# ipygany: Interactive library for 3-D mesh analysis\n", 9 | "\n", 10 | "## A Jupyter - ParaView bridge\n", 11 | "\n", 12 | "## https://github.com/QuantStack/ipygany\n", 13 | "\n", 14 | "\n", 15 | "A widget enabling VTK loader, Structured and Unstructured grids, Animations, Point cloud visualization and a lot more for 3-D visualization\n", 16 | "\n", 17 | "- BSD-3-Clause License\n", 18 | "\n", 19 | "**Installation:**\n", 20 | "\n", 21 | "```bash\n", 22 | "conda install -c conda-forge ipygany\n", 23 | "```" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "id": "671e1c2d", 29 | "metadata": {}, 30 | "source": [ 31 | "### Features\n", 32 | "\n", 33 | "* Warp\n", 34 | "* IsoColor\n", 35 | "* Threshold" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": null, 41 | "id": "f32f3ff1", 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "# Preparing example's data\n", 46 | "from pyvista import examples\n", 47 | "import numpy as np\n", 48 | "\n", 49 | "from ipywidgets import VBox, FloatSlider\n", 50 | "from ipygany import PolyMesh, Scene, IsoColor, WarpByScalar\n", 51 | "\n", 52 | "pvmesh = examples.download_topo_global()\n", 53 | "ugrid = pvmesh.cast_to_unstructured_grid()" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "id": "e8ca6782", 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "from ipygany import PolyMesh, Scene, IsoColor, WarpByScalar" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "id": "940efad0", 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "# Turn the PyVista mesh into a PolyMesh\n", 74 | "mesh = PolyMesh.from_vtk(ugrid)" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "id": "3ab9fa2c", 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "# Configure color mesh\n", 85 | "colored_mesh = IsoColor(mesh, min=-10421.0, max=6527.0)\n", 86 | "warped_mesh = WarpByScalar(colored_mesh, input='altitude', factor=0.5e-5)" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "id": "d14a7a21", 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "# Link a slider to the warp value\n", 97 | "warp_slider = FloatSlider(min=0., max=5., value=0.5)\n", 98 | "\n", 99 | "def on_slider_change(change):\n", 100 | " warped_mesh.factor = change['new'] * 1e-5" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "id": "cba61a48", 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "warp_slider.observe(on_slider_change, 'value')\n", 111 | "\n", 112 | "VBox((warp_slider, Scene([warped_mesh])))" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": null, 118 | "id": "28d8ee32", 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "from ipygany import ColorBar, colormaps" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "id": "a66fd3e3", 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "bar = ColorBar(colored_mesh)\n", 133 | "bar" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "id": "6caa5c3f", 139 | "metadata": {}, 140 | "source": [ 141 | "* Blues\n", 142 | "* Cividis\n", 143 | "* Greys\n", 144 | "* CubehelixDefault\n", 145 | "* Inferno" 146 | ] 147 | } 148 | ], 149 | "metadata": { 150 | "kernelspec": { 151 | "display_name": "Python 3 (ipykernel)", 152 | "language": "python", 153 | "name": "python3" 154 | }, 155 | "language_info": { 156 | "codemirror_mode": { 157 | "name": "ipython", 158 | "version": 3 159 | }, 160 | "file_extension": ".py", 161 | "mimetype": "text/x-python", 162 | "name": "python", 163 | "nbconvert_exporter": "python", 164 | "pygments_lexer": "ipython3", 165 | "version": "3.9.13" 166 | } 167 | }, 168 | "nbformat": 4, 169 | "nbformat_minor": 5 170 | } 171 | -------------------------------------------------------------------------------- /notebooks/06.More_widget_libraries/extras/PyWWT.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## PyWWT - widget interface to the World Wide Telescope \n", 8 | "\n", 9 | "### https://github.com/WorldWideTelescope/pywwt\n", 10 | "\n", 11 | "World Wide Telescope (WWT) was developed by Microsoft for displaying images of the sky in a variety of projects and several layers; it is like `leaflet` for the sky. Now maintained by the American Astronomical Society (AAS), it now has a widget interface.\n", 12 | "\n", 13 | "A javascript API has been available for WWT for a while. The PyWWT package includes javascript to call that API with front ends for both ipywidgets and qt.\n", 14 | "\n", 15 | "### Installation\n", 16 | "\n", 17 | "`pywwt` is on PyPI and on the `wwt` conda channel." 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "from pywwt.jupyter import WWTJupyterWidget\n", 27 | "\n", 28 | "wwt = WWTJupyterWidget()\n", 29 | "wwt" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "Several properties of the display can eclipsing binary changed from Python" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "wwt.constellation_figures = True\n", 46 | "\n", 47 | "wwt.constellation_boundary_color = 'azure'\n", 48 | "wwt.constellation_figure_color = '#D3BC8D'\n", 49 | "wwt.constellation_selection_color = (1, 0, 1)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "In addition to interacting with the display with mouse/keyboard, you can manipulate it programmatically." 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "from astropy import units as u\n", 66 | "from astropy.coordinates import SkyCoord\n", 67 | "\n", 68 | "orion_neb = SkyCoord.from_name('Orion Nebula')\n", 69 | "wwt.center_on_coordinates(orion_neb, fov=10 * u.degree, instant=False)" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": {}, 75 | "source": [ 76 | "A variety of markers can be added to the display, and one can construct tours of the sky." 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "metadata": {}, 83 | "outputs": [], 84 | "source": [ 85 | "wwt.load_tour('http://www.worldwidetelescope.org/docs/wtml/tourone.wtt')" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": null, 91 | "metadata": {}, 92 | "outputs": [], 93 | "source": [ 94 | "wwt.pause_tour()" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "## Display a local image\n", 102 | "\n", 103 | "One goal this week is to wrap the widget below (which displays images stored in a format called FITS that is widely used in astronomy) up in a easily installable widgets. The widget will be demoed during the tutorial but is not yet installable. Code will be in https://github.com/eteq/astrowidgets" 104 | ] 105 | } 106 | ], 107 | "metadata": { 108 | "kernelspec": { 109 | "display_name": "widgets-tutorial", 110 | "language": "python", 111 | "name": "widgets-tutorial" 112 | }, 113 | "language_info": { 114 | "codemirror_mode": { 115 | "name": "ipython", 116 | "version": 3 117 | }, 118 | "file_extension": ".py", 119 | "mimetype": "text/x-python", 120 | "name": "python", 121 | "nbconvert_exporter": "python", 122 | "pygments_lexer": "ipython3", 123 | "version": "3.8.3" 124 | } 125 | }, 126 | "nbformat": 4, 127 | "nbformat_minor": 4 128 | } 129 | -------------------------------------------------------------------------------- /notebooks/06.More_widget_libraries/extras/extra_examples/solutions/bqplot-as-control/box-widget.py: -------------------------------------------------------------------------------- 1 | box = widgets.HBox(children=[amplitude_control, result]) 2 | box 3 | -------------------------------------------------------------------------------- /notebooks/06.More_widget_libraries/extras/extra_examples/solutions/bqplot-as-control/update_line.py: -------------------------------------------------------------------------------- 1 | def update_line(change): 2 | new_x, new_y = fourier_series(change['new']) 3 | line.x = new_x 4 | line.y = new_y 5 | -------------------------------------------------------------------------------- /notebooks/06.More_widget_libraries/extras/extra_examples/vaex.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Vaex - Out of core dataframes\n", 8 | " * vaex\n", 9 | " * https://github.com/vaexio/vaex\n", 10 | " * http://vaex.io\n", 11 | "\n", 12 | "Install from conda:\n", 13 | "\n", 14 | "```\n", 15 | "$ conda install -c conda-forge vaex\n", 16 | "```\n", 17 | "\n", 18 | "or pip\n", 19 | "```\n", 20 | "$ pip install vaex\n", 21 | "```\n", 22 | "\n", 23 | "Get some data at: https://docs.vaex.io/en/latest/datasets.html\n", 24 | "\n", 25 | "Or try out the remote dataframe server: http://dataframe.vaex.io/" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "import vaex" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "df = vaex.open('ws://dataframe.vaex.io/yellow_taxi_2009_2015_f32')\n", 44 | "df" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": null, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "# zoom in region (since this is a dirty dataset with many outliers)\n", 54 | "limits = [\n", 55 | " (-74.58162065531823, -72.98973106619209),\n", 56 | " (39.95576227783618, 41.56497466621446)\n", 57 | "]\n", 58 | "heatmap = df.widget.heatmap(df.pickup_longitude, df.pickup_latitude, transform='log', shape=400, limits=limits)\n", 59 | "heatmap" 60 | ] 61 | } 62 | ], 63 | "metadata": { 64 | "kernelspec": { 65 | "display_name": "widgets-tutorial", 66 | "language": "python", 67 | "name": "widgets-tutorial" 68 | }, 69 | "language_info": { 70 | "codemirror_mode": { 71 | "name": "ipython", 72 | "version": 3 73 | }, 74 | "file_extension": ".py", 75 | "mimetype": "text/x-python", 76 | "name": "python", 77 | "nbconvert_exporter": "python", 78 | "pygments_lexer": "ipython3", 79 | "version": "3.6.4" 80 | } 81 | }, 82 | "nbformat": 4, 83 | "nbformat_minor": 2 84 | } 85 | -------------------------------------------------------------------------------- /notebooks/06.More_widget_libraries/extras/ipympl.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# ipympl: The Matplotlib Jupyter Widget Backend\n", 8 | "\n", 9 | "## https://github.com/matplotlib/ipympl\n", 10 | "\n", 11 | "\n", 12 | "Enabling interaction with matplotlib charts in the Jupyter notebook and JupyterLab\n", 13 | "\n", 14 | "- BSD-3-Clause\n", 15 | "\n", 16 | "**Installation:**\n", 17 | "\n", 18 | "```bash\n", 19 | "conda install -c conda-forge ipympl\n", 20 | "```" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "Enabling the `widget` backend. This requires ipympl. ipympl can be install via pip or conda." 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "%matplotlib widget" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "import numpy as np\n", 46 | "import matplotlib.pyplot as plt\n", 47 | "from ipywidgets import VBox, FloatSlider" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "When using the `widget` backend from ipympl, fig.canvas is a proper Jupyter interactive widget, which can be embedded in Layout classes like HBox and Vbox.\n", 55 | "\n", 56 | "One can bound figure attributes to other widget values." 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "plt.ioff()\n", 66 | "plt.clf()\n", 67 | "\n", 68 | "slider = FloatSlider(\n", 69 | " value=1.0,\n", 70 | " min=0.02,\n", 71 | " max=2.0\n", 72 | ")\n", 73 | "\n", 74 | "fig1 = plt.figure(1)\n", 75 | "\n", 76 | "x1 = np.linspace(0, 20, 500)\n", 77 | "\n", 78 | "lines = plt.plot(x1, np.sin(slider.value * x1))\n", 79 | "\n", 80 | "def update_lines(change):\n", 81 | " lines[0].set_data(x1, np.sin(change.new * x1))\n", 82 | " fig1.canvas.draw()\n", 83 | " fig1.canvas.flush_events()\n", 84 | "\n", 85 | "slider.observe(update_lines, names='value')\n", 86 | "\n", 87 | "VBox([slider, fig1.canvas])" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "from mpl_toolkits.mplot3d import axes3d\n", 97 | "\n", 98 | "fig2 = plt.figure(2)\n", 99 | "ax = fig2.add_subplot(111, projection='3d')\n", 100 | "\n", 101 | "# Grab some test data.\n", 102 | "X, Y, Z = axes3d.get_test_data(0.05)\n", 103 | "\n", 104 | "# Plot a basic wireframe.\n", 105 | "ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)\n", 106 | "\n", 107 | "fig2.canvas" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": { 114 | "scrolled": false 115 | }, 116 | "outputs": [], 117 | "source": [ 118 | "np.random.seed(0)\n", 119 | "\n", 120 | "n_bins = 10\n", 121 | "x2 = np.random.randn(1000, 3)\n", 122 | "\n", 123 | "fig3, axes = plt.subplots(nrows=2, ncols=2)\n", 124 | "ax0, ax1, ax2, ax3 = axes.flatten()\n", 125 | "\n", 126 | "colors = ['red', 'tan', 'lime']\n", 127 | "ax0.hist(x2, n_bins, density=1, histtype='bar', color=colors, label=colors)\n", 128 | "ax0.legend(prop={'size': 10})\n", 129 | "ax0.set_title('bars with legend')\n", 130 | "\n", 131 | "ax1.hist(x2, n_bins, density=1, histtype='bar', stacked=True)\n", 132 | "ax1.set_title('stacked bar')\n", 133 | "\n", 134 | "ax2.hist(x2, n_bins, histtype='step', stacked=True, fill=False)\n", 135 | "ax2.set_title('stack step (unfilled)')\n", 136 | "\n", 137 | "# Make a multiple-histogram of data-sets with different length.\n", 138 | "x_multi = [np.random.randn(n) for n in [10000, 5000, 2000]]\n", 139 | "ax3.hist(x_multi, n_bins, histtype='bar')\n", 140 | "ax3.set_title('different sample sizes')\n", 141 | "\n", 142 | "fig3.tight_layout()\n", 143 | "fig3.canvas" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "metadata": {}, 150 | "outputs": [], 151 | "source": [] 152 | } 153 | ], 154 | "metadata": { 155 | "kernelspec": { 156 | "display_name": "widgets-tutorial", 157 | "language": "python", 158 | "name": "widgets-tutorial" 159 | }, 160 | "language_info": { 161 | "codemirror_mode": { 162 | "name": "ipython", 163 | "version": 3 164 | }, 165 | "file_extension": ".py", 166 | "mimetype": "text/x-python", 167 | "name": "python", 168 | "nbconvert_exporter": "python", 169 | "pygments_lexer": "ipython3", 170 | "version": "3.8.3" 171 | } 172 | }, 173 | "nbformat": 4, 174 | "nbformat_minor": 4 175 | } 176 | -------------------------------------------------------------------------------- /notebooks/06.More_widget_libraries/extras/ipytree.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# ipytree: Interactive tree view based on ipywidgets\n", 8 | "\n", 9 | "## https://github.com/QuantStack/ipytree/\n", 10 | "\n", 11 | "\n", 12 | "ipytree is a jupyter interactive widget library which provides a tree widget to the Jupyter notebook.\n", 13 | "\n", 14 | "- MIT Licensed\n", 15 | "\n", 16 | "**Installation:**\n", 17 | "\n", 18 | "```bash\n", 19 | "conda install -c conda-forge ipytree\n", 20 | "```" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "## Create a tree" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "from ipywidgets import Text, link\n", 37 | "from ipytree import Tree, Node" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "tree = Tree()\n", 47 | "tree.add_node(Node('node1'))\n", 48 | "\n", 49 | "node2 = Node('node2')\n", 50 | "tree.add_node(node2)\n", 51 | "\n", 52 | "tree" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": null, 58 | "metadata": {}, 59 | "outputs": [], 60 | "source": [ 61 | "node3 = Node('node3', disabled=True)\n", 62 | "node4 = Node('node4')\n", 63 | "node5 = Node('node5', [Node('1'), Node('2')])\n", 64 | "node2.add_node(node3)\n", 65 | "node2.add_node(node4)\n", 66 | "node2.add_node(node5)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "## Change node name" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "name = Text('node3')\n", 83 | "link((name, 'value'), (node3, 'name'))\n", 84 | "\n", 85 | "name" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": {}, 91 | "source": [ 92 | "## Change node icon" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": null, 98 | "metadata": {}, 99 | "outputs": [], 100 | "source": [ 101 | "node4.icon = 'archive'\n", 102 | "node4.icon_style = 'warning'" 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": {}, 108 | "source": [ 109 | "## Handle click" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": null, 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "def handle_click(event):\n", 119 | " event['owner'].name = 'I am selected!!!' if event['new'] else 'I am not selected...' \n", 120 | "\n", 121 | "node4.observe(handle_click, 'selected')\n", 122 | "node4.selected = True" 123 | ] 124 | } 125 | ], 126 | "metadata": { 127 | "kernelspec": { 128 | "display_name": "widgets-tutorial", 129 | "language": "python", 130 | "name": "widgets-tutorial" 131 | }, 132 | "language_info": { 133 | "codemirror_mode": { 134 | "name": "ipython", 135 | "version": 3 136 | }, 137 | "file_extension": ".py", 138 | "mimetype": "text/x-python", 139 | "name": "python", 140 | "nbconvert_exporter": "python", 141 | "pygments_lexer": "ipython3", 142 | "version": "3.7.3" 143 | } 144 | }, 145 | "nbformat": 4, 146 | "nbformat_minor": 2 147 | } 148 | -------------------------------------------------------------------------------- /notebooks/06.More_widget_libraries/extras/ipyvolume.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# ipyvolume: 3D plotting in the notebook\n", 8 | "\n", 9 | "## https://github.com/maartenbreddels/ipyvolume\n", 10 | "\n", 11 | "IPyvolume is a Python library to visualize 3d volumes and glyphs (e.g. 3d scatter plots), in the Jupyter notebook, with minimal configuration and effort. It is currently pre-1.0, so use at own risk. IPyvolume’s volshow is to 3d arrays what matplotlib’s imshow is to 2d arrays.\n", 12 | "\n", 13 | "- MIT Licensed\n", 14 | "\n", 15 | "By Maarten Breddels\n", 16 | "\n", 17 | "**Installation:**\n", 18 | "\n", 19 | "```bash\n", 20 | "conda install -c conda-forge ipyvolume\n", 21 | "```" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "# 3-D Scatter Plots" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "import ipyvolume as ipv\n", 38 | "import numpy as np\n", 39 | "import ipywidgets as widgets" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "x, y, z = np.random.random((3, 10000))\n", 49 | "ipv.figure()\n", 50 | "scatter = ipv.scatter(x, y, z, size=1, marker=\"sphere\")\n", 51 | "ipv.show()" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "ipv.save('standalone.html')" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "# Open the HTML file from the file browser" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "x, y, z, u, v, w = np.random.random((6, 1000))*2-1\n", 79 | "selected = np.random.randint(0, 1000, 100)\n", 80 | "fig = ipv.figure()\n", 81 | "quiver = ipv.quiver(x, y, z, u, v, w, size=5, size_selected=8, selected=selected)\n", 82 | "ipv.show()" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "size = widgets.FloatSlider(min=0, max=30, step=0.1)\n", 92 | "size_selected = widgets.FloatSlider(min=0, max=30, step=0.1)\n", 93 | "color = widgets.ColorPicker()\n", 94 | "color_selected = widgets.ColorPicker()\n", 95 | "widgets.jslink((quiver, 'size'), (size, 'value'))\n", 96 | "widgets.jslink((quiver, 'size_selected'), (size_selected, 'value'))\n", 97 | "widgets.jslink((quiver, 'color'), (color, 'value'))\n", 98 | "widgets.jslink((quiver, 'color_selected'), (color_selected, 'value'))\n", 99 | "widgets.VBox([size, size_selected, color, color_selected])" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "# Animations" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": null, 112 | "metadata": {}, 113 | "outputs": [], 114 | "source": [ 115 | "# create 2d grids: x, y, and r\n", 116 | "u = np.linspace(-10, 10, 25)\n", 117 | "x, y = np.meshgrid(u, u)\n", 118 | "r = np.sqrt(x**2+y**2)\n", 119 | "print(\"x,y and z are of shape\", x.shape)\n", 120 | "# and turn them into 1d\n", 121 | "x = x.flatten()\n", 122 | "y = y.flatten()\n", 123 | "r = r.flatten()\n", 124 | "print(\"and flattened of shape\", x.shape)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [ 133 | "# create a sequence of 15 time elements\n", 134 | "time = np.linspace(0, np.pi*2, 15)\n", 135 | "z = np.array([(np.cos(r + t) * np.exp(-r/5)) for t in time])\n", 136 | "print(\"z is of shape\", z.shape)" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "# draw the scatter plot, and add controls with animate_glyphs\n", 146 | "ipv.figure()\n", 147 | "s = ipv.scatter(x, z, y, marker=\"sphere\")\n", 148 | "ipv.animation_control(s, interval=200)\n", 149 | "ipv.ylim(-3,3)\n", 150 | "ipv.show()" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "metadata": {}, 157 | "outputs": [], 158 | "source": [ 159 | "# Now also include, color, which containts rgb values\n", 160 | "color = np.array([[np.cos(r + t), 1-np.abs(z[i]), 0.1+z[i]*0] for i, t in enumerate(time)])\n", 161 | "size = (z+1)\n", 162 | "print(\"color is of shape\", color.shape)" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": null, 168 | "metadata": {}, 169 | "outputs": [], 170 | "source": [ 171 | "color = np.transpose(color, (0, 2, 1)) # flip the last axes" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": null, 177 | "metadata": {}, 178 | "outputs": [], 179 | "source": [ 180 | "ipv.figure()\n", 181 | "s = ipv.scatter(x, z, y, color=color, size=size, marker=\"sphere\")\n", 182 | "ipv.animation_control(s, interval=200)\n", 183 | "ipv.ylim(-3, 3)\n", 184 | "ipv.show()" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": null, 190 | "metadata": {}, 191 | "outputs": [], 192 | "source": [] 193 | } 194 | ], 195 | "metadata": { 196 | "kernelspec": { 197 | "display_name": "widgets-tutorial", 198 | "language": "python", 199 | "name": "widgets-tutorial" 200 | }, 201 | "language_info": { 202 | "codemirror_mode": { 203 | "name": "ipython", 204 | "version": 3 205 | }, 206 | "file_extension": ".py", 207 | "mimetype": "text/x-python", 208 | "name": "python", 209 | "nbconvert_exporter": "python", 210 | "pygments_lexer": "ipython3", 211 | "version": "3.8.10" 212 | } 213 | }, 214 | "nbformat": 4, 215 | "nbformat_minor": 4 216 | } 217 | -------------------------------------------------------------------------------- /notebooks/06.More_widget_libraries/extras/ipywebrtc.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# ipywebrtc: Video streaming\n", 8 | "\n", 9 | "## https://github.com/maartenbreddels/ipywebrtc\n", 10 | "\n", 11 | "\n", 12 | "ipywebrtc is a jupyter interactive widget library which provides video streaming to the Jupyter notebook.\n", 13 | "\n", 14 | "- MIT Licensed\n", 15 | "\n", 16 | "It provides means to capture videos/images/audio from the user camera, or from other widgets like a `Video` or an ipyvolume plot.\n", 17 | "\n", 18 | "**Installation:**\n", 19 | "\n", 20 | "```bash\n", 21 | "conda install -c conda-forge ipywebrtc\n", 22 | "```" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": null, 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "import ipywebrtc as webrtc\n", 32 | "import ipyvolume as ipv\n", 33 | "import ipywidgets as widgets" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "video = webrtc.VideoStream.from_file('../Big.Buck.Bunny.mp4')" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "video" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "camera = webrtc.CameraStream()" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "camera" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "fig = ipv.figure(render_continuous=True)\n", 79 | "back = ipv.plot_plane(\"back\", texture=video)\n", 80 | "right = ipv.plot_plane(\"right\", texture=camera)\n", 81 | "ipv.show()" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [ 90 | "right.texture = fig" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "room = webrtc.chat(room='scipy2018', stream=fig)" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": null, 105 | "metadata": {}, 106 | "outputs": [], 107 | "source": [ 108 | "back.texture = room.streams[1]" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "recorder = webrtc.VideoRecorder(stream=fig, filename='record')\n", 118 | "recorder" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [ 127 | "recorder.video.value[:1000]" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "room.close()\n", 137 | "camera.close()\n", 138 | "video.close()" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": {}, 145 | "outputs": [], 146 | "source": [] 147 | } 148 | ], 149 | "metadata": { 150 | "kernelspec": { 151 | "display_name": "widgets-tutorial", 152 | "language": "python", 153 | "name": "widgets-tutorial" 154 | }, 155 | "language_info": { 156 | "codemirror_mode": { 157 | "name": "ipython", 158 | "version": 3 159 | }, 160 | "file_extension": ".py", 161 | "mimetype": "text/x-python", 162 | "name": "python", 163 | "nbconvert_exporter": "python", 164 | "pygments_lexer": "ipython3", 165 | "version": "3.8.3" 166 | } 167 | }, 168 | "nbformat": 4, 169 | "nbformat_minor": 2 170 | } 171 | -------------------------------------------------------------------------------- /notebooks/06.More_widget_libraries/extras/solutions/ipyvuetify/radio_group.py: -------------------------------------------------------------------------------- 1 | rg = v.RadioGroup(v_model=options[0], 2 | children=[v.Radio(label=k, value=k) for k in options], 3 | label="Pizza topping:") 4 | rg -------------------------------------------------------------------------------- /notebooks/06.More_widget_libraries/piston.vtu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/06.More_widget_libraries/piston.vtu -------------------------------------------------------------------------------- /notebooks/07.Widgets_outside_notebook_context/07.00-voila.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Dashboarding with widgets \n", 8 | "\n", 9 | "Using `voilà`, creating dashboards with widgets is much easier than it used to be." 10 | ] 11 | } 12 | ], 13 | "metadata": { 14 | "kernelspec": { 15 | "display_name": "widgets-tutorial", 16 | "language": "python", 17 | "name": "widgets-tutorial" 18 | }, 19 | "language_info": { 20 | "codemirror_mode": { 21 | "name": "ipython", 22 | "version": 3 23 | }, 24 | "file_extension": ".py", 25 | "mimetype": "text/x-python", 26 | "name": "python", 27 | "nbconvert_exporter": "python", 28 | "pygments_lexer": "ipython3", 29 | "version": "3.9.13" 30 | } 31 | }, 32 | "nbformat": 4, 33 | "nbformat_minor": 4 34 | } 35 | -------------------------------------------------------------------------------- /notebooks/07.Widgets_outside_notebook_context/07.01-voila-basics.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# So easy, *voilà*!\n", 8 | "\n", 9 | "In this example notebook, we demonstrate how voila can render Jupyter notebooks with interactions requiring a roundtrip to the kernel." 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "## Jupyter Widgets" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import ipywidgets as widgets\n", 26 | "\n", 27 | "slider = widgets.FloatSlider(description='x', value=4)\n", 28 | "text = widgets.FloatText(disabled=True, description='$x^2$')\n", 29 | "\n", 30 | "def compute(*ignore):\n", 31 | " text.value = str(slider.value ** 2)\n", 32 | "\n", 33 | "slider.observe(compute, 'value')\n", 34 | "\n", 35 | "widgets.VBox([slider, text])" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "## Basic outputs of code cells" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": { 49 | "tags": [ 50 | "skip" 51 | ] 52 | }, 53 | "outputs": [], 54 | "source": [ 55 | "import pandas as pd\n", 56 | "\n", 57 | "iris = pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv')\n", 58 | "iris" 59 | ] 60 | } 61 | ], 62 | "metadata": { 63 | "celltoolbar": "Tags", 64 | "kernelspec": { 65 | "display_name": "widgets-tutorial", 66 | "language": "python", 67 | "name": "widgets-tutorial" 68 | }, 69 | "language_info": { 70 | "codemirror_mode": { 71 | "name": "ipython", 72 | "version": 3 73 | }, 74 | "file_extension": ".py", 75 | "mimetype": "text/x-python", 76 | "name": "python", 77 | "nbconvert_exporter": "python", 78 | "pygments_lexer": "ipython3", 79 | "version": "3.9.13" 80 | }, 81 | "widgets": { 82 | "application/vnd.jupyter.widget-state+json": { 83 | "state": {}, 84 | "version_major": 2, 85 | "version_minor": 0 86 | } 87 | } 88 | }, 89 | "nbformat": 4, 90 | "nbformat_minor": 4 91 | } 92 | -------------------------------------------------------------------------------- /notebooks/07.Widgets_outside_notebook_context/07.02-voila-vuetify.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "\n", 11 | "import ipyvuetify as v\n", 12 | "import ipywidgets as widgets\n", 13 | "\n", 14 | "from bqplot import pyplot as plt\n", 15 | "import bqplot" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "# Deploying voilà\n", 23 | "\n", 24 | "## First histogram plot: bqplot" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "n = 200\n", 34 | "\n", 35 | "x = np.linspace(0.0, 10.0, n)\n", 36 | "y = np.cumsum(np.random.randn(n)*10).astype(int)\n", 37 | "\n", 38 | "fig = plt.figure( title='Histogram')\n", 39 | "np.random.seed(0)\n", 40 | "hist = plt.hist(y, bins=25)\n", 41 | "fig" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": null, 47 | "metadata": {}, 48 | "outputs": [], 49 | "source": [ 50 | "slider = v.Slider(thumb_label='always', class_=\"px-4\", v_model=30)\n", 51 | "widgets.link((slider, 'v_model'), (hist, 'bins'))\n", 52 | "slider" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "# Line chart" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "fig2 = plt.figure( title='Line Chart')\n", 69 | "np.random.seed(0)\n", 70 | "p = plt.plot(x, y)\n", 71 | "fig2" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": null, 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "selector = bqplot.interacts.BrushIntervalSelector(scale=p.scales['x'])\n", 81 | "\n", 82 | "def update_range(*args):\n", 83 | " if selector.selected is not None and selector.selected.shape == (2,):\n", 84 | " mask = (x > selector.selected[0]) & (x < selector.selected[1])\n", 85 | " hist.sample = y[mask]\n", 86 | " \n", 87 | "selector.observe(update_range, 'selected')\n", 88 | "fig2.interaction = selector" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "# Set up voila vuetify layout\n", 96 | "The voila vuetify template does not render output from the notebook, it only shows widget with the mount_id metadata." 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": { 103 | "scrolled": true 104 | }, 105 | "outputs": [], 106 | "source": [ 107 | "fig.layout.width = 'auto'\n", 108 | "fig.layout.height = 'auto'\n", 109 | "fig.layout.min_height = '300px' # so it still shows nicely in the notebook\n", 110 | "\n", 111 | "fig2.layout.width = 'auto'\n", 112 | "fig2.layout.height = 'auto'\n", 113 | "fig2.layout.min_height = '300px' # so it still shows nicely in the notebook\n", 114 | "\n", 115 | "\n", 116 | "content_main = v.Tabs(_metadata={'mount_id': 'content-main'}, children=[\n", 117 | " v.Tab(children=['Tab1']),\n", 118 | " v.Tab(children=['Tab2']),\n", 119 | " v.TabItem(children=[\n", 120 | " v.Layout(row=True, wrap=True, align_center=True, children=[\n", 121 | " v.Flex(xs12=True, lg6=True, xl4=True, children=[\n", 122 | " fig, slider\n", 123 | " ]),\n", 124 | " v.Flex(xs12=True, lg6=True, xl4=True, children=[\n", 125 | " fig2\n", 126 | " ]),\n", 127 | " ])\n", 128 | " ]),\n", 129 | " v.TabItem(children=[\n", 130 | " v.Container(children=['Lorum ipsum'])\n", 131 | " ])\n", 132 | "])\n", 133 | "# no need to display content_main for the voila-vuetify template\n", 134 | "# but might be useful for debugging\n", 135 | "# content_main" 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "metadata": {}, 141 | "source": [ 142 | "# Running locally with voila\n", 143 | "```\n", 144 | "$ voila --template vuetify-default --enable_nbextensions=True ./notebooks/07.02-voila-vuetify.ipynb\n", 145 | "```\n", 146 | "\n", 147 | "# Run it on mybinder\n", 148 | " * Put it on a github repo, e.g. https://github.com/maartenbreddels/voila-demo\n", 149 | " * Add a noteook, e.g. voila-vuetify.ipynb\n", 150 | " * Make sure the kernelspec name is vanilla \"python3\" (modify this in the classical notebook under Edit->Edit Notebook Metadata)\n", 151 | " * put in a requirements.txt, with at least voila and voila-vuetify\n", 152 | " * Create a mybinder on https://ovh.mybinder.org/\n", 153 | " * GitHub URL: e.g. https://github.com/maartenbreddels/voila-demo/\n", 154 | " * Change 'File' to 'URL'\n", 155 | " * URL to open: e.g. `voila/render/voila-vuetify.ipynb`\n", 156 | " " 157 | ] 158 | } 159 | ], 160 | "metadata": { 161 | "kernelspec": { 162 | "display_name": "widgets-tutorial", 163 | "language": "python", 164 | "name": "widgets-tutorial" 165 | }, 166 | "language_info": { 167 | "codemirror_mode": { 168 | "name": "ipython", 169 | "version": 3 170 | }, 171 | "file_extension": ".py", 172 | "mimetype": "text/x-python", 173 | "name": "python", 174 | "nbconvert_exporter": "python", 175 | "pygments_lexer": "ipython3", 176 | "version": "3.9.13" 177 | } 178 | }, 179 | "nbformat": 4, 180 | "nbformat_minor": 4 181 | } 182 | -------------------------------------------------------------------------------- /notebooks/07.Widgets_outside_notebook_context/07.03-jupyterlite.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "d5cdbafb-2160-4a2b-a6d6-ac69e54d613e", 6 | "metadata": {}, 7 | "source": [ 8 | "

\n", 9 | " \n", 10 | "

" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "id": "8fb76af9-a3f0-4421-a266-f4f65c4d84af", 16 | "metadata": {}, 17 | "source": [ 18 | "



" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "id": "298eada0-8213-43ea-a2c9-8eac84cf92e3", 24 | "metadata": {}, 25 | "source": [ 26 | "# Lightweight Jupyter Frontend running in the browser\n", 27 | "\n", 28 | "https://jupyterlite.github.io/demo/lab/index.html\n", 29 | "\n", 30 | "### ✅ no server\n", 31 | "\n", 32 | "### ✅ no command line\n", 33 | "\n", 34 | "### ✅ no need to install Python and other packages locally" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "id": "897c4537-6d44-447c-a000-298f393ec295", 40 | "metadata": {}, 41 | "source": [ 42 | "














" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "id": "b6ec8f63-789e-40bd-bc7d-69776dffe5b7", 48 | "metadata": {}, 49 | "source": [ 50 | "## Boots in seconds\n", 51 | "\n", 52 | "Useful to quickly try something out: https://jupyterlite.github.io/demo/lab/index.html.\n", 53 | "\n", 54 | "

\n", 55 | " \n", 56 | "

" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "id": "38c5d85c-d15c-451b-a418-068ef30c847e", 62 | "metadata": {}, 63 | "source": [ 64 | "














" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "id": "e3dcf01a-9ab0-4cc5-bdad-50ccf058fa3d", 70 | "metadata": {}, 71 | "source": [ 72 | "# Create your own in minutes! ⏲️\n", 73 | "\n", 74 | "## The JupyterLite Demo repo: https://github.com/jupyterlite/demo\n", 75 | "\n", 76 | "![](https://user-images.githubusercontent.com/21197331/125816904-5768008a-77de-4cb3-8013-f3999b135c02.gif)" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "id": "c6e0c255-c73c-4385-a481-9c38fa66b56a", 82 | "metadata": {}, 83 | "source": [ 84 | "














" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "id": "6631e409-04c5-4d4c-bbb9-537c26657477", 90 | "metadata": {}, 91 | "source": [ 92 | "# Standing on the shoulders of giants 💁‍♂️\n", 93 | "\n", 94 | "## ✳️ Built from the ground-up using JupyterLab components and extensions\n", 95 | "\n", 96 | "## ✳️ The frontend communicates to the in-browser kernels via the Jupyter Protocol\n", 97 | "\n", 98 | "![lite-ecosystem](https://user-images.githubusercontent.com/591645/162748538-e44fea00-f727-4055-b795-8f5fc5c6b133.png)" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "id": "56e8c83a-352a-4cf5-879c-2d2c01976d34", 104 | "metadata": {}, 105 | "source": [ 106 | "














" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "id": "c5de27a2-80dc-4814-a7ac-290f9e4ab552", 112 | "metadata": {}, 113 | "source": [ 114 | "# Working with files\n", 115 | "\n", 116 | "## ✳️ A deployment can provide a set of files and notebooks available by default \n", 117 | "## ✳️ Combine Offline Notebook storage in browser (`localStorage` or `IndexedDB`) with server files\n", 118 | "## ✳️ Read `IndexedDB` from Python" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "id": "37a43870-6e41-4d7f-9520-119924d40791", 124 | "metadata": {}, 125 | "source": [ 126 | "














" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "id": "4292e49c-df57-42c5-97b2-faa500ac329a", 132 | "metadata": {}, 133 | "source": [ 134 | "# Share Files Easily\n", 135 | "\n", 136 | "

\n", 137 | " \n", 138 | "

" 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "id": "90c23bce-4e96-4d6a-86b6-2dd490ed1edf", 144 | "metadata": {}, 145 | "source": [ 146 | "














" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "id": "c96b8fac-76d6-40c0-a96d-70193e0c1870", 152 | "metadata": {}, 153 | "source": [ 154 | "# Real Time Collaboration via WebRTC\n", 155 | "\n", 156 | "## PyData Global 2021 Presentation by Kevin Jahns: https://www.youtube.com/watch?v=QtoJsK8QYNk\n", 157 | "\n", 158 | "\n", 159 | "

\n", 160 | " \n", 161 | "

" 162 | ] 163 | }, 164 | { 165 | "cell_type": "markdown", 166 | "id": "832ce472-82d6-454a-aed7-6cc55cd702d5", 167 | "metadata": {}, 168 | "source": [ 169 | "














" 170 | ] 171 | }, 172 | { 173 | "cell_type": "markdown", 174 | "id": "599f87b0-fe45-4820-8a18-611b17fcc650", 175 | "metadata": {}, 176 | "source": [ 177 | "# Limitations\n", 178 | "\n", 179 | "## ❇️ Suited for lighter workloads\n", 180 | "\n", 181 | "## ❇️ `micropip` / `piplite` to install packages\n", 182 | "\n", 183 | "- Pure Python wheels for now (up until Pyodide 0.19 at least)\n", 184 | "- This would be improved by the on-going work by leveraging the conda forge tooling. \n", 185 | "\n", 186 | "## ❇️ Accessing files from the Python kernel\n", 187 | "\n", 188 | "## ❇️ Initial asset download can be big (~30MB), then cached by the browser" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "id": "27d2211c-e64a-4bbf-b410-4396a5c289e4", 194 | "metadata": {}, 195 | "source": [ 196 | "< [So easy, *voilà*!](08.01-voila-basics.ipynb) | [Contents](00.00-index.ipynb) |" 197 | ] 198 | } 199 | ], 200 | "metadata": { 201 | "kernelspec": { 202 | "display_name": "widgets-tutorial", 203 | "language": "python", 204 | "name": "widgets-tutorial" 205 | }, 206 | "language_info": { 207 | "codemirror_mode": { 208 | "name": "ipython", 209 | "version": 3 210 | }, 211 | "file_extension": ".py", 212 | "mimetype": "text/x-python", 213 | "name": "python", 214 | "nbconvert_exporter": "python", 215 | "pygments_lexer": "ipython3", 216 | "version": "3.9.13" 217 | } 218 | }, 219 | "nbformat": 4, 220 | "nbformat_minor": 5 221 | } 222 | -------------------------------------------------------------------------------- /notebooks/Big.Buck.Bunny.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/Big.Buck.Bunny.mp4 -------------------------------------------------------------------------------- /notebooks/Flow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
You
[Not supported by viewer]
Code
[Not supported by viewer]
Jupyter
Notebook
[Not supported by viewer]
Kernel
[Not supported by viewer]
Execute
[Not supported by viewer]
Output
[Not supported by viewer]
Display
[Not supported by viewer]
-------------------------------------------------------------------------------- /notebooks/README.md: -------------------------------------------------------------------------------- 1 | Notebooks for tutorial on Jupyter widgets at SciPy. 2 | 3 | Start at [index.ipynb](00.00-index.ipynb). 4 | -------------------------------------------------------------------------------- /notebooks/images/Binary_Star_Sim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/images/Binary_Star_Sim.png -------------------------------------------------------------------------------- /notebooks/images/WidgetArch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/images/WidgetArch.png -------------------------------------------------------------------------------- /notebooks/images/covid-dash1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/images/covid-dash1.png -------------------------------------------------------------------------------- /notebooks/images/covid-dash2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/images/covid-dash2.png -------------------------------------------------------------------------------- /notebooks/images/plot-as-control.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/images/plot-as-control.gif -------------------------------------------------------------------------------- /notebooks/invalid_keypress.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/invalid_keypress.mp3 -------------------------------------------------------------------------------- /notebooks/reference_guides/images/align-content.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | flex-start 8 | 9 | center 10 | 11 | space-between 12 | 13 | stretch 14 | 15 | space-around 16 | 17 | flex-end 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /notebooks/reference_guides/images/align-items.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | flex-start 12 | 13 | 14 | 15 | 16 | 17 | center 18 | 19 | 20 | 21 | 22 | 23 | baseline 24 | 25 | 26 | 27 | 28 | 29 | stretch 30 | 31 | 32 | 33 | 34 | 35 | flex-end 36 | text text 37 | text text 38 | text text 39 | text text 40 | 41 | 42 | -------------------------------------------------------------------------------- /notebooks/reference_guides/images/align-self.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | flex-end 12 | flex-start 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /notebooks/reference_guides/images/flex-container.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | container 19 | 20 | -------------------------------------------------------------------------------- /notebooks/reference_guides/images/flex-direction1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /notebooks/reference_guides/images/flex-grow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 1 11 | 1 12 | 1 13 | 14 | 15 | 16 | 17 | 1 18 | 2 19 | 1 20 | 21 | -------------------------------------------------------------------------------- /notebooks/reference_guides/images/flex-items.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | items 35 | 36 | -------------------------------------------------------------------------------- /notebooks/reference_guides/images/flex-wrap.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /notebooks/reference_guides/images/flexbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/reference_guides/images/flexbox.png -------------------------------------------------------------------------------- /notebooks/reference_guides/images/grid-area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/reference_guides/images/grid-area.png -------------------------------------------------------------------------------- /notebooks/reference_guides/images/grid-cell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/reference_guides/images/grid-cell.png -------------------------------------------------------------------------------- /notebooks/reference_guides/images/grid-justify-self-center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/reference_guides/images/grid-justify-self-center.png -------------------------------------------------------------------------------- /notebooks/reference_guides/images/grid-justify-self-end.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/reference_guides/images/grid-justify-self-end.png -------------------------------------------------------------------------------- /notebooks/reference_guides/images/grid-justify-self-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/reference_guides/images/grid-justify-self-start.png -------------------------------------------------------------------------------- /notebooks/reference_guides/images/grid-justify-self-stretch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/reference_guides/images/grid-justify-self-stretch.png -------------------------------------------------------------------------------- /notebooks/reference_guides/images/grid-line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/reference_guides/images/grid-line.png -------------------------------------------------------------------------------- /notebooks/reference_guides/images/grid-start-end-a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/reference_guides/images/grid-start-end-a.png -------------------------------------------------------------------------------- /notebooks/reference_guides/images/grid-start-end-b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/reference_guides/images/grid-start-end-b.png -------------------------------------------------------------------------------- /notebooks/reference_guides/images/grid-start-end-d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/reference_guides/images/grid-start-end-d.png -------------------------------------------------------------------------------- /notebooks/reference_guides/images/grid-track.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/tutorial/95da869df06b890bd79a7e569672dd8a2725ce01/notebooks/reference_guides/images/grid-track.png -------------------------------------------------------------------------------- /notebooks/reference_guides/images/justify-content.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | flex-start 15 | flex-end 16 | 17 | 18 | 19 | 20 | center 21 | 22 | 23 | 24 | 25 | space-between 26 | 27 | 28 | 29 | 30 | space-around 31 | 32 | -------------------------------------------------------------------------------- /notebooks/reference_guides/images/order-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 1 19 | -1 20 | 1 21 | 2 22 | 5 23 | 1 24 | 1 25 | 2 26 | 3 27 | 28 | 29 | 30 | 2 31 | 2 32 | 99 33 | 34 | -------------------------------------------------------------------------------- /notebooks/solutions/intro/intro-example.py: -------------------------------------------------------------------------------- 1 | widgets.interact(f, x=(-10, 10)) 2 | -------------------------------------------------------------------------------- /outline.md: -------------------------------------------------------------------------------- 1 | # Outline 2 | 3 | 1. Start at 0:00 Introduction and Jupyter Lab overview (5 min) (Matt) 4 | 2. Start 0:05 Widget overview: (Youness) 5 | - Overview/introduction (15 min) 6 | - @interact notebook and example (5 min) 7 | - Interactive plots with mpl-interactions (5 min) 8 | - Widget basics (10 min) 9 | - Widget List and exercise (15 min) 10 | - Output widget (5 min) 11 | - Break (10 min) 12 | 3. Start 1:10 Widget events (Ian) 13 | - Events: observe, link, traitlets (20 min) 14 | - Exercise (10 min) 15 | - Break (5 min) 16 | 4. Start 1:45 Widget styling overview: (Matt) 17 | - Widget layout concepts (10 min) 18 | - Widget layout examples/exercises (10 min) 19 | - Break (5 min) 20 | 5. Start 2:10 More about how to build complex libraries (Itay) 21 | - Considerations when designing more complex widget applications (15 min) 22 | 6. Start 2:25 More widget libraries (Mariana, Itay) 23 | - High level presentation of multiple interactive widgets libraries (ipcanvas, ipycytoscape, ipydatagrid, ipygany, etc) (50 min) 24 | - Break (10 min) 25 | 7. Start 3:30 Widgets outside the context of the notebook (Mariana) 26 | - Voila, jupyterlite (15 min) 27 | 8. Start 3:40 ipywidgets 8 (Itay) 28 | - New features (5 min) 29 | - Highlights of transition (5 min) 30 | 9. Starts 3:50 Flexible/Q&A time (Everyone) 31 | 32 | ## Software versions 33 | 34 | Core: 35 | 36 | + jupyterlab 3 37 | + ipywidgets 7.6+ 38 | 39 | New this year: 40 | 41 | - ipycytoscape 42 | - ipydatagrid 43 | -------------------------------------------------------------------------------- /postBuild: -------------------------------------------------------------------------------- 1 | ipython kernel install --name widgets-tutorial --display-name widgets-tutorial --sys-prefix 2 | -------------------------------------------------------------------------------- /repl/jupyter-lite.json: -------------------------------------------------------------------------------- 1 | { 2 | "jupyter-lite-schema-version": 0, 3 | "jupyter-config-data": { 4 | "disabledExtensions": [ 5 | "jupyterlab-kernel-spy", 6 | "@jupyterlite/javascript-kernel-extension" 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | bqplot==0.12.33 2 | branca==0.5.0 3 | ipycanvas==0.12.0 4 | ipycytoscape==1.3.3 5 | ipydatagrid==1.1.12 6 | ipyevents==2.0.1 7 | ipygany==0.5.0 8 | ipyleaflet==0.16.0 9 | ipympl==0.9.1 10 | ipytree==0.2.1 11 | ipyvolume==0.6.0a8 12 | ipyvuetify==1.8.2 13 | ipywidgets==7.7.1 14 | jupyterlab==3.6.7 15 | mpl-interactions==0.22.0 16 | numpy 17 | orjson==3.9.15 18 | pandas 19 | pythreejs==2.3.0 20 | pyvista==0.34.0 21 | requests 22 | scikit-image 23 | scipy 24 | sidecar==0.5.1 25 | voila==0.3.8 26 | vtk==9.1.0 27 | 28 | jupyterlab-language-pack-fr-FR 29 | jupyterlab-language-pack-zh-CN 30 | jupyterlab_miami_nights 31 | jupyterlite==0.1.0b11 32 | theme-darcula 33 | jupyterlab_github 34 | -------------------------------------------------------------------------------- /test_kernel_name.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from tools.kernel_names import get_kernel_name 4 | 5 | TUTORIAL_KERNEL_NAME = 'widget-tutorial' 6 | NOTEBOOK_DIRECTORY = Path(__file__) / '..' / 'notebooks' 7 | 8 | 9 | def test_kernel_name(): 10 | notebooks = NOTEBOOK_DIRECTORY.glob('**/*.ipynb') 11 | for notebook in notebooks: 12 | kernel_name = get_kernel_name(str(notebook)) 13 | assert kernel_name == TUTORIAL_KERNEL_NAME 14 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # Tools for managing a collection of notebooks 2 | 3 | ## Credit and license 4 | 5 | Some of these tools (and all of the idea for them) is from [Jake VanerPlas](https://jakevdp.github.io/)'s [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook). 6 | 7 | The files `add_navigation.py` and `generate_contents.py` are modified from the same files in the Handbook, whose code license, a copy of which is in [licenses/LICENSE-CODE](licenses/LICENSE-CODE). 8 | -------------------------------------------------------------------------------- /tools/add_navigation.py: -------------------------------------------------------------------------------- 1 | import os 2 | import itertools 3 | 4 | import nbformat 5 | from nbformat.v4.nbbase import new_markdown_cell 6 | 7 | from generate_contents import (NOTEBOOK_DIR, REG, 8 | iter_notebooks, get_notebook_title, 9 | is_title) 10 | 11 | 12 | def prev_this_next(it): 13 | a, b, c = itertools.tee(it, 3) 14 | next(c) 15 | return zip(itertools.chain([None], a), b, itertools.chain(c, [None])) 16 | 17 | 18 | PREV_TEMPLATE = "< [{title}]({url}) " 19 | CONTENTS = "| [Contents](00.00-index.ipynb) |" 20 | NEXT_TEMPLATE = " [{title}]({url}) >" 21 | NAV_COMMENT = "\n" 22 | 23 | 24 | def iter_navbars(): 25 | for prev_nb, nb, next_nb in prev_this_next(iter_notebooks(NOTEBOOK_DIR)): 26 | navbar = NAV_COMMENT 27 | if prev_nb: 28 | navbar += PREV_TEMPLATE.format(title=get_notebook_title(prev_nb), 29 | url=prev_nb) 30 | navbar += CONTENTS 31 | if next_nb: 32 | navbar += NEXT_TEMPLATE.format(title=get_notebook_title(next_nb), 33 | url=next_nb) 34 | yield os.path.join(NOTEBOOK_DIR, nb), navbar 35 | 36 | 37 | def write_navbars(): 38 | for nb_name, navbar in iter_navbars(): 39 | nb = nbformat.read(nb_name, as_version=4) 40 | nb_file = os.path.basename(nb_name) 41 | is_comment = lambda cell: cell.source.startswith(NAV_COMMENT) 42 | 43 | for idx, cell in enumerate(nb.cells): 44 | if is_comment(cell): 45 | print("- amending navbar for {0}".format(nb_file)) 46 | cell.source = navbar 47 | break 48 | elif is_title(cell): 49 | print("- inserting navbar for {0}".format(nb_file)) 50 | nb.cells.insert(idx, new_markdown_cell(source=navbar)) 51 | break 52 | 53 | if is_comment(nb.cells[-1]): 54 | nb.cells[-1].source = navbar 55 | else: 56 | nb.cells.append(new_markdown_cell(source=navbar)) 57 | nbformat.write(nb, nb_name) 58 | 59 | 60 | if __name__ == '__main__': 61 | write_navbars() 62 | -------------------------------------------------------------------------------- /tools/check_load_and_images.py: -------------------------------------------------------------------------------- 1 | """ 2 | Iterate through and execute all notebooks 3 | """ 4 | from collections import defaultdict 5 | from pathlib import Path 6 | import random 7 | import re 8 | 9 | from execute_notebooks import print_exceptions 10 | 11 | 12 | def main(): 13 | p = Path('.') 14 | notebook_paths = p.glob('**/*.ipynb') 15 | patterns = dict( 16 | loads=re.compile(r'%load ([^\s]+\.py)'), 17 | images=re.compile(r'\!\[.+\]\((.*images/[^\s]+(?:png|jpg|jpeg|svg))\)') 18 | ) 19 | exceptions = defaultdict(list) 20 | 21 | for nb_path in notebook_paths: 22 | if '.ipynb_checkpoints' in str(nb_path): 23 | continue 24 | print(nb_path) 25 | with open(nb_path) as f: 26 | contents = f.read() 27 | for name, pattern in patterns.items(): 28 | print(f'Checking {name}...') 29 | matches = pattern.findall(contents) 30 | for match in matches: 31 | print(f'\t{match}') 32 | p = nb_path.parent / match 33 | try: 34 | with open(p) as _: 35 | pass 36 | except FileNotFoundError as e: 37 | exceptions[str(nb_path)].append(str(e)) 38 | 39 | print_exceptions(exceptions) 40 | 41 | 42 | if __name__ == '__main__': 43 | main() 44 | -------------------------------------------------------------------------------- /tools/dependency_finder.py: -------------------------------------------------------------------------------- 1 | import re 2 | import itertools 3 | from pathlib import Path 4 | 5 | import nbformat 6 | from stdlib_list import stdlib_list 7 | 8 | VALID_PACKAGE_CHARS = '[a-zA-Z0-9_]' 9 | REG = re.compile(f'^\s*import ({VALID_PACKAGE_CHARS}+)|^\s*from ({VALID_PACKAGE_CHARS}+)\b+\simport', re.ASCII) 10 | 11 | 12 | def import_statements(code_source): 13 | """ 14 | Find and return all lines in the code cells of a notebook that contain 15 | import statements. 16 | 17 | Parameters 18 | ---------- 19 | 20 | code_source : an nbformat NotebookNode object or str 21 | Notebook whose cells will be checked for import statements or the 22 | contents of a Python file (as a string). 23 | 24 | Returns 25 | ------- 26 | 27 | list 28 | List of strings, each an import statement from the notebook. 29 | """ 30 | if isinstance(code_source, str): 31 | import_code = [code_source] 32 | else: 33 | import_cells = [c for c in code_source['cells'] if 34 | c['cell_type'] == 'code' and 'import' in c['source']] 35 | import_code = [c['source'] for c in import_cells] 36 | 37 | imports = [line for code in import_code 38 | for line in code.split('\n') if REG.match(line)] 39 | 40 | return imports 41 | 42 | 43 | def dependency_names_from_import_statements(imports, unique=True): 44 | """ 45 | Extract the package names from a list of import statements. 46 | 47 | Parameters 48 | ---------- 49 | 50 | imports : list 51 | List of import statements from which package names will be extracted. 52 | 53 | unique : bool, optional 54 | If ``True``, return list of unique package names, otherwise return 55 | package names in same order as input. 56 | """ 57 | packages = [] 58 | for i in imports: 59 | imp = REG.search(i) 60 | for g in imp.groups(): 61 | if g is not None: 62 | packages.append(g) 63 | continue 64 | 65 | if unique: 66 | packages = list(set(packages)) 67 | 68 | return packages 69 | 70 | 71 | def bad_imports(imports, as_written=False): 72 | """ 73 | Try a bunch of imports and report whether each was successful. 74 | 75 | Parameters 76 | ---------- 77 | imports : list 78 | List of import statements (e.g. ``import numpy`` or 79 | ``from astropy import units``) to try. Leading whitespace in the 80 | imports list is fine; it will be stripped before trying the import. 81 | 82 | as_written : bool, optional 83 | If ``True``, test the import statements exactly as they are passed in. 84 | Otherwise, just test the package name (i.e. the top-level import). 85 | 86 | Returns 87 | ------- 88 | 89 | list 90 | List of bool, ``True`` if the import fails, ``False`` if it succeeds. 91 | """ 92 | 93 | result = [] 94 | for imp in imports: 95 | if as_written: 96 | test = imp.strip() 97 | else: 98 | test = dependency_names_from_import_statements([imp])[0] 99 | test = 'import ' + test 100 | try: 101 | exec(test) 102 | except ModuleNotFoundError: 103 | result.append(True) 104 | else: 105 | result.append(False) 106 | 107 | return result 108 | 109 | 110 | def identify_dependencies(directory, nb_version=4, 111 | exclude_hidden=True, skip=None, 112 | notebooks=True, python_files=True, 113 | verbose=False): 114 | """ 115 | Find all notebooks in or below a directory, grab their import 116 | statements, and translate that to a list of dependencies. 117 | 118 | Parameters 119 | ---------- 120 | 121 | directory : str 122 | Path to directory to be searched for notebook. All subdirectories of 123 | this path will be searched. 124 | 125 | nb_version : int, optional 126 | Notebook version to assume when reading notebooks. 127 | 128 | exclude_hidden : bool, optional 129 | Exclude hidden directories or files (i.e. those whose name begins 130 | with ``.``). 131 | 132 | skip : list of str, optional 133 | List of notebook or directory names to skip. If a directory name is 134 | part of the list then all notebooks below that directory will be 135 | skipped. The name must match exactly to cause a skip. 136 | 137 | notebooks : bool, optional 138 | If ``True``, check for imports in notebooks. 139 | 140 | python_files : bool, optional 141 | If ``True``, check for imports in python files (i.e. files that 142 | end ``.py``). 143 | 144 | verbose: bool, optional 145 | If ``True``, print summary of progress while working. 146 | """ 147 | p = Path(directory) 148 | 149 | notebook_paths = p.glob('**/*.ipynb') if notebooks else [] 150 | python_paths = p.glob('**/*.py') if python_files else [] 151 | 152 | dep_info = { 153 | 'path': [], 154 | 'imports': [], 155 | 'packages': [], 156 | 'missing': [], 157 | } 158 | 159 | for path in itertools.chain(notebook_paths, python_paths): 160 | # Skip any directories or files that start with a dot... 161 | hidden = [part.startswith('.') and part != '..' for part in path.parts] 162 | 163 | skips = any(part in skip for part in path.parts) if skip else False 164 | 165 | if any(hidden) or skips: 166 | if verbose: 167 | print(f'...Skipping {path}', path.parts[-1]) 168 | continue 169 | 170 | if path.suffix == '.ipynb': 171 | nbnode = nbformat.read(str(path), nb_version) 172 | else: 173 | with path.open() as f: 174 | nbnode = f.read() 175 | imports = import_statements(nbnode) 176 | 177 | bads = bad_imports(imports) 178 | any_bad = any(bads) 179 | deps = dependency_names_from_import_statements(imports, unique=False) 180 | 181 | # print(f'Checked file { path } found { "SOME" if any_bad else "no" } bad imports') 182 | if any_bad: 183 | bad_list = [p for p, b in zip(deps, bads) if b] 184 | if verbose: 185 | print(f' Missing packages: {bad_list}') 186 | 187 | dep_info['path'].extend([str(path)] * len(imports)) 188 | dep_info['imports'].extend(imports) 189 | dep_info['packages'].extend(deps) 190 | dep_info['missing'].extend(bads) 191 | 192 | return dep_info 193 | 194 | 195 | def packages_to_install(dep_info, exclude=None): 196 | """ 197 | Produce a list of packages that need to be installed to use this set of 198 | materials. Python standard library modules are excluded. 199 | 200 | Parameters 201 | ---------- 202 | 203 | dep_info : dict 204 | Dictionary of dependency information, generated by 205 | `identify_dependencies`. 206 | 207 | exclude : list, optional 208 | List of packages to exclude from the results. 209 | 210 | Returns 211 | ------- 212 | 213 | list 214 | List of packages needed for whatever set of files this was run on. 215 | """ 216 | 217 | if exclude is None: 218 | exclude = [] 219 | 220 | packages = list(set(dep_info['packages'])) 221 | standard = stdlib_list("3.6") 222 | 223 | packages = [p for p in packages if p not in standard and p not in exclude] 224 | return packages 225 | 226 | 227 | if __name__ == '__main__': 228 | # Some day add options... 229 | directory = '.' 230 | dep_info = identify_dependencies(directory, skip=['setup.py']) 231 | to_install = packages_to_install(dep_info) 232 | print(' '.join(to_install)) 233 | -------------------------------------------------------------------------------- /tools/execute_notebooks.py: -------------------------------------------------------------------------------- 1 | """ 2 | Iterate through and execute all notebooks 3 | """ 4 | from collections import defaultdict 5 | from pathlib import Path 6 | import random 7 | 8 | import nbformat as nbf 9 | from nbconvert.preprocessors import ExecutePreprocessor 10 | 11 | 12 | def print_exceptions(exceptions): 13 | if exceptions: 14 | print('⚠️' * 20) 15 | print('\n\nFailures in these notebooks:') 16 | print('\n'.join(exceptions.keys()), '\n\n') 17 | print('⚠️' * 20) 18 | for nb, error_list in exceptions.items(): 19 | face = random.choice(["😮", "😱", "🤬"]) 20 | print(face * 20) 21 | errors = "\n".join(error_list) 22 | print(f'{nb}: \n{errors}') 23 | print(face * 20) 24 | else: 25 | print('🎉🎉 Everything worked! 🎉🎉') 26 | 27 | 28 | def main(): 29 | p = Path('.') 30 | notebook_paths = p.glob('**/*.ipynb') 31 | exceptions = defaultdict(list) 32 | for nb_path in notebook_paths: 33 | if '.ipynb_checkpoints' in str(nb_path): 34 | continue 35 | print(nb_path) 36 | notebook = nbf.read(nb_path, 4) 37 | try: 38 | ep = ExecutePreprocessor() 39 | print('About to execute') 40 | ep.preprocess(notebook) 41 | print('Done executing') 42 | except Exception as e: 43 | exceptions[str(nb_path)].append(str(e)) 44 | print_exceptions(exceptions) 45 | 46 | 47 | 48 | if __name__ == '__main__': 49 | main() 50 | -------------------------------------------------------------------------------- /tools/generate_contents.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | from argparse import ArgumentParser 4 | from pathlib import Path 5 | 6 | import nbformat 7 | 8 | NOTEBOOK_DIR = os.path.join(os.path.dirname(__file__), '..', 'notebooks') 9 | 10 | NBVERSION = 4 11 | 12 | REG = re.compile(r'.*(\d\d)\.(\d\d)-(.*)\.ipynb') 13 | 14 | NO_NUMBER = ['00'] 15 | 16 | TOC_STYLES = ['header_ulist', 'nested_list'] 17 | 18 | 19 | def iter_notebooks(directory): 20 | p = Path(directory) 21 | notebooks = p.glob('**/*.ipynb') 22 | notebooks = sorted(notebooks, key=str) 23 | notebooks = [nb for nb in notebooks if not '.ipynb_checkpoints' in str(nb)] 24 | return notebooks 25 | 26 | 27 | def is_title(cell): 28 | return cell.source.startswith('# ') 29 | 30 | 31 | def get_notebook_title(nb_file): 32 | nb = nbformat.read(nb_file, 33 | as_version=NBVERSION) 34 | for cell in nb.cells: 35 | if is_title(cell): 36 | return cell.source[1:].splitlines()[0].strip() 37 | else: 38 | # Apparently there was no heading, raise an error 39 | raise ValueError(f'No title found for {nb_file}.') 40 | 41 | 42 | def gen_contents(directory=None, path_prefix=None, 43 | auto_number=False, toc_style=None): 44 | 45 | current_chapter = -1 46 | root = Path(directory) 47 | for nb in iter_notebooks(directory): 48 | if path_prefix: 49 | nb_url = os.path.join(path_prefix, str(nb)) 50 | else: 51 | nb_url = str(nb.relative_to(root)) 52 | 53 | matches = REG.match(str(nb)) 54 | if matches: 55 | chapter, section, title = matches.groups() 56 | else: 57 | continue 58 | 59 | # Generate auto chapter and section numbers even if 60 | # we do not end up using them 61 | if chapter != current_chapter: 62 | current_chapter = chapter 63 | current_section = 1 64 | else: 65 | current_section += 1 66 | 67 | title = get_notebook_title(nb) 68 | 69 | # No spaces allowed in URLs... 70 | nb_url = nb_url.replace(' ', '%20') 71 | if section == '00': 72 | if toc_style == 'nested_list': 73 | yield f'{chapter}. [{title}]({nb_url})' 74 | else: 75 | if chapter in NO_NUMBER: 76 | yield '\n### [{0}]({1})'.format(title, nb_url) 77 | else: 78 | yield '\n### [{0}. {1}]({2})'.format(int(chapter), 79 | title, nb_url) 80 | else: 81 | if toc_style == 'nested_list': 82 | yield f' {section}. [{title}]({nb_url})' 83 | else: 84 | yield "- [{0}]({1})".format(title, nb_url) 85 | 86 | 87 | def contents(directory=None, **kwd): 88 | """ 89 | Generate the table of contents as a string. 90 | """ 91 | return '\n'.join(gen_contents(directory, **kwd)) 92 | 93 | 94 | def write_notebook(toc, filename, toc_name="Table of Contents"): 95 | notebook = nbformat.v4.new_notebook() 96 | toc_cell_contents = '\n'.join(['# {}'.format(toc_name), toc]) 97 | toc_cell = nbformat.v4.new_markdown_cell(toc_cell_contents) 98 | notebook['cells'] = [toc_cell] 99 | nbformat.write(notebook, filename) 100 | 101 | 102 | if __name__ == '__main__': 103 | parser = ArgumentParser(description='Generate a table of contents from a ' 104 | 'directory of notebooks. The notebooks ' 105 | 'should be named so that when sorted ' 106 | 'by name they are in the order they ' 107 | 'should appear in the table of contents.') 108 | parser.add_argument('-o', '--output', 109 | help='Destination for the table of contents. If the ' 110 | 'extension is .ipynb then the output is a ' 111 | 'Jupyter notebook, otherwise the content is ' 112 | 'plain markdown. Default is to print to stdout.') 113 | parser.add_argument('-d', '--directory', 114 | help='Directory containing the notebooks from which ' 115 | 'to generate contents. Default value is ' 116 | '{}'.format(NOTEBOOK_DIR)) 117 | parser.add_argument('-p', '--path-prefix', 118 | help='If set, this string will be prepended to the ' 119 | 'links to the notebook in the generated table ' 120 | 'of contents.') 121 | parser.add_argument('--auto-number', action='store_true', 122 | help='Automatically number table of contents entries.') 123 | parser.add_argument('--toc-style', choices=TOC_STYLES, 124 | default='nested_list', 125 | help='Set the style for the table of contents.') 126 | args = parser.parse_args() 127 | directory = args.directory or NOTEBOOK_DIR 128 | toc = contents(directory, path_prefix=args.path_prefix, 129 | auto_number=args.auto_number, 130 | toc_style=args.toc_style) 131 | if not args.output: 132 | print(toc) 133 | elif args.output.endswith('.ipynb'): 134 | write_notebook(toc, args.output) 135 | else: 136 | with open(args.output, 'w') as f: 137 | f.write(toc) 138 | print('\n', 70 * '#', '\n') 139 | -------------------------------------------------------------------------------- /tools/kernel_names.py: -------------------------------------------------------------------------------- 1 | from argparse import ArgumentParser 2 | from pathlib import Path 3 | 4 | import nbformat 5 | 6 | NB_VERSION = 4 7 | 8 | 9 | def change_kernel_name(notebook_name, kernel_name, display_name=None): 10 | """ 11 | Change the name of the notebook kernel. 12 | """ 13 | dname = display_name if display_name else kernel_name 14 | notebook = nbformat.read(notebook_name, NB_VERSION) 15 | current_kname = notebook['metadata']['kernelspec']['name'] 16 | current_dname = notebook['metadata']['kernelspec']['display_name'] 17 | if current_kname == kernel_name and current_dname == dname: 18 | print('not changing kernel of {}'.format(notebook_name)) 19 | return 20 | 21 | notebook['metadata']['kernelspec']['name'] = kernel_name 22 | notebook['metadata']['kernelspec']['display_name'] = dname 23 | # print('\nHad this been a real operation, would have changed {}'.format(notebook_name)) 24 | # print('\t\tcurrent: {} to new: {}\n'.format(current_kname, kernel_name)) 25 | nbformat.write(notebook, notebook_name) 26 | 27 | 28 | def get_kernel_name(notebook_name): 29 | """ 30 | Return the name of the kernel in the notebook. 31 | """ 32 | notebook = nbformat.read(notebook_name, NB_VERSION) 33 | kname = notebook['metadata']['kernelspec']['name'] 34 | return kname 35 | 36 | 37 | if __name__ == '__main__': 38 | parser = ArgumentParser(description='Get or set kernel names for all ' 39 | 'notebooks in a directory.') 40 | parser.add_argument('-d', '--directory', default='.', 41 | help='Directory in which to look for notebooks.') 42 | parser.add_argument('-s', '--set', 43 | dest='kernel_name', 44 | metavar='kernel_name', 45 | help="Set the kernel to this name for each notebook.") 46 | parser.add_argument('--display-name', 47 | help="Display name of the kernel (default is same as " 48 | "kernel name).") 49 | 50 | args = parser.parse_args() 51 | 52 | directory = args.directory if args.directory else '.' 53 | p = Path(directory) 54 | notebooks = list(p.glob('**/*.ipynb')) 55 | 56 | if not notebooks: 57 | raise RuntimeError('No notebooks found at path {}'.format(directory)) 58 | 59 | for notebook in notebooks: 60 | nb_str = str(notebook) 61 | if '.ipynb_checkpoints' in nb_str: 62 | continue 63 | if args.kernel_name: 64 | change_kernel_name(nb_str, args.kernel_name, 65 | display_name=args.display_name) 66 | else: 67 | kname = get_kernel_name(nb_str) 68 | print(f'{nb_str}\n\t{kname}') 69 | -------------------------------------------------------------------------------- /tools/licenses/LICENSE-CODE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jacob VanderPlas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | --------------------------------------------------------------------------------