├── .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: `[](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 | [](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 | " | "
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 | "\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 | "\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 | "\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 | "
\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 | "\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 | "\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",
9 | " \n",
10 | "
\n",
55 | " \n",
56 | "
\n",
137 | " \n",
138 | "
\n",
160 | " \n",
161 | "