Merge pull request #6051 from chronitis/interact-widget-on-demand

Interact on_demand option
Brian E. Granger 12 years ago
commit 1e8b4ee586

@ -9,7 +9,7 @@ from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRang
from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select
from .widget_selectioncontainer import Tab, Accordion
from .widget_string import HTML, Latex, Text, Textarea
from .interaction import interact, interactive, fixed
from .interaction import interact, interactive, fixed, interact_manual
# Deprecated classes
from .widget_bool import CheckboxWidget, ToggleButtonWidget

@ -23,7 +23,7 @@ from inspect import getcallargs
from IPython.core.getipython import get_ipython
from IPython.html.widgets import (Widget, Text,
FloatSlider, IntSlider, Checkbox, Dropdown,
Box, DOMWidget)
Box, Button, DOMWidget)
from IPython.display import display, clear_output
from IPython.utils.py3compat import string_types, unicode_type
from IPython.utils.traitlets import HasTraits, Any, Unicode
@ -114,7 +114,7 @@ def _widget_from_abbrev(abbrev, default=empty):
"""Build a Widget instance given an abbreviation or Widget."""
if isinstance(abbrev, Widget) or isinstance(abbrev, fixed):
return abbrev
widget = _widget_abbrev(abbrev)
if default is not empty and isinstance(abbrev, (list, tuple, dict)):
# if it's not a single-value abbreviation,
@ -175,6 +175,7 @@ def interactive(__interact_f, **kwargs):
"""Build a group of widgets to interact with a function."""
f = __interact_f
co = kwargs.pop('clear_output', True)
manual = kwargs.pop('__manual', False)
kwargs_widgets = []
container = Box()
container.result = None
@ -194,16 +195,23 @@ def interactive(__interact_f, **kwargs):
# so that traitlets notices the update. We skip any objects (such as fixed) that
# are not DOMWidgets.
c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
# If we are only to run the function on demand, add a button to request this
if manual:
manual_button = Button(description="Run %s" % f.__name__)
c.append(manual_button)
container.children = c
# Build the callback
def call_f(name, old, new):
def call_f(name=None, old=None, new=None):
container.kwargs = {}
for widget in kwargs_widgets:
value = widget.value
container.kwargs[widget.description] = value
if co:
clear_output(wait=True)
if manual:
manual_button.disabled = True
try:
container.result = f(**container.kwargs)
except Exception as e:
@ -212,18 +220,27 @@ def interactive(__interact_f, **kwargs):
container.log.warn("Exception in interact callback: %s", e, exc_info=True)
else:
ip.showtraceback()
finally:
if manual:
manual_button.disabled = False
# Wire up the widgets
for widget in kwargs_widgets:
widget.on_trait_change(call_f, 'value')
# If we are doing manual running, the callback is only triggered by the button
# Otherwise, it is triggered for every trait change received
# On-demand running also suppresses running the fucntion with the initial parameters
if manual:
manual_button.on_click(call_f)
else:
for widget in kwargs_widgets:
widget.on_trait_change(call_f, 'value')
container.on_displayed(lambda _: call_f(None, None, None))
container.on_displayed(lambda _: call_f(None, None, None))
return container
def interact(__interact_f=None, **kwargs):
"""interact(f, **kwargs)
Interact with a function using widgets."""
# positional arg support in: https://gist.github.com/8851331
if __interact_f is not None:
@ -249,6 +266,16 @@ def interact(__interact_f=None, **kwargs):
return f
return dec
def interact_manual(__interact_f=None, **kwargs):
"""interact_manual(f, **kwargs)
As `interact()`, generates widgets for each argument, but rather than running
the function after each widget change, adds a "Run" button and waits for it
to be clicked. Useful if the function is long-running and has several
parameters to change.
"""
return interact(__interact_f, __manual=True, **kwargs)
class fixed(HasTraits):
"""A pseudo-widget whose value is fixed and never synced to the client."""
value = Any(help="Any Python object")

@ -481,6 +481,19 @@ def test_custom_description():
description='foo',
)
def test_interact_manual_button():
c = interactive(f, __manual=True)
w = c.children[0]
check_widget(w, cls=widgets.Button)
def test_interact_manual_nocall():
callcount = 0
def calltest(testarg):
callcount += 1
c = interactive(calltest, testarg=5, __manual=True)
c.children[0].value = 10
nt.assert_equal(callcount, 0)
def test_int_range_logic():
irsw = widgets.IntRangeSlider
w = irsw(value=(2, 4), min=0, max=6)

Loading…
Cancel
Save