##// END OF EJS Templates
s/const/fixed/
MinRK -
Show More
@@ -1,12 +1,12 b''
1 from .widget import Widget, DOMWidget, CallbackDispatcher
1 from .widget import Widget, DOMWidget, CallbackDispatcher
2
2
3 from .widget_bool import CheckboxWidget, ToggleButtonWidget
3 from .widget_bool import CheckboxWidget, ToggleButtonWidget
4 from .widget_button import ButtonWidget
4 from .widget_button import ButtonWidget
5 from .widget_container import ContainerWidget, PopupWidget
5 from .widget_container import ContainerWidget, PopupWidget
6 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
6 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
7 from .widget_image import ImageWidget
7 from .widget_image import ImageWidget
8 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
8 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
9 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
9 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
10 from .widget_selectioncontainer import TabWidget, AccordionWidget
10 from .widget_selectioncontainer import TabWidget, AccordionWidget
11 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
11 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
12 from .interaction import interact, interactive, const
12 from .interaction import interact, interactive, fixed
@@ -1,248 +1,248 b''
1 """Interact with functions using widgets."""
1 """Interact with functions using widgets."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2013, the IPython Development Team.
4 # Copyright (c) 2013, the IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 from __future__ import print_function
15 from __future__ import print_function
16
16
17 try: # Python >= 3.3
17 try: # Python >= 3.3
18 from inspect import signature, Parameter
18 from inspect import signature, Parameter
19 except ImportError:
19 except ImportError:
20 from IPython.utils.signatures import signature, Parameter
20 from IPython.utils.signatures import signature, Parameter
21 from inspect import getcallargs
21 from inspect import getcallargs
22
22
23 from IPython.html.widgets import (Widget, TextWidget,
23 from IPython.html.widgets import (Widget, TextWidget,
24 FloatSliderWidget, IntSliderWidget, CheckboxWidget, DropdownWidget,
24 FloatSliderWidget, IntSliderWidget, CheckboxWidget, DropdownWidget,
25 ContainerWidget, DOMWidget)
25 ContainerWidget, DOMWidget)
26 from IPython.display import display, clear_output
26 from IPython.display import display, clear_output
27 from IPython.utils.py3compat import string_types, unicode_type
27 from IPython.utils.py3compat import string_types, unicode_type
28 from IPython.utils.traitlets import HasTraits, Any, Unicode
28 from IPython.utils.traitlets import HasTraits, Any, Unicode
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Classes and Functions
31 # Classes and Functions
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34
34
35 def _matches(o, pattern):
35 def _matches(o, pattern):
36 """Match a pattern of types in a sequence."""
36 """Match a pattern of types in a sequence."""
37 if not len(o) == len(pattern):
37 if not len(o) == len(pattern):
38 return False
38 return False
39 comps = zip(o,pattern)
39 comps = zip(o,pattern)
40 return all(isinstance(obj,kind) for obj,kind in comps)
40 return all(isinstance(obj,kind) for obj,kind in comps)
41
41
42
42
43 def _get_min_max_value(min, max, value):
43 def _get_min_max_value(min, max, value):
44 """Return min, max, value given input values with possible None."""
44 """Return min, max, value given input values with possible None."""
45 if value is None:
45 if value is None:
46 if not max > min:
46 if not max > min:
47 raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
47 raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
48 value = min + abs(min-max)/2
48 value = min + abs(min-max)/2
49 value = type(min)(value)
49 value = type(min)(value)
50 elif min is None and max is None:
50 elif min is None and max is None:
51 if value == 0.0:
51 if value == 0.0:
52 min, max, value = 0.0, 1.0, 0.5
52 min, max, value = 0.0, 1.0, 0.5
53 elif value == 0:
53 elif value == 0:
54 min, max, value = 0, 1, 0
54 min, max, value = 0, 1, 0
55 elif isinstance(value, float):
55 elif isinstance(value, float):
56 min, max = (-value, 3.0*value) if value > 0 else (3.0*value, -value)
56 min, max = (-value, 3.0*value) if value > 0 else (3.0*value, -value)
57 elif isinstance(value, int):
57 elif isinstance(value, int):
58 min, max = (-value, 3*value) if value > 0 else (3*value, -value)
58 min, max = (-value, 3*value) if value > 0 else (3*value, -value)
59 else:
59 else:
60 raise TypeError('expected a number, got: %r' % value)
60 raise TypeError('expected a number, got: %r' % value)
61 else:
61 else:
62 raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
62 raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
63 return min, max, value
63 return min, max, value
64
64
65 def _widget_abbrev_single_value(o):
65 def _widget_abbrev_single_value(o):
66 """Make widgets from single values, which can be used written as parameter defaults."""
66 """Make widgets from single values, which can be used written as parameter defaults."""
67 if isinstance(o, string_types):
67 if isinstance(o, string_types):
68 return TextWidget(value=unicode_type(o))
68 return TextWidget(value=unicode_type(o))
69 elif isinstance(o, dict):
69 elif isinstance(o, dict):
70 # get a single value in a Python 2+3 way:
70 # get a single value in a Python 2+3 way:
71 value = next(iter(o.values()))
71 value = next(iter(o.values()))
72 return DropdownWidget(value=value, values=o)
72 return DropdownWidget(value=value, values=o)
73 elif isinstance(o, bool):
73 elif isinstance(o, bool):
74 return CheckboxWidget(value=o)
74 return CheckboxWidget(value=o)
75 elif isinstance(o, float):
75 elif isinstance(o, float):
76 min, max, value = _get_min_max_value(None, None, o)
76 min, max, value = _get_min_max_value(None, None, o)
77 return FloatSliderWidget(value=o, min=min, max=max)
77 return FloatSliderWidget(value=o, min=min, max=max)
78 elif isinstance(o, int):
78 elif isinstance(o, int):
79 min, max, value = _get_min_max_value(None, None, o)
79 min, max, value = _get_min_max_value(None, None, o)
80 return IntSliderWidget(value=o, min=min, max=max)
80 return IntSliderWidget(value=o, min=min, max=max)
81 else:
81 else:
82 return None
82 return None
83
83
84 def _widget_abbrev(o):
84 def _widget_abbrev(o):
85 """Make widgets from abbreviations: single values, lists or tuples."""
85 """Make widgets from abbreviations: single values, lists or tuples."""
86 if isinstance(o, (list, tuple)):
86 if isinstance(o, (list, tuple)):
87 if _matches(o, (int, int)):
87 if _matches(o, (int, int)):
88 min, max, value = _get_min_max_value(o[0], o[1], None)
88 min, max, value = _get_min_max_value(o[0], o[1], None)
89 return IntSliderWidget(value=value, min=min, max=max)
89 return IntSliderWidget(value=value, min=min, max=max)
90 elif _matches(o, (int, int, int)):
90 elif _matches(o, (int, int, int)):
91 min, max, value = _get_min_max_value(o[0], o[1], None)
91 min, max, value = _get_min_max_value(o[0], o[1], None)
92 return IntSliderWidget(value=value, min=min, max=max, step=o[2])
92 return IntSliderWidget(value=value, min=min, max=max, step=o[2])
93 elif _matches(o, (float, float)):
93 elif _matches(o, (float, float)):
94 min, max, value = _get_min_max_value(o[0], o[1], None)
94 min, max, value = _get_min_max_value(o[0], o[1], None)
95 return FloatSliderWidget(value=value, min=min, max=max)
95 return FloatSliderWidget(value=value, min=min, max=max)
96 elif _matches(o, (float, float, float)):
96 elif _matches(o, (float, float, float)):
97 min, max, value = _get_min_max_value(o[0], o[1], None)
97 min, max, value = _get_min_max_value(o[0], o[1], None)
98 return FloatSliderWidget(value=value, min=min, max=max, step=o[2])
98 return FloatSliderWidget(value=value, min=min, max=max, step=o[2])
99 elif _matches(o, (float, float, int)):
99 elif _matches(o, (float, float, int)):
100 min, max, value = _get_min_max_value(o[0], o[1], None)
100 min, max, value = _get_min_max_value(o[0], o[1], None)
101 return FloatSliderWidget(value=value, min=min, max=max, step=float(o[2]))
101 return FloatSliderWidget(value=value, min=min, max=max, step=float(o[2]))
102 elif all(isinstance(x, string_types) for x in o):
102 elif all(isinstance(x, string_types) for x in o):
103 return DropdownWidget(value=unicode_type(o[0]),
103 return DropdownWidget(value=unicode_type(o[0]),
104 values=[unicode_type(k) for k in o])
104 values=[unicode_type(k) for k in o])
105 else:
105 else:
106 return _widget_abbrev_single_value(o)
106 return _widget_abbrev_single_value(o)
107
107
108 def _widget_from_abbrev(abbrev):
108 def _widget_from_abbrev(abbrev):
109 """Build a Widget intstance given an abbreviation or Widget."""
109 """Build a Widget intstance given an abbreviation or Widget."""
110 if isinstance(abbrev, Widget) or isinstance(abbrev, const):
110 if isinstance(abbrev, Widget) or isinstance(abbrev, fixed):
111 return abbrev
111 return abbrev
112
112
113 widget = _widget_abbrev(abbrev)
113 widget = _widget_abbrev(abbrev)
114 if widget is None:
114 if widget is None:
115 raise ValueError("%r cannot be transformed to a Widget" % abbrev)
115 raise ValueError("%r cannot be transformed to a Widget" % abbrev)
116 return widget
116 return widget
117
117
118 def _yield_abbreviations_for_parameter(param, kwargs):
118 def _yield_abbreviations_for_parameter(param, kwargs):
119 """Get an abbreviation for a function parameter."""
119 """Get an abbreviation for a function parameter."""
120 name = param.name
120 name = param.name
121 kind = param.kind
121 kind = param.kind
122 ann = param.annotation
122 ann = param.annotation
123 default = param.default
123 default = param.default
124 empty = Parameter.empty
124 empty = Parameter.empty
125 not_found = (None, None)
125 not_found = (None, None)
126 if kind == Parameter.POSITIONAL_OR_KEYWORD:
126 if kind == Parameter.POSITIONAL_OR_KEYWORD:
127 if name in kwargs:
127 if name in kwargs:
128 yield name, kwargs.pop(name)
128 yield name, kwargs.pop(name)
129 elif ann is not empty:
129 elif ann is not empty:
130 if default is empty:
130 if default is empty:
131 yield name, ann
131 yield name, ann
132 else:
132 else:
133 yield name, ann
133 yield name, ann
134 elif default is not empty:
134 elif default is not empty:
135 yield name, default
135 yield name, default
136 else:
136 else:
137 yield not_found
137 yield not_found
138 elif kind == Parameter.KEYWORD_ONLY:
138 elif kind == Parameter.KEYWORD_ONLY:
139 if name in kwargs:
139 if name in kwargs:
140 yield name, kwargs.pop(name)
140 yield name, kwargs.pop(name)
141 elif ann is not empty:
141 elif ann is not empty:
142 yield name, ann
142 yield name, ann
143 elif default is not empty:
143 elif default is not empty:
144 yield name, default
144 yield name, default
145 else:
145 else:
146 yield not_found
146 yield not_found
147 elif kind == Parameter.VAR_KEYWORD:
147 elif kind == Parameter.VAR_KEYWORD:
148 # In this case name=kwargs and we yield the items in kwargs with their keys.
148 # In this case name=kwargs and we yield the items in kwargs with their keys.
149 for k, v in kwargs.copy().items():
149 for k, v in kwargs.copy().items():
150 kwargs.pop(k)
150 kwargs.pop(k)
151 yield k, v
151 yield k, v
152
152
153 def _find_abbreviations(f, kwargs):
153 def _find_abbreviations(f, kwargs):
154 """Find the abbreviations for a function and kwargs passed to interact."""
154 """Find the abbreviations for a function and kwargs passed to interact."""
155 new_kwargs = []
155 new_kwargs = []
156 for param in signature(f).parameters.values():
156 for param in signature(f).parameters.values():
157 for name, value in _yield_abbreviations_for_parameter(param, kwargs):
157 for name, value in _yield_abbreviations_for_parameter(param, kwargs):
158 if value is None:
158 if value is None:
159 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
159 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
160 new_kwargs.append((name, value))
160 new_kwargs.append((name, value))
161 return new_kwargs
161 return new_kwargs
162
162
163 def _widgets_from_abbreviations(seq):
163 def _widgets_from_abbreviations(seq):
164 """Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
164 """Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
165 result = []
165 result = []
166 for name, abbrev in seq:
166 for name, abbrev in seq:
167 widget = _widget_from_abbrev(abbrev)
167 widget = _widget_from_abbrev(abbrev)
168 widget.description = name
168 widget.description = name
169 result.append(widget)
169 result.append(widget)
170 return result
170 return result
171
171
172 def interactive(__interact_f, **kwargs):
172 def interactive(__interact_f, **kwargs):
173 """Build a group of widgets to interact with a function."""
173 """Build a group of widgets to interact with a function."""
174 f = __interact_f
174 f = __interact_f
175 co = kwargs.pop('clear_output', True)
175 co = kwargs.pop('clear_output', True)
176 kwargs_widgets = []
176 kwargs_widgets = []
177 container = ContainerWidget()
177 container = ContainerWidget()
178 container.result = None
178 container.result = None
179 container.args = []
179 container.args = []
180 container.kwargs = dict()
180 container.kwargs = dict()
181 kwargs = kwargs.copy()
181 kwargs = kwargs.copy()
182
182
183 new_kwargs = _find_abbreviations(f, kwargs)
183 new_kwargs = _find_abbreviations(f, kwargs)
184 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
184 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
185 # that will lead to a valid call of the function. This protects against unspecified
185 # that will lead to a valid call of the function. This protects against unspecified
186 # and doubly-specified arguments.
186 # and doubly-specified arguments.
187 getcallargs(f, **{n:v for n,v in new_kwargs})
187 getcallargs(f, **{n:v for n,v in new_kwargs})
188 # Now build the widgets from the abbreviations.
188 # Now build the widgets from the abbreviations.
189 kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
189 kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
190 kwargs_widgets.extend(_widgets_from_abbreviations(sorted(kwargs.items(), key = lambda x: x[0])))
190 kwargs_widgets.extend(_widgets_from_abbreviations(sorted(kwargs.items(), key = lambda x: x[0])))
191
191
192 # This has to be done as an assignment, not using container.children.append,
192 # This has to be done as an assignment, not using container.children.append,
193 # so that traitlets notices the update. We skip any objects (such as const) that
193 # so that traitlets notices the update. We skip any objects (such as fixed) that
194 # are not DOMWidgets.
194 # are not DOMWidgets.
195 c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
195 c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
196 container.children = c
196 container.children = c
197
197
198 # Build the callback
198 # Build the callback
199 def call_f(name, old, new):
199 def call_f(name, old, new):
200 container.args = []
200 container.args = []
201 for widget in kwargs_widgets:
201 for widget in kwargs_widgets:
202 value = widget.value
202 value = widget.value
203 container.kwargs[widget.description] = value
203 container.kwargs[widget.description] = value
204 if co:
204 if co:
205 clear_output(wait=True)
205 clear_output(wait=True)
206 container.result = f(*container.args, **container.kwargs)
206 container.result = f(*container.args, **container.kwargs)
207
207
208 # Wire up the widgets
208 # Wire up the widgets
209 for widget in kwargs_widgets:
209 for widget in kwargs_widgets:
210 widget.on_trait_change(call_f, 'value')
210 widget.on_trait_change(call_f, 'value')
211
211
212 container.on_displayed(lambda _: call_f(None, None, None))
212 container.on_displayed(lambda _: call_f(None, None, None))
213
213
214 return container
214 return container
215
215
216 def interact(__interact_f=None, **kwargs):
216 def interact(__interact_f=None, **kwargs):
217 """interact(f, **kwargs)
217 """interact(f, **kwargs)
218
218
219 Interact with a function using widgets."""
219 Interact with a function using widgets."""
220 # positional arg support in: https://gist.github.com/8851331
220 # positional arg support in: https://gist.github.com/8851331
221 if __interact_f is not None:
221 if __interact_f is not None:
222 # This branch handles the cases:
222 # This branch handles the cases:
223 # 1. interact(f, **kwargs)
223 # 1. interact(f, **kwargs)
224 # 2. @interact
224 # 2. @interact
225 # def f(*args, **kwargs):
225 # def f(*args, **kwargs):
226 # ...
226 # ...
227 f = __interact_f
227 f = __interact_f
228 w = interactive(f, **kwargs)
228 w = interactive(f, **kwargs)
229 f.widget = w
229 f.widget = w
230 display(w)
230 display(w)
231 else:
231 else:
232 # This branch handles the case:
232 # This branch handles the case:
233 # @interact(a=30, b=40)
233 # @interact(a=30, b=40)
234 # def f(*args, **kwargs):
234 # def f(*args, **kwargs):
235 # ...
235 # ...
236 def dec(f):
236 def dec(f):
237 w = interactive(f, **kwargs)
237 w = interactive(f, **kwargs)
238 f.widget = w
238 f.widget = w
239 display(w)
239 display(w)
240 return f
240 return f
241 return dec
241 return dec
242
242
243 class const(HasTraits):
243 class fixed(HasTraits):
244 """A pseudo-widget whose value is constant and never client synced."""
244 """A pseudo-widget whose value is fixed and never synced to the client."""
245 value = Any(help="Any Python object")
245 value = Any(help="Any Python object")
246 description = Unicode('', help="Any Python object")
246 description = Unicode('', help="Any Python object")
247 def __init__(self, value, **kwargs):
247 def __init__(self, value, **kwargs):
248 super(const, self).__init__(value=value, **kwargs)
248 super(fixed, self).__init__(value=value, **kwargs)
General Comments 0
You need to be logged in to leave comments. Login now