Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • grosch1/jupyter-dashboarding
  • petrova1/jupyter-dashboarding
2 results
Show changes
Commits on Source (6)
Showing
with 2537 additions and 266 deletions
# Jupyter Web Applications
Example web applications using voila and voila-vuetify
## Tutorials
For an in-depth tutorial of `ipywidgets`, please refer to the official [tutorial](https://github.com/jupyter-widgets/tutorial).
A summary can be found under [ipywidgets](./ipywidgets), which contains the bare minimum of information to get you started.
A tutorial for creating your own `ipyvuetify` dashboard can be found [here](./dashboards/vuetify-custom.ipynb).
## Example apps
You can find a collection of example either on the official [voila-gallery website](https://voila-gallery.org/) or in the subdirectory [voila-examples](./voila-examples).
Unless otherwise specified in the notebooks, you can start voila webapp over the command-line using `voila <your-notebook>`. For possible configuration options, use `voila --help`.
If using JupyterLab with `jupyterlab-preview` installed, you can render a preview of your app with the yellow-grey circle button in the notebook toolbar.
![JupyterLab preview](./images/jupyterlab_preview_pfeil.png "jupyterlab-preview button")
## The different ways to use Voila
The following two examples show how a standalone Jupyter notebook can be turned into a separate app, from the command-line integration.
### Rendering a notebook including interactive widgets and rich mime-type rendering
![Voila basics](./images/voila-basics.gif)
### Rendering a notebook making use of a custom widget library ([bqplot](https://github.com/bloomberg/bqplot
![Voila bqplot](./images/voila-bqplot.gif)
### Voilà dashboards with other language kernels
Voilà is built upon Jupyter standard formats and protocols, and is agnostic to the programming language of the notebook. In this example, we present an example of a Voilà application powered by the C++ Jupyter kernel [xeus-cling](https://github.com/QuantStack/xeus-cling), and the [xleaflet](https://github.com/QuantStack/xleaflet) project.
![Voila cling](./images/voila-cling.gif)
### To use the gridstack template, pass option --template=gridstack to the voila command line
![Voila gridstack](./images/voila-gridstack.gif)
### Using the voila-vuetify template with Jupyter widgets based on vuetify UI components which implement Google's Material Design Spec with the Vue.js framework
![Voila vuetify](./images/voila-vuetify.gif)
\ No newline at end of file
%% Cell type:code id: tags:
``` python
import ipywidgets as widgets
slider = widgets.FloatSlider(description='$x$', value=4)
text = widgets.FloatText(disabled=True, description='$x^2$')
def compute(*ignore):
text.value = str(slider.value ** 2)
slider.observe(compute, 'value')
widgets.VBox([slider, text])
```
%% Output
%% Cell type:code id: tags:
``` python
import pandas as pd
iris = pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv')
iris
```
%% Output
sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa
.. ... ... ... ... ...
145 6.7 3.0 5.2 2.3 virginica
146 6.3 2.5 5.0 1.9 virginica
147 6.5 3.0 5.2 2.0 virginica
148 6.2 3.4 5.4 2.3 virginica
149 5.9 3.0 5.1 1.8 virginica
[150 rows x 5 columns]
%% Cell type:code id: tags:
``` python
```
images/WidgetArch.png

22.5 KiB

images/jupyterlab_preview_pfeil.png

21.5 KiB

images/voila-basics.gif

3 MiB

images/voila-bqplot.gif

2.15 MiB

images/voila-cling.gif

3.83 MiB

images/voila-gridstack.gif

1.6 MiB

images/voila-vuetify.gif

2.79 MiB

%% Cell type:markdown id: tags:
# Widget List
%% Cell type:code id: tags:
``` python
import ipywidgets as widgets
```
%% Cell type:markdown id: tags:
## Numeric widgets
%% Cell type:markdown id: tags:
There are many widgets distributed with ipywidgets that are designed to display numeric values. Widgets exist for displaying integers and floats, both bounded and unbounded. The integer widgets share a similar naming scheme to their floating point counterparts. By replacing `Float` with `Int` in the widget name, you can find the Integer equivalent.
%% Cell type:markdown id: tags:
### IntSlider
%% Cell type:code id: tags:
``` python
widgets.IntSlider(
value=7,
min=0,
max=10,
step=1,
description='Test:',
disabled=False,
continuous_update=False,
orientation='horizontal',
readout=True,
readout_format='d'
)
```
%% Output
%% Cell type:markdown id: tags:
### FloatSlider
%% Cell type:code id: tags:
``` python
widgets.FloatSlider(
value=7.5,
min=0,
max=10.0,
step=0.1,
description='Test:',
disabled=False,
continuous_update=False,
orientation='horizontal',
readout=True,
readout_format='.1f',
)
```
%% Output
%% Cell type:markdown id: tags:
Sliders can also be **displayed vertically**.
%% Cell type:code id: tags:
``` python
widgets.FloatSlider(
value=7.5,
min=0,
max=10.0,
step=0.1,
description='Test:',
disabled=False,
continuous_update=False,
orientation='vertical',
readout=True,
readout_format='.1f',
)
```
%% Output
%% Cell type:markdown id: tags:
### FloatLogSlider
%% Cell type:markdown id: tags:
The `FloatLogSlider` has a log scale, which makes it easy to have a slider that covers a wide range of positive magnitudes. The `min` and `max` refer to the minimum and maximum exponents of the `base`, and the `value` refers to the actual value of the slider.
%% Cell type:code id: tags:
``` python
widgets.FloatLogSlider(
value=10,
base=10,
min=-10, # max exponent of base
max=10, # min exponent of base
step=0.2, # exponent step
description='Log Slider'
)
```
%% Output
%% Cell type:markdown id: tags:
### IntRangeSlider
%% Cell type:code id: tags:
``` python
widgets.IntRangeSlider(
value=[5, 7],
min=0,
max=10,
step=1,
description='Test:',
disabled=False,
continuous_update=False,
orientation='horizontal',
readout=True,
readout_format='d',
)
```
%% Output
%% Cell type:markdown id: tags:
### FloatRangeSlider
%% Cell type:code id: tags:
``` python
widgets.FloatRangeSlider(
value=[5, 7.5],
min=0,
max=10.0,
step=0.1,
description='Test:',
disabled=False,
continuous_update=False,
orientation='horizontal',
readout=True,
readout_format='.1f',
)
```
%% Output
%% Cell type:markdown id: tags:
### IntProgress
%% Cell type:code id: tags:
``` python
widgets.IntProgress(
value=7,
min=0,
max=10,
step=1,
description='Loading:',
bar_style='', # 'success', 'info', 'warning', 'danger' or ''
orientation='horizontal'
)
```
%% Output
%% Cell type:markdown id: tags:
### FloatProgress
%% Cell type:code id: tags:
``` python
widgets.FloatProgress(
value=7.5,
min=0,
max=10.0,
step=0.1,
description='Loading:',
bar_style='info',
orientation='horizontal'
)
```
%% Output
%% Cell type:markdown id: tags:
The numerical text boxes that impose some limit on the data (range, integer-only) impose that restriction when the user presses enter.
### BoundedIntText
%% Cell type:code id: tags:
``` python
widgets.BoundedIntText(
value=7,
min=0,
max=10,
step=1,
description='Text:',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
### BoundedFloatText
%% Cell type:code id: tags:
``` python
widgets.BoundedFloatText(
value=7.5,
min=0,
max=10.0,
step=0.1,
description='Text:',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
### IntText
%% Cell type:code id: tags:
``` python
widgets.IntText(
value=7,
description='Any:',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
### FloatText
%% Cell type:code id: tags:
``` python
widgets.FloatText(
value=7.5,
description='Any:',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
## Boolean widgets
%% Cell type:markdown id: tags:
There are three widgets that are designed to display a boolean value.
%% Cell type:markdown id: tags:
### ToggleButton
%% Cell type:code id: tags:
``` python
widgets.ToggleButton(
value=False,
description='Click me',
disabled=False,
button_style='', # 'success', 'info', 'warning', 'danger' or ''
tooltip='Description',
icon='check'
)
```
%% Output
%% Cell type:markdown id: tags:
### Checkbox
%% Cell type:code id: tags:
``` python
widgets.Checkbox(
value=False,
description='Check me',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
### Valid
The valid widget provides a read-only indicator.
%% Cell type:code id: tags:
``` python
widgets.Valid(
value=False,
description='Valid!',
)
```
%% Output
%% Cell type:markdown id: tags:
## Selection widgets
%% Cell type:markdown id: tags:
There are several widgets that can be used to display single selection lists, and two that can be used to select multiple values. All inherit from the same base class. You can specify the **enumeration of selectable options by passing a list** (options are either (label, value) pairs, or simply values for which the labels are derived by calling `str`).
%% Cell type:markdown id: tags:
### Dropdown
%% Cell type:code id: tags:
``` python
widgets.Dropdown(
options=['1', '2', '3'],
value='2',
description='Number:',
disabled=False,
)
```
%% Output
%% Cell type:markdown id: tags:
The following is also valid, displaying the words `'One', 'Two', 'Three'` as the dropdown choices but returning the values `1, 2, 3`.
%% Cell type:code id: tags:
``` python
widgets.Dropdown(
options=[('One', 1), ('Two', 2), ('Three', 3)],
value=2,
description='Number:',
)
```
%% Output
%% Cell type:markdown id: tags:
### RadioButtons
%% Cell type:code id: tags:
``` python
widgets.RadioButtons(
options=['pepperoni', 'pineapple', 'anchovies'],
# value='pineapple',
description='Pizza topping:',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
### Select
%% Cell type:code id: tags:
``` python
widgets.Select(
options=['Linux', 'Windows', 'OSX'],
value='OSX',
# rows=10,
description='OS:',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
### SelectionSlider
%% Cell type:code id: tags:
``` python
widgets.SelectionSlider(
options=['scrambled', 'sunny side up', 'poached', 'over easy'],
value='sunny side up',
description='I like my eggs ...',
disabled=False,
continuous_update=False,
orientation='horizontal',
readout=True
)
```
%% Output
%% Cell type:markdown id: tags:
### SelectionRangeSlider
The value, index, and label keys are 2-tuples of the min and max values selected. The options must be nonempty.
%% Cell type:code id: tags:
``` python
import datetime
dates = [datetime.date(2015,i,1) for i in range(1,13)]
options = [(i.strftime('%b'), i) for i in dates]
widgets.SelectionRangeSlider(
options=options,
index=(0,11),
description='Months (2015)',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
### ToggleButtons
%% Cell type:code id: tags:
``` python
widgets.ToggleButtons(
options=['Slow', 'Regular', 'Fast'],
description='Speed:',
disabled=False,
button_style='', # 'success', 'info', 'warning', 'danger' or ''
tooltips=['Description of slow', 'Description of regular', 'Description of fast'],
# icons=['check'] * 3
)
```
%% Output
%% Cell type:markdown id: tags:
### SelectMultiple
Multiple values can be selected with <kbd>shift</kbd> and/or <kbd>ctrl</kbd> (or <kbd>command</kbd>) pressed and mouse clicks or arrow keys.
%% Cell type:code id: tags:
``` python
widgets.SelectMultiple(
options=['Apples', 'Oranges', 'Pears'],
value=['Oranges'],
#rows=10,
description='Fruits',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
## String widgets
%% Cell type:markdown id: tags:
There are several widgets that can be used to display a string value. The `Text`, `Textarea`, and `Combobox` widgets accept input. The `HTML` and `HTMLMath` widgets display a string as HTML (`HTMLMath` also renders math). The `Label` widget can be used to construct a custom control label.
%% Cell type:markdown id: tags:
### Text
%% Cell type:code id: tags:
``` python
widgets.Text(
value='Hello World',
placeholder='Type something',
description='String:',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
### Textarea
%% Cell type:code id: tags:
``` python
widgets.Textarea(
value='Hello World',
placeholder='Type something',
description='String:',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
### Combobox
%% Cell type:code id: tags:
``` python
widgets.Combobox(
# value='John',
placeholder='Choose Someone',
options=['Paul', 'John', 'George', 'Ringo'],
description='Combobox:',
ensure_option=True,
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
### Label
The `Label` widget is useful if you need to build a custom description next to a control using similar styling to the built-in control descriptions.
%% Cell type:code id: tags:
``` python
widgets.HBox([widgets.Label(value="The $m$ in $E=mc^2$:"), widgets.FloatSlider()])
```
%% Output
%% Cell type:markdown id: tags:
### HTML
%% Cell type:code id: tags:
``` python
widgets.HTML(
value="Hello <b>World</b>",
placeholder='Some HTML',
description='Some HTML',
)
```
%% Output
%% Cell type:markdown id: tags:
### HTML Math
%% Cell type:code id: tags:
``` python
widgets.HTMLMath(
value=r"Some math and <i>HTML</i>: \(x^2\) and $$\frac{x+1}{x-1}$$",
placeholder='Some HTML',
description='Some HTML',
)
```
%% Output
%% Cell type:markdown id: tags:
## Image
%% Cell type:code id: tags:
``` python
file = open("images/WidgetArch.png", "rb")
file = open("../images/WidgetArch.png", "rb")
image = file.read()
widgets.Image(
value=image,
format='png',
width=300,
height=400,
)
```
%% Output
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-2-90d673488eed> in <module>
1 file = open("../images/WidgetArch.png", "rb")
2 image = file.read()
----> 3 widgets.Image(
4 value=image,
5 format='png',
NameError: name 'widgets' is not defined
%% Cell type:markdown id: tags:
## Button
%% Cell type:code id: tags:
``` python
widgets.Button(
description='Click me',
disabled=False,
button_style='', # 'success', 'info', 'warning', 'danger' or ''
tooltip='Click me',
icon='check'
)
```
%% Output
%% Cell type:markdown id: tags:
## Output
The `Output` widget can capture and display stdout, stderr and [rich output generated by IPython](http://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html#module-IPython.display). For detailed documentation, see the [output widget examples](https://ipywidgets.readthedocs.io/en/latest/examples/Output Widget.html).
%% Cell type:markdown id: tags:
## Play (Animation) widget
%% Cell type:markdown id: tags:
The `Play` widget is useful to perform animations by iterating on a sequence of integers with a certain speed. The value of the slider below is linked to the player.
%% Cell type:code id: tags:
``` python
play = widgets.Play(
# interval=10,
value=50,
min=0,
max=100,
step=1,
description="Press play",
disabled=False
)
slider = widgets.IntSlider()
widgets.jslink((play, 'value'), (slider, 'value'))
widgets.HBox([play, slider])
```
%% Output
%% Cell type:markdown id: tags:
## Date picker
The date picker widget works in Chrome, Firefox and IE Edge, but does not currently work in Safari because it does not support the HTML date input field.
%% Cell type:code id: tags:
``` python
widgets.DatePicker(
description='Pick a Date',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
## Color picker
%% Cell type:code id: tags:
``` python
widgets.ColorPicker(
concise=False,
description='Pick a color',
value='blue',
disabled=False
)
```
%% Output
%% Cell type:markdown id: tags:
## File Upload
The `FileUpload` allows to upload any type of file(s) as bytes.
%% Cell type:code id: tags:
``` python
widgets.FileUpload(
accept='', # Accepted file extension e.g. '.txt', '.pdf', 'image/*', 'image/*,.pdf'
multiple=False # True to accept multiple files upload else False
)
```
%% Output
%% Cell type:markdown id: tags:
## Controller
The `Controller` allows a game controller to be used as an input device.
%% Cell type:code id: tags:
``` python
widgets.Controller(
index=0,
)
```
%% Output
%% Cell type:markdown id: tags:
## Container/Layout widgets
These widgets are used to hold other widgets, called children. Each has a `children` property that may be set either when the widget is created or later.
%% Cell type:markdown id: tags:
### Box
%% Cell type:code id: tags:
``` python
items = [widgets.Label(str(i)) for i in range(4)]
widgets.Box(items)
```
%% Output
%% Cell type:markdown id: tags:
### HBox
%% Cell type:code id: tags:
``` python
items = [widgets.Label(str(i)) for i in range(4)]
widgets.HBox(items)
```
%% Output
%% Cell type:markdown id: tags:
### VBox
%% Cell type:code id: tags:
``` python
items = [widgets.Label(str(i)) for i in range(4)]
left_box = widgets.VBox([items[0], items[1]])
right_box = widgets.VBox([items[2], items[3]])
widgets.HBox([left_box, right_box])
```
%% Output
%% Cell type:markdown id: tags:
### GridBox
This box uses the HTML Grid specification to lay out its children in two dimensional grid. The example below lays out the 8 items inside in 3 columns and as many rows as needed to accommodate the items.
%% Cell type:code id: tags:
``` python
items = [widgets.Label(str(i)) for i in range(8)]
widgets.GridBox(items, layout=widgets.Layout(grid_template_columns="repeat(3, 100px)"))
```
%% Output
%% Cell type:markdown id: tags:
### Accordion
%% Cell type:code id: tags:
``` python
accordion = widgets.Accordion(children=[widgets.IntSlider(), widgets.Text()])
accordion.set_title(0, 'Slider')
accordion.set_title(1, 'Text')
accordion
```
%% Output
%% Cell type:markdown id: tags:
### Tabs
In this example the children are set after the tab is created. Titles for the tabs are set in the same way they are for `Accordion`.
%% Cell type:code id: tags:
``` python
tab_contents = ['P0', 'P1', 'P2', 'P3', 'P4']
children = [widgets.Text(description=name) for name in tab_contents]
tab = widgets.Tab()
tab.children = children
for i in range(len(children)):
tab.set_title(i, str(i))
tab
```
%% Output
%% Cell type:markdown id: tags:
### Accordion and Tab use `selected_index`, not value
Unlike the rest of the widgets discussed earlier, the container widgets `Accordion` and `Tab` update their `selected_index` attribute when the user changes which accordion or tab is selected. That means that you can both see what the user is doing *and* programmatically set what the user sees by setting the value of `selected_index`.
Setting `selected_index = None` closes all of the accordions or deselects all tabs.
%% Cell type:markdown id: tags:
In the cells below try displaying or setting the `selected_index` of the `tab` and/or `accordion`.
%% Cell type:code id: tags:
``` python
tab.selected_index = 3
```
%% Cell type:code id: tags:
``` python
accordion.selected_index = None
```
%% Cell type:markdown id: tags:
### Nesting tabs and accordions
Tabs and accordions can be nested as deeply as you want. If you have a few minutes, try nesting a few accordions or putting an accordion inside a tab or a tab inside an accordion.
The example below makes a couple of tabs with an accordion children in one of them
%% Cell type:code id: tags:
``` python
tab_nest = widgets.Tab()
tab_nest.children = [accordion, accordion]
tab_nest.set_title(0, 'An accordion')
tab_nest.set_title(1, 'Copy of the accordion')
tab_nest
```
%% Output
......
%% Cell type:markdown id: tags:
# Simple Widget Introduction
%% Cell type:markdown id: tags:
## What are widgets?
%% Cell type:markdown id: tags:
Widgets are eventful python objects that have a representation in the browser, often as a control like a slider, textbox, etc.
%% Cell type:markdown id: tags:
## What can they be used for?
%% Cell type:markdown id: tags:
You can use widgets to build **interactive GUIs** for your notebooks.
You can also use widgets to **synchronize stateful and stateless information** between Python and JavaScript.
%% Cell type:markdown id: tags:
## Using widgets
%% Cell type:markdown id: tags:
To use the widget framework, you need to import `ipywidgets`.
%% Cell type:code id: tags:
``` python
import ipywidgets as widgets
```
%% Cell type:markdown id: tags:
### repr
%% Cell type:markdown id: tags:
Widgets have their own display `repr` which allows them to be displayed using IPython's display framework. Constructing and returning an `IntSlider` automatically displays the widget (as seen below). Widgets are displayed inside the output area below the code cell. Clearing cell output will also remove the widget.
%% Cell type:code id: tags:
``` python
widgets.IntSlider()
```
%% Output
%% Cell type:markdown id: tags:
### display()
%% Cell type:markdown id: tags:
You can also explicitly display the widget using `display(...)`.
%% Cell type:code id: tags:
``` python
from IPython.display import display
w = widgets.IntSlider()
display(w)
```
%% Output
%% Cell type:markdown id: tags:
### Multiple display() calls
%% Cell type:markdown id: tags:
If you display the same widget twice, the displayed instances in the front-end will remain in sync with each other. Try dragging the slider below and watch the slider above.
%% Cell type:code id: tags:
``` python
display(w)
```
%% Output
%% Cell type:markdown id: tags:
## Why does displaying the same widget twice work?
%% Cell type:markdown id: tags:
Widgets are represented in the back-end by a single object. Each time a widget is displayed, a new representation of that same object is created in the front-end. These representations are called views.
![Kernel & front-end diagram](../images/WidgetModelView.png)
%% Cell type:markdown id: tags:
## Widget properties
%% Cell type:markdown id: tags:
All of the IPython widgets share a similar naming scheme. To read the value of a widget, you can query its `value` property.
%% Cell type:code id: tags:
``` python
w = widgets.IntSlider()
display(w)
```
%% Output
%% Cell type:code id: tags:
``` python
w.value
```
%% Output
43
57
%% Cell type:markdown id: tags:
Similarly, to set a widget's value, you can set its `value` property.
%% Cell type:code id: tags:
``` python
w.value = 100
```
%% Cell type:markdown id: tags:
### Keys
%% Cell type:markdown id: tags:
In addition to `value`, most widgets share `keys`, `description`, and `disabled`. To see the entire list of synchronized, stateful properties of any specific widget, you can query the `keys` property. Generally you should not interact with properties starting with an underscore.
%% Cell type:code id: tags:
``` python
w.keys
```
%% Output
['_dom_classes',
'_model_module',
'_model_module_version',
'_model_name',
'_view_count',
'_view_module',
'_view_module_version',
'_view_name',
'continuous_update',
'description',
'description_tooltip',
'disabled',
'layout',
'max',
'min',
'orientation',
'readout',
'readout_format',
'step',
'style',
'value']
%% Cell type:markdown id: tags:
### Shorthand for setting the initial values of widget properties
%% Cell type:markdown id: tags:
While creating a widget, you can set some or all of the initial values of that widget by defining them as keyword arguments in the widget's constructor (as seen below).
%% Cell type:code id: tags:
``` python
widgets.Text(value='Hello World!', disabled=True)
```
%% Output
%% Cell type:markdown id: tags:
## Linking two similar widgets
%% Cell type:markdown id: tags:
If you need to display the same value two different ways, you'll have to use two different widgets. Instead of attempting to manually synchronize the values of the two widgets, you can use the `link` or `jslink` function to link two properties together (the difference between these is discussed in [Widget Events](08.00-Widget_Events.ipynb)). Below, the values of two widgets are linked together.
If you need to display the same value two different ways, you'll have to use two different widgets. Instead of attempting to manually synchronize the values of the two widgets, you can use the `link` or `jslink` function to link two properties together (the difference between these is discussed in Widget Events). Below, the values of two widgets are linked together.
%% Cell type:code id: tags:
``` python
a = widgets.FloatText()
b = widgets.FloatSlider()
display(a,b)
mylink = widgets.link((a, 'value'), (b, 'value'))
```
%% Output
%% Cell type:markdown id: tags:
### Unlinking widgets
%% Cell type:markdown id: tags:
Unlinking the widgets is simple. All you have to do is call `.unlink` on the link object. Try changing one of the widgets above after unlinking to see that they can be independently changed.
%% Cell type:code id: tags:
``` python
# mylink.unlink()
```
%% Cell type:markdown id: tags:
# In-Depth Tutorial
https://github.com/jupyter-widgets/tutorial
......
%% Cell type:markdown id: tags:
# Widget Events
%% Cell type:markdown id: tags:
## Special events
%% Cell type:markdown id: tags:
The `Button` is not used to represent a data type. Instead the button widget is used to handle mouse clicks. The `on_click` method of the `Button` can be used to register function to be called when the button is clicked. The doc string of the `on_click` can be seen below.
%% Cell type:code id: tags:
``` python
import ipywidgets as widgets
from IPython.display import display
button = widgets.Button(description="Click Me!")
display(button)
```
%% Output
%% Cell type:code id: tags:
``` python
print(widgets.Button.on_click.__doc__)
```
%% Output
Register a callback to execute when the button is clicked.
The callback will be called with one argument, the clicked button
widget instance.
Parameters
----------
remove: bool (optional)
Set to true to remove the callback from the list of callbacks.
%% Cell type:markdown id: tags:
### Example
%% Cell type:markdown id: tags:
Since button clicks are stateless, they are transmitted from the front-end to the back-end using custom messages. By using the `on_click` method, a button that prints a message when it has been clicked is shown below. To capture `print`s (or any other kind of output including errors) and ensure it is displayed, be sure to send it to an `Output` widget (or put the information you want to display into an `HTML` widget).
%% Cell type:code id: tags:
``` python
button = widgets.Button(description="Click Me!")
# button = widgets.Button(description="Click Me!")
output = widgets.Output()
display(button, output)
@output.capture()
def on_button_clicked(b):
print("Button clicked.")
button.on_click(on_button_clicked)
```
%% Output
%% Cell type:markdown id: tags:
## Traitlet events
%% Cell type:markdown id: tags:
Widget properties are IPython traitlets and traitlets are eventful. To handle changes, the `observe` method of the widget can be used to register a callback. The doc string for `observe` can be seen below.
%% Cell type:code id: tags:
``` python
print(widgets.Widget.observe.__doc__)
```
%% Output
Setup a handler to be called when a trait changes.
This is used to setup dynamic notifications of trait changes.
Parameters
----------
handler : callable
A callable that is called when a trait changes. Its
signature should be ``handler(change)``, where ``change`` is a
dictionary. The change dictionary at least holds a 'type' key.
* ``type``: the type of notification.
Other keys may be passed depending on the value of 'type'. In the
case where type is 'change', we also have the following keys:
* ``owner`` : the HasTraits instance
* ``old`` : the old value of the modified trait attribute
* ``new`` : the new value of the modified trait attribute
* ``name`` : the name of the modified trait attribute.
names : list, str, All
If names is All, the handler will apply to all traits. If a list
of str, handler will apply to all names in the list. If a
str, the handler will apply just to that name.
type : str, All (default: 'change')
The type of notification to filter by. If equal to All, then all
notifications are passed to the observe handler.
%% Cell type:markdown id: tags:
### Registering callbacks to trait changes in the kernel
Since attributes of widgets on the Python side are traitlets, you can register handlers to the change events whenever the model gets updates from the front-end.
The handler passed to observe will be called with one change argument. The change object holds at least a `type` key and a `name` key, corresponding respectively to the type of notification and the name of the attribute that triggered the notification.
Other keys may be passed depending on the value of `type`. In the case where type is `change`, we also have the following keys:
- `owner` : the HasTraits instance
- `old` : the old value of the modified trait attribute
- `new` : the new value of the modified trait attribute
- `name` : the name of the modified trait attribute.
%% Cell type:code id: tags:
``` python
caption = widgets.Label(value='The values of range1 and range2 are synchronized')
slider = widgets.IntSlider(min=-5, max=5, value=1, description='Slider')
widgets.IntSlider()
```
def handle_slider_change(change):
caption.value = 'The slider value is ' + (
'negative' if change.new < 0 else 'nonnegative'
)
%% Output
slider.observe(handle_slider_change, names='value')
display(caption, slider)
```
%% Cell type:markdown id: tags:
%% Output
### Registering callbacks to trait changes in the kernel
Since attributes of widgets on the Python side are traitlets, you can register handlers to the change events whenever the model gets updates from the front-end.
The handler passed to observe will be called with one change argument. The change object holds at least a `type` key and a `name` key, corresponding respectively to the type of notification and the name of the attribute that triggered the notification.
%% Cell type:markdown id: tags:
### Callback signatures
%% Cell type:markdown id: tags:
Mentioned in the doc string, the callback registered must have the signature `handler(change)` where `change` is a dictionary holding the information about the change.
Using this method, an example of how to output an `IntSlider`'s value as it is changed can be seen below.
%% Cell type:code id: tags:
``` python
int_range = widgets.IntSlider()
output2 = widgets.Output()
display(int_range, output2)
def on_value_change(change):
output2.clear_output()
with output2:
print(change['new'])
int_range.observe(on_value_change, names='value')
```
%% Output
%% Cell type:code id: tags:
``` python
caption = widgets.Label(value='The values of range1 and range2 are synchronized')
slider = widgets.IntSlider(min=-5, max=5, value=1, description='Slider')
def handle_slider_change(change):
caption.value = 'The slider value is ' + (
'negative' if change.new < 0 else 'nonnegative'
)
slider.observe(handle_slider_change, names='value')
display(caption, slider)
```
%% Output
%% Cell type:markdown id: tags:
### Why `observe` instead of `link`?
%% Cell type:markdown id: tags:
Using `link` is great if no transformation of the values is needed. `observe` is useful if some kind of calculation needs to be done with the values or if the values that are related have different types.
The example below converts between Celsius and Farhenheit. As written, changing the temperature in Celcius will update the temperature in Farenheit, but not the other way around. You will add that as an exercise.
%% Cell type:code id: tags:
``` python
def C_to_F(temp):
return 1.8 * temp + 32
def F_to_C(temp):
return (temp -32) / 1.8
degree_C = widgets.FloatText(description='Temp $^\circ$C', value=0)
degree_F = widgets.FloatText(description='Temp $^\circ$F', value=C_to_F(degree_C.value))
def on_C_change(change):
degree_F.value = C_to_F(change['new'])
degree_C.observe(on_C_change, names='value')
display(degree_C, degree_F)
```
%% Output
%% Cell type:markdown id: tags:
### Exercise
Add a callback that is called when `degree_F` is changed. An outline of the callback function is below. Fill it in, and make `degree_F` `observe` call `on_F_change` if the `value` changes.
%% Cell type:code id: tags:
``` python
def on_F_change(change):
degree_C.value = # Fill this in!
# Add line here to have degree_F observe changes in value and call on_F_change
```
%% Cell type:markdown id: tags:
## Advanced Widget Linking
%% Cell type:markdown id: tags:
In an earlier notebook you used `link` to link the value of one widget to another.
There are a couple of other linking methods that offer more flexibility:
+ `dlink` is a *directional* link; updates happen in one direction but not the other.
+ `jslink` and `jsdlink` do the linking in the front end (i.e. in JavaScript without any communication to Python).
%% Cell type:markdown id: tags:
### Linking traitlets attributes in the kernel (ie. in Python)
The first method is to use the `link` and `dlink`. This only works if we are interacting with a live kernel.
%% Cell type:code id: tags:
``` python
caption = widgets.Label(value='The values of slider1 and slider2 are synchronized')
sliders1, slider2 = widgets.IntSlider(description='Slider 1'),\
widgets.IntSlider(description='Slider 2')
display(caption, sliders1, slider2)
l = widgets.link((sliders1, 'value'), (slider2, 'value'))
```
%% Output
%% Cell type:code id: tags:
``` python
caption = widgets.HTML(value='Changes in source values are reflected in target1, but changes in target1 do not affect source')
source, target1 = widgets.IntSlider(description='Source'),\
widgets.IntSlider(description='Target 1')
display(caption, source, target1)
dl = widgets.dlink((source, 'value'), (target1, 'value'))
```
%% Output
%% Cell type:markdown id: tags:
Links can be broken by calling `unlink`.
%% Cell type:code id: tags:
``` python
l.unlink()
dl.unlink()
```
%% Cell type:markdown id: tags:
Function `widgets.jslink` returns a `Link` widget. The link can be broken by calling the `unlink` method.
%% Cell type:markdown id: tags:
### Linking widgets attributes from the client side
%% Cell type:markdown id: tags:
You can also directly link widget attributes in the browser using the link widgets, in either a unidirectional or a bidirectional fashion.
Javascript links persist when embedding widgets in html web pages without a kernel.
%% Cell type:code id: tags:
``` python
caption = widgets.Label(value='The values of range1 and range2 are synchronized')
range1, range2 = widgets.IntSlider(description='Range 1'),\
widgets.IntSlider(description='Range 2')
display(caption, range1, range2)
l = widgets.jslink((range1, 'value'), (range2, 'value'))
```
%% Output
%% Cell type:code id: tags:
``` python
caption = widgets.Label(value='Changes in source_range values are reflected in target_range1')
source_range, target_range1 = widgets.IntSlider(description='Source range'),\
widgets.IntSlider(description='Target range 1')
display(caption, source_range, target_range1)
dl = widgets.jsdlink((source_range, 'value'), (target_range1, 'value'))
```
%% Output
%% Cell type:markdown id: tags:
The links can be broken by calling the `unlink` method.
%% Cell type:code id: tags:
``` python
l.unlink()
dl.unlink()
```
%% Cell type:markdown id: tags:
### The difference between linking in the kernel and linking in the client
Linking in the kernel means linking via python. If two sliders are linked in the kernel, when one slider is changed the browser sends a message to the kernel (python in this case) updating the changed slider, the link widget in the kernel then propagates the change to the other slider object in the kernel, and then the other slider's kernel object sends a message to the browser to update the other slider's views in the browser. If the kernel is not running (as in a static web page), then the controls will not be linked.
Linking using jslink (i.e., on the browser side) means contructing the link in Javascript. When one slider is changed, Javascript running in the browser changes the value of the other slider in the browser, without needing to communicate with the kernel at all. If the sliders are attached to kernel objects, each slider will update their kernel-side objects independently.
To see the difference between the two, go to the [static version of this page in the ipywidgets documentation](http://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Events.html) and try out the sliders near the bottom. The ones linked in the kernel with `link` and `dlink` are no longer linked, but the ones linked in the browser with `jslink` and `jsdlink` are still linked.
%% Cell type:markdown id: tags:
### Continuous vs delayed updates
Some widgets offer a choice with their `continuous_update` attribute between continually updating values or only updating values when a user submits the value (for example, by pressing Enter or navigating away from the control). In the next example, we see the "Delayed" controls only transmit their value after the user finishes dragging the slider or submitting the textbox. The "Continuous" controls continually transmit their values as they are changed. Try typing a two-digit number into each of the text boxes, or dragging each of the sliders, to see the difference.
%% Cell type:code id: tags:
``` python
a = widgets.IntSlider(description="Delayed", continuous_update=False)
b = widgets.IntText(description="Delayed", continuous_update=False)
c = widgets.IntSlider(description="Continuous", continuous_update=True)
d = widgets.IntText(description="Continuous", continuous_update=True)
widgets.link((a, 'value'), (b, 'value'))
widgets.link((a, 'value'), (c, 'value'))
widgets.link((a, 'value'), (d, 'value'))
widgets.VBox([a,b,c,d])
```
%% Output
%% Cell type:markdown id: tags:
Sliders, `Text`, and `Textarea` controls default to `continuous_update=True`. `IntText` and other text boxes for entering integer or float numbers default to `continuous_update=False` (since often you'll want to type an entire number before submitting the value by pressing enter or navigating out of the box).
%% Cell type:code id: tags:
``` python
```
......
%% Cell type:markdown id: tags:
## Predefined styles
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.
For example, the `Button` widget has a `button_style` attribute that may take 5 different values:
- `'primary'`
- `'success'`
- `'info'`
- `'warning'`
- `'danger'`
besides the default empty string ''.
%% Cell type:code id: tags:
``` python
from ipywidgets import Button, IntSlider
Button(description='Danger Button', button_style='danger')
```
%% Output
%% Cell type:markdown id: tags:
## The `style` attribute
While the `layout` attribute only exposes layout-related CSS properties for the top-level DOM element of widgets, the
`style` attribute is used to expose non-layout related styling attributes of widgets.
However, the properties of the `style` attribute are specific to each widget type.
%% Cell type:code id: tags:
``` python
b1 = Button(description='Custom color')
b1.style.button_color = 'lightgreen'
b1
```
%% Output
%% Cell type:markdown id: tags:
You can get a list of the style attributes for a widget with the `keys` property.
%% Cell type:code id: tags:
``` python
b1.style.keys
```
%% Output
['_model_module',
'_model_module_version',
'_model_name',
'_view_count',
'_view_module',
'_view_module_version',
'_view_name',
'button_color',
'font_weight']
%% Cell type:markdown id: tags:
Just like the `layout` attribute, widget styles can be assigned to other widgets.
%% Cell type:code id: tags:
``` python
b2 = Button()
b2.style = b1.style
b2
```
%% Output
%% Cell type:markdown id: tags:
Widget styling attributes are specific to each widget type.
%% Cell type:code id: tags:
``` python
s1 = IntSlider(description='Blue handle')
s1.style.handle_color = 'lightblue'
s1
```
%% Output
%% Cell type:markdown id: tags:
There is a [list of all style keys](Table%20of%20widget%20keys%20and%20style%20keys.ipynb#Style-keys).
There is a [list of all style keys](https://github.com/jupyter-widgets/tutorial/blob/1dde8988b0ba083b1644b87514e3bcad1559f3af/notebooks/Table%20of%20widget%20keys%20and%20style%20keys.ipynb#Style-keys).
......
%% Cell type:markdown id: tags:
# Layout and Styling of Jupyter widgets
This section presents how to layout and style Jupyter interactive widgets to build rich and *reactive* widget-based applications.
%% Cell type:markdown id: tags:
## The `layout` attribute.
Jupyter interactive widgets have a `layout` attribute exposing a number of CSS properties that impact how widgets are laid out.
### Exposed CSS properties
<div class="alert alert-info" style="margin: 20px">
The following properties map to the values of the CSS properties of the same name (underscores being replaced with dashes), applied to the top DOM elements of the corresponding widget.
</div>
#### Sizes
- `height`
- `width`
- `max_height`
- `max_width`
- `min_height`
- `min_width`
#### Display
- `visibility`
- `display`
- `overflow`
- `overflow_x` (deprecated in `7.5`, use `overflow` instead)
- `overflow_y` (deprecated in `7.5`, use `overflow` instead)
#### Box model
- `border`
- `margin`
- `padding`
#### Positioning
- `top`
- `left`
- `bottom`
- `right`
#### Image/media
- `object_fit`
- `object_position`
#### Flexbox
- `order`
- `flex_flow`
- `align_items`
- `flex`
- `align_self`
- `align_content`
- `justify_content`
- `justify_items`
#### Grid layout
- `grid_auto_columns`
- `grid_auto_flow`
- `grid_auto_rows`
- `grid_gap`
- `grid_template_rows`
- `grid_template_columns`
- `grid_template_areas`
- `grid_row`
- `grid_column`
- `grid_area`
### Shorthand CSS properties
You may have noticed that certain CSS properties such as `margin-[top/right/bottom/left]` seem to be missing. The same holds for `padding-[top/right/bottom/left]` etc.
In fact, you can atomically specify `[top/right/bottom/left]` margins via the `margin` attribute alone by passing the string `'100px 150px 100px 80px'` for a respectively `top`, `right`, `bottom` and `left` margins of `100`, `150`, `100` and `80` pixels.
Similarly, the `flex` attribute can hold values for `flex-grow`, `flex-shrink` and `flex-basis`. The `border` attribute is a shorthand property for `border-width`, `border-style (required)`, and `border-color`.
%% Cell type:markdown id: tags:
## Simple examples
%% Cell type:markdown id: tags:
The following example shows how to resize a `Button` so that its views have a height of `80px` and a width of `50%` of the available space. It also includes an example of setting a CSS property that requires multiple values (a border, in thise case):
%% Cell type:code id: tags:
``` python
from ipywidgets import Button, Layout
b = Button(description='(50% width, 80px height) button',
layout=Layout(width='50%', height='80px', border='2px dotted blue'))
b
```
%% Output
%% Cell type:markdown id: tags:
The `layout` property can be shared between multiple widgets and assigned directly.
%% Cell type:code id: tags:
``` python
Button(description='Another button with the same layout', layout=b.layout)
```
%% Output
%% Cell type:markdown id: tags:
### Is simple layout really simple?
The cell below adds a `min_width` and `max_width` to the button layout. The effect may be surprising; in CSS if max/min width are present they override the width.
%% Cell type:code id: tags:
``` python
b.layout.min_width='10%'
b.layout.max_width='20%'
```
%% Cell type:markdown id: tags:
### Natural sizes, and arrangements using HBox and VBox
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:
%% Cell type:code id: tags:
``` python
from ipywidgets import Button, HBox, VBox
words = ['correct', 'horse', 'battery', 'staple']
items = [Button(description=w) for w in words]
left_box = VBox([items[0], items[1]])
right_box = VBox([items[2], items[3]])
HBox([left_box, right_box])
```
%% Output
%% Cell type:markdown id: tags:
## Flexbox and Grid
The *Flexbox* CSS specification is great for laying out items in a single direction, horizontally or vertically. As we saw in the previous example, two dimensional layout can be done with flexbox by using a combination of horizontal and vertical components.
The *Grid* CSS specifation is designed to be used for two dimensional layout. There are properties for specifying the number of items in each row or column, how they should be sized, and how items should be aligned.
### For more information about Flexbox and Grid
The are notebooks with more detail about [widgets and the Flexbox model](reference_guides/guide-flex-box.ipynb) and [widgets and the Grid model](reference_guides/guide-grid-box.ipynb). The code examples from each of those notebooks is included here also.
If you want to learn more about CSS layout after this tutorial, take a look at this [excellent set of articles on CSS layout at MDN](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout). The Flexbox and Grid articles each have links to more extensive guides at the end of the article.
%% Cell type:markdown id: tags:
## The Flexbox layout
The `HBox` and `VBox` classes above are special cases of the `Box` widget.
The `Box` widget enables the entire CSS flexbox spec as well as the Grid layout spec, enabling rich reactive layouts in the Jupyter notebook. It aims at providing an efficient way to lay out, align and distribute space among items in a container.
Again, the whole flexbox spec is exposed via the `layout` attribute of the container widget (`Box`) and the contained items. One may share the same `layout` attribute among all the contained items.
### Acknowledgement
The following flexbox tutorial on the flexbox layout follows the lines of the article [A Complete Guide to Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) by Chris Coyier, and uses text and various images from the article [with permission](https://css-tricks.com/license/).
### Basics and terminology
The flexbox layout spectrum is excellent for laying out items in a single direction, either horizontally or vertically.
Since flexbox is a whole module and not a single property, it involves a lot of things including its whole set of properties. Some of them are meant to be set on the container (parent element, known as "flex container") whereas the others are meant to be set on the children (known as "flex items").
If regular layout is based on both block and inline flow directions, the flex layout is based on "flex-flow directions". Please have a look at this figure from the specification, explaining the main idea behind the flex layout.
![Flexbox](./images/flexbox.png)
Basically, items will be laid out following either the `main axis` (from `main-start` to `main-end`) or the `cross axis` (from `cross-start` to `cross-end`).
- `main axis` - The main axis of a flex container is the primary axis along which flex items are laid out. Beware, it is not necessarily horizontal; it depends on the flex-direction property (see below).
- `main-start | main-end` - The flex items are placed within the container starting from main-start and going to main-end.
- `main size` - A flex item's width or height, whichever is in the main dimension, is the item's main size. The flex item's main size property is either the ‘width’ or ‘height’ property, whichever is in the main dimension.
cross axis - The axis perpendicular to the main axis is called the cross axis. Its direction depends on the main axis direction.
- `cross-start | cross-end` - Flex lines are filled with items and placed into the container starting on the cross-start side of the flex container and going toward the cross-end side.
- `cross size` - The width or height of a flex item, whichever is in the cross dimension, is the item's cross size. The cross size property is whichever of ‘width’ or ‘height’ that is in the cross dimension.
### The VBox and HBox helpers
The `VBox` and `HBox` helper classes provide simple defaults to arrange child widgets in vertical and horizontal boxes. They are roughly equivalent to:
```Python
def VBox(*pargs, **kwargs):
"""Displays multiple widgets vertically using the flexible box model."""
box = Box(*pargs, **kwargs)
box.layout.display = 'flex'
box.layout.flex_flow = 'column'
box.layout.align_items = 'stretch'
return box
def HBox(*pargs, **kwargs):
"""Displays multiple widgets horizontally using the flexible box model."""
box = Box(*pargs, **kwargs)
box.layout.display = 'flex'
box.layout.align_items = 'stretch'
return box
```
%% Cell type:markdown id: tags:
### Examples
**Four buttons in a VBox. Items stretch to the maximum width, in a vertical box taking `50%` of the available space.**
%% Cell type:code id: tags:
``` python
from ipywidgets import Layout, Button, Box
items_layout = Layout(width='auto') # override the default width of the button to 'auto' to let the button grow
box_layout = Layout(display='flex',
flex_flow='column',
align_items='stretch',
border='solid',
width='50%')
words = ['correct', 'horse', 'battery', 'staple']
items = [Button(description=word, layout=items_layout, button_style='danger') for word in words]
box = Box(children=items, layout=box_layout)
box
```
%% Output
%% Cell type:markdown id: tags:
**Three buttons in an HBox. Items flex proportionally to their weight.**
%% Cell type:code id: tags:
``` python
from ipywidgets import Layout, Button, Box, VBox
# Items flex proportionally to the weight and the left over space around the text
items_auto = [
Button(description='weight=1; auto', layout=Layout(flex='1 1 auto', width='auto'), button_style='danger'),
Button(description='weight=3; auto', layout=Layout(flex='3 1 auto', width='auto'), button_style='danger'),
Button(description='weight=1; auto', layout=Layout(flex='1 1 auto', width='auto'), button_style='danger'),
]
# Items flex proportionally to the weight
items_0 = [
Button(description='weight=1; 0%', layout=Layout(flex='1 1 0%', width='auto'), button_style='danger'),
Button(description='weight=3; 0%', layout=Layout(flex='3 1 0%', width='auto'), button_style='danger'),
Button(description='weight=1; 0%', layout=Layout(flex='1 1 0%', width='auto'), button_style='danger'),
]
box_layout = Layout(display='flex',
flex_flow='row',
align_items='stretch',
width='70%')
box_auto = Box(children=items_auto, layout=box_layout)
box_0 = Box(children=items_0, layout=box_layout)
VBox([box_auto, box_0])
```
%% Output
%% Cell type:markdown id: tags:
**A more advanced example: a reactive form.**
The form is a `VBox` of width '50%'. Each row in the VBox is an HBox, that justifies the content with space between..
%% Cell type:code id: tags:
``` python
from ipywidgets import Layout, Button, Box, FloatText, Textarea, Dropdown, Label, IntSlider
form_item_layout = Layout(
display='flex',
flex_flow='row',
justify_content='space-between'
)
form_items = [
Box([Label(value='Age of the captain'), IntSlider(min=40, max=60)], layout=form_item_layout),
Box([Label(value='Egg style'),
Dropdown(options=['Scrambled', 'Sunny side up', 'Over easy'])], layout=form_item_layout),
Box([Label(value='Ship size'),
FloatText()], layout=form_item_layout),
Box([Label(value='Information'),
Textarea()], layout=form_item_layout)
]
form = Box(form_items, layout=Layout(
display='flex',
flex_flow='column',
border='solid 2px',
align_items='stretch',
width='50%'
))
form
```
%% Output
%% Cell type:markdown id: tags:
**A more advanced example: a carousel.**
%% Cell type:code id: tags:
``` python
from ipywidgets import Layout, Button, Box, Label
item_layout = Layout(height='100px', min_width='40px')
items = [Button(layout=item_layout, description=str(i), button_style='warning') for i in range(40)]
box_layout = Layout(overflow_x='scroll',
border='3px solid black',
width='500px',
height='',
flex_flow='row',
display='flex')
carousel = Box(children=items, layout=box_layout)
VBox([Label('Scroll horizontally:'), carousel])
```
%% Output
%% Cell type:markdown id: tags:
#### *Compatibility note*
The `overflow_x` and `overflow_y` options are deprecated in ipywidgets `7.5`. Instead, use the shorthand property `overflow='scroll hidden'`. The first part specificies overflow in `x`, the second the overflow in `y`.
%% Cell type:markdown id: tags:
## A widget for exploring layout options
Use the dropdowns and sliders in the widget to change the layout of the box containing the colored buttons. Many of the CSS layout options described above are available, and the Python code to generate a `Layout` object reflecting the settings is in a `TextArea` in the widget.
A few questions to answer after the demonstration of this (see the [detailed flexbox guide for a longer discussion](reference_guides/guide-flex-box.ipynb)):
1. What does changing `justify_content` affect? You may find it easier to answer this if you set `wrap` to `wrap`.
2. What does `align_items` affect?
3. How is `align_content` different than `align_items`?
%% Cell type:code id: tags:
``` python
# from layout_preview import layout
# layout
from layout_preview import layout
layout
```
%% Cell type:markdown id: tags:
### Exercises
%% Cell type:markdown id: tags:
**Four buttons in a box revisted: Change order and orientation**
This example, from earlier in this notebook, lays out 4 buttons vertically.
Flexbox allows you to change the order and orientation of the children items in the flexbox without changing the children themselves.
1. Change the `flex_flow` so that the buttons are displayed in a single column in *reverse order*.
2. Change the `flex_flow` so that the buttons are displayed in a single *row* instead of a column.
3. Try setting a few values of `align_items` and describe how it affects the display of the buttons.
4. Make the box narrower by changing the `width`, then change `flex_flow` to lay out the buttons in rows that wrap so that there is a 2x2 grid of buttons.
Feel free to figure out the layout using the tool above and copy/paste the layout here!
%% Cell type:code id: tags:
``` python
from ipywidgets import Layout, Button, Box
items_layout = Layout(width='auto') # override the default width of the button to 'auto' to let the button grow
box_layout = Layout(display='flex',
flex_flow='column',
align_items='stretch',
border='solid',
width='20%')
words = ['correct', 'horse', 'battery', 'staple']
items = [Button(description=word, layout=items_layout, button_style='danger') for word in words]
box = Box(children=items, layout=box_layout)
box
```
%% Output
%% Cell type:markdown id: tags:
**Carousel revisted: item layout**
The code that generated the carousel is reproduced below. Run the cell, then continue reading.
%% Cell type:code id: tags:
``` python
from ipywidgets import Layout, Button, Box, Label
item_layout = Layout(height='100px', min_width='40px')
items = [Button(layout=item_layout, description=str(i), button_style='warning') for i in range(40)]
box_layout = Layout(overflow_x='scroll',
border='3px solid black',
width='500px',
height='',
flex_flow='row',
display='flex')
carousel = Box(children=items, layout=box_layout)
VBox([Label('Scroll horizontally:'), carousel])
```
%% Output
%% Cell type:markdown id: tags:
**To do:**
+ Change the `min_width` for *one* of the `items`, say the first one. Does it affect only the first one, or all of them? Why?
+ Change the `height` of *only* the first button. *Hint:* It needs its own `Layout`.
%% Cell type:code id: tags:
``` python
items[0].layout.min_width = 'FILL IN WITH A WIDTH'
```
%% Cell type:markdown id: tags:
## The Grid layout
The `GridBox` class is a special case of the `Box` widget.
The `Box` widget enables the entire CSS flexbox spec, enabling rich reactive layouts in the Jupyter notebook. It aims at providing an efficient way to lay out, align and distribute space among items in a container.
A more detailed description of the [Grid layout is available](reference_guides/guide-grid-box.ipynb).
The whole grid layout spec is exposed via the `layout` attribute of the container widget (`Box`) and the contained items. One may share the same `layout` attribute among all the contained items.
The following flexbox tutorial on the flexbox layout follows the lines of the article [A Complete Guide to Grid](https://css-tricks.com/snippets/css/complete-guide-grid/) by Chris House, and uses text and various images from the article [with permission](https://css-tricks.com/license/).
### Basics
To get started you have to define a container element as a grid with display: grid, set the column and row sizes with grid-template-rows, grid-template-columns, and grid_template_areas, and then place its child elements into the grid with grid-column and grid-row. Similarly to flexbox, the source order of the grid items doesn't matter. Your CSS can place them in any order, which makes it super easy to rearrange your grid with media queries. Imagine defining the layout of your entire page, and then completely rearranging it to accommodate a different screen width all with only a couple lines of CSS. Grid is one of the most powerful CSS modules ever introduced.
### Important terminology
Before diving into the concepts of Grid it's important to understand the terminology. Since the terms involved here are all kinda conceptually similar, it's easy to confuse them with one another if you don't first memorize their meanings defined by the Grid specification. But don't worry, there aren't many of them.
**Grid Container**
The element on which `display: grid` is applied. It's the direct parent of all the grid items. In this example container is the grid container.
```html
<div class="container">
<div class="item item-1"></div>
<div class="item item-2"></div>
<div class="item item-3"></div>
</div>
```
**Grid Item**
The children (e.g. direct descendants) of the grid container. Here the item elements are grid items, but sub-item isn't.
```html
<div class="container">
<div class="item"></div>
<div class="item">
<p class="sub-item"></p>
</div>
<div class="item"></div>
</div>
```
**Grid Line**
The dividing lines that make up the structure of the grid. They can be either vertical ("column grid lines") or horizontal ("row grid lines") and reside on either side of a row or column. Here the yellow line is an example of a column grid line.
![grid-line](images/grid-line.png)
**Grid Track**
The space between two adjacent grid lines. You can think of them like the columns or rows of the grid. Here's the grid track between the second and third row grid lines.
![grid-track](images/grid-track.png)
A more detailed description of the [Grid layout is available](reference_guides/guide-grid-box.ipynb). The [Grid layout guide on MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout#Guides) is also excellent.
%% Cell type:code id: tags:
``` python
from ipywidgets import Button, GridBox, Layout, ButtonStyle
```
%% Cell type:markdown id: tags:
The first example defines a 3x3 grid and places 9 buttons into the grid.
%% Cell type:code id: tags:
``` python
GridBox(children=[Button(description=str(i), layout=Layout(width='auto', height='auto'),
style=ButtonStyle(button_color='darkseagreen')) for i in range(9)
],
layout=Layout(
width='50%',
grid_template_columns='100px 50px 100px',
grid_template_rows='80px auto 80px',
grid_gap='5px 10px')
)
```
%% Output
%% Cell type:markdown id: tags:
### Exercises
%% Cell type:markdown id: tags:
**Add more buttons**
Modify the code above to place more buttons in the `GridBox` (do *not* modify the layout). Any number of buttons larger than 9 is fine.
1. What happens to the extra buttons? Are they laid out like the first 9 buttons?
The grid template defines a 3x3 grid. If additional children are placed in the grid their properties are determined by the layout properties `grid_auto_columns`, `grid_auto_rows` and `grid_auto_flow` properties.
2. Set `grid_auto_rows="10px"` and rerun the example with more than 9 buttons.
3. Set `grid_auto_rows` so that the automatically added rows have the same format as the templated rows.
%% Cell type:markdown id: tags:
### An alternate way of defining the grid
The grid can also be set up using a description words. The layout below defines a grid with 4 columns and 3 rows. The first row is a header, the bottom row is a footer, and the middle row has content in the first two columns, then an empty cell, followed by a sidebar.
Widgets are assigned to each of these areas by setting the widgets's layout `grid_area` to the name of the area.
```
"header header header header"
"main main . sidebar "
"footer footer footer footer"
```
%% Cell type:code id: tags:
``` python
header = Button(description='Header',
layout=Layout(width='auto', grid_area='header'),
style=ButtonStyle(button_color='lightblue'))
main = Button(description='Main',
layout=Layout(width='auto', grid_area='main'),
style=ButtonStyle(button_color='moccasin'))
sidebar = Button(description='Sidebar',
layout=Layout(width='auto', grid_area='sidebar'),
style=ButtonStyle(button_color='salmon'))
footer = Button(description='Footer',
layout=Layout(width='auto', grid_area='footer'),
style=ButtonStyle(button_color='olive'))
GridBox(children=[header, main, sidebar, footer],
layout=Layout(
width='50%',
grid_template_rows='auto auto auto',
grid_template_columns='25% 25% 25% 25%',
grid_template_areas='''
"header header header header"
"main main . sidebar "
"footer footer footer footer"
''')
)
```
%% Output
%% Cell type:markdown id: tags:
### Exercises
%% Cell type:markdown id: tags:
**Make the main area larger**
1. Add another row or two to the template area so that the main area is 3 rows high and 2 columns wide.
# Layout Templates
See https://ipywidgets.readthedocs.io/en/latest/examples/Layout%20Templates.html
......
This diff is collapsed.
......@@ -8,7 +8,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "f4d715073b534597ac4e287746cc35b4",
"model_id": "42a1bdd953f94049af663c59ce4391e2",
"version_major": 2,
"version_minor": 0
},
......
%% Cell type:markdown id: tags:
# Usage
Start voila with the gridstack template. If `show_handles` is set to True, you can drag the individual dashboard items around.
```
voila --template=gridstack --VoilaConfiguration.resources='{"gridstack": {"show_handles": True}}' ./dashboards/gridstack-example.ipynb
```
%% Cell type:markdown id: tags:
# Demo
This demo uses voila to render a notebook to a custom HTML page using gridstack.js for the layout of each output. In the cell metadata you can change the default cell with and height (in grid units between 1 and 12) by specifying.
* `grid_row`
* `grid_columns`
%% Cell type:code id: tags:
``` python
import numpy as np
n = 200
x = np.linspace(0.0, 10.0, n)
y = np.cumsum(np.random.randn(n)*10).astype(int)
```
%% Cell type:code id: tags:
``` python
import ipywidgets as widgets
```
%% Cell type:code id: tags:
``` python
label_selected = widgets.Label(value="Selected: 0")
label_selected
```
%% Output
%% Cell type:code id: tags:
``` python
import numpy as np
from bqplot import pyplot as plt
import bqplot
fig = plt.figure( title='Histogram')
np.random.seed(0)
hist = plt.hist(y, bins=25)
hist.scales['sample'].min = float(y.min())
hist.scales['sample'].max = float(y.max())
display(fig)
fig.layout.width = 'auto'
fig.layout.height = 'auto'
fig.layout.min_height = '300px' # so it shows nicely in the notebook
fig.layout.flex = '1'
```
%% Output
%% Cell type:code id: tags:
``` python
import numpy as np
from bqplot import pyplot as plt
import bqplot
fig = plt.figure( title='Line Chart')
np.random.seed(0)
n = 200
p = plt.plot(x, y)
fig
```
%% Output
%% Cell type:code id: tags:
``` python
fig.layout.width = 'auto'
fig.layout.height = 'auto'
fig.layout.min_height = '300px' # so it shows nicely in the notebook
fig.layout.flex = '1'
```
%% Cell type:code id: tags:
``` python
brushintsel = bqplot.interacts.BrushIntervalSelector(scale=p.scales['x'])
```
%% Cell type:code id: tags:
``` python
def update_range(*args):
label_selected.value = "Selected range {}".format(brushintsel.selected)
mask = (x > brushintsel.selected[0]) & (x < brushintsel.selected[1])
hist.sample = y[mask]
brushintsel.observe(update_range, 'selected')
fig.interaction = brushintsel
```
%% Cell type:code id: tags:
``` python
```
......
This diff is collapsed.
%% Cell type:markdown id: tags:
# Usage
```bash
voila -template vuetify-default ./dashboards/vuetify-bqplot.ipynb
```
%% Cell type:code id: tags:
``` python
import ipyvuetify as v
```
%% Cell type:markdown id: tags:
# First histogram plot
%% Cell type:code id: tags:
``` python
import ipywidgets as widgets
import numpy as np
from bqplot import pyplot as plt
import bqplot
n = 200
x = np.linspace(0.0, 10.0, n)
y = np.cumsum(np.random.randn(n)*10).astype(int)
fig = plt.figure( title='Histogram')
np.random.seed(0)
hist = plt.hist(y, bins=25)
hist.scales['sample'].min = float(y.min())
hist.scales['sample'].max = float(y.max())
fig.layout.width = 'auto'
fig.layout.height = 'auto'
fig.layout.min_height = '300px' # so it shows nicely in the notebook
fig
```
%% Output
%% Cell type:code id: tags:
``` python
slider = v.Slider(thumb_label='always', class_="px-4", v_model=30)
widgets.link((slider, 'v_model'), (hist, 'bins'))
slider
```
%% Output
%% Cell type:markdown id: tags:
# Line chart
%% Cell type:code id: tags:
``` python
fig2 = plt.figure( title='Line Chart')
np.random.seed(0)
p = plt.plot(x, y)
fig2.layout.width = 'auto'
fig2.layout.height = 'auto'
fig2.layout.min_height = '300px' # so it shows nicely in the notebook
fig2
```
%% Output
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
brushintsel = bqplot.interacts.BrushIntervalSelector(scale=p.scales['x'])
def update_range(*args):
if brushintsel.selected is not None and brushintsel.selected.shape == (2,):
mask = (x > brushintsel.selected[0]) & (x < brushintsel.selected[1])
hist.sample = y[mask]
brushintsel.observe(update_range, 'selected')
fig2.interaction = brushintsel
```
%% Cell type:markdown id: tags:
# Second histogram plot
%% Cell type:code id: tags:
``` python
n2 = 200
x2 = np.linspace(0.0, 10.0, n)
y2 = np.cumsum(np.random.randn(n)*10).astype(int)
figHist2 = plt.figure( title='Histogram 2')
np.random.seed(0)
hist2 = plt.hist(y2, bins=25)
hist2.scales['sample'].min = float(y2.min())
hist2.scales['sample'].max = float(y2.max())
figHist2.layout.width = 'auto'
figHist2.layout.height = 'auto'
figHist2.layout.min_height = '300px' # so it shows nicely in the notebook
sliderHist2 = v.Slider(_metadata={'mount_id': 'histogram_bins2'}, thumb_label='always', class_='px-4', v_model=5)
from traitlets import link
link((sliderHist2, 'v_model'), (hist2, 'bins'))
display(figHist2)
display(sliderHist2)
```
%% Output
%% Cell type:markdown id: tags:
# Set up voila vuetify layout
The voila vuetify template does not render output from the notebook, it only shows widget with the mount_id metadata.
%% Cell type:code id: tags:
``` python
v.Tabs(_metadata={'mount_id': 'content-main'}, children=[
v.Tab(children=['Tab1']),
v.Tab(children=['Tab2']),
v.TabItem(children=[
v.Layout(row=True, wrap=True, align_center=True, children=[
v.Flex(xs12=True, lg6=True, xl4=True, children=[
fig, slider
]),
v.Flex(xs12=True, lg6=True, xl4=True, children=[
figHist2, sliderHist2
]),
v.Flex(xs12=True, xl4=True, children=[
fig2
]),
])
]),
v.TabItem(children=[
v.Container(children=['Lorum ipsum'])
])
])
```
%% Output
%% Cell type:markdown id: tags:
```bash
voila --template vuetify-default --server_url=/ --base_url=/user/a.grosch@fz-juelich.de/labtest/proxy/8866/ --VoilaConfiguration.file_whitelist="['.*']" dashboards/vuetify-bqplot.ipynb
## Optional
# Using Jupyter Server Proxy
Alternatively, if wanting to access over Jupyter Server Proxy on jupyter-jsc:
> No ssh tunnel needed, but ugly start command.
> Might need to change path to notebook!
%% Cell type:code id: tags:
``` python
port = 8866
```
%% Cell type:code id: tags:
``` python
import os
print("""
voila --template vuetify-default \
--server_url=/ --base_url={prefix}proxy/{port}/ --VoilaConfiguration.file_whitelist="['.*']" \
./jupyter-dashboarding/dashboards/vuetify-bqplot.ipynb
""".format(prefix=os.environ['JUPYTERHUB_SERVICE_PREFIX'], port=port))
```
%% Output
voila --template vuetify-default --server_url=/ --base_url=/user/a.grosch@fz-juelich.de/rhinodiagnost/proxy/8866/ --VoilaConfiguration.file_whitelist="['.*']" ./jupyter-dashboarding/dashboards/vuetify-bqplot.ipynb
%% Cell type:markdown id: tags:
Dashboard now runs on
%% Cell type:code id: tags:
``` python
print("""
https://jupyter-jsc.fz-juelich.de{prefix}proxy/{port}
""".format(prefix=os.environ['JUPYTERHUB_SERVICE_PREFIX'], port=port))
```
%% Output
https://jupyter-jsc.fz-juelich.de/user/a.grosch@fz-juelich.de/rhinodiagnost/proxy/8866
......