##// END OF EJS Templates
Add initial implementation of 2-handle range sliders for integers.
Gordon Ball -
Show More
@@ -1,311 +1,327 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // IntWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["widgets/js/widget"], function(WidgetManager){
18 18
19 19 var IntSliderView = IPython.DOMWidgetView.extend({
20 20 render : function(){
21 21 // Called when view is rendered.
22 22 this.$el
23 23 .addClass('widget-hbox-single');
24 24 this.$label = $('<div />')
25 25 .appendTo(this.$el)
26 26 .addClass('widget-hlabel')
27 27 .hide();
28 28
29 29 this.$slider = $('<div />')
30 30 .slider({})
31 31 .addClass('slider');
32 32 // Put the slider in a container
33 33 this.$slider_container = $('<div />')
34 34 .addClass('widget-hslider')
35 35 .append(this.$slider);
36 36 this.$el_to_style = this.$slider_container; // Set default element to style
37 37 this.$el.append(this.$slider_container);
38 38
39 39 this.$readout = $('<div/>')
40 40 .appendTo(this.$el)
41 41 .addClass('widget-hreadout')
42 42 .hide();
43 43
44 44 // Set defaults.
45 45 this.update();
46 46 },
47 47
48 48 update : function(options){
49 49 // Update the contents of this view
50 50 //
51 51 // Called when the model is changed. The model may have been
52 52 // changed by another view or by a state update from the back-end.
53 53 if (options === undefined || options.updated_view != this) {
54 54 // JQuery slider option keys. These keys happen to have a
55 55 // one-to-one mapping with the corrosponding keys of the model.
56 var jquery_slider_keys = ['step', 'max', 'min', 'disabled'];
56 var jquery_slider_keys = ['step', 'max', 'min', 'disabled', 'range'];
57 57 var that = this;
58 58 _.each(jquery_slider_keys, function(key, i) {
59 59 var model_value = that.model.get(key);
60 60 if (model_value !== undefined) {
61 61 that.$slider.slider("option", key, model_value);
62 62 }
63 63 });
64 64
65 65 // WORKAROUND FOR JQUERY SLIDER BUG.
66 66 // The horizontal position of the slider handle
67 67 // depends on the value of the slider at the time
68 68 // of orientation change. Before applying the new
69 69 // workaround, we set the value to the minimum to
70 70 // make sure that the horizontal placement of the
71 71 // handle in the vertical slider is always
72 72 // consistent.
73 73 var orientation = this.model.get('orientation');
74 74 var value = this.model.get('min');
75 if (this.model.get('range')) {
76 this.$slider.slider('option', 'values', [value, value]);
77 } else {
75 78 this.$slider.slider('option', 'value', value);
79 }
76 80 this.$slider.slider('option', 'orientation', orientation);
77 81 value = this.model.get('value');
82 if (this.model.get('range')) {
83 this.$slider.slider('option', 'values', value);
84 this.$readout.text(value.join("-"));
85 } else {
78 86 this.$slider.slider('option', 'value', value);
79 87 this.$readout.text(value);
88 }
89
80 90
81 91 // Use the right CSS classes for vertical & horizontal sliders
82 92 if (orientation=='vertical') {
83 93 this.$slider_container
84 94 .removeClass('widget-hslider')
85 95 .addClass('widget-vslider');
86 96 this.$el
87 97 .removeClass('widget-hbox-single')
88 98 .addClass('widget-vbox-single');
89 99 this.$label
90 100 .removeClass('widget-hlabel')
91 101 .addClass('widget-vlabel');
92 102 this.$readout
93 103 .removeClass('widget-hreadout')
94 104 .addClass('widget-vreadout');
95 105
96 106 } else {
97 107 this.$slider_container
98 108 .removeClass('widget-vslider')
99 109 .addClass('widget-hslider');
100 110 this.$el
101 111 .removeClass('widget-vbox-single')
102 112 .addClass('widget-hbox-single');
103 113 this.$label
104 114 .removeClass('widget-vlabel')
105 115 .addClass('widget-hlabel');
106 116 this.$readout
107 117 .removeClass('widget-vreadout')
108 118 .addClass('widget-hreadout');
109 119 }
110 120
111 121 var description = this.model.get('description');
112 122 if (description.length === 0) {
113 123 this.$label.hide();
114 124 } else {
115 125 this.$label.text(description);
116 126 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
117 127 this.$label.show();
118 128 }
119 129
120 130 var readout = this.model.get('readout');
121 131 if (readout) {
122 132 this.$readout.show();
123 133 } else {
124 134 this.$readout.hide();
125 135 }
126 136 }
127 137 return IntSliderView.__super__.update.apply(this);
128 138 },
129 139
130 140 events: {
131 141 // Dictionary of events and their handlers.
132 142 "slide" : "handleSliderChange"
133 143 },
134 144
135 145 handleSliderChange: function(e, ui) {
136 146 // Called when the slider value is changed.
137 147
138 148 // Calling model.set will trigger all of the other views of the
139 149 // model to update.
150 if (this.model.get("range")) {
151 var actual_value = ui.values.map(this._validate_slide_value);
152 this.$readout.text(actual_value.join("-"));
153 } else {
140 154 var actual_value = this._validate_slide_value(ui.value);
141 this.model.set('value', actual_value, {updated_view: this});
142 155 this.$readout.text(actual_value);
156 }
157 this.model.set('value', actual_value, {updated_view: this});
143 158 this.touch();
159
144 160 },
145 161
146 162 _validate_slide_value: function(x) {
147 163 // Validate the value of the slider before sending it to the back-end
148 164 // and applying it to the other views on the page.
149 165
150 166 // Double bit-wise not truncates the decimel (int cast).
151 167 return ~~x;
152 168 },
153 169 });
154 170 WidgetManager.register_widget_view('IntSliderView', IntSliderView);
155 171
156 172
157 173 var IntTextView = IPython.DOMWidgetView.extend({
158 174 render : function(){
159 175 // Called when view is rendered.
160 176 this.$el
161 177 .addClass('widget-hbox-single');
162 178 this.$label = $('<div />')
163 179 .appendTo(this.$el)
164 180 .addClass('widget-hlabel')
165 181 .hide();
166 182 this.$textbox = $('<input type="text" />')
167 183 .addClass('form-control')
168 184 .addClass('widget-numeric-text')
169 185 .appendTo(this.$el);
170 186 this.$el_to_style = this.$textbox; // Set default element to style
171 187 this.update(); // Set defaults.
172 188 },
173 189
174 190 update : function(options){
175 191 // Update the contents of this view
176 192 //
177 193 // Called when the model is changed. The model may have been
178 194 // changed by another view or by a state update from the back-end.
179 195 if (options === undefined || options.updated_view != this) {
180 196 var value = this.model.get('value');
181 197 if (this._parse_value(this.$textbox.val()) != value) {
182 198 this.$textbox.val(value);
183 199 }
184 200
185 201 if (this.model.get('disabled')) {
186 202 this.$textbox.attr('disabled','disabled');
187 203 } else {
188 204 this.$textbox.removeAttr('disabled');
189 205 }
190 206
191 207 var description = this.model.get('description');
192 208 if (description.length === 0) {
193 209 this.$label.hide();
194 210 } else {
195 211 this.$label.text(description);
196 212 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
197 213 this.$label.show();
198 214 }
199 215 }
200 216 return IntTextView.__super__.update.apply(this);
201 217 },
202 218
203 219 events: {
204 220 // Dictionary of events and their handlers.
205 221 "keyup input" : "handleChanging",
206 222 "paste input" : "handleChanging",
207 223 "cut input" : "handleChanging",
208 224
209 225 // Fires only when control is validated or looses focus.
210 226 "change input" : "handleChanged"
211 227 },
212 228
213 229 handleChanging: function(e) {
214 230 // Handles and validates user input.
215 231
216 232 // Try to parse value as a int.
217 233 var numericalValue = 0;
218 234 if (e.target.value !== '') {
219 235 var trimmed = e.target.value.trim();
220 236 if (!(['-', '-.', '.', '+.', '+'].indexOf(trimmed) >= 0)) {
221 237 numericalValue = this._parse_value(e.target.value);
222 238 }
223 239 }
224 240
225 241 // If parse failed, reset value to value stored in model.
226 242 if (isNaN(numericalValue)) {
227 243 e.target.value = this.model.get('value');
228 244 } else if (!isNaN(numericalValue)) {
229 245 if (this.model.get('max') !== undefined) {
230 246 numericalValue = Math.min(this.model.get('max'), numericalValue);
231 247 }
232 248 if (this.model.get('min') !== undefined) {
233 249 numericalValue = Math.max(this.model.get('min'), numericalValue);
234 250 }
235 251
236 252 // Apply the value if it has changed.
237 253 if (numericalValue != this.model.get('value')) {
238 254
239 255 // Calling model.set will trigger all of the other views of the
240 256 // model to update.
241 257 this.model.set('value', numericalValue, {updated_view: this});
242 258 this.touch();
243 259 }
244 260 }
245 261 },
246 262
247 263 handleChanged: function(e) {
248 264 // Applies validated input.
249 265 if (this.model.get('value') != e.target.value) {
250 266 e.target.value = this.model.get('value');
251 267 }
252 268 },
253 269
254 270 _parse_value: function(value) {
255 271 // Parse the value stored in a string.
256 272 return parseInt(value);
257 273 },
258 274 });
259 275 WidgetManager.register_widget_view('IntTextView', IntTextView);
260 276
261 277
262 278 var ProgressView = IPython.DOMWidgetView.extend({
263 279 render : function(){
264 280 // Called when view is rendered.
265 281 this.$el
266 282 .addClass('widget-hbox-single');
267 283 this.$label = $('<div />')
268 284 .appendTo(this.$el)
269 285 .addClass('widget-hlabel')
270 286 .hide();
271 287 this.$progress = $('<div />')
272 288 .addClass('progress')
273 289 .addClass('widget-progress')
274 290 .appendTo(this.$el);
275 291 this.$el_to_style = this.$progress; // Set default element to style
276 292 this.$bar = $('<div />')
277 293 .addClass('progress-bar')
278 294 .css('width', '50%')
279 295 .appendTo(this.$progress);
280 296 this.update(); // Set defaults.
281 297 },
282 298
283 299 update : function(){
284 300 // Update the contents of this view
285 301 //
286 302 // Called when the model is changed. The model may have been
287 303 // changed by another view or by a state update from the back-end.
288 304 var value = this.model.get('value');
289 305 var max = this.model.get('max');
290 306 var min = this.model.get('min');
291 307 var percent = 100.0 * (value - min) / (max - min);
292 308 this.$bar.css('width', percent + '%');
293 309
294 310 var description = this.model.get('description');
295 311 if (description.length === 0) {
296 312 this.$label.hide();
297 313 } else {
298 314 this.$label.text(description);
299 315 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
300 316 this.$label.show();
301 317 }
302 318 return ProgressView.__super__.update.apply(this);
303 319 },
304 320 });
305 321 WidgetManager.register_widget_view('ProgressView', ProgressView);
306 322
307 323
308 324 // Return the slider and text views so they can be inheritted to create the
309 325 // float versions.
310 326 return [IntSliderView, IntTextView];
311 327 });
@@ -1,12 +1,12 b''
1 1 from .widget import Widget, DOMWidget, CallbackDispatcher
2 2
3 3 from .widget_bool import CheckboxWidget, ToggleButtonWidget
4 4 from .widget_button import ButtonWidget
5 5 from .widget_container import ContainerWidget, PopupWidget
6 6 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
7 7 from .widget_image import ImageWidget
8 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
8 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget, IntRangeSliderWidget
9 9 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
10 10 from .widget_selectioncontainer import TabWidget, AccordionWidget
11 11 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
12 12 from .interaction import interact, interactive, fixed
@@ -1,257 +1,266 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, TextWidget,
25 25 FloatSliderWidget, IntSliderWidget, CheckboxWidget, DropdownWidget,
26 ContainerWidget, DOMWidget)
26 ContainerWidget, DOMWidget, IntRangeSliderWidget)
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 TextWidget(value=unicode_type(o))
74 74 elif isinstance(o, dict):
75 75 return DropdownWidget(values=o)
76 76 elif isinstance(o, bool):
77 77 return CheckboxWidget(value=o)
78 78 elif isinstance(o, float):
79 79 min, max, value = _get_min_max_value(None, None, o)
80 80 return FloatSliderWidget(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 IntSliderWidget(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 DropdownWidget(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 = IntSliderWidget
97 97 else:
98 98 cls = FloatSliderWidget
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 = IntSliderWidget
107 107 else:
108 108 cls = FloatSliderWidget
109 109 return cls(value=value, min=min, max=max, step=step)
110 elif _matches(o, [float_or_int]*4):
111 min, low, high, max = o
112 if not min <= low <= high <= max:
113 raise ValueError("Range input expects min <= low <= high <= max, got %r" % o)
114 if all(isinstance(_, int) for _ in o):
115 cls = IntRangeSliderWidget
116 else:
117 cls = FloatRangeSliderWidget
118 return cls(value=(low, high), min=min, max=max)
110 119 else:
111 120 return _widget_abbrev_single_value(o)
112 121
113 122 def _widget_from_abbrev(abbrev, default=empty):
114 123 """Build a Widget instance given an abbreviation or Widget."""
115 124 if isinstance(abbrev, Widget) or isinstance(abbrev, fixed):
116 125 return abbrev
117 126
118 127 widget = _widget_abbrev(abbrev)
119 128 if default is not empty and isinstance(abbrev, (list, tuple, dict)):
120 129 # if it's not a single-value abbreviation,
121 130 # set the initial value from the default
122 131 try:
123 132 widget.value = default
124 133 except Exception:
125 134 # ignore failure to set default
126 135 pass
127 136 if widget is None:
128 137 raise ValueError("%r cannot be transformed to a Widget" % (abbrev,))
129 138 return widget
130 139
131 140 def _yield_abbreviations_for_parameter(param, kwargs):
132 141 """Get an abbreviation for a function parameter."""
133 142 name = param.name
134 143 kind = param.kind
135 144 ann = param.annotation
136 145 default = param.default
137 146 not_found = (name, empty, empty)
138 147 if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
139 148 if name in kwargs:
140 149 value = kwargs.pop(name)
141 150 elif ann is not empty:
142 151 value = ann
143 152 elif default is not empty:
144 153 value = default
145 154 else:
146 155 yield not_found
147 156 yield (name, value, default)
148 157 elif kind == Parameter.VAR_KEYWORD:
149 158 # In this case name=kwargs and we yield the items in kwargs with their keys.
150 159 for k, v in kwargs.copy().items():
151 160 kwargs.pop(k)
152 161 yield k, v, empty
153 162
154 163 def _find_abbreviations(f, kwargs):
155 164 """Find the abbreviations for a function and kwargs passed to interact."""
156 165 new_kwargs = []
157 166 for param in signature(f).parameters.values():
158 167 for name, value, default in _yield_abbreviations_for_parameter(param, kwargs):
159 168 if value is empty:
160 169 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
161 170 new_kwargs.append((name, value, default))
162 171 return new_kwargs
163 172
164 173 def _widgets_from_abbreviations(seq):
165 174 """Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
166 175 result = []
167 176 for name, abbrev, default in seq:
168 177 widget = _widget_from_abbrev(abbrev, default)
169 178 if not widget.description:
170 179 widget.description = name
171 180 result.append(widget)
172 181 return result
173 182
174 183 def interactive(__interact_f, **kwargs):
175 184 """Build a group of widgets to interact with a function."""
176 185 f = __interact_f
177 186 co = kwargs.pop('clear_output', True)
178 187 kwargs_widgets = []
179 188 container = ContainerWidget()
180 189 container.result = None
181 190 container.args = []
182 191 container.kwargs = dict()
183 192 kwargs = kwargs.copy()
184 193
185 194 new_kwargs = _find_abbreviations(f, kwargs)
186 195 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
187 196 # that will lead to a valid call of the function. This protects against unspecified
188 197 # and doubly-specified arguments.
189 198 getcallargs(f, **{n:v for n,v,_ in new_kwargs})
190 199 # Now build the widgets from the abbreviations.
191 200 kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
192 201
193 202 # This has to be done as an assignment, not using container.children.append,
194 203 # so that traitlets notices the update. We skip any objects (such as fixed) that
195 204 # are not DOMWidgets.
196 205 c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
197 206 container.children = c
198 207
199 208 # Build the callback
200 209 def call_f(name, old, new):
201 210 container.kwargs = {}
202 211 for widget in kwargs_widgets:
203 212 value = widget.value
204 213 container.kwargs[widget.description] = value
205 214 if co:
206 215 clear_output(wait=True)
207 216 try:
208 217 container.result = f(**container.kwargs)
209 218 except Exception as e:
210 219 ip = get_ipython()
211 220 if ip is None:
212 221 container.log.warn("Exception in interact callback: %s", e, exc_info=True)
213 222 else:
214 223 ip.showtraceback()
215 224
216 225 # Wire up the widgets
217 226 for widget in kwargs_widgets:
218 227 widget.on_trait_change(call_f, 'value')
219 228
220 229 container.on_displayed(lambda _: call_f(None, None, None))
221 230
222 231 return container
223 232
224 233 def interact(__interact_f=None, **kwargs):
225 234 """interact(f, **kwargs)
226 235
227 236 Interact with a function using widgets."""
228 237 # positional arg support in: https://gist.github.com/8851331
229 238 if __interact_f is not None:
230 239 # This branch handles the cases:
231 240 # 1. interact(f, **kwargs)
232 241 # 2. @interact
233 242 # def f(*args, **kwargs):
234 243 # ...
235 244 f = __interact_f
236 245 w = interactive(f, **kwargs)
237 246 f.widget = w
238 247 display(w)
239 248 return f
240 249 else:
241 250 # This branch handles the case:
242 251 # @interact(a=30, b=40)
243 252 # def f(*args, **kwargs):
244 253 # ...
245 254 def dec(f):
246 255 w = interactive(f, **kwargs)
247 256 f.widget = w
248 257 display(w)
249 258 return f
250 259 return dec
251 260
252 261 class fixed(HasTraits):
253 262 """A pseudo-widget whose value is fixed and never synced to the client."""
254 263 value = Any(help="Any Python object")
255 264 description = Unicode('', help="Any Python object")
256 265 def __init__(self, value, **kwargs):
257 266 super(fixed, self).__init__(value=value, **kwargs)
@@ -1,60 +1,90 b''
1 1 """IntWidget class.
2 2
3 3 Represents an unbounded int using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from .widget import DOMWidget
17 from IPython.utils.traitlets import Unicode, CInt, Bool, Enum
17 from IPython.utils.traitlets import Unicode, CInt, Bool, Enum, Tuple
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Classes
21 21 #-----------------------------------------------------------------------------
22 22 class _IntWidget(DOMWidget):
23 23 value = CInt(0, help="Int value", sync=True)
24 24 disabled = Bool(False, help="Enable or disable user changes", sync=True)
25 25 description = Unicode(help="Description of the value this widget represents", sync=True)
26 26
27 27
28 28 class _BoundedIntWidget(_IntWidget):
29 29 step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
30 30 max = CInt(100, help="Max value", sync=True)
31 31 min = CInt(0, help="Min value", sync=True)
32 32
33 33 def __init__(self, *pargs, **kwargs):
34 34 """Constructor"""
35 35 DOMWidget.__init__(self, *pargs, **kwargs)
36 36 self.on_trait_change(self._validate, ['value', 'min', 'max'])
37 37
38 38 def _validate(self, name, old, new):
39 39 """Validate value, max, min."""
40 40 if self.min > new or new > self.max:
41 41 self.value = min(max(new, self.min), self.max)
42 42
43 43
44 44 class IntTextWidget(_IntWidget):
45 45 _view_name = Unicode('IntTextView', sync=True)
46 46
47 47
48 48 class BoundedIntTextWidget(_BoundedIntWidget):
49 49 _view_name = Unicode('IntTextView', sync=True)
50 50
51 51
52 52 class IntSliderWidget(_BoundedIntWidget):
53 53 _view_name = Unicode('IntSliderView', sync=True)
54 54 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
55 55 help="Vertical or horizontal.", sync=True)
56 range = Bool(False, help="Display a range selector", sync=True)
56 57 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
57 58
58 59
59 60 class IntProgressWidget(_BoundedIntWidget):
60 61 _view_name = Unicode('ProgressView', sync=True)
62
63 class _IntRangeWidget(_IntWidget):
64 value = Tuple(CInt, CInt, default_value=(0, 1), help="Low and high int values", sync=True)
65
66 class _BoundedIntRangeWidget(_IntRangeWidget):
67 step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
68 max = CInt(100, help="Max value", sync=True)
69 min = CInt(0, help="Min value", sync=True)
70
71 def __init__(self, *pargs, **kwargs):
72 """Constructor"""
73 DOMWidget.__init__(self, *pargs, **kwargs)
74 self.on_trait_change(self._validate, ['value', 'min', 'max'])
75
76 def _validate(self, name, old, new):
77 """Validate min <= low <= high <= max"""
78 if name == "value":
79 low, high = new
80 low = max(low, self.min)
81 high = min(high, self.max)
82 self.value = (min(low, high), max(low, high))
83
84
85 class IntRangeSliderWidget(_BoundedIntRangeWidget):
86 _view_name = Unicode('IntSliderView', sync=True)
87 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
88 help="Vertical or horizontal.", sync=True)
89 range = Bool(True, help="Display a range selector", sync=True)
90 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
General Comments 0
You need to be logged in to leave comments. Login now