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