##// 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 .appendTo(this.$el)
25 .appendTo(this.$el)
26 .addClass('widget-hlabel')
26 .addClass('widget-hlabel')
27 .hide();
27 .hide();
28
28
29 this.$slider = $('<div />')
29 this.$slider = $('<div />')
30 .slider({})
30 .slider({})
31 .addClass('slider');
31 .addClass('slider');
32 // Put the slider in a container
32 // Put the slider in a container
33 this.$slider_container = $('<div />')
33 this.$slider_container = $('<div />')
34 .addClass('widget-hslider')
34 .addClass('widget-hslider')
35 .append(this.$slider);
35 .append(this.$slider);
36 this.$el_to_style = this.$slider_container; // Set default element to style
36 this.$el_to_style = this.$slider_container; // Set default element to style
37 this.$el.append(this.$slider_container);
37 this.$el.append(this.$slider_container);
38
38
39 this.$readout = $('<div/>')
39 this.$readout = $('<div/>')
40 .appendTo(this.$el)
40 .appendTo(this.$el)
41 .addClass('widget-hreadout')
41 .addClass('widget-hreadout')
42 .hide();
42 .hide();
43
43
44 // Set defaults.
44 // Set defaults.
45 this.update();
45 this.update();
46 },
46 },
47
47
48 update : function(options){
48 update : function(options){
49 // Update the contents of this view
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 // changed by another view or by a state update from the back-end.
52 // changed by another view or by a state update from the back-end.
53 if (options === undefined || options.updated_view != this) {
53 if (options === undefined || options.updated_view != this) {
54 // JQuery slider option keys. These keys happen to have a
54 // JQuery slider option keys. These keys happen to have a
55 // one-to-one mapping with the corrosponding keys of the model.
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 var that = this;
57 var that = this;
58 _.each(jquery_slider_keys, function(key, i) {
58 _.each(jquery_slider_keys, function(key, i) {
59 var model_value = that.model.get(key);
59 var model_value = that.model.get(key);
@@ -68,15 +68,25 b' define(["widgets/js/widget"], function(WidgetManager){'
68 // of orientation change. Before applying the new
68 // of orientation change. Before applying the new
69 // workaround, we set the value to the minimum to
69 // workaround, we set the value to the minimum to
70 // make sure that the horizontal placement of the
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 // consistent.
72 // consistent.
73 var orientation = this.model.get('orientation');
73 var orientation = this.model.get('orientation');
74 var value = this.model.get('min');
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 this.$slider.slider('option', 'orientation', orientation);
80 this.$slider.slider('option', 'orientation', orientation);
77 value = this.model.get('value');
81 value = this.model.get('value');
78 this.$slider.slider('option', 'value', value);
82 if (this.model.get('range')) {
79 this.$readout.text(value);
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 // Use the right CSS classes for vertical & horizontal sliders
91 // Use the right CSS classes for vertical & horizontal sliders
82 if (orientation=='vertical') {
92 if (orientation=='vertical') {
@@ -116,7 +126,7 b' define(["widgets/js/widget"], function(WidgetManager){'
116 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
126 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
117 this.$label.show();
127 this.$label.show();
118 }
128 }
119
129
120 var readout = this.model.get('readout');
130 var readout = this.model.get('readout');
121 if (readout) {
131 if (readout) {
122 this.$readout.show();
132 this.$readout.show();
@@ -126,21 +136,27 b' define(["widgets/js/widget"], function(WidgetManager){'
126 }
136 }
127 return IntSliderView.__super__.update.apply(this);
137 return IntSliderView.__super__.update.apply(this);
128 },
138 },
129
139
130 events: {
140 events: {
131 // Dictionary of events and their handlers.
141 // Dictionary of events and their handlers.
132 "slide" : "handleSliderChange"
142 "slide" : "handleSliderChange"
133 },
143 },
134
144
135 handleSliderChange: function(e, ui) {
145 handleSliderChange: function(e, ui) {
136 // Called when the slider value is changed.
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 // model to update.
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 this.model.set('value', actual_value, {updated_view: this});
157 this.model.set('value', actual_value, {updated_view: this});
142 this.$readout.text(actual_value);
143 this.touch();
158 this.touch();
159
144 },
160 },
145
161
146 _validate_slide_value: function(x) {
162 _validate_slide_value: function(x) {
@@ -154,7 +170,7 b' define(["widgets/js/widget"], function(WidgetManager){'
154 WidgetManager.register_widget_view('IntSliderView', IntSliderView);
170 WidgetManager.register_widget_view('IntSliderView', IntSliderView);
155
171
156
172
157 var IntTextView = IPython.DOMWidgetView.extend({
173 var IntTextView = IPython.DOMWidgetView.extend({
158 render : function(){
174 render : function(){
159 // Called when view is rendered.
175 // Called when view is rendered.
160 this.$el
176 this.$el
@@ -170,18 +186,18 b' define(["widgets/js/widget"], function(WidgetManager){'
170 this.$el_to_style = this.$textbox; // Set default element to style
186 this.$el_to_style = this.$textbox; // Set default element to style
171 this.update(); // Set defaults.
187 this.update(); // Set defaults.
172 },
188 },
173
189
174 update : function(options){
190 update : function(options){
175 // Update the contents of this view
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 // changed by another view or by a state update from the back-end.
194 // changed by another view or by a state update from the back-end.
179 if (options === undefined || options.updated_view != this) {
195 if (options === undefined || options.updated_view != this) {
180 var value = this.model.get('value');
196 var value = this.model.get('value');
181 if (this._parse_value(this.$textbox.val()) != value) {
197 if (this._parse_value(this.$textbox.val()) != value) {
182 this.$textbox.val(value);
198 this.$textbox.val(value);
183 }
199 }
184
200
185 if (this.model.get('disabled')) {
201 if (this.model.get('disabled')) {
186 this.$textbox.attr('disabled','disabled');
202 this.$textbox.attr('disabled','disabled');
187 } else {
203 } else {
@@ -208,20 +224,20 b' define(["widgets/js/widget"], function(WidgetManager){'
208
224
209 // Fires only when control is validated or looses focus.
225 // Fires only when control is validated or looses focus.
210 "change input" : "handleChanged"
226 "change input" : "handleChanged"
211 },
227 },
212
228
213 handleChanging: function(e) {
229 handleChanging: function(e) {
214 // Handles and validates user input.
230 // Handles and validates user input.
215
231
216 // Try to parse value as a int.
232 // Try to parse value as a int.
217 var numericalValue = 0;
233 var numericalValue = 0;
218 if (e.target.value !== '') {
234 if (e.target.value !== '') {
219 var trimmed = e.target.value.trim();
235 var trimmed = e.target.value.trim();
220 if (!(['-', '-.', '.', '+.', '+'].indexOf(trimmed) >= 0)) {
236 if (!(['-', '-.', '.', '+.', '+'].indexOf(trimmed) >= 0)) {
221 numericalValue = this._parse_value(e.target.value);
237 numericalValue = this._parse_value(e.target.value);
222 }
238 }
223 }
239 }
224
240
225 // If parse failed, reset value to value stored in model.
241 // If parse failed, reset value to value stored in model.
226 if (isNaN(numericalValue)) {
242 if (isNaN(numericalValue)) {
227 e.target.value = this.model.get('value');
243 e.target.value = this.model.get('value');
@@ -232,18 +248,18 b' define(["widgets/js/widget"], function(WidgetManager){'
232 if (this.model.get('min') !== undefined) {
248 if (this.model.get('min') !== undefined) {
233 numericalValue = Math.max(this.model.get('min'), numericalValue);
249 numericalValue = Math.max(this.model.get('min'), numericalValue);
234 }
250 }
235
251
236 // Apply the value if it has changed.
252 // Apply the value if it has changed.
237 if (numericalValue != this.model.get('value')) {
253 if (numericalValue != this.model.get('value')) {
238
254
239 // Calling model.set will trigger all of the other views of the
255 // Calling model.set will trigger all of the other views of the
240 // model to update.
256 // model to update.
241 this.model.set('value', numericalValue, {updated_view: this});
257 this.model.set('value', numericalValue, {updated_view: this});
242 this.touch();
258 this.touch();
243 }
259 }
244 }
260 }
245 },
261 },
246
262
247 handleChanged: function(e) {
263 handleChanged: function(e) {
248 // Applies validated input.
264 // Applies validated input.
249 if (this.model.get('value') != e.target.value) {
265 if (this.model.get('value') != e.target.value) {
@@ -279,18 +295,18 b' define(["widgets/js/widget"], function(WidgetManager){'
279 .appendTo(this.$progress);
295 .appendTo(this.$progress);
280 this.update(); // Set defaults.
296 this.update(); // Set defaults.
281 },
297 },
282
298
283 update : function(){
299 update : function(){
284 // Update the contents of this view
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 // changed by another view or by a state update from the back-end.
303 // changed by another view or by a state update from the back-end.
288 var value = this.model.get('value');
304 var value = this.model.get('value');
289 var max = this.model.get('max');
305 var max = this.model.get('max');
290 var min = this.model.get('min');
306 var min = this.model.get('min');
291 var percent = 100.0 * (value - min) / (max - min);
307 var percent = 100.0 * (value - min) / (max - min);
292 this.$bar.css('width', percent + '%');
308 this.$bar.css('width', percent + '%');
293
309
294 var description = this.model.get('description');
310 var description = this.model.get('description');
295 if (description.length === 0) {
311 if (description.length === 0) {
296 this.$label.hide();
312 this.$label.hide();
@@ -300,7 +316,7 b' define(["widgets/js/widget"], function(WidgetManager){'
300 this.$label.show();
316 this.$label.show();
301 }
317 }
302 return ProgressView.__super__.update.apply(this);
318 return ProgressView.__super__.update.apply(this);
303 },
319 },
304 });
320 });
305 WidgetManager.register_widget_view('ProgressView', ProgressView);
321 WidgetManager.register_widget_view('ProgressView', ProgressView);
306
322
@@ -5,7 +5,7 b' from .widget_button import ButtonWidget'
5 from .widget_container import ContainerWidget, PopupWidget
5 from .widget_container import ContainerWidget, PopupWidget
6 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
6 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
7 from .widget_image import ImageWidget
7 from .widget_image import ImageWidget
8 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
8 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget, IntRangeSliderWidget
9 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
9 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
10 from .widget_selectioncontainer import TabWidget, AccordionWidget
10 from .widget_selectioncontainer import TabWidget, AccordionWidget
11 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
11 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
@@ -23,7 +23,7 b' from inspect import getcallargs'
23 from IPython.core.getipython import get_ipython
23 from IPython.core.getipython import get_ipython
24 from IPython.html.widgets import (Widget, TextWidget,
24 from IPython.html.widgets import (Widget, TextWidget,
25 FloatSliderWidget, IntSliderWidget, CheckboxWidget, DropdownWidget,
25 FloatSliderWidget, IntSliderWidget, CheckboxWidget, DropdownWidget,
26 ContainerWidget, DOMWidget)
26 ContainerWidget, DOMWidget, IntRangeSliderWidget)
27 from IPython.display import display, clear_output
27 from IPython.display import display, clear_output
28 from IPython.utils.py3compat import string_types, unicode_type
28 from IPython.utils.py3compat import string_types, unicode_type
29 from IPython.utils.traitlets import HasTraits, Any, Unicode
29 from IPython.utils.traitlets import HasTraits, Any, Unicode
@@ -107,6 +107,15 b' def _widget_abbrev(o):'
107 else:
107 else:
108 cls = FloatSliderWidget
108 cls = FloatSliderWidget
109 return cls(value=value, min=min, max=max, step=step)
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 else:
119 else:
111 return _widget_abbrev_single_value(o)
120 return _widget_abbrev_single_value(o)
112
121
@@ -1,4 +1,4 b''
1 """IntWidget class.
1 """IntWidget class.
2
2
3 Represents an unbounded int using a widget.
3 Represents an unbounded int using a widget.
4 """
4 """
@@ -14,13 +14,13 b' Represents an unbounded int using a widget.'
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from .widget import DOMWidget
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 # Classes
20 # Classes
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 class _IntWidget(DOMWidget):
22 class _IntWidget(DOMWidget):
23 value = CInt(0, help="Int value", sync=True)
23 value = CInt(0, help="Int value", sync=True)
24 disabled = Bool(False, help="Enable or disable user changes", sync=True)
24 disabled = Bool(False, help="Enable or disable user changes", sync=True)
25 description = Unicode(help="Description of the value this widget represents", sync=True)
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 class IntSliderWidget(_BoundedIntWidget):
52 class IntSliderWidget(_BoundedIntWidget):
53 _view_name = Unicode('IntSliderView', sync=True)
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 help="Vertical or horizontal.", sync=True)
55 help="Vertical or horizontal.", sync=True)
56 range = Bool(False, help="Display a range selector", sync=True)
56 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
57 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
57
58
58
59
59 class IntProgressWidget(_BoundedIntWidget):
60 class IntProgressWidget(_BoundedIntWidget):
60 _view_name = Unicode('ProgressView', sync=True)
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