#!/usr/bin/env python
# coding: utf-8
# # Using Interact
# The `interact` function (`ipywidgets.interact`) automatically creates user interface (UI) controls for exploring code and data interactively. It is the easiest way to get started using IPython's widgets.
# In[ ]:
# Imports for JupyterLite
try:
import piplite
await piplite.install(['ipywidgets'])
except ImportError:
pass
# In[ ]:
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
# ## Basic `interact`
# At the most basic level, `interact` autogenerates UI controls for function arguments, and then calls the function with those arguments when you manipulate the controls interactively. To use `interact`, you need to define a function that you want to explore. Here is a function that returns its only argument `x`.
# In[ ]:
def f(x):
return x
# When you pass this function as the first argument to `interact` along with an integer keyword argument (`x=10`), a slider is generated and bound to the function parameter.
# In[ ]:
interact(f, x=10);
# When you move the slider, the function is called, and its return value is printed.
#
# If you pass `True` or `False`, `interact` will generate a checkbox:
# In[ ]:
interact(f, x=True);
# If you pass a string, `interact` will generate a text box.
# In[ ]:
interact(f, x='Hi there!');
# `interact` can also be used as a decorator. This allows you to define a function and interact with it in a single shot. As this example shows, `interact` also works with functions that have multiple arguments.
# In[ ]:
@interact(x=True, y=1.0)
def g(x, y):
return (x, y)
# ## Fixing arguments using `fixed`
# There are times when you may want to explore a function using `interact`, but fix one or more of its arguments to specific values. This can be accomplished by wrapping values with the `fixed` function.
# In[ ]:
def h(p, q):
return (p, q)
# When we call `interact`, we pass `fixed(20)` for q to hold it fixed at a value of `20`.
# In[ ]:
interact(h, p=5, q=fixed(20));
# Notice that a slider is only produced for `p` as the value of `q` is fixed.
# ## Widget abbreviations
# When you pass an integer-valued keyword argument of `10` (`x=10`) to `interact`, it generates an integer-valued slider control with a range of `[-10,+3*10]`. In this case, `10` is an *abbreviation* for an actual slider widget:
#
# ```python
# IntSlider(min=-10, max=30, step=1, value=10)
# ```
#
# In fact, we can get the same result if we pass this `IntSlider` as the keyword argument for `x`:
# In[ ]:
interact(f, x=widgets.IntSlider(min=-10, max=30, step=1, value=10));
# The following table gives an overview of different argument types, and how they map to interactive controls:
#
#
# Keyword argument | Widget |
# `True` or `False` | Checkbox |
# `'Hi there'` | Text |
# `value` or `(min,max)` or `(min,max,step)` if integers are passed | IntSlider |
# `value` or `(min,max)` or `(min,max,step)` if floats are passed | FloatSlider |
# `['orange','apple']` or `[('one', 1), ('two', 2)] | Dropdown |
#
# Note that a dropdown is used if a list or a list of tuples is given (signifying discrete choices), and a slider is used if a tuple is given (signifying a range).
# You have seen how the checkbox and text widgets work above. Here, more details about the different abbreviations for sliders and dropdowns are given.
#
# If a 2-tuple of integers is passed `(min, max)`, an integer-valued slider is produced with those minimum and maximum values (inclusively). In this case, the default step size of `1` is used.
# In[ ]:
interact(f, x=(0,4));
# If a 3-tuple of integers is passed `(min,max,step)`, the step size can also be set.
# In[ ]:
interact(f, x=(0,8,2));
# A float-valued slider is produced if *any* of the elements of the tuples are floats. Here the minimum is `0.0`, the maximum is `10.0` and step size is `0.1` (the default).
# In[ ]:
interact(f, x=(0.0,10.0));
# The step size can be changed by passing a third element in the tuple.
# In[ ]:
interact(f, x=(0.0,10.0,0.01));
# For both integer and float-valued sliders, you can pick the initial value of the widget by passing a default keyword argument to the underlying Python function. Here we set the initial value of a float slider to `5.5`.
# In[ ]:
@interact(x=(0.0,20.0,0.5))
def h(x=5.5):
return x
# Dropdown menus are constructed by passing a list of strings. In this case, the strings are both used as the names in the dropdown menu UI and passed to the underlying Python function.
# In[ ]:
interact(f, x=['apples','oranges']);
# If you want a dropdown menu that passes non-string values to the Python function, you can pass a list of `('label', value)` pairs. The first items are the names in the dropdown menu UI and the second items are values that are the arguments passed to the underlying Python function.
# In[ ]:
interact(f, x=[('one', 10), ('two', 20)]);
# Finally, if you need more granular control than that afforded by the abbreviation, you can pass a `ValueWidget` instance as the argument. A `ValueWidget` is a widget that aims to control a single value. Most of the widgets [bundled with ipywidgets](Widget%20List.ipynb) inherit from `ValueWidget`. For more information, see [this section](Widget%20Custom.ipynb#DOMWidget,-ValueWidget-and-Widget) on widget types.
# In[ ]:
interact(f, x=widgets.Combobox(options=["Chicago", "New York", "Washington"], value="Chicago"));
# ## `interactive`
# 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.
#
# 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`.
# 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.
# In[ ]:
from IPython.display import display
def f(a, b):
display(a + b)
return a+b
# Unlike `interact`, `interactive` returns a `Widget` instance rather than immediately displaying the widget.
# In[ ]:
w = interactive(f, a=10, b=20)
# The widget is an `interactive`, a subclass of `VBox`, which is a container for other widgets.
# In[ ]:
type(w)
# The children of the `interactive` are two integer-valued sliders and an output widget, produced by the widget abbreviations above.
# In[ ]:
w.children
# To actually display the widgets, you can use IPython's `display` function.
# In[ ]:
display(w)
# 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.
#
# Here are the current keyword arguments. If you rerun this cell after manipulating the sliders, the values will have changed.
# In[ ]:
w.kwargs
# Here is the current return value of the function.
# In[ ]:
w.result
# ## Disabling continuous updates
# When interacting with long running functions, realtime feedback is a burden instead of being helpful. See the following example:
# In[ ]:
def slow_function(i):
print(int(i),list(x for x in range(int(i)) if
str(x)==str(x)[::-1] and
str(x**2)==str(x**2)[::-1]))
return
# In[ ]:
get_ipython().run_cell_magic('time', '', 'slow_function(1e6)\n')
# Notice that the output is updated even while dragging the mouse on the slider. This is not useful for long running functions due to lagging:
# In[ ]:
from ipywidgets import FloatSlider
interact(slow_function,i=FloatSlider(min=1e5, max=1e7, step=1e5));
# There are two ways to mitigate this. You can either only execute on demand, or restrict execution to mouse release events.
# ### `interact_manual`
# 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.
# In[ ]:
interact_manual(slow_function,i=FloatSlider(min=1e5, max=1e7, step=1e5));
# You can do the same thing with `interactive` by using a `dict` as the second argument, as shown below.
# In[ ]:
slow = interactive(slow_function, {'manual': True}, i=widgets.FloatSlider(min=1e4, max=1e6, step=1e4))
slow
# ### `continuous_update`
# If you are using slider widgets, you can set the `continuous_update` kwarg to `False`. `continuous_update` is a kwarg of slider widgets that restricts executions to mouse release events.
# In[ ]:
interact(slow_function,i=FloatSlider(min=1e5, max=1e7, step=1e5, continuous_update=False));
# ## More control over the user interface: `interactive_output`
#
# `interactive_output` provides additional flexibility: you can control how the UI elements are laid out.
#
# 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.
# In[ ]:
a = widgets.IntSlider()
b = widgets.IntSlider()
c = widgets.IntSlider()
ui = widgets.HBox([a, b, c])
def f(a, b, c):
print((a, b, c))
out = widgets.interactive_output(f, {'a': a, 'b': b, 'c': c})
display(ui, out)
# ## Arguments that are dependent on each other
# Arguments that are dependent on each other can be expressed manually using `observe`. See the following example, where one variable is used to describe the bounds of another. For more information, please see the [widget events example notebook](./Widget%20Events.ipynb).
# In[ ]:
x_widget = FloatSlider(min=0.0, max=10.0, step=0.05)
y_widget = FloatSlider(min=0.5, max=10.0, step=0.05, value=5.0)
def update_x_range(*args):
x_widget.max = 2.0 * y_widget.value
y_widget.observe(update_x_range, 'value')
def printer(x, y):
print(x, y)
interact(printer,x=x_widget, y=y_widget);
# ## Flickering and jumping output
#
# On occasion, you may notice interact output flickering and jumping, causing the notebook scroll position to change as the output is updated. The interactive control has a layout, so we can set its height to an appropriate value (currently chosen manually) so that it will not change size as it is updated.
#
# In[ ]:
get_ipython().run_line_magic('matplotlib', 'inline')
from ipywidgets import interactive
import matplotlib.pyplot as plt
import numpy as np
def f(m, b):
plt.figure(2)
x = np.linspace(-10, 10, num=1000)
plt.plot(x, m * x + b)
plt.ylim(-5, 5)
plt.show()
interactive_plot = interactive(f, m=(-2.0, 2.0), b=(-3, 3, 0.5))
output = interactive_plot.children[-1]
output.layout.height = '350px'
interactive_plot
# ## Interact with multiple functions
#
# You may want to have a single widget interact with multiple functions. This is possible by simply linking the widget to both functions using the interactive_output() function. The order of execution of the functions will be the order they were linked to the widget.
#
# In[1]:
import ipywidgets as widgets
from IPython.display import display
a = widgets.IntSlider(value=5, min=0, max=10)
def f1(a):
display(a)
def f2(a):
display(a * 2)
out1 = widgets.interactive_output(f1, {'a': a})
out2 = widgets.interactive_output(f2, {'a': a})
display(a)
display(out1)
display(out2)
# In[ ]: