##// END OF EJS Templates
Change from on_demand kwarg to interact_choose function
Gordon Ball -
Show More
@@ -1,23 +1,23 b''
1 1 from .widget import Widget, DOMWidget, CallbackDispatcher
2 2
3 3 from .widget_bool import Checkbox, ToggleButton
4 4 from .widget_button import Button
5 5 from .widget_box import Box, Popup, FlexBox, HBox, VBox
6 6 from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider
7 7 from .widget_image import Image
8 8 from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider
9 9 from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select
10 10 from .widget_selectioncontainer import Tab, Accordion
11 11 from .widget_string import HTML, Latex, Text, Textarea
12 from .interaction import interact, interactive, fixed
12 from .interaction import interact, interactive, fixed, interact_choose
13 13
14 14 # Deprecated classes
15 15 from .widget_bool import CheckboxWidget, ToggleButtonWidget
16 16 from .widget_button import ButtonWidget
17 17 from .widget_box import ContainerWidget, PopupWidget
18 18 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
19 19 from .widget_image import ImageWidget
20 20 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
21 21 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
22 22 from .widget_selectioncontainer import TabWidget, AccordionWidget
23 23 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
@@ -1,274 +1,284 b''
1 1 """Interact with functions using widgets."""
2 2
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2013, the IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 from __future__ import print_function
16 16
17 17 try: # Python >= 3.3
18 18 from inspect import signature, Parameter
19 19 except ImportError:
20 20 from IPython.utils.signatures import signature, Parameter
21 21 from inspect import getcallargs
22 22
23 23 from IPython.core.getipython import get_ipython
24 24 from IPython.html.widgets import (Widget, Text,
25 25 FloatSlider, IntSlider, Checkbox, Dropdown,
26 26 Box, Button, DOMWidget)
27 27 from IPython.display import display, clear_output
28 28 from IPython.utils.py3compat import string_types, unicode_type
29 29 from IPython.utils.traitlets import HasTraits, Any, Unicode
30 30
31 31 empty = Parameter.empty
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Classes and Functions
35 35 #-----------------------------------------------------------------------------
36 36
37 37
38 38 def _matches(o, pattern):
39 39 """Match a pattern of types in a sequence."""
40 40 if not len(o) == len(pattern):
41 41 return False
42 42 comps = zip(o,pattern)
43 43 return all(isinstance(obj,kind) for obj,kind in comps)
44 44
45 45
46 46 def _get_min_max_value(min, max, value=None, step=None):
47 47 """Return min, max, value given input values with possible None."""
48 48 if value is None:
49 49 if not max > min:
50 50 raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
51 51 value = min + abs(min-max)/2
52 52 value = type(min)(value)
53 53 elif min is None and max is None:
54 54 if value == 0.0:
55 55 min, max, value = 0.0, 1.0, 0.5
56 56 elif value == 0:
57 57 min, max, value = 0, 1, 0
58 58 elif isinstance(value, (int, float)):
59 59 min, max = (-value, 3*value) if value > 0 else (3*value, -value)
60 60 else:
61 61 raise TypeError('expected a number, got: %r' % value)
62 62 else:
63 63 raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
64 64 if step is not None:
65 65 # ensure value is on a step
66 66 r = (value - min) % step
67 67 value = value - r
68 68 return min, max, value
69 69
70 70 def _widget_abbrev_single_value(o):
71 71 """Make widgets from single values, which can be used as parameter defaults."""
72 72 if isinstance(o, string_types):
73 73 return Text(value=unicode_type(o))
74 74 elif isinstance(o, dict):
75 75 return Dropdown(values=o)
76 76 elif isinstance(o, bool):
77 77 return Checkbox(value=o)
78 78 elif isinstance(o, float):
79 79 min, max, value = _get_min_max_value(None, None, o)
80 80 return FloatSlider(value=o, min=min, max=max)
81 81 elif isinstance(o, int):
82 82 min, max, value = _get_min_max_value(None, None, o)
83 83 return IntSlider(value=o, min=min, max=max)
84 84 else:
85 85 return None
86 86
87 87 def _widget_abbrev(o):
88 88 """Make widgets from abbreviations: single values, lists or tuples."""
89 89 float_or_int = (float, int)
90 90 if isinstance(o, (list, tuple)):
91 91 if o and all(isinstance(x, string_types) for x in o):
92 92 return Dropdown(values=[unicode_type(k) for k in o])
93 93 elif _matches(o, (float_or_int, float_or_int)):
94 94 min, max, value = _get_min_max_value(o[0], o[1])
95 95 if all(isinstance(_, int) for _ in o):
96 96 cls = IntSlider
97 97 else:
98 98 cls = FloatSlider
99 99 return cls(value=value, min=min, max=max)
100 100 elif _matches(o, (float_or_int, float_or_int, float_or_int)):
101 101 step = o[2]
102 102 if step <= 0:
103 103 raise ValueError("step must be >= 0, not %r" % step)
104 104 min, max, value = _get_min_max_value(o[0], o[1], step=step)
105 105 if all(isinstance(_, int) for _ in o):
106 106 cls = IntSlider
107 107 else:
108 108 cls = FloatSlider
109 109 return cls(value=value, min=min, max=max, step=step)
110 110 else:
111 111 return _widget_abbrev_single_value(o)
112 112
113 113 def _widget_from_abbrev(abbrev, default=empty):
114 114 """Build a Widget instance given an abbreviation or Widget."""
115 115 if isinstance(abbrev, Widget) or isinstance(abbrev, fixed):
116 116 return abbrev
117 117
118 118 widget = _widget_abbrev(abbrev)
119 119 if default is not empty and isinstance(abbrev, (list, tuple, dict)):
120 120 # if it's not a single-value abbreviation,
121 121 # set the initial value from the default
122 122 try:
123 123 widget.value = default
124 124 except Exception:
125 125 # ignore failure to set default
126 126 pass
127 127 if widget is None:
128 128 raise ValueError("%r cannot be transformed to a Widget" % (abbrev,))
129 129 return widget
130 130
131 131 def _yield_abbreviations_for_parameter(param, kwargs):
132 132 """Get an abbreviation for a function parameter."""
133 133 name = param.name
134 134 kind = param.kind
135 135 ann = param.annotation
136 136 default = param.default
137 137 not_found = (name, empty, empty)
138 138 if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
139 139 if name in kwargs:
140 140 value = kwargs.pop(name)
141 141 elif ann is not empty:
142 142 value = ann
143 143 elif default is not empty:
144 144 value = default
145 145 else:
146 146 yield not_found
147 147 yield (name, value, default)
148 148 elif kind == Parameter.VAR_KEYWORD:
149 149 # In this case name=kwargs and we yield the items in kwargs with their keys.
150 150 for k, v in kwargs.copy().items():
151 151 kwargs.pop(k)
152 152 yield k, v, empty
153 153
154 154 def _find_abbreviations(f, kwargs):
155 155 """Find the abbreviations for a function and kwargs passed to interact."""
156 156 new_kwargs = []
157 157 for param in signature(f).parameters.values():
158 158 for name, value, default in _yield_abbreviations_for_parameter(param, kwargs):
159 159 if value is empty:
160 160 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
161 161 new_kwargs.append((name, value, default))
162 162 return new_kwargs
163 163
164 164 def _widgets_from_abbreviations(seq):
165 165 """Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
166 166 result = []
167 167 for name, abbrev, default in seq:
168 168 widget = _widget_from_abbrev(abbrev, default)
169 169 if not widget.description:
170 170 widget.description = name
171 171 result.append(widget)
172 172 return result
173 173
174 174 def interactive(__interact_f, **kwargs):
175 175 """Build a group of widgets to interact with a function."""
176 176 f = __interact_f
177 177 co = kwargs.pop('clear_output', True)
178 on_demand = kwargs.pop('on_demand', False)
178 choose = kwargs.pop('__choose', False)
179 179 kwargs_widgets = []
180 180 container = Box()
181 181 container.result = None
182 182 container.args = []
183 183 container.kwargs = dict()
184 184 kwargs = kwargs.copy()
185 185
186 186 new_kwargs = _find_abbreviations(f, kwargs)
187 187 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
188 188 # that will lead to a valid call of the function. This protects against unspecified
189 189 # and doubly-specified arguments.
190 190 getcallargs(f, **{n:v for n,v,_ in new_kwargs})
191 191 # Now build the widgets from the abbreviations.
192 192 kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
193 193
194 194 # This has to be done as an assignment, not using container.children.append,
195 195 # so that traitlets notices the update. We skip any objects (such as fixed) that
196 196 # are not DOMWidgets.
197 197 c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
198 198
199 199 # If we are only to run the function on demand, add a button to request this
200 if on_demand:
201 on_demand_button = Button(description="Run %s" % f.__name__)
202 c.append(on_demand_button)
200 if choose:
201 choose_button = Button(description="Run %s" % f.__name__)
202 c.append(choose_button)
203 203 container.children = c
204 204
205 205 # Build the callback
206 206 def call_f(name=None, old=None, new=None):
207 207 container.kwargs = {}
208 208 for widget in kwargs_widgets:
209 209 value = widget.value
210 210 container.kwargs[widget.description] = value
211 211 if co:
212 212 clear_output(wait=True)
213 if on_demand:
214 on_demand_button.disabled = True
213 if choose:
214 choose_button.disabled = True
215 215 try:
216 216 container.result = f(**container.kwargs)
217 217 except Exception as e:
218 218 ip = get_ipython()
219 219 if ip is None:
220 220 container.log.warn("Exception in interact callback: %s", e, exc_info=True)
221 221 else:
222 222 ip.showtraceback()
223 223 finally:
224 if on_demand:
225 on_demand_button.disabled = False
224 if choose:
225 choose_button.disabled = False
226 226
227 227 # Wire up the widgets
228 # If we are doing on demand running, the callback is only triggered by the button
228 # If we are doing choose running, the callback is only triggered by the button
229 229 # Otherwise, it is triggered for every trait change received
230 230 # On-demand running also suppresses running the fucntion with the initial parameters
231 if on_demand:
232 on_demand_button.on_click(call_f)
231 if choose:
232 choose_button.on_click(call_f)
233 233 else:
234 234 for widget in kwargs_widgets:
235 235 widget.on_trait_change(call_f, 'value')
236 236
237 237 container.on_displayed(lambda _: call_f(None, None, None))
238 238
239 239 return container
240 240
241 241 def interact(__interact_f=None, **kwargs):
242 242 """interact(f, **kwargs)
243 243
244 244 Interact with a function using widgets."""
245 245 # positional arg support in: https://gist.github.com/8851331
246 246 if __interact_f is not None:
247 247 # This branch handles the cases:
248 248 # 1. interact(f, **kwargs)
249 249 # 2. @interact
250 250 # def f(*args, **kwargs):
251 251 # ...
252 252 f = __interact_f
253 253 w = interactive(f, **kwargs)
254 254 f.widget = w
255 255 display(w)
256 256 return f
257 257 else:
258 258 # This branch handles the case:
259 259 # @interact(a=30, b=40)
260 260 # def f(*args, **kwargs):
261 261 # ...
262 262 def dec(f):
263 263 w = interactive(f, **kwargs)
264 264 f.widget = w
265 265 display(w)
266 266 return f
267 267 return dec
268 268
269 def interact_choose(__interact_f=None, **kwargs):
270 """interact_choose(f, **kwargs)
271
272 As `interact()`, generates widgets for each argument, but rather than running
273 the function after each widget change, adds a "Run" button and waits for it
274 to be clicked. Useful if the function is long-running and has several
275 parameters to change.
276 """
277 return interact(__interact_f, __choose=True, **kwargs)
278
269 279 class fixed(HasTraits):
270 280 """A pseudo-widget whose value is fixed and never synced to the client."""
271 281 value = Any(help="Any Python object")
272 282 description = Unicode('', help="Any Python object")
273 283 def __init__(self, value, **kwargs):
274 284 super(fixed, self).__init__(value=value, **kwargs)
@@ -1,612 +1,612 b''
1 1 """Test interact and interactive."""
2 2
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (C) 2014 The IPython Development Team
5 5 #
6 6 # Distributed under the terms of the BSD License. The full license is in
7 7 # the file COPYING, distributed as part of this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Imports
12 12 #-----------------------------------------------------------------------------
13 13
14 14 from __future__ import print_function
15 15
16 16 from collections import OrderedDict
17 17
18 18 import nose.tools as nt
19 19 import IPython.testing.tools as tt
20 20
21 21 # from IPython.core.getipython import get_ipython
22 22 from IPython.html import widgets
23 23 from IPython.html.widgets import interact, interactive, Widget, interaction
24 24 from IPython.utils.py3compat import annotate
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Utility stuff
28 28 #-----------------------------------------------------------------------------
29 29
30 30 class DummyComm(object):
31 31 comm_id = 'a-b-c-d'
32 32 def send(self, *args, **kwargs):
33 33 pass
34 34
35 35 def close(self, *args, **kwargs):
36 36 pass
37 37
38 38 _widget_attrs = {}
39 39 displayed = []
40 40
41 41 def setup():
42 42 _widget_attrs['comm'] = Widget.comm
43 43 Widget.comm = DummyComm()
44 44 _widget_attrs['_ipython_display_'] = Widget._ipython_display_
45 45 def raise_not_implemented(*args, **kwargs):
46 46 raise NotImplementedError()
47 47 Widget._ipython_display_ = raise_not_implemented
48 48
49 49 def teardown():
50 50 for attr, value in _widget_attrs.items():
51 51 setattr(Widget, attr, value)
52 52
53 53 def f(**kwargs):
54 54 pass
55 55
56 56 def clear_display():
57 57 global displayed
58 58 displayed = []
59 59
60 60 def record_display(*args):
61 61 displayed.extend(args)
62 62
63 63 #-----------------------------------------------------------------------------
64 64 # Actual tests
65 65 #-----------------------------------------------------------------------------
66 66
67 67 def check_widget(w, **d):
68 68 """Check a single widget against a dict"""
69 69 for attr, expected in d.items():
70 70 if attr == 'cls':
71 71 nt.assert_is(w.__class__, expected)
72 72 else:
73 73 value = getattr(w, attr)
74 74 nt.assert_equal(value, expected,
75 75 "%s.%s = %r != %r" % (w.__class__.__name__, attr, value, expected)
76 76 )
77 77
78 78 def check_widgets(container, **to_check):
79 79 """Check that widgets are created as expected"""
80 80 # build a widget dictionary, so it matches
81 81 widgets = {}
82 82 for w in container.children:
83 83 widgets[w.description] = w
84 84
85 85 for key, d in to_check.items():
86 86 nt.assert_in(key, widgets)
87 87 check_widget(widgets[key], **d)
88 88
89 89
90 90 def test_single_value_string():
91 91 a = u'hello'
92 92 c = interactive(f, a=a)
93 93 w = c.children[0]
94 94 check_widget(w,
95 95 cls=widgets.Text,
96 96 description='a',
97 97 value=a,
98 98 )
99 99
100 100 def test_single_value_bool():
101 101 for a in (True, False):
102 102 c = interactive(f, a=a)
103 103 w = c.children[0]
104 104 check_widget(w,
105 105 cls=widgets.Checkbox,
106 106 description='a',
107 107 value=a,
108 108 )
109 109
110 110 def test_single_value_dict():
111 111 for d in [
112 112 dict(a=5),
113 113 dict(a=5, b='b', c=dict),
114 114 ]:
115 115 c = interactive(f, d=d)
116 116 w = c.children[0]
117 117 check_widget(w,
118 118 cls=widgets.Dropdown,
119 119 description='d',
120 120 values=d,
121 121 value=next(iter(d.values())),
122 122 )
123 123
124 124 def test_single_value_float():
125 125 for a in (2.25, 1.0, -3.5):
126 126 c = interactive(f, a=a)
127 127 w = c.children[0]
128 128 check_widget(w,
129 129 cls=widgets.FloatSlider,
130 130 description='a',
131 131 value=a,
132 132 min= -a if a > 0 else 3*a,
133 133 max= 3*a if a > 0 else -a,
134 134 step=0.1,
135 135 readout=True,
136 136 )
137 137
138 138 def test_single_value_int():
139 139 for a in (1, 5, -3):
140 140 c = interactive(f, a=a)
141 141 nt.assert_equal(len(c.children), 1)
142 142 w = c.children[0]
143 143 check_widget(w,
144 144 cls=widgets.IntSlider,
145 145 description='a',
146 146 value=a,
147 147 min= -a if a > 0 else 3*a,
148 148 max= 3*a if a > 0 else -a,
149 149 step=1,
150 150 readout=True,
151 151 )
152 152
153 153 def test_list_tuple_2_int():
154 154 with nt.assert_raises(ValueError):
155 155 c = interactive(f, tup=(1,1))
156 156 with nt.assert_raises(ValueError):
157 157 c = interactive(f, tup=(1,-1))
158 158 for min, max in [ (0,1), (1,10), (1,2), (-5,5), (-20,-19) ]:
159 159 c = interactive(f, tup=(min, max), lis=[min, max])
160 160 nt.assert_equal(len(c.children), 2)
161 161 d = dict(
162 162 cls=widgets.IntSlider,
163 163 min=min,
164 164 max=max,
165 165 step=1,
166 166 readout=True,
167 167 )
168 168 check_widgets(c, tup=d, lis=d)
169 169
170 170 def test_list_tuple_3_int():
171 171 with nt.assert_raises(ValueError):
172 172 c = interactive(f, tup=(1,2,0))
173 173 with nt.assert_raises(ValueError):
174 174 c = interactive(f, tup=(1,2,-1))
175 175 for min, max, step in [ (0,2,1), (1,10,2), (1,100,2), (-5,5,4), (-100,-20,4) ]:
176 176 c = interactive(f, tup=(min, max, step), lis=[min, max, step])
177 177 nt.assert_equal(len(c.children), 2)
178 178 d = dict(
179 179 cls=widgets.IntSlider,
180 180 min=min,
181 181 max=max,
182 182 step=step,
183 183 readout=True,
184 184 )
185 185 check_widgets(c, tup=d, lis=d)
186 186
187 187 def test_list_tuple_2_float():
188 188 with nt.assert_raises(ValueError):
189 189 c = interactive(f, tup=(1.0,1.0))
190 190 with nt.assert_raises(ValueError):
191 191 c = interactive(f, tup=(0.5,-0.5))
192 192 for min, max in [ (0.5, 1.5), (1.1,10.2), (1,2.2), (-5.,5), (-20,-19.) ]:
193 193 c = interactive(f, tup=(min, max), lis=[min, max])
194 194 nt.assert_equal(len(c.children), 2)
195 195 d = dict(
196 196 cls=widgets.FloatSlider,
197 197 min=min,
198 198 max=max,
199 199 step=.1,
200 200 readout=True,
201 201 )
202 202 check_widgets(c, tup=d, lis=d)
203 203
204 204 def test_list_tuple_3_float():
205 205 with nt.assert_raises(ValueError):
206 206 c = interactive(f, tup=(1,2,0.0))
207 207 with nt.assert_raises(ValueError):
208 208 c = interactive(f, tup=(-1,-2,1.))
209 209 with nt.assert_raises(ValueError):
210 210 c = interactive(f, tup=(1,2.,-1.))
211 211 for min, max, step in [ (0.,2,1), (1,10.,2), (1,100,2.), (-5.,5.,4), (-100,-20.,4.) ]:
212 212 c = interactive(f, tup=(min, max, step), lis=[min, max, step])
213 213 nt.assert_equal(len(c.children), 2)
214 214 d = dict(
215 215 cls=widgets.FloatSlider,
216 216 min=min,
217 217 max=max,
218 218 step=step,
219 219 readout=True,
220 220 )
221 221 check_widgets(c, tup=d, lis=d)
222 222
223 223 def test_list_tuple_str():
224 224 values = ['hello', 'there', 'guy']
225 225 first = values[0]
226 226 dvalues = OrderedDict((v,v) for v in values)
227 227 c = interactive(f, tup=tuple(values), lis=list(values))
228 228 nt.assert_equal(len(c.children), 2)
229 229 d = dict(
230 230 cls=widgets.Dropdown,
231 231 value=first,
232 232 values=dvalues
233 233 )
234 234 check_widgets(c, tup=d, lis=d)
235 235
236 236 def test_list_tuple_invalid():
237 237 for bad in [
238 238 (),
239 239 (5, 'hi'),
240 240 ('hi', 5),
241 241 ({},),
242 242 (None,),
243 243 ]:
244 244 with nt.assert_raises(ValueError):
245 245 print(bad) # because there is no custom message in assert_raises
246 246 c = interactive(f, tup=bad)
247 247
248 248 def test_defaults():
249 249 @annotate(n=10)
250 250 def f(n, f=4.5, g=1):
251 251 pass
252 252
253 253 c = interactive(f)
254 254 check_widgets(c,
255 255 n=dict(
256 256 cls=widgets.IntSlider,
257 257 value=10,
258 258 ),
259 259 f=dict(
260 260 cls=widgets.FloatSlider,
261 261 value=4.5,
262 262 ),
263 263 g=dict(
264 264 cls=widgets.IntSlider,
265 265 value=1,
266 266 ),
267 267 )
268 268
269 269 def test_default_values():
270 270 @annotate(n=10, f=(0, 10.), g=5, h={'a': 1, 'b': 2}, j=['hi', 'there'])
271 271 def f(n, f=4.5, g=1, h=2, j='there'):
272 272 pass
273 273
274 274 c = interactive(f)
275 275 check_widgets(c,
276 276 n=dict(
277 277 cls=widgets.IntSlider,
278 278 value=10,
279 279 ),
280 280 f=dict(
281 281 cls=widgets.FloatSlider,
282 282 value=4.5,
283 283 ),
284 284 g=dict(
285 285 cls=widgets.IntSlider,
286 286 value=5,
287 287 ),
288 288 h=dict(
289 289 cls=widgets.Dropdown,
290 290 values={'a': 1, 'b': 2},
291 291 value=2
292 292 ),
293 293 j=dict(
294 294 cls=widgets.Dropdown,
295 295 values={'hi':'hi', 'there':'there'},
296 296 value='there'
297 297 ),
298 298 )
299 299
300 300 def test_default_out_of_bounds():
301 301 @annotate(f=(0, 10.), h={'a': 1}, j=['hi', 'there'])
302 302 def f(f='hi', h=5, j='other'):
303 303 pass
304 304
305 305 c = interactive(f)
306 306 check_widgets(c,
307 307 f=dict(
308 308 cls=widgets.FloatSlider,
309 309 value=5.,
310 310 ),
311 311 h=dict(
312 312 cls=widgets.Dropdown,
313 313 values={'a': 1},
314 314 value=1,
315 315 ),
316 316 j=dict(
317 317 cls=widgets.Dropdown,
318 318 values={'hi':'hi', 'there':'there'},
319 319 value='hi',
320 320 ),
321 321 )
322 322
323 323 def test_annotations():
324 324 @annotate(n=10, f=widgets.FloatText())
325 325 def f(n, f):
326 326 pass
327 327
328 328 c = interactive(f)
329 329 check_widgets(c,
330 330 n=dict(
331 331 cls=widgets.IntSlider,
332 332 value=10,
333 333 ),
334 334 f=dict(
335 335 cls=widgets.FloatText,
336 336 ),
337 337 )
338 338
339 339 def test_priority():
340 340 @annotate(annotate='annotate', kwarg='annotate')
341 341 def f(kwarg='default', annotate='default', default='default'):
342 342 pass
343 343
344 344 c = interactive(f, kwarg='kwarg')
345 345 check_widgets(c,
346 346 kwarg=dict(
347 347 cls=widgets.Text,
348 348 value='kwarg',
349 349 ),
350 350 annotate=dict(
351 351 cls=widgets.Text,
352 352 value='annotate',
353 353 ),
354 354 )
355 355
356 356 @nt.with_setup(clear_display)
357 357 def test_decorator_kwarg():
358 358 with tt.monkeypatch(interaction, 'display', record_display):
359 359 @interact(a=5)
360 360 def foo(a):
361 361 pass
362 362 nt.assert_equal(len(displayed), 1)
363 363 w = displayed[0].children[0]
364 364 check_widget(w,
365 365 cls=widgets.IntSlider,
366 366 value=5,
367 367 )
368 368
369 369 @nt.with_setup(clear_display)
370 370 def test_decorator_no_call():
371 371 with tt.monkeypatch(interaction, 'display', record_display):
372 372 @interact
373 373 def foo(a='default'):
374 374 pass
375 375 nt.assert_equal(len(displayed), 1)
376 376 w = displayed[0].children[0]
377 377 check_widget(w,
378 378 cls=widgets.Text,
379 379 value='default',
380 380 )
381 381
382 382 @nt.with_setup(clear_display)
383 383 def test_call_interact():
384 384 def foo(a='default'):
385 385 pass
386 386 with tt.monkeypatch(interaction, 'display', record_display):
387 387 ifoo = interact(foo)
388 388 nt.assert_equal(len(displayed), 1)
389 389 w = displayed[0].children[0]
390 390 check_widget(w,
391 391 cls=widgets.Text,
392 392 value='default',
393 393 )
394 394
395 395 @nt.with_setup(clear_display)
396 396 def test_call_interact_kwargs():
397 397 def foo(a='default'):
398 398 pass
399 399 with tt.monkeypatch(interaction, 'display', record_display):
400 400 ifoo = interact(foo, a=10)
401 401 nt.assert_equal(len(displayed), 1)
402 402 w = displayed[0].children[0]
403 403 check_widget(w,
404 404 cls=widgets.IntSlider,
405 405 value=10,
406 406 )
407 407
408 408 @nt.with_setup(clear_display)
409 409 def test_call_decorated_on_trait_change():
410 410 """test calling @interact decorated functions"""
411 411 d = {}
412 412 with tt.monkeypatch(interaction, 'display', record_display):
413 413 @interact
414 414 def foo(a='default'):
415 415 d['a'] = a
416 416 return a
417 417 nt.assert_equal(len(displayed), 1)
418 418 w = displayed[0].children[0]
419 419 check_widget(w,
420 420 cls=widgets.Text,
421 421 value='default',
422 422 )
423 423 # test calling the function directly
424 424 a = foo('hello')
425 425 nt.assert_equal(a, 'hello')
426 426 nt.assert_equal(d['a'], 'hello')
427 427
428 428 # test that setting trait values calls the function
429 429 w.value = 'called'
430 430 nt.assert_equal(d['a'], 'called')
431 431
432 432 @nt.with_setup(clear_display)
433 433 def test_call_decorated_kwargs_on_trait_change():
434 434 """test calling @interact(foo=bar) decorated functions"""
435 435 d = {}
436 436 with tt.monkeypatch(interaction, 'display', record_display):
437 437 @interact(a='kwarg')
438 438 def foo(a='default'):
439 439 d['a'] = a
440 440 return a
441 441 nt.assert_equal(len(displayed), 1)
442 442 w = displayed[0].children[0]
443 443 check_widget(w,
444 444 cls=widgets.Text,
445 445 value='kwarg',
446 446 )
447 447 # test calling the function directly
448 448 a = foo('hello')
449 449 nt.assert_equal(a, 'hello')
450 450 nt.assert_equal(d['a'], 'hello')
451 451
452 452 # test that setting trait values calls the function
453 453 w.value = 'called'
454 454 nt.assert_equal(d['a'], 'called')
455 455
456 456 def test_fixed():
457 457 c = interactive(f, a=widgets.fixed(5), b='text')
458 458 nt.assert_equal(len(c.children), 1)
459 459 w = c.children[0]
460 460 check_widget(w,
461 461 cls=widgets.Text,
462 462 value='text',
463 463 description='b',
464 464 )
465 465
466 466 def test_default_description():
467 467 c = interactive(f, b='text')
468 468 w = c.children[0]
469 469 check_widget(w,
470 470 cls=widgets.Text,
471 471 value='text',
472 472 description='b',
473 473 )
474 474
475 475 def test_custom_description():
476 476 c = interactive(f, b=widgets.Text(value='text', description='foo'))
477 477 w = c.children[0]
478 478 check_widget(w,
479 479 cls=widgets.Text,
480 480 value='text',
481 481 description='foo',
482 482 )
483 483
484 def test_interact_ondemand_button():
485 c = interactive(f, on_demand=True)
484 def test_interact_choose_button():
485 c = interactive(f, __choose=True)
486 486 w = c.children[0]
487 487 check_widget(w, cls=widgets.Button)
488 488
489 def test_interact_ondemand_nocall():
489 def test_interact_choose_nocall():
490 490 callcount = 0
491 491 def calltest(testarg):
492 492 callcount += 1
493 c = interactive(calltest, testarg=5, on_demand=True)
493 c = interactive(calltest, testarg=5, __choose=True)
494 494 c.children[0].value = 10
495 495 nt.assert_equal(callcount, 0)
496 496
497 497 def test_int_range_logic():
498 498 irsw = widgets.IntRangeSlider
499 499 w = irsw(value=(2, 4), min=0, max=6)
500 500 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
501 501 w.value = (4, 2)
502 502 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
503 503 w.value = (-1, 7)
504 504 check_widget(w, cls=irsw, value=(0, 6), min=0, max=6)
505 505 w.min = 3
506 506 check_widget(w, cls=irsw, value=(3, 6), min=3, max=6)
507 507 w.max = 3
508 508 check_widget(w, cls=irsw, value=(3, 3), min=3, max=3)
509 509
510 510 w.min = 0
511 511 w.max = 6
512 512 w.lower = 2
513 513 w.upper = 4
514 514 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
515 515 w.value = (0, 1) #lower non-overlapping range
516 516 check_widget(w, cls=irsw, value=(0, 1), min=0, max=6)
517 517 w.value = (5, 6) #upper non-overlapping range
518 518 check_widget(w, cls=irsw, value=(5, 6), min=0, max=6)
519 519 w.value = (-1, 4) #semi out-of-range
520 520 check_widget(w, cls=irsw, value=(0, 4), min=0, max=6)
521 521 w.lower = 2
522 522 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
523 523 w.value = (-2, -1) #wholly out of range
524 524 check_widget(w, cls=irsw, value=(0, 0), min=0, max=6)
525 525 w.value = (7, 8)
526 526 check_widget(w, cls=irsw, value=(6, 6), min=0, max=6)
527 527
528 528 with nt.assert_raises(ValueError):
529 529 w.min = 7
530 530 with nt.assert_raises(ValueError):
531 531 w.max = -1
532 532 with nt.assert_raises(ValueError):
533 533 w.lower = 5
534 534 with nt.assert_raises(ValueError):
535 535 w.upper = 1
536 536
537 537 w = irsw(min=2, max=3)
538 538 check_widget(w, min=2, max=3)
539 539 w = irsw(min=100, max=200)
540 540 check_widget(w, lower=125, upper=175, value=(125, 175))
541 541
542 542 with nt.assert_raises(ValueError):
543 543 irsw(value=(2, 4), lower=3)
544 544 with nt.assert_raises(ValueError):
545 545 irsw(value=(2, 4), upper=3)
546 546 with nt.assert_raises(ValueError):
547 547 irsw(value=(2, 4), lower=3, upper=3)
548 548 with nt.assert_raises(ValueError):
549 549 irsw(min=2, max=1)
550 550 with nt.assert_raises(ValueError):
551 551 irsw(lower=5)
552 552 with nt.assert_raises(ValueError):
553 553 irsw(upper=5)
554 554
555 555
556 556 def test_float_range_logic():
557 557 frsw = widgets.FloatRangeSlider
558 558 w = frsw(value=(.2, .4), min=0., max=.6)
559 559 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
560 560 w.value = (.4, .2)
561 561 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
562 562 w.value = (-.1, .7)
563 563 check_widget(w, cls=frsw, value=(0., .6), min=0., max=.6)
564 564 w.min = .3
565 565 check_widget(w, cls=frsw, value=(.3, .6), min=.3, max=.6)
566 566 w.max = .3
567 567 check_widget(w, cls=frsw, value=(.3, .3), min=.3, max=.3)
568 568
569 569 w.min = 0.
570 570 w.max = .6
571 571 w.lower = .2
572 572 w.upper = .4
573 573 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
574 574 w.value = (0., .1) #lower non-overlapping range
575 575 check_widget(w, cls=frsw, value=(0., .1), min=0., max=.6)
576 576 w.value = (.5, .6) #upper non-overlapping range
577 577 check_widget(w, cls=frsw, value=(.5, .6), min=0., max=.6)
578 578 w.value = (-.1, .4) #semi out-of-range
579 579 check_widget(w, cls=frsw, value=(0., .4), min=0., max=.6)
580 580 w.lower = .2
581 581 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
582 582 w.value = (-.2, -.1) #wholly out of range
583 583 check_widget(w, cls=frsw, value=(0., 0.), min=0., max=.6)
584 584 w.value = (.7, .8)
585 585 check_widget(w, cls=frsw, value=(.6, .6), min=.0, max=.6)
586 586
587 587 with nt.assert_raises(ValueError):
588 588 w.min = .7
589 589 with nt.assert_raises(ValueError):
590 590 w.max = -.1
591 591 with nt.assert_raises(ValueError):
592 592 w.lower = .5
593 593 with nt.assert_raises(ValueError):
594 594 w.upper = .1
595 595
596 596 w = frsw(min=2, max=3)
597 597 check_widget(w, min=2, max=3)
598 598 w = frsw(min=1., max=2.)
599 599 check_widget(w, lower=1.25, upper=1.75, value=(1.25, 1.75))
600 600
601 601 with nt.assert_raises(ValueError):
602 602 frsw(value=(2, 4), lower=3)
603 603 with nt.assert_raises(ValueError):
604 604 frsw(value=(2, 4), upper=3)
605 605 with nt.assert_raises(ValueError):
606 606 frsw(value=(2, 4), lower=3, upper=3)
607 607 with nt.assert_raises(ValueError):
608 608 frsw(min=.2, max=.1)
609 609 with nt.assert_raises(ValueError):
610 610 frsw(lower=5)
611 611 with nt.assert_raises(ValueError):
612 612 frsw(upper=5)
General Comments 0
You need to be logged in to leave comments. Login now