From 5d97cfcbc37246a9cc81a957ceaeb3277956ca81 2014-06-25 14:49:56 From: Gordon Ball Date: 2014-06-25 14:49:56 Subject: [PATCH] Add 'on_demand' option to interact() so that long-running functions can be started only when explicitly requested --- diff --git a/IPython/html/widgets/interaction.py b/IPython/html/widgets/interaction.py index 1b59de8..93b6e7c 100644 --- a/IPython/html/widgets/interaction.py +++ b/IPython/html/widgets/interaction.py @@ -23,7 +23,7 @@ from inspect import getcallargs from IPython.core.getipython import get_ipython from IPython.html.widgets import (Widget, TextWidget, FloatSliderWidget, IntSliderWidget, CheckboxWidget, DropdownWidget, - ContainerWidget, DOMWidget) + ContainerWidget, DOMWidget, ButtonWidget) 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) + on_demand = kwargs.pop('on_demand', False) kwargs_widgets = [] container = ContainerWidget() container.result = None @@ -194,10 +195,15 @@ 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 on_demand: + on_demand_button = ButtonWidget(description="Run %s" % f.__name__) + c.append(on_demand_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 @@ -214,16 +220,22 @@ def interactive(__interact_f, **kwargs): ip.showtraceback() # Wire up the widgets - for widget in kwargs_widgets: - widget.on_trait_change(call_f, 'value') + # If we are doing on demand 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 on_demand: + on_demand_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: