##// END OF EJS Templates
Adding interact.py.
Brian E. Granger -
Show More
@@ -0,0 +1,123 b''
1 """Interact with functions using widgets.
2 """
3
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2013, the IPython Development Team.
6 #
7 # Distributed under the terms of the Modified BSD License.
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
15
16 from IPython.html.widgets import (Widget, StringWidget,
17 FloatRangeWidget, IntRangeWidget, BoolWidget, SelectionWidget,
18 ContainerWidget)
19 from IPython.display import display, clear_output
20 from IPython.utils.py3compat import string_types, unicode_type
21
22 #-----------------------------------------------------------------------------
23 # Classes and Functions
24 #-----------------------------------------------------------------------------
25
26
27 def _matches(o, pattern):
28 if not len(o) == len(pattern):
29 return False
30 comps = zip(o,pattern)
31 return all(isinstance(obj,kind) for obj,kind in comps)
32
33
34 def _min_max_value(o):
35 min = o[0]
36 max = o[1]
37 if not max > min:
38 raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
39 value = min + abs(o[0]-o[1])/2
40 return min, max, value
41
42 def _widget_abbrev(o):
43 if isinstance(o, string_types):
44 return StringWidget(value=unicode_type(o))
45 elif isinstance(o, dict):
46 values = [unicode_type(k) for k in o]
47 w = SelectionWidget(value=values[0], values=values)
48 w.actual_values = o
49 return w
50 # Special case float and int == 0.0
51 # get_range(value):
52 elif isinstance(o, bool):
53 return BoolWidget(value=o)
54 elif isinstance(o, float):
55 return FloatRangeWidget(value=o, min=-o, max=3.0*o)
56 elif isinstance(o, int):
57 return IntRangeWidget(value=o, min=-o, max=3*o)
58 if isinstance(o, (list, tuple)):
59 if _matches(o, (int, int)):
60 min, max, value = _min_max_value(o)
61 return IntRangeWidget(value=int(value), min=min, max=max)
62 elif _matches(o, (int, int, int)):
63 min, max, value = _min_max_value(o)
64 return IntRangeWidget(value=int(value), min=min, max=max, step=o[2])
65 elif _matches(o, (float, float)):
66 min, max, value = _min_max_value(o)
67 return FloatRangeWidget(value=value, min=min, max=max)
68 elif _matches(o, (float, float, float)):
69 min, max, value = _min_max_value(o)
70 return FloatRangeWidget(value=value, min=min, max=max, step=o[2])
71 elif _matches(o, (float, float, int)):
72 min, max, value = _min_max_value(o)
73 return FloatRangeWidget(value=value, min=min, max=max, step=float(o[2]))
74 elif all(isinstance(x, string_types) for x in o):
75 return SelectionWidget(value=unicode_type(o[0]),
76 values=[unicode_type(k) for k in o])
77
78
79 def interactive(f, **kwargs):
80 """Interact with a function using widgets."""
81
82 co = kwargs.pop('clear_output', True)
83 # First convert all args to Widget instances
84 widgets = []
85 container = ContainerWidget()
86 container.result = None
87 container.arguments = dict()
88 for key, value in kwargs.items():
89 if isinstance(value, Widget):
90 widget = value
91 else:
92 widget = _widget_abbrev(value)
93 if widget is None:
94 raise ValueError("Object cannot be transformed to a Widget")
95 widgets.append((key,widget))
96 widget.parent = container
97 widgets.sort(key=lambda e: e[1].__class__.__name__)
98
99 # Build the callback
100 def call_f(name, old, new):
101 actual_kwargs = {}
102 for key, widget in widgets:
103 value = widget.value
104 if hasattr(widget, 'actual_values'):
105 value = widget.actual_values[value]
106 container.arguments[key] = value
107 actual_kwargs[key] = value
108 if co:
109 clear_output(wait=True)
110 container.result = f(**actual_kwargs)
111
112 # Wire up the widgets
113 for key, widget in widgets:
114 widget.on_trait_change(call_f, 'value')
115 widget.description = key
116
117 container.on_displayed(lambda : call_f(None, None, None))
118
119 return container
120
121 def interact(f, **kwargs):
122 w = interactive(f, **kwargs)
123 display(w)
General Comments 0
You need to be logged in to leave comments. Login now