##// END OF EJS Templates
Added bootstrap3 progress bar classes
Jonathan Frederic -
Show More
@@ -1,367 +1,382 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 "widgets/js/widget",
6 6 "jqueryui",
7 7 "bootstrap",
8 8 ], function(widget, $){
9 9
10 10 var IntSliderView = widget.DOMWidgetView.extend({
11 11 render : function(){
12 12 // Called when view is rendered.
13 13 this.$el
14 14 .addClass('widget-hbox-single');
15 15 this.$label = $('<div />')
16 16 .appendTo(this.$el)
17 17 .addClass('widget-hlabel')
18 18 .hide();
19 19
20 20 this.$slider = $('<div />')
21 21 .slider({})
22 22 .addClass('slider');
23 23 // Put the slider in a container
24 24 this.$slider_container = $('<div />')
25 25 .addClass('widget-hslider')
26 26 .append(this.$slider);
27 27 this.$el.append(this.$slider_container);
28 28
29 29 this.$readout = $('<div/>')
30 30 .appendTo(this.$el)
31 31 .addClass('widget-hreadout')
32 32 .hide();
33 33
34 34 this.model.on('change:slider_color', function(sender, value) {
35 35 this.$slider.find('a').css('background', value);
36 36 }, this);
37 37
38 38 // Set defaults.
39 39 this.update();
40 40 },
41 41
42 42 update_attr: function(name, value) {
43 43 // Set a css attr of the widget view.
44 44 if (name == 'color') {
45 45 this.$readout.css(name, value);
46 46 } else if (name.substring(0, 4) == 'font') {
47 47 this.$readout.css(name, value);
48 48 } else if (name.substring(0, 6) == 'border') {
49 49 this.$slider.find('a').css(name, value);
50 50 this.$slider_container.css(name, value);
51 51 } else if (name == 'width' || name == 'height' || name == 'background') {
52 52 this.$slider_container.css(name, value);
53 53 } else {
54 54 this.$slider.css(name, value);
55 55 }
56 56 },
57 57
58 58 update : function(options){
59 59 // Update the contents of this view
60 60 //
61 61 // Called when the model is changed. The model may have been
62 62 // changed by another view or by a state update from the back-end.
63 63 if (options === undefined || options.updated_view != this) {
64 64 // JQuery slider option keys. These keys happen to have a
65 65 // one-to-one mapping with the corrosponding keys of the model.
66 66 var jquery_slider_keys = ['step', 'max', 'min', 'disabled'];
67 67 var that = this;
68 68 that.$slider.slider({});
69 69 _.each(jquery_slider_keys, function(key, i) {
70 70 var model_value = that.model.get(key);
71 71 if (model_value !== undefined) {
72 72 that.$slider.slider("option", key, model_value);
73 73 }
74 74 });
75 75 var range_value = this.model.get("_range");
76 76 if (range_value !== undefined) {
77 77 this.$slider.slider("option", "range", range_value);
78 78 }
79 79
80 80 // WORKAROUND FOR JQUERY SLIDER BUG.
81 81 // The horizontal position of the slider handle
82 82 // depends on the value of the slider at the time
83 83 // of orientation change. Before applying the new
84 84 // workaround, we set the value to the minimum to
85 85 // make sure that the horizontal placement of the
86 86 // handle in the vertical slider is always
87 87 // consistent.
88 88 var orientation = this.model.get('orientation');
89 89 var min = this.model.get('min');
90 90 var max = this.model.get('max');
91 91 if (this.model.get('_range')) {
92 92 this.$slider.slider('option', 'values', [min, min]);
93 93 } else {
94 94 this.$slider.slider('option', 'value', min);
95 95 }
96 96 this.$slider.slider('option', 'orientation', orientation);
97 97 var value = this.model.get('value');
98 98 if (this.model.get('_range')) {
99 99 // values for the range case are validated python-side in
100 100 // _Bounded{Int,Float}RangeWidget._validate
101 101 this.$slider.slider('option', 'values', value);
102 102 this.$readout.text(value.join("-"));
103 103 } else {
104 104 if(value > max) {
105 105 value = max;
106 106 }
107 107 else if(value < min){
108 108 value = min;
109 109 }
110 110 this.$slider.slider('option', 'value', value);
111 111 this.$readout.text(value);
112 112 }
113 113
114 114 if(this.model.get('value')!=value) {
115 115 this.model.set('value', value, {updated_view: this});
116 116 this.touch();
117 117 }
118 118
119 119 // Use the right CSS classes for vertical & horizontal sliders
120 120 if (orientation=='vertical') {
121 121 this.$slider_container
122 122 .removeClass('widget-hslider')
123 123 .addClass('widget-vslider');
124 124 this.$el
125 125 .removeClass('widget-hbox-single');
126 126 this.$label
127 127 .removeClass('widget-hlabel')
128 128 .addClass('widget-vlabel');
129 129 this.$readout
130 130 .removeClass('widget-hreadout')
131 131 .addClass('widget-vreadout');
132 132
133 133 } else {
134 134 this.$slider_container
135 135 .removeClass('widget-vslider')
136 136 .addClass('widget-hslider');
137 137 this.$el
138 138 .addClass('widget-hbox-single');
139 139 this.$label
140 140 .removeClass('widget-vlabel')
141 141 .addClass('widget-hlabel');
142 142 this.$readout
143 143 .removeClass('widget-vreadout')
144 144 .addClass('widget-hreadout');
145 145 }
146 146
147 147 var description = this.model.get('description');
148 148 if (description.length === 0) {
149 149 this.$label.hide();
150 150 } else {
151 151 this.$label.text(description);
152 152 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
153 153 this.$label.show();
154 154 }
155 155
156 156 var readout = this.model.get('readout');
157 157 if (readout) {
158 158 this.$readout.show();
159 159 } else {
160 160 this.$readout.hide();
161 161 }
162 162 }
163 163 return IntSliderView.__super__.update.apply(this);
164 164 },
165 165
166 166 events: {
167 167 // Dictionary of events and their handlers.
168 168 "slide" : "handleSliderChange"
169 169 },
170 170
171 171 handleSliderChange: function(e, ui) {
172 172 // Called when the slider value is changed.
173 173
174 174 // Calling model.set will trigger all of the other views of the
175 175 // model to update.
176 176 if (this.model.get("_range")) {
177 177 var actual_value = ui.values.map(this._validate_slide_value);
178 178 this.$readout.text(actual_value.join("-"));
179 179 } else {
180 180 var actual_value = this._validate_slide_value(ui.value);
181 181 this.$readout.text(actual_value);
182 182 }
183 183 this.model.set('value', actual_value, {updated_view: this});
184 184 this.touch();
185 185 },
186 186
187 187 _validate_slide_value: function(x) {
188 188 // Validate the value of the slider before sending it to the back-end
189 189 // and applying it to the other views on the page.
190 190
191 191 // Double bit-wise not truncates the decimel (int cast).
192 192 return ~~x;
193 193 },
194 194 });
195 195
196 196
197 197 var IntTextView = widget.DOMWidgetView.extend({
198 198 render : function(){
199 199 // Called when view is rendered.
200 200 this.$el
201 201 .addClass('widget-hbox-single');
202 202 this.$label = $('<div />')
203 203 .appendTo(this.$el)
204 204 .addClass('widget-hlabel')
205 205 .hide();
206 206 this.$textbox = $('<input type="text" />')
207 207 .addClass('form-control')
208 208 .addClass('widget-numeric-text')
209 209 .appendTo(this.$el);
210 210 this.update(); // Set defaults.
211 211 },
212 212
213 213 update : function(options){
214 214 // Update the contents of this view
215 215 //
216 216 // Called when the model is changed. The model may have been
217 217 // changed by another view or by a state update from the back-end.
218 218 if (options === undefined || options.updated_view != this) {
219 219 var value = this.model.get('value');
220 220 if (this._parse_value(this.$textbox.val()) != value) {
221 221 this.$textbox.val(value);
222 222 }
223 223
224 224 if (this.model.get('disabled')) {
225 225 this.$textbox.attr('disabled','disabled');
226 226 } else {
227 227 this.$textbox.removeAttr('disabled');
228 228 }
229 229
230 230 var description = this.model.get('description');
231 231 if (description.length === 0) {
232 232 this.$label.hide();
233 233 } else {
234 234 this.$label.text(description);
235 235 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
236 236 this.$label.show();
237 237 }
238 238 }
239 239 return IntTextView.__super__.update.apply(this);
240 240 },
241 241
242 242 update_attr: function(name, value) {
243 243 // Set a css attr of the widget view.
244 244 this.$textbox.css(name, value);
245 245 },
246 246
247 247 events: {
248 248 // Dictionary of events and their handlers.
249 249 "keyup input" : "handleChanging",
250 250 "paste input" : "handleChanging",
251 251 "cut input" : "handleChanging",
252 252
253 253 // Fires only when control is validated or looses focus.
254 254 "change input" : "handleChanged"
255 255 },
256 256
257 257 handleChanging: function(e) {
258 258 // Handles and validates user input.
259 259
260 260 // Try to parse value as a int.
261 261 var numericalValue = 0;
262 262 if (e.target.value !== '') {
263 263 var trimmed = e.target.value.trim();
264 264 if (!(['-', '-.', '.', '+.', '+'].indexOf(trimmed) >= 0)) {
265 265 numericalValue = this._parse_value(e.target.value);
266 266 }
267 267 }
268 268
269 269 // If parse failed, reset value to value stored in model.
270 270 if (isNaN(numericalValue)) {
271 271 e.target.value = this.model.get('value');
272 272 } else if (!isNaN(numericalValue)) {
273 273 if (this.model.get('max') !== undefined) {
274 274 numericalValue = Math.min(this.model.get('max'), numericalValue);
275 275 }
276 276 if (this.model.get('min') !== undefined) {
277 277 numericalValue = Math.max(this.model.get('min'), numericalValue);
278 278 }
279 279
280 280 // Apply the value if it has changed.
281 281 if (numericalValue != this.model.get('value')) {
282 282
283 283 // Calling model.set will trigger all of the other views of the
284 284 // model to update.
285 285 this.model.set('value', numericalValue, {updated_view: this});
286 286 this.touch();
287 287 }
288 288 }
289 289 },
290 290
291 291 handleChanged: function(e) {
292 292 // Applies validated input.
293 293 if (this.model.get('value') != e.target.value) {
294 294 e.target.value = this.model.get('value');
295 295 }
296 296 },
297 297
298 298 _parse_value: function(value) {
299 299 // Parse the value stored in a string.
300 300 return parseInt(value);
301 301 },
302 302 });
303 303
304 304
305 305 var ProgressView = widget.DOMWidgetView.extend({
306 306 render : function(){
307 307 // Called when view is rendered.
308 308 this.$el
309 309 .addClass('widget-hbox-single');
310 310 this.$label = $('<div />')
311 311 .appendTo(this.$el)
312 312 .addClass('widget-hlabel')
313 313 .hide();
314 314 this.$progress = $('<div />')
315 315 .addClass('progress')
316 316 .addClass('widget-progress')
317 317 .appendTo(this.$el);
318 318 this.$bar = $('<div />')
319 319 .addClass('progress-bar')
320 320 .css('width', '50%')
321 321 .appendTo(this.$progress);
322 322 this.update(); // Set defaults.
323
324 this.model.on('change:bar_style', function(model, value) {
325 this.update_bar_style();
326 }, this);
327 this.update_bar_style('');
323 328 },
324 329
325 330 update : function(){
326 331 // Update the contents of this view
327 332 //
328 333 // Called when the model is changed. The model may have been
329 334 // changed by another view or by a state update from the back-end.
330 335 var value = this.model.get('value');
331 336 var max = this.model.get('max');
332 337 var min = this.model.get('min');
333 338 var percent = 100.0 * (value - min) / (max - min);
334 339 this.$bar.css('width', percent + '%');
335 340
336 341 var description = this.model.get('description');
337 342 if (description.length === 0) {
338 343 this.$label.hide();
339 344 } else {
340 345 this.$label.text(description);
341 346 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
342 347 this.$label.show();
343 348 }
344 349 return ProgressView.__super__.update.apply(this);
345 350 },
346 351
352 update_bar_style: function(previous_trait_value) {
353 var class_map = {
354 success: ['progress-bar-success'],
355 info: ['progress-bar-info'],
356 warning: ['progress-bar-warning'],
357 danger: ['progress-bar-danger']
358 };
359 this.update_mapped_classes(class_map, 'bar_style', previous_trait_value, this.$bar);
360 },
361
347 362 update_attr: function(name, value) {
348 363 // Set a css attr of the widget view.
349 364 if (name.substring(0, 6) == 'border' || name == 'width' ||
350 365 name == 'height' || name == 'background' || name == 'margin' ||
351 366 name == 'padding') {
352 367
353 368 this.$progress.css(name, value);
354 369 } else if (name == 'color') {
355 370 this.$bar.css('background', value);
356 371 } else {
357 372 this.$bar.css(name, value);
358 373 }
359 374 },
360 375 });
361 376
362 377 return {
363 378 'IntSliderView': IntSliderView,
364 379 'IntTextView': IntTextView,
365 380 'ProgressView': ProgressView,
366 381 };
367 382 });
@@ -1,48 +1,48 b''
1 1 """Bool class.
2 2
3 3 Represents a boolean 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, Bool
17 from IPython.utils.traitlets import Unicode, Bool, CaselessStrEnum
18 18 from IPython.utils.warn import DeprecatedClass
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Classes
22 22 #-----------------------------------------------------------------------------
23 23 class _Bool(DOMWidget):
24 24 """A base class for creating widgets that represent booleans."""
25 25 value = Bool(False, help="Bool value", sync=True)
26 26 description = Unicode('', help="Description of the boolean (label).", sync=True)
27 27 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
28 28
29 29
30 30 class Checkbox(_Bool):
31 31 """Displays a boolean `value`."""
32 32 _view_name = Unicode('CheckboxView', sync=True)
33 33
34 34
35 35 class ToggleButton(_Bool):
36 36 """Displays a boolean `value`."""
37 37
38 38 _view_name = Unicode('ToggleButtonView', sync=True)
39 39
40 40 button_style = CaselessStrEnum(
41 41 values=['primary', 'success', 'info', 'warning', 'danger', ''],
42 42 default_value='', allow_none=True, sync=True, help="""Use a
43 43 predefined styling for the button.""")
44 44
45 45
46 46 # Remove in IPython 4.0
47 47 CheckboxWidget = DeprecatedClass(Checkbox, 'CheckboxWidget')
48 48 ToggleButtonWidget = DeprecatedClass(ToggleButton, 'ToggleButtonWidget')
@@ -1,70 +1,70 b''
1 1 """Button class.
2 2
3 3 Represents a button in the frontend using a widget. Allows user to listen for
4 4 click events on the button and trigger backend code when the clicks are fired.
5 5 """
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (c) 2013, the IPython Development Team.
8 8 #
9 9 # Distributed under the terms of the Modified BSD License.
10 10 #
11 11 # The full license is in the file COPYING.txt, distributed with this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17 from .widget import DOMWidget, CallbackDispatcher
18 from IPython.utils.traitlets import Unicode, Bool
18 from IPython.utils.traitlets import Unicode, Bool, CaselessStrEnum
19 19 from IPython.utils.warn import DeprecatedClass
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Classes
23 23 #-----------------------------------------------------------------------------
24 24 class Button(DOMWidget):
25 25 """Button widget.
26 26
27 27 This widget has an `on_click` method that allows you to listen for the
28 28 user clicking on the button. The click event itself is stateless."""
29 29 _view_name = Unicode('ButtonView', sync=True)
30 30
31 31 # Keys
32 32 description = Unicode('', help="Description of the button (label).", sync=True)
33 33 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
34 34
35 35 button_style = CaselessStrEnum(
36 36 values=['primary', 'success', 'info', 'warning', 'danger', ''],
37 37 default_value='', allow_none=True, sync=True, help="""Use a
38 38 predefined styling for the button.""")
39 39
40 40 def __init__(self, **kwargs):
41 41 """Constructor"""
42 42 super(Button, self).__init__(**kwargs)
43 43 self._click_handlers = CallbackDispatcher()
44 44 self.on_msg(self._handle_button_msg)
45 45
46 46 def on_click(self, callback, remove=False):
47 47 """Register a callback to execute when the button is clicked.
48 48
49 49 The callback will be called with one argument,
50 50 the clicked button widget instance.
51 51
52 52 Parameters
53 53 ----------
54 54 remove : bool (optional)
55 55 Set to true to remove the callback from the list of callbacks."""
56 56 self._click_handlers.register_callback(callback, remove=remove)
57 57
58 58 def _handle_button_msg(self, _, content):
59 59 """Handle a msg from the front-end.
60 60
61 61 Parameters
62 62 ----------
63 63 content: dict
64 64 Content of the msg."""
65 65 if content.get('event', '') == 'click':
66 66 self._click_handlers(self)
67 67
68 68
69 69 # Remove in IPython 4.0
70 70 ButtonWidget = DeprecatedClass(Button, 'ButtonWidget')
@@ -1,171 +1,177 b''
1 1 """Float class.
2 2
3 3 Represents an unbounded float 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, CFloat, Bool, Enum, Tuple
17 from IPython.utils.traitlets import Unicode, CFloat, Bool, CaselessStrEnum, Tuple
18 18 from IPython.utils.warn import DeprecatedClass
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Classes
22 22 #-----------------------------------------------------------------------------
23 23 class _Float(DOMWidget):
24 24 value = CFloat(0.0, help="Float value", sync=True)
25 25 disabled = Bool(False, help="Enable or disable user changes", sync=True)
26 26 description = Unicode(help="Description of the value this widget represents", sync=True)
27 27
28 28
29 29 class _BoundedFloat(_Float):
30 30 max = CFloat(100.0, help="Max value", sync=True)
31 31 min = CFloat(0.0, help="Min value", sync=True)
32 32 step = CFloat(0.1, help="Minimum step that the value can take (ignored by some views)", sync=True)
33 33
34 34 def __init__(self, *pargs, **kwargs):
35 35 """Constructor"""
36 36 DOMWidget.__init__(self, *pargs, **kwargs)
37 37 self._validate('value', None, self.value)
38 38 self.on_trait_change(self._validate, ['value', 'min', 'max'])
39 39
40 40 def _validate(self, name, old, new):
41 41 """Validate value, max, min."""
42 42 if self.min > new or new > self.max:
43 43 self.value = min(max(new, self.min), self.max)
44 44
45 45
46 46 class FloatText(_Float):
47 47 _view_name = Unicode('FloatTextView', sync=True)
48 48
49 49
50 50 class BoundedFloatText(_BoundedFloat):
51 51 _view_name = Unicode('FloatTextView', sync=True)
52 52
53 53
54 54 class FloatSlider(_BoundedFloat):
55 55 _view_name = Unicode('FloatSliderView', sync=True)
56 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
57 help="Vertical or horizontal.", sync=True)
56 orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
57 default_value='horizontal',
58 help="Vertical or horizontal.", allow_none=False, sync=True)
58 59 _range = Bool(False, help="Display a range selector", sync=True)
59 60 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
60 61 slider_color = Unicode(sync=True)
61 62
62 63
63 64 class FloatProgress(_BoundedFloat):
64 65 _view_name = Unicode('ProgressView', sync=True)
65 66
67 bar_style = CaselessStrEnum(
68 values=['success', 'info', 'warning', 'danger', ''],
69 default_value='', allow_none=True, sync=True, help="""Use a
70 predefined styling for the progess bar.""")
71
66 72 class _FloatRange(_Float):
67 73 value = Tuple(CFloat, CFloat, default_value=(0.0, 1.0), help="Tuple of (lower, upper) bounds", sync=True)
68 74 lower = CFloat(0.0, help="Lower bound", sync=False)
69 75 upper = CFloat(1.0, help="Upper bound", sync=False)
70 76
71 77 def __init__(self, *pargs, **kwargs):
72 78 value_given = 'value' in kwargs
73 79 lower_given = 'lower' in kwargs
74 80 upper_given = 'upper' in kwargs
75 81 if value_given and (lower_given or upper_given):
76 82 raise ValueError("Cannot specify both 'value' and 'lower'/'upper' for range widget")
77 83 if lower_given != upper_given:
78 84 raise ValueError("Must specify both 'lower' and 'upper' for range widget")
79 85
80 86 DOMWidget.__init__(self, *pargs, **kwargs)
81 87
82 88 # ensure the traits match, preferring whichever (if any) was given in kwargs
83 89 if value_given:
84 90 self.lower, self.upper = self.value
85 91 else:
86 92 self.value = (self.lower, self.upper)
87 93
88 94 self.on_trait_change(self._validate, ['value', 'upper', 'lower'])
89 95
90 96 def _validate(self, name, old, new):
91 97 if name == 'value':
92 98 self.lower, self.upper = min(new), max(new)
93 99 elif name == 'lower':
94 100 self.value = (new, self.value[1])
95 101 elif name == 'upper':
96 102 self.value = (self.value[0], new)
97 103
98 104 class _BoundedFloatRange(_FloatRange):
99 105 step = CFloat(1.0, help="Minimum step that the value can take (ignored by some views)", sync=True)
100 106 max = CFloat(100.0, help="Max value", sync=True)
101 107 min = CFloat(0.0, help="Min value", sync=True)
102 108
103 109 def __init__(self, *pargs, **kwargs):
104 110 any_value_given = 'value' in kwargs or 'upper' in kwargs or 'lower' in kwargs
105 111 _FloatRange.__init__(self, *pargs, **kwargs)
106 112
107 113 # ensure a minimal amount of sanity
108 114 if self.min > self.max:
109 115 raise ValueError("min must be <= max")
110 116
111 117 if any_value_given:
112 118 # if a value was given, clamp it within (min, max)
113 119 self._validate("value", None, self.value)
114 120 else:
115 121 # otherwise, set it to 25-75% to avoid the handles overlapping
116 122 self.value = (0.75*self.min + 0.25*self.max,
117 123 0.25*self.min + 0.75*self.max)
118 124 # callback already set for 'value', 'lower', 'upper'
119 125 self.on_trait_change(self._validate, ['min', 'max'])
120 126
121 127
122 128 def _validate(self, name, old, new):
123 129 if name == "min":
124 130 if new > self.max:
125 131 raise ValueError("setting min > max")
126 132 self.min = new
127 133 elif name == "max":
128 134 if new < self.min:
129 135 raise ValueError("setting max < min")
130 136 self.max = new
131 137
132 138 low, high = self.value
133 139 if name == "value":
134 140 low, high = min(new), max(new)
135 141 elif name == "upper":
136 142 if new < self.lower:
137 143 raise ValueError("setting upper < lower")
138 144 high = new
139 145 elif name == "lower":
140 146 if new > self.upper:
141 147 raise ValueError("setting lower > upper")
142 148 low = new
143 149
144 150 low = max(self.min, min(low, self.max))
145 151 high = min(self.max, max(high, self.min))
146 152
147 153 # determine the order in which we should update the
148 154 # lower, upper traits to avoid a temporary inverted overlap
149 155 lower_first = high < self.lower
150 156
151 157 self.value = (low, high)
152 158 if lower_first:
153 159 self.lower = low
154 160 self.upper = high
155 161 else:
156 162 self.upper = high
157 163 self.lower = low
158 164
159 165
160 166 class FloatRangeSlider(_BoundedFloatRange):
161 167 _view_name = Unicode('FloatSliderView', sync=True)
162 168 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
163 169 help="Vertical or horizontal.", sync=True)
164 170 _range = Bool(True, help="Display a range selector", sync=True)
165 171 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
166 172
167 173 # Remove in IPython 4.0
168 174 FloatTextWidget = DeprecatedClass(FloatText, 'FloatTextWidget')
169 175 BoundedFloatTextWidget = DeprecatedClass(BoundedFloatText, 'BoundedFloatTextWidget')
170 176 FloatSliderWidget = DeprecatedClass(FloatSlider, 'FloatSliderWidget')
171 177 FloatProgressWidget = DeprecatedClass(FloatProgress, 'FloatProgressWidget')
@@ -1,175 +1,181 b''
1 1 """Int 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, Tuple
17 from IPython.utils.traitlets import Unicode, CInt, Bool, CaselessStrEnum, Tuple
18 18 from IPython.utils.warn import DeprecatedClass
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Classes
22 22 #-----------------------------------------------------------------------------
23 23 class _Int(DOMWidget):
24 24 """Base class used to create widgets that represent an int."""
25 25 value = CInt(0, help="Int value", sync=True)
26 26 disabled = Bool(False, help="Enable or disable user changes", sync=True)
27 27 description = Unicode(help="Description of the value this widget represents", sync=True)
28 28
29 29
30 30 class _BoundedInt(_Int):
31 31 """Base class used to create widgets that represent a int that is bounded
32 32 by a minium and maximum."""
33 33 step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
34 34 max = CInt(100, help="Max value", sync=True)
35 35 min = CInt(0, help="Min value", sync=True)
36 36
37 37 def __init__(self, *pargs, **kwargs):
38 38 """Constructor"""
39 39 DOMWidget.__init__(self, *pargs, **kwargs)
40 40 self.on_trait_change(self._validate, ['value', 'min', 'max'])
41 41
42 42 def _validate(self, name, old, new):
43 43 """Validate value, max, min."""
44 44 if self.min > new or new > self.max:
45 45 self.value = min(max(new, self.min), self.max)
46 46
47 47
48 48 class IntText(_Int):
49 49 """Textbox widget that represents a int."""
50 50 _view_name = Unicode('IntTextView', sync=True)
51 51
52 52
53 53 class BoundedIntText(_BoundedInt):
54 54 """Textbox widget that represents a int bounded by a minimum and maximum value."""
55 55 _view_name = Unicode('IntTextView', sync=True)
56 56
57 57
58 58 class IntSlider(_BoundedInt):
59 59 """Slider widget that represents a int bounded by a minimum and maximum value."""
60 60 _view_name = Unicode('IntSliderView', sync=True)
61 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
61 orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
62 default_value='horizontal', allow_none=False,
62 63 help="Vertical or horizontal.", sync=True)
63 64 _range = Bool(False, help="Display a range selector", sync=True)
64 65 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
65 66 slider_color = Unicode(sync=True)
66 67
67 68
68 69 class IntProgress(_BoundedInt):
69 70 """Progress bar that represents a int bounded by a minimum and maximum value."""
70 71 _view_name = Unicode('ProgressView', sync=True)
71 72
73 bar_style = CaselessStrEnum(
74 values=['success', 'info', 'warning', 'danger', ''],
75 default_value='', allow_none=True, sync=True, help="""Use a
76 predefined styling for the progess bar.""")
77
72 78 class _IntRange(_Int):
73 79 value = Tuple(CInt, CInt, default_value=(0, 1), help="Tuple of (lower, upper) bounds", sync=True)
74 80 lower = CInt(0, help="Lower bound", sync=False)
75 81 upper = CInt(1, help="Upper bound", sync=False)
76 82
77 83 def __init__(self, *pargs, **kwargs):
78 84 value_given = 'value' in kwargs
79 85 lower_given = 'lower' in kwargs
80 86 upper_given = 'upper' in kwargs
81 87 if value_given and (lower_given or upper_given):
82 88 raise ValueError("Cannot specify both 'value' and 'lower'/'upper' for range widget")
83 89 if lower_given != upper_given:
84 90 raise ValueError("Must specify both 'lower' and 'upper' for range widget")
85 91
86 92 DOMWidget.__init__(self, *pargs, **kwargs)
87 93
88 94 # ensure the traits match, preferring whichever (if any) was given in kwargs
89 95 if value_given:
90 96 self.lower, self.upper = self.value
91 97 else:
92 98 self.value = (self.lower, self.upper)
93 99
94 100 self.on_trait_change(self._validate, ['value', 'upper', 'lower'])
95 101
96 102 def _validate(self, name, old, new):
97 103 if name == 'value':
98 104 self.lower, self.upper = min(new), max(new)
99 105 elif name == 'lower':
100 106 self.value = (new, self.value[1])
101 107 elif name == 'upper':
102 108 self.value = (self.value[0], new)
103 109
104 110 class _BoundedIntRange(_IntRange):
105 111 step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
106 112 max = CInt(100, help="Max value", sync=True)
107 113 min = CInt(0, help="Min value", sync=True)
108 114
109 115 def __init__(self, *pargs, **kwargs):
110 116 any_value_given = 'value' in kwargs or 'upper' in kwargs or 'lower' in kwargs
111 117 _IntRange.__init__(self, *pargs, **kwargs)
112 118
113 119 # ensure a minimal amount of sanity
114 120 if self.min > self.max:
115 121 raise ValueError("min must be <= max")
116 122
117 123 if any_value_given:
118 124 # if a value was given, clamp it within (min, max)
119 125 self._validate("value", None, self.value)
120 126 else:
121 127 # otherwise, set it to 25-75% to avoid the handles overlapping
122 128 self.value = (0.75*self.min + 0.25*self.max,
123 129 0.25*self.min + 0.75*self.max)
124 130 # callback already set for 'value', 'lower', 'upper'
125 131 self.on_trait_change(self._validate, ['min', 'max'])
126 132
127 133 def _validate(self, name, old, new):
128 134 if name == "min":
129 135 if new > self.max:
130 136 raise ValueError("setting min > max")
131 137 self.min = new
132 138 elif name == "max":
133 139 if new < self.min:
134 140 raise ValueError("setting max < min")
135 141 self.max = new
136 142
137 143 low, high = self.value
138 144 if name == "value":
139 145 low, high = min(new), max(new)
140 146 elif name == "upper":
141 147 if new < self.lower:
142 148 raise ValueError("setting upper < lower")
143 149 high = new
144 150 elif name == "lower":
145 151 if new > self.upper:
146 152 raise ValueError("setting lower > upper")
147 153 low = new
148 154
149 155 low = max(self.min, min(low, self.max))
150 156 high = min(self.max, max(high, self.min))
151 157
152 158 # determine the order in which we should update the
153 159 # lower, upper traits to avoid a temporary inverted overlap
154 160 lower_first = high < self.lower
155 161
156 162 self.value = (low, high)
157 163 if lower_first:
158 164 self.lower = low
159 165 self.upper = high
160 166 else:
161 167 self.upper = high
162 168 self.lower = low
163 169
164 170 class IntRangeSlider(_BoundedIntRange):
165 171 _view_name = Unicode('IntSliderView', sync=True)
166 172 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
167 173 help="Vertical or horizontal.", sync=True)
168 174 _range = Bool(True, help="Display a range selector", sync=True)
169 175 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
170 176
171 177 # Remove in IPython 4.0
172 178 IntTextWidget = DeprecatedClass(IntText, 'IntTextWidget')
173 179 BoundedIntTextWidget = DeprecatedClass(BoundedIntText, 'BoundedIntTextWidget')
174 180 IntSliderWidget = DeprecatedClass(IntSlider, 'IntSliderWidget')
175 181 IntProgressWidget = DeprecatedClass(IntProgress, 'IntProgressWidget')
@@ -1,144 +1,149 b''
1 1 """Selection classes.
2 2
3 3 Represents an enumeration 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
17 17 from collections import OrderedDict
18 18 from threading import Lock
19 19
20 20 from .widget import DOMWidget
21 from IPython.utils.traitlets import Unicode, List, Bool, Any, Dict, TraitError
21 from IPython.utils.traitlets import Unicode, List, Bool, Any, Dict, TraitError, CaselessStrEnum
22 22 from IPython.utils.py3compat import unicode_type
23 23 from IPython.utils.warn import DeprecatedClass
24 24
25 25 #-----------------------------------------------------------------------------
26 26 # SelectionWidget
27 27 #-----------------------------------------------------------------------------
28 28 class _Selection(DOMWidget):
29 29 """Base class for Selection widgets
30 30
31 31 ``values`` can be specified as a list or dict. If given as a list,
32 32 it will be transformed to a dict of the form ``{str(value):value}``.
33 33 """
34 34
35 35 value = Any(help="Selected value")
36 36 values = Dict(help="""Dictionary of {name: value} the user can select.
37 37
38 38 The keys of this dictionary are the strings that will be displayed in the UI,
39 39 representing the actual Python choices.
40 40
41 41 The keys of this dictionary are also available as value_names.
42 42 """)
43 43 value_name = Unicode(help="The name of the selected value", sync=True)
44 44 value_names = List(Unicode, help="""Read-only list of names for each value.
45 45
46 46 If values is specified as a list, this is the string representation of each element.
47 47 Otherwise, it is the keys of the values dictionary.
48 48
49 49 These strings are used to display the choices in the front-end.""", sync=True)
50 50 disabled = Bool(False, help="Enable or disable user changes", sync=True)
51 51 description = Unicode(help="Description of the value this widget represents", sync=True)
52 52
53 53
54 54 def __init__(self, *args, **kwargs):
55 55 self.value_lock = Lock()
56 56 self._in_values_changed = False
57 57 if 'values' in kwargs:
58 58 values = kwargs['values']
59 59 # convert list values to an dict of {str(v):v}
60 60 if isinstance(values, list):
61 61 # preserve list order with an OrderedDict
62 62 kwargs['values'] = OrderedDict((unicode_type(v), v) for v in values)
63 63 # python3.3 turned on hash randomization by default - this means that sometimes, randomly
64 64 # we try to set value before setting values, due to dictionary ordering. To fix this, force
65 65 # the setting of self.values right now, before anything else runs
66 66 self.values = kwargs.pop('values')
67 67 DOMWidget.__init__(self, *args, **kwargs)
68 68
69 69 def _values_changed(self, name, old, new):
70 70 """Handles when the values dict has been changed.
71 71
72 72 Setting values implies setting value names from the keys of the dict.
73 73 """
74 74 self._in_values_changed = True
75 75 try:
76 76 self.value_names = list(new.keys())
77 77 finally:
78 78 self._in_values_changed = False
79 79
80 80 # ensure that the chosen value is one of the choices
81 81 if self.value not in new.values():
82 82 self.value = next(iter(new.values()))
83 83
84 84 def _value_names_changed(self, name, old, new):
85 85 if not self._in_values_changed:
86 86 raise TraitError("value_names is a read-only proxy to values.keys(). Use the values dict instead.")
87 87
88 88 def _value_changed(self, name, old, new):
89 89 """Called when value has been changed"""
90 90 if self.value_lock.acquire(False):
91 91 try:
92 92 # Reverse dictionary lookup for the value name
93 93 for k,v in self.values.items():
94 94 if new == v:
95 95 # set the selected value name
96 96 self.value_name = k
97 97 return
98 98 # undo the change, and raise KeyError
99 99 self.value = old
100 100 raise KeyError(new)
101 101 finally:
102 102 self.value_lock.release()
103 103
104 104 def _value_name_changed(self, name, old, new):
105 105 """Called when the value name has been changed (typically by the frontend)."""
106 106 if self.value_lock.acquire(False):
107 107 try:
108 108 self.value = self.values[new]
109 109 finally:
110 110 self.value_lock.release()
111 111
112 112
113 113 class ToggleButtons(_Selection):
114 114 """Group of toggle buttons that represent an enumeration. Only one toggle
115 115 button can be toggled at any point in time."""
116 116 _view_name = Unicode('ToggleButtonsView', sync=True)
117 117
118 118 button_style = CaselessStrEnum(
119 119 values=['primary', 'success', 'info', 'warning', 'danger', ''],
120 120 default_value='', allow_none=True, sync=True, help="""Use a
121 121 predefined styling for the buttons.""")
122 122
123 123
124 124 class Dropdown(_Selection):
125 125 """Allows you to select a single item from a dropdown."""
126 126 _view_name = Unicode('DropdownView', sync=True)
127 127
128 button_style = CaselessStrEnum(
129 values=['primary', 'success', 'info', 'warning', 'danger', ''],
130 default_value='', allow_none=True, sync=True, help="""Use a
131 predefined styling for the buttons.""")
132
128 133
129 134 class RadioButtons(_Selection):
130 135 """Group of radio buttons that represent an enumeration. Only one radio
131 136 button can be toggled at any point in time."""
132 137 _view_name = Unicode('RadioButtonsView', sync=True)
133 138
134 139
135 140 class Select(_Selection):
136 141 """Listbox that only allows one item to be selected at any given time."""
137 142 _view_name = Unicode('SelectView', sync=True)
138 143
139 144
140 145 # Remove in IPython 4.0
141 146 ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
142 147 DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
143 148 RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget')
144 149 SelectWidget = DeprecatedClass(Select, 'SelectWidget')
General Comments 0
You need to be logged in to leave comments. Login now