##// END OF EJS Templates
Add initial implementation of 2-handle range sliders for integers.
Gordon Ball -
Show More
@@ -25,35 +25,35 b' define(["widgets/js/widget"], function(WidgetManager){'
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 // Put the slider in a container
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 // Called when the model is changed. The model may have been
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);
@@ -68,15 +68,25 b' define(["widgets/js/widget"], function(WidgetManager){'
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 // handle in the vertical slider is always
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 this.$slider.slider('option', 'value', value);
75 if (this.model.get('range')) {
76 this.$slider.slider('option', 'values', [value, value]);
77 } else {
78 this.$slider.slider('option', 'value', value);
79 }
76 80 this.$slider.slider('option', 'orientation', orientation);
77 81 value = this.model.get('value');
78 this.$slider.slider('option', 'value', value);
79 this.$readout.text(value);
82 if (this.model.get('range')) {
83 this.$slider.slider('option', 'values', value);
84 this.$readout.text(value.join("-"));
85 } else {
86 this.$slider.slider('option', 'value', value);
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') {
@@ -116,7 +126,7 b' define(["widgets/js/widget"], function(WidgetManager){'
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();
@@ -126,21 +136,27 b' define(["widgets/js/widget"], function(WidgetManager){'
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 handleSliderChange: function(e, ui) {
145 handleSliderChange: function(e, ui) {
136 146 // Called when the slider value is changed.
137 147
138 // Calling model.set will trigger all of the other views of the
148 // Calling model.set will trigger all of the other views of the
139 149 // model to update.
140 var actual_value = this._validate_slide_value(ui.value);
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 {
154 var actual_value = this._validate_slide_value(ui.value);
155 this.$readout.text(actual_value);
156 }
141 157 this.model.set('value', actual_value, {updated_view: this});
142 this.$readout.text(actual_value);
143 158 this.touch();
159
144 160 },
145 161
146 162 _validate_slide_value: function(x) {
@@ -154,7 +170,7 b' define(["widgets/js/widget"], function(WidgetManager){'
154 170 WidgetManager.register_widget_view('IntSliderView', IntSliderView);
155 171
156 172
157 var IntTextView = IPython.DOMWidgetView.extend({
173 var IntTextView = IPython.DOMWidgetView.extend({
158 174 render : function(){
159 175 // Called when view is rendered.
160 176 this.$el
@@ -170,18 +186,18 b' define(["widgets/js/widget"], function(WidgetManager){'
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 // Called when the model is changed. The model may have been
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 {
@@ -208,20 +224,20 b' define(["widgets/js/widget"], function(WidgetManager){'
208 224
209 225 // Fires only when control is validated or looses focus.
210 226 "change input" : "handleChanged"
211 },
212
213 handleChanging: function(e) {
227 },
228
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 numericalValue = this._parse_value(e.target.value);
222 }
237 numericalValue = this._parse_value(e.target.value);
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');
@@ -232,18 +248,18 b' define(["widgets/js/widget"], function(WidgetManager){'
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
239 // Calling model.set will trigger all of the other views of the
254
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) {
@@ -279,18 +295,18 b' define(["widgets/js/widget"], function(WidgetManager){'
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 // Called when the model is changed. The model may have been
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();
@@ -300,7 +316,7 b' define(["widgets/js/widget"], function(WidgetManager){'
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
@@ -5,7 +5,7 b' 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
@@ -23,7 +23,7 b' from inspect import getcallargs'
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
@@ -107,6 +107,15 b' def _widget_abbrev(o):'
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
@@ -1,4 +1,4 b''
1 """IntWidget class.
1 """IntWidget class.
2 2
3 3 Represents an unbounded int using a widget.
4 4 """
@@ -14,13 +14,13 b' Represents an unbounded int using a widget.'
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 value = CInt(0, help="Int value", sync=True)
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
@@ -51,10 +51,40 b' class BoundedIntTextWidget(_BoundedIntWidget):'
51 51
52 52 class IntSliderWidget(_BoundedIntWidget):
53 53 _view_name = Unicode('IntSliderView', sync=True)
54 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
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