##// END OF EJS Templates
Prevent output images from being larger than their container.
Prevent output images from being larger than their container.

File last commit:

r20362:1ebc968a
r20376:453bb830
Show More
interaction.py
344 lines | 12.3 KiB | text/x-python | PythonLexer
Brian E. Granger
Utter interact insanity....
r15140 """Interact with functions using widgets."""
Brian E. Granger
Adding interact.py.
r15132
Min RK
allow interact(instancemethod)...
r19930 # Copyright (c) IPython Development Team.
Brian E. Granger
Adding interact.py.
r15132 # Distributed under the terms of the Modified BSD License.
Brian E. Granger
Utter interact insanity....
r15140 from __future__ import print_function
Thomas Kluyver
Get widgets from function annotations and default arguments....
r15137 try: # Python >= 3.3
from inspect import signature, Parameter
except ImportError:
from IPython.utils.signatures import signature, Parameter
Brian E. Granger
Utter interact insanity....
r15140 from inspect import getcallargs
Thomas Kluyver
Get widgets from function annotations and default arguments....
r15137
MinRK
catch errors at a lower level in interact...
r15193 from IPython.core.getipython import get_ipython
Jonathan Frederic
Renamed *Widget to *,...
r17598 from IPython.html.widgets import (Widget, Text,
FloatSlider, IntSlider, Checkbox, Dropdown,
Jonathan Frederic
Revert "Use Output widget with Interact"
r20362 Box, Button, DOMWidget)
Brian E. Granger
Adding interact.py.
r15132 from IPython.display import display, clear_output
from IPython.utils.py3compat import string_types, unicode_type
MinRK
set default value from dict
r15398 from IPython.utils.traitlets import HasTraits, Any, Unicode
MinRK
set default value from signature defaults in interact...
r15348
empty = Parameter.empty
Brian E. Granger
Adding interact.py.
r15132
def _matches(o, pattern):
Brian E. Granger
Utter interact insanity....
r15140 """Match a pattern of types in a sequence."""
Brian E. Granger
Adding interact.py.
r15132 if not len(o) == len(pattern):
return False
comps = zip(o,pattern)
return all(isinstance(obj,kind) for obj,kind in comps)
MinRK
ensure range widgets get value that is on a step
r15148 def _get_min_max_value(min, max, value=None, step=None):
Brian E. Granger
Updating interact to work with latest state of widgets.
r15135 """Return min, max, value given input values with possible None."""
if value is None:
if not max > min:
raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
value = min + abs(min-max)/2
value = type(min)(value)
elif min is None and max is None:
if value == 0.0:
min, max, value = 0.0, 1.0, 0.5
elif value == 0:
min, max, value = 0, 1, 0
MinRK
simplify float/int slider abbreviation logic...
r15150 elif isinstance(value, (int, float)):
Brian E. Granger
Utter interact insanity....
r15140 min, max = (-value, 3*value) if value > 0 else (3*value, -value)
Brian E. Granger
Updating interact to work with latest state of widgets.
r15135 else:
Thomas Kluyver
Get widgets from function annotations and default arguments....
r15137 raise TypeError('expected a number, got: %r' % value)
Brian E. Granger
Updating interact to work with latest state of widgets.
r15135 else:
raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
MinRK
simplify float/int slider abbreviation logic...
r15150 if step is not None:
MinRK
ensure range widgets get value that is on a step
r15148 # ensure value is on a step
r = (value - min) % step
value = value - r
Brian E. Granger
Adding interact.py.
r15132 return min, max, value
Thomas Kluyver
Get widgets from function annotations and default arguments....
r15137 def _widget_abbrev_single_value(o):
MinRK
simplify float/int slider abbreviation logic...
r15150 """Make widgets from single values, which can be used as parameter defaults."""
Brian E. Granger
Adding interact.py.
r15132 if isinstance(o, string_types):
Jonathan Frederic
Renamed *Widget to *,...
r17598 return Text(value=unicode_type(o))
Brian E. Granger
Adding interact.py.
r15132 elif isinstance(o, dict):
Nicholas Bollweg
squashing the whitespace changes
r20287 return Dropdown(options=o)
Brian E. Granger
Adding interact.py.
r15132 elif isinstance(o, bool):
Jonathan Frederic
Renamed *Widget to *,...
r17598 return Checkbox(value=o)
Brian E. Granger
Adding interact.py.
r15132 elif isinstance(o, float):
Brian E. Granger
Updating interact to work with latest state of widgets.
r15135 min, max, value = _get_min_max_value(None, None, o)
Jonathan Frederic
Renamed *Widget to *,...
r17598 return FloatSlider(value=o, min=min, max=max)
Brian E. Granger
Adding interact.py.
r15132 elif isinstance(o, int):
Brian E. Granger
Updating interact to work with latest state of widgets.
r15135 min, max, value = _get_min_max_value(None, None, o)
Jonathan Frederic
Renamed *Widget to *,...
r17598 return IntSlider(value=o, min=min, max=max)
Brian E. Granger
Utter interact insanity....
r15140 else:
return None
Thomas Kluyver
Get widgets from function annotations and default arguments....
r15137
def _widget_abbrev(o):
"""Make widgets from abbreviations: single values, lists or tuples."""
MinRK
simplify float/int slider abbreviation logic...
r15150 float_or_int = (float, int)
Brian E. Granger
Adding interact.py.
r15132 if isinstance(o, (list, tuple)):
MinRK
simplify float/int slider abbreviation logic...
r15150 if o and all(isinstance(x, string_types) for x in o):
Nicholas Bollweg
squashing the whitespace changes
r20287 return Dropdown(options=[unicode_type(k) for k in o])
MinRK
simplify float/int slider abbreviation logic...
r15150 elif _matches(o, (float_or_int, float_or_int)):
MinRK
ensure range widgets get value that is on a step
r15148 min, max, value = _get_min_max_value(o[0], o[1])
MinRK
simplify float/int slider abbreviation logic...
r15150 if all(isinstance(_, int) for _ in o):
Jonathan Frederic
Renamed *Widget to *,...
r17598 cls = IntSlider
MinRK
simplify float/int slider abbreviation logic...
r15150 else:
Jonathan Frederic
Renamed *Widget to *,...
r17598 cls = FloatSlider
MinRK
simplify float/int slider abbreviation logic...
r15150 return cls(value=value, min=min, max=max)
elif _matches(o, (float_or_int, float_or_int, float_or_int)):
step = o[2]
if step <= 0:
raise ValueError("step must be >= 0, not %r" % step)
min, max, value = _get_min_max_value(o[0], o[1], step=step)
if all(isinstance(_, int) for _ in o):
Jonathan Frederic
Renamed *Widget to *,...
r17598 cls = IntSlider
MinRK
simplify float/int slider abbreviation logic...
r15150 else:
Jonathan Frederic
Renamed *Widget to *,...
r17598 cls = FloatSlider
MinRK
simplify float/int slider abbreviation logic...
r15150 return cls(value=value, min=min, max=max, step=step)
Thomas Kluyver
Get widgets from function annotations and default arguments....
r15137 else:
return _widget_abbrev_single_value(o)
MinRK
set default value from signature defaults in interact...
r15348 def _widget_from_abbrev(abbrev, default=empty):
"""Build a Widget instance given an abbreviation or Widget."""
MinRK
s/const/fixed/
r15147 if isinstance(abbrev, Widget) or isinstance(abbrev, fixed):
Brian E. Granger
Utter interact insanity....
r15140 return abbrev
Gordon Ball
Add 'on_demand' option to interact() so that long-running functions can be started only when explicitly requested
r17058
Brian E. Granger
Utter interact insanity....
r15140 widget = _widget_abbrev(abbrev)
MinRK
set default value from dict
r15398 if default is not empty and isinstance(abbrev, (list, tuple, dict)):
MinRK
set default value from signature defaults in interact...
r15348 # if it's not a single-value abbreviation,
# set the initial value from the default
try:
widget.value = default
MinRK
set default value from dict
r15398 except Exception:
# ignore failure to set default
MinRK
set default value from signature defaults in interact...
r15348 pass
Thomas Kluyver
Get widgets from function annotations and default arguments....
r15137 if widget is None:
MinRK
fix ValueError format message
r15151 raise ValueError("%r cannot be transformed to a Widget" % (abbrev,))
Thomas Kluyver
Get widgets from function annotations and default arguments....
r15137 return widget
MinRK
remove positional arg support from interact
r15145 def _yield_abbreviations_for_parameter(param, kwargs):
Brian E. Granger
Utter interact insanity....
r15140 """Get an abbreviation for a function parameter."""
name = param.name
kind = param.kind
ann = param.annotation
default = param.default
MinRK
set default value from signature defaults in interact...
r15348 not_found = (name, empty, empty)
if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
Brian E. Granger
Utter interact insanity....
r15140 if name in kwargs:
MinRK
set default value from signature defaults in interact...
r15348 value = kwargs.pop(name)
Brian E. Granger
Utter interact insanity....
r15140 elif ann is not empty:
MinRK
set default value from signature defaults in interact...
r15348 value = ann
Brian E. Granger
Utter interact insanity....
r15140 elif default is not empty:
MinRK
set default value from signature defaults in interact...
r15348 value = default
Brian E. Granger
Utter interact insanity....
r15140 else:
MinRK
remove positional arg support from interact
r15145 yield not_found
MinRK
set default value from signature defaults in interact...
r15348 yield (name, value, default)
Brian E. Granger
Utter interact insanity....
r15140 elif kind == Parameter.VAR_KEYWORD:
# In this case name=kwargs and we yield the items in kwargs with their keys.
for k, v in kwargs.copy().items():
kwargs.pop(k)
MinRK
set default value from signature defaults in interact...
r15348 yield k, v, empty
Brian E. Granger
Adding interact.py.
r15132
MinRK
remove positional arg support from interact
r15145 def _find_abbreviations(f, kwargs):
"""Find the abbreviations for a function and kwargs passed to interact."""
Brian E. Granger
Utter interact insanity....
r15140 new_kwargs = []
for param in signature(f).parameters.values():
MinRK
set default value from signature defaults in interact...
r15348 for name, value, default in _yield_abbreviations_for_parameter(param, kwargs):
if value is empty:
Brian E. Granger
Utter interact insanity....
r15140 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
MinRK
set default value from signature defaults in interact...
r15348 new_kwargs.append((name, value, default))
MinRK
remove positional arg support from interact
r15145 return new_kwargs
Brian E. Granger
Utter interact insanity....
r15140
def _widgets_from_abbreviations(seq):
"""Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
result = []
MinRK
set default value from signature defaults in interact...
r15348 for name, abbrev, default in seq:
widget = _widget_from_abbrev(abbrev, default)
Jessica B. Hamrick
Only set widget description in interact if it does not already exist
r16657 if not widget.description:
widget.description = name
Min RK
Don't use widget.description as kwarg key...
r20026 widget._kwarg = name
Brian E. Granger
Utter interact insanity....
r15140 result.append(widget)
return result
MinRK
remove positional arg support from interact
r15145 def interactive(__interact_f, **kwargs):
Torsten Bittner
Add documentation for interactive function.
r20061 """
Builds a group of interactive widgets tied to a function and places the
group into a Box container.
Returns
-------
container : a Box instance containing multiple widgets
Parameters
----------
__interact_f : function
The function to which the interactive widgets are tied. The **kwargs
should match the function signature.
**kwargs : various, optional
An interactive widget is created for each keyword argument that is a
valid widget abbreviation.
"""
MinRK
remove positional arg support from interact
r15145 f = __interact_f
Brian E. Granger
Adding interact.py.
r15132 co = kwargs.pop('clear_output', True)
Gordon Ball
s/choose/manual/g
r17923 manual = kwargs.pop('__manual', False)
Brian E. Granger
Utter interact insanity....
r15140 kwargs_widgets = []
Jonathan Frederic
s/Container/Box
r17637 container = Box()
Brian E. Granger
Adding interact.py.
r15132 container.result = None
Brian E. Granger
Utter interact insanity....
r15140 container.args = []
Brian E. Granger
Updating interact to work with latest state of widgets.
r15135 container.kwargs = dict()
Brian E. Granger
Utter interact insanity....
r15140 kwargs = kwargs.copy()
MinRK
remove positional arg support from interact
r15145 new_kwargs = _find_abbreviations(f, kwargs)
Brian E. Granger
Utter interact insanity....
r15140 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
# that will lead to a valid call of the function. This protects against unspecified
# and doubly-specified arguments.
MinRK
set default value from signature defaults in interact...
r15348 getcallargs(f, **{n:v for n,v,_ in new_kwargs})
Brian E. Granger
Utter interact insanity....
r15140 # Now build the widgets from the abbreviations.
kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
Thomas Kluyver
Get widgets from function annotations and default arguments....
r15137 # This has to be done as an assignment, not using container.children.append,
MinRK
s/const/fixed/
r15147 # so that traitlets notices the update. We skip any objects (such as fixed) that
Brian E. Granger
Adding const pseudo-widget for fixing arguments to interact.
r15142 # are not DOMWidgets.
MinRK
remove positional arg support from interact
r15145 c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
Gordon Ball
Add 'on_demand' option to interact() so that long-running functions can be started only when explicitly requested
r17058
# If we are only to run the function on demand, add a button to request this
Gordon Ball
s/choose/manual/g
r17923 if manual:
manual_button = Button(description="Run %s" % f.__name__)
c.append(manual_button)
Brian E. Granger
Adding const pseudo-widget for fixing arguments to interact.
r15142 container.children = c
Brian E. Granger
Updating interact to new APIs.
r15134
Brian E. Granger
Adding interact.py.
r15132 # Build the callback
Gordon Ball
Add 'on_demand' option to interact() so that long-running functions can be started only when explicitly requested
r17058 def call_f(name=None, old=None, new=None):
Jonathan Frederic
Revert "Use Output widget with Interact"
r20362 container.kwargs = {}
for widget in kwargs_widgets:
value = widget.value
container.kwargs[widget._kwarg] = value
if co:
clear_output(wait=True)
if manual:
manual_button.disabled = True
try:
container.result = f(**container.kwargs)
except Exception as e:
ip = get_ipython()
if ip is None:
container.log.warn("Exception in interact callback: %s", e, exc_info=True)
else:
ip.showtraceback()
finally:
Gordon Ball
s/choose/manual/g
r17923 if manual:
Jonathan Frederic
Revert "Use Output widget with Interact"
r20362 manual_button.disabled = False
Brian E. Granger
Adding interact.py.
r15132
# Wire up the widgets
Gordon Ball
s/choose/manual/g
r17923 # If we are doing manual running, the callback is only triggered by the button
Gordon Ball
Add 'on_demand' option to interact() so that long-running functions can be started only when explicitly requested
r17058 # Otherwise, it is triggered for every trait change received
Torsten Bittner
Add documentation for interactive function.
r20061 # On-demand running also suppresses running the function with the initial parameters
Gordon Ball
s/choose/manual/g
r17923 if manual:
manual_button.on_click(call_f)
Gordon Ball
Add 'on_demand' option to interact() so that long-running functions can be started only when explicitly requested
r17058 else:
for widget in kwargs_widgets:
widget.on_trait_change(call_f, 'value')
Brian E. Granger
Adding interact.py.
r15132
Gordon Ball
Add 'on_demand' option to interact() so that long-running functions can be started only when explicitly requested
r17058 container.on_displayed(lambda _: call_f(None, None, None))
Brian E. Granger
Adding interact.py.
r15132
return container
MinRK
remove positional arg support from interact
r15145 def interact(__interact_f=None, **kwargs):
Torsten Bittner
Add documentation for interact function.
r20051 """
Displays interactive widgets which are tied to a function.
Expects the first argument to be a function. Parameters to this function are
Torsten Bittner
Add documentation for interactive function.
r20061 widget abbreviations passed in as keyword arguments (**kwargs). Can be used
as a decorator (see examples).
Torsten Bittner
Add documentation for interact function.
r20051
Returns
-------
Torsten Bittner
Add documentation for interactive function.
r20061 f : __interact_f with interactive widget attached to it.
Torsten Bittner
Add documentation for interact function.
r20051
Parameters
----------
__interact_f : function
The function to which the interactive widgets are tied. The **kwargs
should match the function signature. Passed to :func:`interactive()`
**kwargs : various, optional
Torsten Bittner
Add documentation for interactive function.
r20061 An interactive widget is created for each keyword argument that is a
valid widget abbreviation. Passed to :func:`interactive()`
Torsten Bittner
Add documentation for interact function.
r20051
Examples
--------
Renders an interactive text field that shows the greeting with the passed in
text.
Gordon Ball
Add 'on_demand' option to interact() so that long-running functions can be started only when explicitly requested
r17058
Torsten Bittner
Add documentation for interact function.
r20051 1. Invocation of interact as a function
def greeting(text="World"):
print "Hello {}".format(text)
interact(greeting, text="IPython Widgets")
2. Invocation of interact as a decorator
@interact
def greeting(text="World"):
print "Hello {}".format(text)
3. Invocation of interact as a decorator with named parameters
@interact(text="IPython Widgets")
def greeting(text="World"):
print "Hello {}".format(text)
Renders an interactive slider widget and prints square of number.
1. Invocation of interact as a function
def square(num=1):
print "{} squared is {}".format(num, num*num)
interact(square, num=5)
2. Invocation of interact as a decorator
@interact
def square(num=2):
print "{} squared is {}".format(num, num*num)
3. Invocation of interact as a decorator with named parameters
@interact(num=5)
def square(num=2):
print "{} squared is {}".format(num, num*num)
"""
MinRK
remove positional arg support from interact
r15145 # positional arg support in: https://gist.github.com/8851331
if __interact_f is not None:
Torsten Bittner
Add documentation for interact function.
r20051 # This branch handles the cases 1 and 2
MinRK
remove positional arg support from interact
r15145 # 1. interact(f, **kwargs)
Brian E. Granger
Adding decorator forms of interact. Yeah!
r15141 # 2. @interact
# def f(*args, **kwargs):
# ...
MinRK
remove positional arg support from interact
r15145 f = __interact_f
w = interactive(f, **kwargs)
Min RK
allow interact(instancemethod)...
r19930 try:
f.widget = w
except AttributeError:
# some things (instancemethods) can't have attributes attached,
# so wrap in a lambda
f = lambda *args, **kwargs: __interact_f(*args, **kwargs)
f.widget = w
Brian E. Granger
Adding decorator forms of interact. Yeah!
r15141 display(w)
MinRK
return f with @interact, not just @interact(**kwargs)
r15152 return f
Brian E. Granger
Adding decorator forms of interact. Yeah!
r15141 else:
Torsten Bittner
Add documentation for interact function.
r20051 # This branch handles the case 3
MinRK
remove positional arg support from interact
r15145 # @interact(a=30, b=40)
Brian E. Granger
Adding decorator forms of interact. Yeah!
r15141 # def f(*args, **kwargs):
# ...
def dec(f):
Min RK
allow interact(instancemethod)...
r19930 return interact(f, **kwargs)
Brian E. Granger
Adding decorator forms of interact. Yeah!
r15141 return dec
Brian E. Granger
Utter interact insanity....
r15140
Gordon Ball
s/choose/manual/g
r17923 def interact_manual(__interact_f=None, **kwargs):
"""interact_manual(f, **kwargs)
Gordon Ball
Change from on_demand kwarg to interact_choose function
r17709
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.
"""
Gordon Ball
s/choose/manual/g
r17923 return interact(__interact_f, __manual=True, **kwargs)
Gordon Ball
Change from on_demand kwarg to interact_choose function
r17709
MinRK
s/const/fixed/
r15147 class fixed(HasTraits):
"""A pseudo-widget whose value is fixed and never synced to the client."""
Brian E. Granger
Adding const pseudo-widget for fixing arguments to interact.
r15142 value = Any(help="Any Python object")
description = Unicode('', help="Any Python object")
def __init__(self, value, **kwargs):
MinRK
s/const/fixed/
r15147 super(fixed, self).__init__(value=value, **kwargs)