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.

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.

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.

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:

interact(f, x=True);

If you pass a string, interact will generate a text box.

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.

@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.

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.

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:

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:

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 argumentWidget
`True` or `False`Checkbox
`'Hi there'`Text
`value` or `(min,max)` or `(min,max,step)` if integers are passedIntSlider
`value` or `(min,max)` or `(min,max,step)` if floats are passedFloatSlider
`['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.

interact(f, x=(0,4));

If a 3-tuple of integers is passed (min,max,step), the step size can also be set.

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).

interact(f, x=(0.0,10.0));

The step size can be changed by passing a third element in the tuple.

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.

@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.

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.

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 inherit from ValueWidget. For more information, see this section on widget types.

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.

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.

w = interactive(f, a=10, b=20)

The widget is an interactive, a subclass of VBox, which is a container for other widgets.

type(w)
ipywidgets.widgets.interaction.interactive

The children of the interactive are two integer-valued sliders and an output widget, produced by the widget abbreviations above.

w.children
(IntSlider(value=10, description='a', max=30, min=-10),
 IntSlider(value=20, description='b', max=60, min=-20),
 Output(outputs=({'output_type': 'display_data', 'metadata': {}, 'data': {'text/plain': '30'}},)))

To actually display the widgets, you can use IPython’s display function.

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.

w.kwargs
{'a': 10, 'b': 20}

Here is the current return value of the function.

w.result
30

Disabling continuous updates#

When interacting with long running functions, realtime feedback is a burden instead of being helpful. See the following example:

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
%%time
slow_function(1e6)
1000000 [0, 1, 2, 3, 11, 22, 101, 111, 121, 202, 212, 1001, 1111, 2002, 10001, 10101, 10201, 11011, 11111, 11211, 20002, 20102, 100001, 101101, 110011, 111111, 200002]
CPU times: user 245 ms, sys: 0 ns, total: 245 ms
Wall time: 245 ms

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:

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.

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.

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.

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.

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.

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.

%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
Matplotlib is building the font cache; this may take a moment.
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
Cell In[34], line 1
----> 1 get_ipython().run_line_magic('matplotlib', 'inline')
      2 from ipywidgets import interactive
      3 import matplotlib.pyplot as plt

File ~/checkouts/readthedocs.org/user_builds/ipywidgets/conda/latest/lib/python3.11/site-packages/IPython/core/interactiveshell.py:2432, in InteractiveShell.run_line_magic(self, magic_name, line, _stack_depth)
   2430     kwargs['local_ns'] = self.get_local_scope(stack_depth)
   2431 with self.builtin_trap:
-> 2432     result = fn(*args, **kwargs)
   2434 # The code below prevents the output from being displayed
   2435 # when using magics with decorator @output_can_be_silenced
   2436 # when the last Python token in the expression is a ';'.
   2437 if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False):

File ~/checkouts/readthedocs.org/user_builds/ipywidgets/conda/latest/lib/python3.11/site-packages/IPython/core/magics/pylab.py:99, in PylabMagics.matplotlib(self, line)
     97     print("Available matplotlib backends: %s" % backends_list)
     98 else:
---> 99     gui, backend = self.shell.enable_matplotlib(args.gui.lower() if isinstance(args.gui, str) else args.gui)
    100     self._show_matplotlib_backend(args.gui, backend)

File ~/checkouts/readthedocs.org/user_builds/ipywidgets/conda/latest/lib/python3.11/site-packages/IPython/core/interactiveshell.py:3606, in InteractiveShell.enable_matplotlib(self, gui)
   3585 def enable_matplotlib(self, gui=None):
   3586     """Enable interactive matplotlib and inline figure support.
   3587 
   3588     This takes the following steps:
   (...)
   3604         display figures inline.
   3605     """
-> 3606     from matplotlib_inline.backend_inline import configure_inline_support
   3608     from IPython.core import pylabtools as pt
   3609     gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select)

File ~/checkouts/readthedocs.org/user_builds/ipywidgets/conda/latest/lib/python3.11/site-packages/matplotlib_inline/__init__.py:1
----> 1 from . import backend_inline, config  # noqa
      2 __version__ = "0.1.6"  # noqa

File ~/checkouts/readthedocs.org/user_builds/ipywidgets/conda/latest/lib/python3.11/site-packages/matplotlib_inline/backend_inline.py:8
      6 import matplotlib
      7 from matplotlib import colors
----> 8 from matplotlib.backends import backend_agg
      9 from matplotlib.backends.backend_agg import FigureCanvasAgg
     10 from matplotlib._pylab_helpers import Gcf

File ~/checkouts/readthedocs.org/user_builds/ipywidgets/conda/latest/lib/python3.11/site-packages/matplotlib/backends/backend_agg.py:31
     29 import matplotlib as mpl
     30 from matplotlib import _api, cbook
---> 31 from matplotlib.backend_bases import (
     32     _Backend, FigureCanvasBase, FigureManagerBase, RendererBase)
     33 from matplotlib.font_manager import fontManager as _fontManager, get_font
     34 from matplotlib.ft2font import (LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING,
     35                                 LOAD_DEFAULT, LOAD_NO_AUTOHINT)

File ~/checkouts/readthedocs.org/user_builds/ipywidgets/conda/latest/lib/python3.11/site-packages/matplotlib/backend_bases.py:46
     43 import numpy as np
     45 import matplotlib as mpl
---> 46 from matplotlib import (
     47     _api, backend_tools as tools, cbook, colors, _docstring, text,
     48     _tight_bbox, transforms, widgets, is_interactive, rcParams)
     49 from matplotlib._pylab_helpers import Gcf
     50 from matplotlib.backend_managers import ToolManager

File ~/checkouts/readthedocs.org/user_builds/ipywidgets/conda/latest/lib/python3.11/site-packages/matplotlib/text.py:16
     14 from . import _api, artist, cbook, _docstring
     15 from .artist import Artist
---> 16 from .font_manager import FontProperties
     17 from .patches import FancyArrowPatch, FancyBboxPatch, Rectangle
     18 from .textpath import TextPath, TextToPath  # noqa # Logically located here

File ~/checkouts/readthedocs.org/user_builds/ipywidgets/conda/latest/lib/python3.11/site-packages/matplotlib/font_manager.py:1582
   1578     _log.info("generated new fontManager")
   1579     return fm
-> 1582 fontManager = _load_fontmanager()
   1583 findfont = fontManager.findfont
   1584 get_font_names = fontManager.get_font_names

File ~/checkouts/readthedocs.org/user_builds/ipywidgets/conda/latest/lib/python3.11/site-packages/matplotlib/font_manager.py:1576, in _load_fontmanager(try_read_cache)
   1574             _log.debug("Using fontManager instance from %s", fm_path)
   1575             return fm
-> 1576 fm = FontManager()
   1577 json_dump(fm, fm_path)
   1578 _log.info("generated new fontManager")

File ~/checkouts/readthedocs.org/user_builds/ipywidgets/conda/latest/lib/python3.11/site-packages/matplotlib/font_manager.py:1043, in FontManager.__init__(self, size, weight)
   1040 for path in [*findSystemFonts(paths, fontext=fontext),
   1041              *findSystemFonts(fontext=fontext)]:
   1042     try:
-> 1043         self.addfont(path)
   1044     except OSError as exc:
   1045         _log.info("Failed to open font file %s: %s", path, exc)

File ~/checkouts/readthedocs.org/user_builds/ipywidgets/conda/latest/lib/python3.11/site-packages/matplotlib/font_manager.py:1076, in FontManager.addfont(self, path)
   1074     self.afmlist.append(prop)
   1075 else:
-> 1076     font = ft2font.FT2Font(path)
   1077     prop = ttfFontProperty(font)
   1078     self.ttflist.append(prop)

KeyboardInterrupt: 

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.

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)