##// END OF EJS Templates
Pad interact widgets.
Jonathan Frederic -
Show More
@@ -1,272 +1,279 b''
1 @widget-width: 350px;
1 @widget-width: 350px;
2 @widget-width-short: 150px;
2 @widget-width-short: 150px;
3
3
4 // Pad interact widgets by default.
5 .widget-interact {
6 div, input {
7 padding: 2.5px;
8 }
9 }
10
4 .widget-area {
11 .widget-area {
5 /*
12 /*
6 LESS file that styles IPython notebook widgets and the area they sit in.
13 LESS file that styles IPython notebook widgets and the area they sit in.
7
14
8 The widget area typically looks something like this:
15 The widget area typically looks something like this:
9 +------------------------------------------+
16 +------------------------------------------+
10 | widget-area |
17 | widget-area |
11 | +--------+---------------------------+ |
18 | +--------+---------------------------+ |
12 | | prompt | widget-subarea | |
19 | | prompt | widget-subarea | |
13 | | | +--------+ +--------+ | |
20 | | | +--------+ +--------+ | |
14 | | | | widget | | widget | | |
21 | | | | widget | | widget | | |
15 | | | +--------+ +--------+ | |
22 | | | +--------+ +--------+ | |
16 | +--------+---------------------------+ |
23 | +--------+---------------------------+ |
17 +------------------------------------------+
24 +------------------------------------------+
18 */
25 */
19
26
20 page-break-inside : avoid;
27 page-break-inside : avoid;
21 .hbox();
28 .hbox();
22
29
23 .widget-subarea {
30 .widget-subarea {
24 padding : 0.44em 0.4em 0.4em 1px;
31 padding : 0.44em 0.4em 0.4em 1px;
25 margin-left : 6px;
32 margin-left : 6px;
26
33
27 .border-box-sizing();
34 .border-box-sizing();
28 .vbox();
35 .vbox();
29 .box-flex2();
36 .box-flex2();
30 .align-start();
37 .align-start();
31 }
38 }
32
39
33 &.connection-problems .prompt:after {
40 &.connection-problems .prompt:after {
34 content: @fa-var-chain-broken;
41 content: @fa-var-chain-broken;
35 font-family: 'FontAwesome';
42 font-family: 'FontAwesome';
36 color: @brand-danger;
43 color: @brand-danger;
37 font-size: @notebook_font_size;
44 font-size: @notebook_font_size;
38 top: 3px;
45 top: 3px;
39 padding: 3px;
46 padding: 3px;
40 }
47 }
41 }
48 }
42
49
43 /* THE CLASSES BELOW CAN APPEAR ANYWHERE IN THE DOM (POSSIBLEY OUTSIDE OF
50 /* THE CLASSES BELOW CAN APPEAR ANYWHERE IN THE DOM (POSSIBLEY OUTSIDE OF
44 THE WIDGET AREA). */
51 THE WIDGET AREA). */
45
52
46 .slide-track {
53 .slide-track {
47 /* Slider Track */
54 /* Slider Track */
48 border : 1px solid #CCCCCC;
55 border : 1px solid #CCCCCC;
49 background : #FFFFFF;
56 background : #FFFFFF;
50
57
51 .corner-all(); /* Round the corners of the slide track */
58 .corner-all(); /* Round the corners of the slide track */
52 }
59 }
53
60
54 .widget-hslider {
61 .widget-hslider {
55 /* Horizontal jQuery Slider
62 /* Horizontal jQuery Slider
56
63
57 Both the horizontal and vertical versions of the slider are characterized
64 Both the horizontal and vertical versions of the slider are characterized
58 by a styled div that contains an invisible jQuery slide div which
65 by a styled div that contains an invisible jQuery slide div which
59 contains a visible slider handle div. This is requred so we can control
66 contains a visible slider handle div. This is requred so we can control
60 how the slider is drawn and 'fix' the issue where the slide handle
67 how the slider is drawn and 'fix' the issue where the slide handle
61 doesn't stop at the end of the slide.
68 doesn't stop at the end of the slide.
62
69
63 Both horizontal and vertical sliders have this div nesting:
70 Both horizontal and vertical sliders have this div nesting:
64 +------------------------------------------+
71 +------------------------------------------+
65 | widget-(h/v)slider |
72 | widget-(h/v)slider |
66 | +--------+---------------------------+ |
73 | +--------+---------------------------+ |
67 | | ui-slider | |
74 | | ui-slider | |
68 | | +------------------+ | |
75 | | +------------------+ | |
69 | | | ui-slider-handle | | |
76 | | | ui-slider-handle | | |
70 | | +------------------+ | |
77 | | +------------------+ | |
71 | +--------+---------------------------+ |
78 | +--------+---------------------------+ |
72 +------------------------------------------+
79 +------------------------------------------+
73 */
80 */
74
81
75 /* Fix the padding of the slide track so the ui-slider is sized
82 /* Fix the padding of the slide track so the ui-slider is sized
76 correctly. */
83 correctly. */
77 padding-left : 8px;
84 padding-left : 8px;
78 padding-right : 2px;
85 padding-right : 2px;
79 overflow : visible;
86 overflow : visible;
80
87
81 /* Default size of the slider */
88 /* Default size of the slider */
82 width : @widget-width;
89 width : @widget-width;
83 height : 5px;
90 height : 5px;
84 max-height : 5px;
91 max-height : 5px;
85 margin-top : 13px;
92 margin-top : 13px;
86 margin-bottom: 10px;
93 margin-bottom: 10px;
87
94
88 /* Style the slider track */
95 /* Style the slider track */
89 .slide-track();
96 .slide-track();
90
97
91 /* Make the div a flex box (makes FF behave correctly). */
98 /* Make the div a flex box (makes FF behave correctly). */
92 .hbox();
99 .hbox();
93
100
94 .ui-slider {
101 .ui-slider {
95 /* Inner, invisible slide div */
102 /* Inner, invisible slide div */
96 border : 0px;
103 border : 0px;
97 background : none;
104 background : none;
98
105
99 .hbox();
106 .hbox();
100 .box-flex1();
107 .box-flex1();
101
108
102 .ui-slider-handle {
109 .ui-slider-handle {
103 width: 12px;
110 width: 12px;
104 height: 28px;
111 height: 28px;
105 margin-top: -8px;
112 margin-top: -8px;
106 border-radius: @border-radius-base;
113 border-radius: @border-radius-base;
107 }
114 }
108
115
109 .ui-slider-range {
116 .ui-slider-range {
110 height : 12px;
117 height : 12px;
111 margin-top : -4px;
118 margin-top : -4px;
112 background : @page-backdrop-color;
119 background : @page-backdrop-color;
113 }
120 }
114 }
121 }
115 }
122 }
116
123
117 .widget-vslider {
124 .widget-vslider {
118 /* Vertical jQuery Slider */
125 /* Vertical jQuery Slider */
119
126
120 /* Fix the padding of the slide track so the ui-slider is sized
127 /* Fix the padding of the slide track so the ui-slider is sized
121 correctly. */
128 correctly. */
122 padding-bottom : 5px;
129 padding-bottom : 5px;
123 overflow : visible;
130 overflow : visible;
124
131
125 /* Default size of the slider */
132 /* Default size of the slider */
126 width : 5px;
133 width : 5px;
127 max-width : 5px;
134 max-width : 5px;
128 height : 250px;
135 height : 250px;
129 margin-left : 12px;
136 margin-left : 12px;
130
137
131 /* Style the slider track */
138 /* Style the slider track */
132 .slide-track();
139 .slide-track();
133
140
134 /* Make the div a flex box (makes FF behave correctly). */
141 /* Make the div a flex box (makes FF behave correctly). */
135 .vbox();
142 .vbox();
136
143
137 .ui-slider {
144 .ui-slider {
138 /* Inner, invisible slide div */
145 /* Inner, invisible slide div */
139 border : 0px;
146 border : 0px;
140 background : none;
147 background : none;
141 margin-left : -4px;
148 margin-left : -4px;
142 margin-top : 5px;
149 margin-top : 5px;
143
150
144 .vbox();
151 .vbox();
145 .box-flex1();
152 .box-flex1();
146
153
147 .ui-slider-handle {
154 .ui-slider-handle {
148 width : 28px;
155 width : 28px;
149 height : 12px;
156 height : 12px;
150 margin-left : -9px;
157 margin-left : -9px;
151 border-radius: @border-radius-base;
158 border-radius: @border-radius-base;
152 }
159 }
153
160
154 .ui-slider-range {
161 .ui-slider-range {
155 width : 12px;
162 width : 12px;
156 margin-left : -1px;
163 margin-left : -1px;
157 background : @page-backdrop-color;
164 background : @page-backdrop-color;
158 }
165 }
159 }
166 }
160 }
167 }
161
168
162 .widget-text {
169 .widget-text {
163 /* String Textbox - used for TextBoxView and TextAreaView */
170 /* String Textbox - used for TextBoxView and TextAreaView */
164 width : @widget-width;
171 width : @widget-width;
165 margin : 0px;
172 margin : 0px;
166 }
173 }
167
174
168 .widget-listbox {
175 .widget-listbox {
169 /* Listbox */
176 /* Listbox */
170 width : @widget-width;
177 width : @widget-width;
171 margin-bottom : 0px;
178 margin-bottom : 0px;
172 }
179 }
173
180
174 .widget-numeric-text {
181 .widget-numeric-text {
175 /* Single Line Textbox - used for IntTextView and FloatTextView */
182 /* Single Line Textbox - used for IntTextView and FloatTextView */
176 width : @widget-width-short;
183 width : @widget-width-short;
177 margin : 0px;
184 margin : 0px;
178 }
185 }
179
186
180 .widget-progress {
187 .widget-progress {
181 /* Progress Bar */
188 /* Progress Bar */
182 margin-top: 6px;
189 margin-top: 6px;
183 min-width : @widget-width;
190 min-width : @widget-width;
184
191
185 .progress-bar {
192 .progress-bar {
186 /* Disable progress bar animation */
193 /* Disable progress bar animation */
187 -webkit-transition : none;
194 -webkit-transition : none;
188 -moz-transition : none;
195 -moz-transition : none;
189 -ms-transition : none;
196 -ms-transition : none;
190 -o-transition : none;
197 -o-transition : none;
191 transition : none;
198 transition : none;
192 }
199 }
193 }
200 }
194
201
195 .widget-combo-btn {
202 .widget-combo-btn {
196 /* ComboBox Main Button */
203 /* ComboBox Main Button */
197 /* Subtract 25px to account for the drop arrow button */
204 /* Subtract 25px to account for the drop arrow button */
198 min-width : @widget-width-short - 25px;
205 min-width : @widget-width-short - 25px;
199 }
206 }
200
207
201 .widget_item .dropdown-menu li a {
208 .widget_item .dropdown-menu li a {
202 color: inherit;
209 color: inherit;
203 }
210 }
204
211
205 .widget-hbox {
212 .widget-hbox {
206 /* Horizontal widgets */
213 /* Horizontal widgets */
207 .hbox();
214 .hbox();
208
215
209 input[type="checkbox"] {
216 input[type="checkbox"] {
210 margin-top: 9px;
217 margin-top: 9px;
211 margin-bottom: 10px;
218 margin-bottom: 10px;
212 }
219 }
213
220
214 .widget-label {
221 .widget-label {
215 /* Horizontal Label */
222 /* Horizontal Label */
216 min-width : 10ex;
223 min-width : 10ex;
217 padding-right : 8px;
224 padding-right : 8px;
218 padding-top : 5px;
225 padding-top : 5px;
219 text-align : right;
226 text-align : right;
220 vertical-align : text-top;
227 vertical-align : text-top;
221 }
228 }
222
229
223 .widget-readout {
230 .widget-readout {
224 padding-left : 8px;
231 padding-left : 8px;
225 padding-top : 5px;
232 padding-top : 5px;
226 text-align : left;
233 text-align : left;
227 vertical-align : text-top;
234 vertical-align : text-top;
228 }
235 }
229 }
236 }
230
237
231 .widget-vbox {
238 .widget-vbox {
232 /* Vertical widgets */
239 /* Vertical widgets */
233 .vbox();
240 .vbox();
234
241
235
242
236 .widget-label {
243 .widget-label {
237 /* Vertical Label */
244 /* Vertical Label */
238 padding-bottom : 5px;
245 padding-bottom : 5px;
239 text-align : center;
246 text-align : center;
240 vertical-align : text-bottom;
247 vertical-align : text-bottom;
241 }
248 }
242
249
243 .widget-readout {
250 .widget-readout {
244 /* Vertical Label */
251 /* Vertical Label */
245 padding-top : 5px;
252 padding-top : 5px;
246 text-align : center;
253 text-align : center;
247 vertical-align : text-top;
254 vertical-align : text-top;
248 }
255 }
249
256
250 }
257 }
251
258
252 .widget-box {
259 .widget-box {
253 /* Box */
260 /* Box */
254 .border-box-sizing();
261 .border-box-sizing();
255 .align-start();
262 .align-start();
256 }
263 }
257
264
258 .widget-radio-box {
265 .widget-radio-box {
259 /* Contains RadioButtonsWidget */
266 /* Contains RadioButtonsWidget */
260 .vbox();
267 .vbox();
261 .border-box-sizing();
268 .border-box-sizing();
262
269
263 padding-top: 4px;
270 padding-top: 4px;
264
271
265 label {
272 label {
266 margin-top: 0px;
273 margin-top: 0px;
267 }
274 }
268 }
275 }
269
276
270 .widget-radio {
277 .widget-radio {
271 margin-left: 20px;
278 margin-left: 20px;
272 }
279 }
@@ -1,344 +1,344 b''
1 """Interact with functions using widgets."""
1 """Interact with functions using widgets."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from __future__ import print_function
6 from __future__ import print_function
7
7
8 try: # Python >= 3.3
8 try: # Python >= 3.3
9 from inspect import signature, Parameter
9 from inspect import signature, Parameter
10 except ImportError:
10 except ImportError:
11 from IPython.utils.signatures import signature, Parameter
11 from IPython.utils.signatures import signature, Parameter
12 from inspect import getcallargs
12 from inspect import getcallargs
13
13
14 from IPython.core.getipython import get_ipython
14 from IPython.core.getipython import get_ipython
15 from IPython.html.widgets import (Widget, Text,
15 from IPython.html.widgets import (Widget, Text,
16 FloatSlider, IntSlider, Checkbox, Dropdown,
16 FloatSlider, IntSlider, Checkbox, Dropdown,
17 Box, Button, DOMWidget)
17 Box, Button, DOMWidget)
18 from IPython.display import display, clear_output
18 from IPython.display import display, clear_output
19 from IPython.utils.py3compat import string_types, unicode_type
19 from IPython.utils.py3compat import string_types, unicode_type
20 from IPython.utils.traitlets import HasTraits, Any, Unicode
20 from IPython.utils.traitlets import HasTraits, Any, Unicode
21
21
22 empty = Parameter.empty
22 empty = Parameter.empty
23
23
24
24
25 def _matches(o, pattern):
25 def _matches(o, pattern):
26 """Match a pattern of types in a sequence."""
26 """Match a pattern of types in a sequence."""
27 if not len(o) == len(pattern):
27 if not len(o) == len(pattern):
28 return False
28 return False
29 comps = zip(o,pattern)
29 comps = zip(o,pattern)
30 return all(isinstance(obj,kind) for obj,kind in comps)
30 return all(isinstance(obj,kind) for obj,kind in comps)
31
31
32
32
33 def _get_min_max_value(min, max, value=None, step=None):
33 def _get_min_max_value(min, max, value=None, step=None):
34 """Return min, max, value given input values with possible None."""
34 """Return min, max, value given input values with possible None."""
35 if value is None:
35 if value is None:
36 if not max > min:
36 if not max > min:
37 raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
37 raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
38 value = min + abs(min-max)/2
38 value = min + abs(min-max)/2
39 value = type(min)(value)
39 value = type(min)(value)
40 elif min is None and max is None:
40 elif min is None and max is None:
41 if value == 0.0:
41 if value == 0.0:
42 min, max, value = 0.0, 1.0, 0.5
42 min, max, value = 0.0, 1.0, 0.5
43 elif value == 0:
43 elif value == 0:
44 min, max, value = 0, 1, 0
44 min, max, value = 0, 1, 0
45 elif isinstance(value, (int, float)):
45 elif isinstance(value, (int, float)):
46 min, max = (-value, 3*value) if value > 0 else (3*value, -value)
46 min, max = (-value, 3*value) if value > 0 else (3*value, -value)
47 else:
47 else:
48 raise TypeError('expected a number, got: %r' % value)
48 raise TypeError('expected a number, got: %r' % value)
49 else:
49 else:
50 raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
50 raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
51 if step is not None:
51 if step is not None:
52 # ensure value is on a step
52 # ensure value is on a step
53 r = (value - min) % step
53 r = (value - min) % step
54 value = value - r
54 value = value - r
55 return min, max, value
55 return min, max, value
56
56
57 def _widget_abbrev_single_value(o):
57 def _widget_abbrev_single_value(o):
58 """Make widgets from single values, which can be used as parameter defaults."""
58 """Make widgets from single values, which can be used as parameter defaults."""
59 if isinstance(o, string_types):
59 if isinstance(o, string_types):
60 return Text(value=unicode_type(o))
60 return Text(value=unicode_type(o))
61 elif isinstance(o, dict):
61 elif isinstance(o, dict):
62 return Dropdown(options=o)
62 return Dropdown(options=o)
63 elif isinstance(o, bool):
63 elif isinstance(o, bool):
64 return Checkbox(value=o)
64 return Checkbox(value=o)
65 elif isinstance(o, float):
65 elif isinstance(o, float):
66 min, max, value = _get_min_max_value(None, None, o)
66 min, max, value = _get_min_max_value(None, None, o)
67 return FloatSlider(value=o, min=min, max=max)
67 return FloatSlider(value=o, min=min, max=max)
68 elif isinstance(o, int):
68 elif isinstance(o, int):
69 min, max, value = _get_min_max_value(None, None, o)
69 min, max, value = _get_min_max_value(None, None, o)
70 return IntSlider(value=o, min=min, max=max)
70 return IntSlider(value=o, min=min, max=max)
71 else:
71 else:
72 return None
72 return None
73
73
74 def _widget_abbrev(o):
74 def _widget_abbrev(o):
75 """Make widgets from abbreviations: single values, lists or tuples."""
75 """Make widgets from abbreviations: single values, lists or tuples."""
76 float_or_int = (float, int)
76 float_or_int = (float, int)
77 if isinstance(o, (list, tuple)):
77 if isinstance(o, (list, tuple)):
78 if o and all(isinstance(x, string_types) for x in o):
78 if o and all(isinstance(x, string_types) for x in o):
79 return Dropdown(options=[unicode_type(k) for k in o])
79 return Dropdown(options=[unicode_type(k) for k in o])
80 elif _matches(o, (float_or_int, float_or_int)):
80 elif _matches(o, (float_or_int, float_or_int)):
81 min, max, value = _get_min_max_value(o[0], o[1])
81 min, max, value = _get_min_max_value(o[0], o[1])
82 if all(isinstance(_, int) for _ in o):
82 if all(isinstance(_, int) for _ in o):
83 cls = IntSlider
83 cls = IntSlider
84 else:
84 else:
85 cls = FloatSlider
85 cls = FloatSlider
86 return cls(value=value, min=min, max=max)
86 return cls(value=value, min=min, max=max)
87 elif _matches(o, (float_or_int, float_or_int, float_or_int)):
87 elif _matches(o, (float_or_int, float_or_int, float_or_int)):
88 step = o[2]
88 step = o[2]
89 if step <= 0:
89 if step <= 0:
90 raise ValueError("step must be >= 0, not %r" % step)
90 raise ValueError("step must be >= 0, not %r" % step)
91 min, max, value = _get_min_max_value(o[0], o[1], step=step)
91 min, max, value = _get_min_max_value(o[0], o[1], step=step)
92 if all(isinstance(_, int) for _ in o):
92 if all(isinstance(_, int) for _ in o):
93 cls = IntSlider
93 cls = IntSlider
94 else:
94 else:
95 cls = FloatSlider
95 cls = FloatSlider
96 return cls(value=value, min=min, max=max, step=step)
96 return cls(value=value, min=min, max=max, step=step)
97 else:
97 else:
98 return _widget_abbrev_single_value(o)
98 return _widget_abbrev_single_value(o)
99
99
100 def _widget_from_abbrev(abbrev, default=empty):
100 def _widget_from_abbrev(abbrev, default=empty):
101 """Build a Widget instance given an abbreviation or Widget."""
101 """Build a Widget instance given an abbreviation or Widget."""
102 if isinstance(abbrev, Widget) or isinstance(abbrev, fixed):
102 if isinstance(abbrev, Widget) or isinstance(abbrev, fixed):
103 return abbrev
103 return abbrev
104
104
105 widget = _widget_abbrev(abbrev)
105 widget = _widget_abbrev(abbrev)
106 if default is not empty and isinstance(abbrev, (list, tuple, dict)):
106 if default is not empty and isinstance(abbrev, (list, tuple, dict)):
107 # if it's not a single-value abbreviation,
107 # if it's not a single-value abbreviation,
108 # set the initial value from the default
108 # set the initial value from the default
109 try:
109 try:
110 widget.value = default
110 widget.value = default
111 except Exception:
111 except Exception:
112 # ignore failure to set default
112 # ignore failure to set default
113 pass
113 pass
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 not_found = (name, empty, empty)
124 not_found = (name, empty, empty)
125 if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
125 if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
126 if name in kwargs:
126 if name in kwargs:
127 value = kwargs.pop(name)
127 value = kwargs.pop(name)
128 elif ann is not empty:
128 elif ann is not empty:
129 value = ann
129 value = ann
130 elif default is not empty:
130 elif default is not empty:
131 value = default
131 value = default
132 else:
132 else:
133 yield not_found
133 yield not_found
134 yield (name, value, default)
134 yield (name, value, default)
135 elif kind == Parameter.VAR_KEYWORD:
135 elif kind == Parameter.VAR_KEYWORD:
136 # In this case name=kwargs and we yield the items in kwargs with their keys.
136 # In this case name=kwargs and we yield the items in kwargs with their keys.
137 for k, v in kwargs.copy().items():
137 for k, v in kwargs.copy().items():
138 kwargs.pop(k)
138 kwargs.pop(k)
139 yield k, v, empty
139 yield k, v, empty
140
140
141 def _find_abbreviations(f, kwargs):
141 def _find_abbreviations(f, kwargs):
142 """Find the abbreviations for a function and kwargs passed to interact."""
142 """Find the abbreviations for a function and kwargs passed to interact."""
143 new_kwargs = []
143 new_kwargs = []
144 for param in signature(f).parameters.values():
144 for param in signature(f).parameters.values():
145 for name, value, default in _yield_abbreviations_for_parameter(param, kwargs):
145 for name, value, default in _yield_abbreviations_for_parameter(param, kwargs):
146 if value is empty:
146 if value is empty:
147 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
147 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
148 new_kwargs.append((name, value, default))
148 new_kwargs.append((name, value, default))
149 return new_kwargs
149 return new_kwargs
150
150
151 def _widgets_from_abbreviations(seq):
151 def _widgets_from_abbreviations(seq):
152 """Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
152 """Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
153 result = []
153 result = []
154 for name, abbrev, default in seq:
154 for name, abbrev, default in seq:
155 widget = _widget_from_abbrev(abbrev, default)
155 widget = _widget_from_abbrev(abbrev, default)
156 if not widget.description:
156 if not widget.description:
157 widget.description = name
157 widget.description = name
158 widget._kwarg = name
158 widget._kwarg = name
159 result.append(widget)
159 result.append(widget)
160 return result
160 return result
161
161
162 def interactive(__interact_f, **kwargs):
162 def interactive(__interact_f, **kwargs):
163 """
163 """
164 Builds a group of interactive widgets tied to a function and places the
164 Builds a group of interactive widgets tied to a function and places the
165 group into a Box container.
165 group into a Box container.
166
166
167 Returns
167 Returns
168 -------
168 -------
169 container : a Box instance containing multiple widgets
169 container : a Box instance containing multiple widgets
170
170
171 Parameters
171 Parameters
172 ----------
172 ----------
173 __interact_f : function
173 __interact_f : function
174 The function to which the interactive widgets are tied. The **kwargs
174 The function to which the interactive widgets are tied. The **kwargs
175 should match the function signature.
175 should match the function signature.
176 **kwargs : various, optional
176 **kwargs : various, optional
177 An interactive widget is created for each keyword argument that is a
177 An interactive widget is created for each keyword argument that is a
178 valid widget abbreviation.
178 valid widget abbreviation.
179 """
179 """
180 f = __interact_f
180 f = __interact_f
181 co = kwargs.pop('clear_output', True)
181 co = kwargs.pop('clear_output', True)
182 manual = kwargs.pop('__manual', False)
182 manual = kwargs.pop('__manual', False)
183 kwargs_widgets = []
183 kwargs_widgets = []
184 container = Box()
184 container = Box(_dom_classes=['widget-interact'])
185 container.result = None
185 container.result = None
186 container.args = []
186 container.args = []
187 container.kwargs = dict()
187 container.kwargs = dict()
188 kwargs = kwargs.copy()
188 kwargs = kwargs.copy()
189
189
190 new_kwargs = _find_abbreviations(f, kwargs)
190 new_kwargs = _find_abbreviations(f, kwargs)
191 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
191 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
192 # that will lead to a valid call of the function. This protects against unspecified
192 # that will lead to a valid call of the function. This protects against unspecified
193 # and doubly-specified arguments.
193 # and doubly-specified arguments.
194 getcallargs(f, **{n:v for n,v,_ in new_kwargs})
194 getcallargs(f, **{n:v for n,v,_ in new_kwargs})
195 # Now build the widgets from the abbreviations.
195 # Now build the widgets from the abbreviations.
196 kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
196 kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
197
197
198 # This has to be done as an assignment, not using container.children.append,
198 # This has to be done as an assignment, not using container.children.append,
199 # so that traitlets notices the update. We skip any objects (such as fixed) that
199 # so that traitlets notices the update. We skip any objects (such as fixed) that
200 # are not DOMWidgets.
200 # are not DOMWidgets.
201 c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
201 c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
202
202
203 # If we are only to run the function on demand, add a button to request this
203 # If we are only to run the function on demand, add a button to request this
204 if manual:
204 if manual:
205 manual_button = Button(description="Run %s" % f.__name__)
205 manual_button = Button(description="Run %s" % f.__name__)
206 c.append(manual_button)
206 c.append(manual_button)
207 container.children = c
207 container.children = c
208
208
209 # Build the callback
209 # Build the callback
210 def call_f(name=None, old=None, new=None):
210 def call_f(name=None, old=None, new=None):
211 container.kwargs = {}
211 container.kwargs = {}
212 for widget in kwargs_widgets:
212 for widget in kwargs_widgets:
213 value = widget.value
213 value = widget.value
214 container.kwargs[widget._kwarg] = value
214 container.kwargs[widget._kwarg] = value
215 if co:
215 if co:
216 clear_output(wait=True)
216 clear_output(wait=True)
217 if manual:
217 if manual:
218 manual_button.disabled = True
218 manual_button.disabled = True
219 try:
219 try:
220 container.result = f(**container.kwargs)
220 container.result = f(**container.kwargs)
221 except Exception as e:
221 except Exception as e:
222 ip = get_ipython()
222 ip = get_ipython()
223 if ip is None:
223 if ip is None:
224 container.log.warn("Exception in interact callback: %s", e, exc_info=True)
224 container.log.warn("Exception in interact callback: %s", e, exc_info=True)
225 else:
225 else:
226 ip.showtraceback()
226 ip.showtraceback()
227 finally:
227 finally:
228 if manual:
228 if manual:
229 manual_button.disabled = False
229 manual_button.disabled = False
230
230
231 # Wire up the widgets
231 # Wire up the widgets
232 # If we are doing manual running, the callback is only triggered by the button
232 # If we are doing manual running, the callback is only triggered by the button
233 # Otherwise, it is triggered for every trait change received
233 # Otherwise, it is triggered for every trait change received
234 # On-demand running also suppresses running the function with the initial parameters
234 # On-demand running also suppresses running the function with the initial parameters
235 if manual:
235 if manual:
236 manual_button.on_click(call_f)
236 manual_button.on_click(call_f)
237 else:
237 else:
238 for widget in kwargs_widgets:
238 for widget in kwargs_widgets:
239 widget.on_trait_change(call_f, 'value')
239 widget.on_trait_change(call_f, 'value')
240
240
241 container.on_displayed(lambda _: call_f(None, None, None))
241 container.on_displayed(lambda _: call_f(None, None, None))
242
242
243 return container
243 return container
244
244
245 def interact(__interact_f=None, **kwargs):
245 def interact(__interact_f=None, **kwargs):
246 """
246 """
247 Displays interactive widgets which are tied to a function.
247 Displays interactive widgets which are tied to a function.
248 Expects the first argument to be a function. Parameters to this function are
248 Expects the first argument to be a function. Parameters to this function are
249 widget abbreviations passed in as keyword arguments (**kwargs). Can be used
249 widget abbreviations passed in as keyword arguments (**kwargs). Can be used
250 as a decorator (see examples).
250 as a decorator (see examples).
251
251
252 Returns
252 Returns
253 -------
253 -------
254 f : __interact_f with interactive widget attached to it.
254 f : __interact_f with interactive widget attached to it.
255
255
256 Parameters
256 Parameters
257 ----------
257 ----------
258 __interact_f : function
258 __interact_f : function
259 The function to which the interactive widgets are tied. The **kwargs
259 The function to which the interactive widgets are tied. The **kwargs
260 should match the function signature. Passed to :func:`interactive()`
260 should match the function signature. Passed to :func:`interactive()`
261 **kwargs : various, optional
261 **kwargs : various, optional
262 An interactive widget is created for each keyword argument that is a
262 An interactive widget is created for each keyword argument that is a
263 valid widget abbreviation. Passed to :func:`interactive()`
263 valid widget abbreviation. Passed to :func:`interactive()`
264
264
265 Examples
265 Examples
266 --------
266 --------
267 Renders an interactive text field that shows the greeting with the passed in
267 Renders an interactive text field that shows the greeting with the passed in
268 text.
268 text.
269
269
270 1. Invocation of interact as a function
270 1. Invocation of interact as a function
271 def greeting(text="World"):
271 def greeting(text="World"):
272 print "Hello {}".format(text)
272 print "Hello {}".format(text)
273 interact(greeting, text="IPython Widgets")
273 interact(greeting, text="IPython Widgets")
274
274
275 2. Invocation of interact as a decorator
275 2. Invocation of interact as a decorator
276 @interact
276 @interact
277 def greeting(text="World"):
277 def greeting(text="World"):
278 print "Hello {}".format(text)
278 print "Hello {}".format(text)
279
279
280 3. Invocation of interact as a decorator with named parameters
280 3. Invocation of interact as a decorator with named parameters
281 @interact(text="IPython Widgets")
281 @interact(text="IPython Widgets")
282 def greeting(text="World"):
282 def greeting(text="World"):
283 print "Hello {}".format(text)
283 print "Hello {}".format(text)
284
284
285 Renders an interactive slider widget and prints square of number.
285 Renders an interactive slider widget and prints square of number.
286
286
287 1. Invocation of interact as a function
287 1. Invocation of interact as a function
288 def square(num=1):
288 def square(num=1):
289 print "{} squared is {}".format(num, num*num)
289 print "{} squared is {}".format(num, num*num)
290 interact(square, num=5)
290 interact(square, num=5)
291
291
292 2. Invocation of interact as a decorator
292 2. Invocation of interact as a decorator
293 @interact
293 @interact
294 def square(num=2):
294 def square(num=2):
295 print "{} squared is {}".format(num, num*num)
295 print "{} squared is {}".format(num, num*num)
296
296
297 3. Invocation of interact as a decorator with named parameters
297 3. Invocation of interact as a decorator with named parameters
298 @interact(num=5)
298 @interact(num=5)
299 def square(num=2):
299 def square(num=2):
300 print "{} squared is {}".format(num, num*num)
300 print "{} squared is {}".format(num, num*num)
301 """
301 """
302 # positional arg support in: https://gist.github.com/8851331
302 # positional arg support in: https://gist.github.com/8851331
303 if __interact_f is not None:
303 if __interact_f is not None:
304 # This branch handles the cases 1 and 2
304 # This branch handles the cases 1 and 2
305 # 1. interact(f, **kwargs)
305 # 1. interact(f, **kwargs)
306 # 2. @interact
306 # 2. @interact
307 # def f(*args, **kwargs):
307 # def f(*args, **kwargs):
308 # ...
308 # ...
309 f = __interact_f
309 f = __interact_f
310 w = interactive(f, **kwargs)
310 w = interactive(f, **kwargs)
311 try:
311 try:
312 f.widget = w
312 f.widget = w
313 except AttributeError:
313 except AttributeError:
314 # some things (instancemethods) can't have attributes attached,
314 # some things (instancemethods) can't have attributes attached,
315 # so wrap in a lambda
315 # so wrap in a lambda
316 f = lambda *args, **kwargs: __interact_f(*args, **kwargs)
316 f = lambda *args, **kwargs: __interact_f(*args, **kwargs)
317 f.widget = w
317 f.widget = w
318 display(w)
318 display(w)
319 return f
319 return f
320 else:
320 else:
321 # This branch handles the case 3
321 # This branch handles the case 3
322 # @interact(a=30, b=40)
322 # @interact(a=30, b=40)
323 # def f(*args, **kwargs):
323 # def f(*args, **kwargs):
324 # ...
324 # ...
325 def dec(f):
325 def dec(f):
326 return interact(f, **kwargs)
326 return interact(f, **kwargs)
327 return dec
327 return dec
328
328
329 def interact_manual(__interact_f=None, **kwargs):
329 def interact_manual(__interact_f=None, **kwargs):
330 """interact_manual(f, **kwargs)
330 """interact_manual(f, **kwargs)
331
331
332 As `interact()`, generates widgets for each argument, but rather than running
332 As `interact()`, generates widgets for each argument, but rather than running
333 the function after each widget change, adds a "Run" button and waits for it
333 the function after each widget change, adds a "Run" button and waits for it
334 to be clicked. Useful if the function is long-running and has several
334 to be clicked. Useful if the function is long-running and has several
335 parameters to change.
335 parameters to change.
336 """
336 """
337 return interact(__interact_f, __manual=True, **kwargs)
337 return interact(__interact_f, __manual=True, **kwargs)
338
338
339 class fixed(HasTraits):
339 class fixed(HasTraits):
340 """A pseudo-widget whose value is fixed and never synced to the client."""
340 """A pseudo-widget whose value is fixed and never synced to the client."""
341 value = Any(help="Any Python object")
341 value = Any(help="Any Python object")
342 description = Unicode('', help="Any Python object")
342 description = Unicode('', help="Any Python object")
343 def __init__(self, value, **kwargs):
343 def __init__(self, value, **kwargs):
344 super(fixed, self).__init__(value=value, **kwargs)
344 super(fixed, self).__init__(value=value, **kwargs)
General Comments 0
You need to be logged in to leave comments. Login now