##// END OF EJS Templates
squashing the whitespace changes
Nicholas Bollweg -
Show More
@@ -1,523 +1,551 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 "base/js/utils",
6 "base/js/utils",
7 "jquery",
7 "jquery",
8 "underscore",
8 "bootstrap",
9 "bootstrap",
9 ], function(widget, utils, $){
10 ], function(widget, utils, $, _){
10
11
11 var DropdownView = widget.DOMWidgetView.extend({
12 var DropdownView = widget.DOMWidgetView.extend({
12 render : function(){
13 render : function(){
13 /**
14 /**
14 * Called when view is rendered.
15 * Called when view is rendered.
15 */
16 */
16 this.$el
17 this.$el
17 .addClass('widget-hbox widget-dropdown');
18 .addClass('widget-hbox widget-dropdown');
18 this.$label = $('<div />')
19 this.$label = $('<div />')
19 .appendTo(this.$el)
20 .appendTo(this.$el)
20 .addClass('widget-label')
21 .addClass('widget-label')
21 .hide();
22 .hide();
22 this.$buttongroup = $('<div />')
23 this.$buttongroup = $('<div />')
23 .addClass('widget_item')
24 .addClass('widget_item')
24 .addClass('btn-group')
25 .addClass('btn-group')
25 .appendTo(this.$el);
26 .appendTo(this.$el);
26 this.$droplabel = $('<button />')
27 this.$droplabel = $('<button />')
27 .addClass('btn btn-default')
28 .addClass('btn btn-default')
28 .addClass('widget-combo-btn')
29 .addClass('widget-combo-btn')
29 .html("&nbsp;")
30 .html("&nbsp;")
30 .appendTo(this.$buttongroup);
31 .appendTo(this.$buttongroup);
31 this.$dropbutton = $('<button />')
32 this.$dropbutton = $('<button />')
32 .addClass('btn btn-default')
33 .addClass('btn btn-default')
33 .addClass('dropdown-toggle')
34 .addClass('dropdown-toggle')
34 .addClass('widget-combo-carrot-btn')
35 .addClass('widget-combo-carrot-btn')
35 .attr('data-toggle', 'dropdown')
36 .attr('data-toggle', 'dropdown')
36 .append($('<span />').addClass("caret"))
37 .append($('<span />').addClass("caret"))
37 .appendTo(this.$buttongroup);
38 .appendTo(this.$buttongroup);
38 this.$droplist = $('<ul />')
39 this.$droplist = $('<ul />')
39 .addClass('dropdown-menu')
40 .addClass('dropdown-menu')
40 .appendTo(this.$buttongroup);
41 .appendTo(this.$buttongroup);
41
42
42 this.model.on('change:button_style', function(model, value) {
43 this.model.on('change:button_style', function(model, value) {
43 this.update_button_style();
44 this.update_button_style();
44 }, this);
45 }, this);
45 this.update_button_style('');
46 this.update_button_style('');
46
47
47 // Set defaults.
48 // Set defaults.
48 this.update();
49 this.update();
49 },
50 },
50
51
51 update : function(options){
52 update : function(options){
52 /**
53 /**
53 * Update the contents of this view
54 * Update the contents of this view
54 *
55 *
55 * Called when the model is changed. The model may have been
56 * Called when the model is changed. The model may have been
56 * changed by another view or by a state update from the back-end.
57 * changed by another view or by a state update from the back-end.
57 */
58 */
58
59
59 if (options === undefined || options.updated_view != this) {
60 if (options === undefined || options.updated_view != this) {
60 var selected_item_text = this.model.get('value_name');
61 var selected_item_text = this.model.get('selected_label');
61 if (selected_item_text.trim().length === 0) {
62 if (selected_item_text.trim().length === 0) {
62 this.$droplabel.html("&nbsp;");
63 this.$droplabel.html("&nbsp;");
63 } else {
64 } else {
64 this.$droplabel.text(selected_item_text);
65 this.$droplabel.text(selected_item_text);
65 }
66 }
66
67
67 var items = this.model.get('_value_names');
68 var items = this.model.get('_options_labels');
68 var $replace_droplist = $('<ul />')
69 var $replace_droplist = $('<ul />')
69 .addClass('dropdown-menu');
70 .addClass('dropdown-menu');
70 // Copy the style
71 // Copy the style
71 $replace_droplist.attr('style', this.$droplist.attr('style'));
72 $replace_droplist.attr('style', this.$droplist.attr('style'));
72 var that = this;
73 var that = this;
73 _.each(items, function(item, i) {
74 _.each(items, function(item, i) {
74 var item_button = $('<a href="#"/>')
75 var item_button = $('<a href="#"/>')
75 .text(item)
76 .text(item)
76 .on('click', $.proxy(that.handle_click, that));
77 .on('click', $.proxy(that.handle_click, that));
77 $replace_droplist.append($('<li />').append(item_button));
78 $replace_droplist.append($('<li />').append(item_button));
78 });
79 });
79
80
80 this.$droplist.replaceWith($replace_droplist);
81 this.$droplist.replaceWith($replace_droplist);
81 this.$droplist.remove();
82 this.$droplist.remove();
82 this.$droplist = $replace_droplist;
83 this.$droplist = $replace_droplist;
83
84
84 if (this.model.get('disabled')) {
85 if (this.model.get('disabled')) {
85 this.$buttongroup.attr('disabled','disabled');
86 this.$buttongroup.attr('disabled','disabled');
86 this.$droplabel.attr('disabled','disabled');
87 this.$droplabel.attr('disabled','disabled');
87 this.$dropbutton.attr('disabled','disabled');
88 this.$dropbutton.attr('disabled','disabled');
88 this.$droplist.attr('disabled','disabled');
89 this.$droplist.attr('disabled','disabled');
89 } else {
90 } else {
90 this.$buttongroup.removeAttr('disabled');
91 this.$buttongroup.removeAttr('disabled');
91 this.$droplabel.removeAttr('disabled');
92 this.$droplabel.removeAttr('disabled');
92 this.$dropbutton.removeAttr('disabled');
93 this.$dropbutton.removeAttr('disabled');
93 this.$droplist.removeAttr('disabled');
94 this.$droplist.removeAttr('disabled');
94 }
95 }
95
96
96 var description = this.model.get('description');
97 var description = this.model.get('description');
97 if (description.length === 0) {
98 if (description.length === 0) {
98 this.$label.hide();
99 this.$label.hide();
99 } else {
100 } else {
100 this.typeset(this.$label, description);
101 this.typeset(this.$label, description);
101 this.$label.show();
102 this.$label.show();
102 }
103 }
103 }
104 }
104 return DropdownView.__super__.update.apply(this);
105 return DropdownView.__super__.update.apply(this);
105 },
106 },
106
107
107 update_button_style: function(previous_trait_value) {
108 update_button_style: function(previous_trait_value) {
108 var class_map = {
109 var class_map = {
109 primary: ['btn-primary'],
110 primary: ['btn-primary'],
110 success: ['btn-success'],
111 success: ['btn-success'],
111 info: ['btn-info'],
112 info: ['btn-info'],
112 warning: ['btn-warning'],
113 warning: ['btn-warning'],
113 danger: ['btn-danger']
114 danger: ['btn-danger']
114 };
115 };
115 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$droplabel);
116 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$droplabel);
116 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$dropbutton);
117 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$dropbutton);
117 },
118 },
118
119
119 update_attr: function(name, value) {
120 update_attr: function(name, value) {
120 /**
121 /**
121 * Set a css attr of the widget view.
122 * Set a css attr of the widget view.
122 */
123 */
123 if (name.substring(0, 6) == 'border' || name == 'background' || name == 'color') {
124 if (name.substring(0, 6) == 'border' || name == 'background' || name == 'color') {
124 this.$droplabel.css(name, value);
125 this.$droplabel.css(name, value);
125 this.$dropbutton.css(name, value);
126 this.$dropbutton.css(name, value);
126 this.$droplist.css(name, value);
127 this.$droplist.css(name, value);
127 } else if (name == 'width') {
128 } else if (name == 'width') {
128 this.$droplist.css(name, value);
129 this.$droplist.css(name, value);
129 this.$droplabel.css(name, value);
130 this.$droplabel.css(name, value);
130 } else if (name == 'padding') {
131 } else if (name == 'padding') {
131 this.$droplist.css(name, value);
132 this.$droplist.css(name, value);
132 this.$buttongroup.css(name, value);
133 this.$buttongroup.css(name, value);
133 } else if (name == 'margin') {
134 } else if (name == 'margin') {
134 this.$buttongroup.css(name, value);
135 this.$buttongroup.css(name, value);
135 } else if (name == 'height') {
136 } else if (name == 'height') {
136 this.$droplabel.css(name, value);
137 this.$droplabel.css(name, value);
137 this.$dropbutton.css(name, value);
138 this.$dropbutton.css(name, value);
138 } else if (name == 'padding' || name == 'margin') {
139 } else if (name == 'padding' || name == 'margin') {
139 this.$el.css(name, value);
140 this.$el.css(name, value);
140 } else {
141 } else {
141 this.$droplist.css(name, value);
142 this.$droplist.css(name, value);
142 this.$droplabel.css(name, value);
143 this.$droplabel.css(name, value);
143 }
144 }
144 },
145 },
145
146
146 handle_click: function (e) {
147 handle_click: function (e) {
147 /**
148 /**
148 * Handle when a value is clicked.
149 * Handle when a value is clicked.
149 *
150 *
150 * Calling model.set will trigger all of the other views of the
151 * Calling model.set will trigger all of the other views of the
151 * model to update.
152 * model to update.
152 */
153 */
153 this.model.set('value_name', $(e.target).text(), {updated_view: this});
154 this.model.set('selected_label', $(e.target).text(), {updated_view: this});
154 this.touch();
155 this.touch();
155
156
156 // Manually hide the droplist.
157 // Manually hide the droplist.
157 e.stopPropagation();
158 e.stopPropagation();
158 e.preventDefault();
159 e.preventDefault();
159 this.$buttongroup.removeClass('open');
160 this.$buttongroup.removeClass('open');
160 },
161 },
161
162
162 });
163 });
163
164
164
165
165 var RadioButtonsView = widget.DOMWidgetView.extend({
166 var RadioButtonsView = widget.DOMWidgetView.extend({
166 render : function(){
167 render : function(){
167 /**
168 /**
168 * Called when view is rendered.
169 * Called when view is rendered.
169 */
170 */
170 this.$el
171 this.$el
171 .addClass('widget-hbox widget-radio');
172 .addClass('widget-hbox widget-radio');
172 this.$label = $('<div />')
173 this.$label = $('<div />')
173 .appendTo(this.$el)
174 .appendTo(this.$el)
174 .addClass('widget-label')
175 .addClass('widget-label')
175 .hide();
176 .hide();
176 this.$container = $('<div />')
177 this.$container = $('<div />')
177 .appendTo(this.$el)
178 .appendTo(this.$el)
178 .addClass('widget-radio-box');
179 .addClass('widget-radio-box');
179 this.update();
180 this.update();
180 },
181 },
181
182
182 update : function(options){
183 update : function(options){
183 /**
184 /**
184 * Update the contents of this view
185 * Update the contents of this view
185 *
186 *
186 * Called when the model is changed. The model may have been
187 * Called when the model is changed. The model may have been
187 * changed by another view or by a state update from the back-end.
188 * changed by another view or by a state update from the back-end.
188 */
189 */
189 if (options === undefined || options.updated_view != this) {
190 if (options === undefined || options.updated_view != this) {
190 // Add missing items to the DOM.
191 // Add missing items to the DOM.
191 var items = this.model.get('_value_names');
192 var items = this.model.get('_options_labels');
192 var disabled = this.model.get('disabled');
193 var disabled = this.model.get('disabled');
193 var that = this;
194 var that = this;
194 _.each(items, function(item, index) {
195 _.each(items, function(item, index) {
195 var item_query = ' :input[data-value="' + encodeURIComponent(item) + '"]';
196 var item_query = ' :input[data-value="' + encodeURIComponent(item) + '"]';
196 if (that.$el.find(item_query).length === 0) {
197 if (that.$el.find(item_query).length === 0) {
197 var $label = $('<label />')
198 var $label = $('<label />')
198 .addClass('radio')
199 .addClass('radio')
199 .text(item)
200 .text(item)
200 .appendTo(that.$container);
201 .appendTo(that.$container);
201
202
202 $('<input />')
203 $('<input />')
203 .attr('type', 'radio')
204 .attr('type', 'radio')
204 .addClass(that.model)
205 .addClass(that.model)
205 .val(item)
206 .val(item)
206 .attr('data-value', encodeURIComponent(item))
207 .attr('data-value', encodeURIComponent(item))
207 .prependTo($label)
208 .prependTo($label)
208 .on('click', $.proxy(that.handle_click, that));
209 .on('click', $.proxy(that.handle_click, that));
209 }
210 }
210
211
211 var $item_element = that.$container.find(item_query);
212 var $item_element = that.$container.find(item_query);
212 if (that.model.get('value_name') == item) {
213 if (that.model.get('selected_label') == item) {
213 $item_element.prop('checked', true);
214 $item_element.prop('checked', true);
214 } else {
215 } else {
215 $item_element.prop('checked', false);
216 $item_element.prop('checked', false);
216 }
217 }
217 $item_element.prop('disabled', disabled);
218 $item_element.prop('disabled', disabled);
218 });
219 });
219
220
220 // Remove items that no longer exist.
221 // Remove items that no longer exist.
221 this.$container.find('input').each(function(i, obj) {
222 this.$container.find('input').each(function(i, obj) {
222 var value = $(obj).val();
223 var value = $(obj).val();
223 var found = false;
224 var found = false;
224 _.each(items, function(item, index) {
225 _.each(items, function(item, index) {
225 if (item == value) {
226 if (item == value) {
226 found = true;
227 found = true;
227 return false;
228 return false;
228 }
229 }
229 });
230 });
230
231
231 if (!found) {
232 if (!found) {
232 $(obj).parent().remove();
233 $(obj).parent().remove();
233 }
234 }
234 });
235 });
235
236
236 var description = this.model.get('description');
237 var description = this.model.get('description');
237 if (description.length === 0) {
238 if (description.length === 0) {
238 this.$label.hide();
239 this.$label.hide();
239 } else {
240 } else {
240 this.$label.text(description);
241 this.$label.text(description);
241 this.typeset(this.$label, description);
242 this.typeset(this.$label, description);
242 this.$label.show();
243 this.$label.show();
243 }
244 }
244 }
245 }
245 return RadioButtonsView.__super__.update.apply(this);
246 return RadioButtonsView.__super__.update.apply(this);
246 },
247 },
247
248
248 update_attr: function(name, value) {
249 update_attr: function(name, value) {
249 /**
250 /**
250 * Set a css attr of the widget view.
251 * Set a css attr of the widget view.
251 */
252 */
252 if (name == 'padding' || name == 'margin') {
253 if (name == 'padding' || name == 'margin') {
253 this.$el.css(name, value);
254 this.$el.css(name, value);
254 } else {
255 } else {
255 this.$container.css(name, value);
256 this.$container.css(name, value);
256 }
257 }
257 },
258 },
258
259
259 handle_click: function (e) {
260 handle_click: function (e) {
260 /**
261 /**
261 * Handle when a value is clicked.
262 * Handle when a value is clicked.
262 *
263 *
263 * Calling model.set will trigger all of the other views of the
264 * Calling model.set will trigger all of the other views of the
264 * model to update.
265 * model to update.
265 */
266 */
266 this.model.set('value_name', $(e.target).val(), {updated_view: this});
267 this.model.set('selected_label', $(e.target).val(), {updated_view: this});
267 this.touch();
268 this.touch();
268 },
269 },
269 });
270 });
270
271
271
272
272 var ToggleButtonsView = widget.DOMWidgetView.extend({
273 var ToggleButtonsView = widget.DOMWidgetView.extend({
273 initialize: function() {
274 initialize: function() {
274 this._css_state = {};
275 this._css_state = {};
275 ToggleButtonsView.__super__.initialize.apply(this, arguments);
276 ToggleButtonsView.__super__.initialize.apply(this, arguments);
276 },
277 },
277
278
278 render: function() {
279 render: function() {
279 /**
280 /**
280 * Called when view is rendered.
281 * Called when view is rendered.
281 */
282 */
282 this.$el
283 this.$el
283 .addClass('widget-hbox widget-toggle-buttons');
284 .addClass('widget-hbox widget-toggle-buttons');
284 this.$label = $('<div />')
285 this.$label = $('<div />')
285 .appendTo(this.$el)
286 .appendTo(this.$el)
286 .addClass('widget-label')
287 .addClass('widget-label')
287 .hide();
288 .hide();
288 this.$buttongroup = $('<div />')
289 this.$buttongroup = $('<div />')
289 .addClass('btn-group')
290 .addClass('btn-group')
290 .appendTo(this.$el);
291 .appendTo(this.$el);
291
292
292 this.model.on('change:button_style', function(model, value) {
293 this.model.on('change:button_style', function(model, value) {
293 this.update_button_style();
294 this.update_button_style();
294 }, this);
295 }, this);
295 this.update_button_style('');
296 this.update_button_style('');
296 this.update();
297 this.update();
297 },
298 },
298
299
299 update : function(options){
300 update : function(options){
300 /**
301 /**
301 * Update the contents of this view
302 * Update the contents of this view
302 *
303 *
303 * Called when the model is changed. The model may have been
304 * Called when the model is changed. The model may have been
304 * changed by another view or by a state update from the back-end.
305 * changed by another view or by a state update from the back-end.
305 */
306 */
306 if (options === undefined || options.updated_view != this) {
307 if (options === undefined || options.updated_view != this) {
307 // Add missing items to the DOM.
308 // Add missing items to the DOM.
308 var items = this.model.get('_value_names');
309 var items = this.model.get('_options_labels');
309 var disabled = this.model.get('disabled');
310 var disabled = this.model.get('disabled');
310 var that = this;
311 var that = this;
311 var item_html;
312 var item_html;
312 _.each(items, function(item, index) {
313 _.each(items, function(item, index) {
313 if (item.trim().length === 0) {
314 if (item.trim().length === 0) {
314 item_html = "&nbsp;";
315 item_html = "&nbsp;";
315 } else {
316 } else {
316 item_html = utils.escape_html(item);
317 item_html = utils.escape_html(item);
317 }
318 }
318 var item_query = '[data-value="' + encodeURIComponent(item) + '"]';
319 var item_query = '[data-value="' + encodeURIComponent(item) + '"]';
319 var $item_element = that.$buttongroup.find(item_query);
320 var $item_element = that.$buttongroup.find(item_query);
320 if (!$item_element.length) {
321 if (!$item_element.length) {
321 $item_element = $('<button/>')
322 $item_element = $('<button/>')
322 .attr('type', 'button')
323 .attr('type', 'button')
323 .addClass('btn btn-default')
324 .addClass('btn btn-default')
324 .html(item_html)
325 .html(item_html)
325 .appendTo(that.$buttongroup)
326 .appendTo(that.$buttongroup)
326 .attr('data-value', encodeURIComponent(item))
327 .attr('data-value', encodeURIComponent(item))
327 .attr('value', item)
328 .attr('value', item)
328 .on('click', $.proxy(that.handle_click, that));
329 .on('click', $.proxy(that.handle_click, that));
329 that.update_style_traits($item_element);
330 that.update_style_traits($item_element);
330 }
331 }
331 if (that.model.get('value_name') == item) {
332 if (that.model.get('selected_label') == item) {
332 $item_element.addClass('active');
333 $item_element.addClass('active');
333 } else {
334 } else {
334 $item_element.removeClass('active');
335 $item_element.removeClass('active');
335 }
336 }
336 $item_element.prop('disabled', disabled);
337 $item_element.prop('disabled', disabled);
337 });
338 });
338
339
339 // Remove items that no longer exist.
340 // Remove items that no longer exist.
340 this.$buttongroup.find('button').each(function(i, obj) {
341 this.$buttongroup.find('button').each(function(i, obj) {
341 var value = $(obj).attr('value');
342 var value = $(obj).attr('value');
342 var found = false;
343 var found = false;
343 _.each(items, function(item, index) {
344 _.each(items, function(item, index) {
344 if (item == value) {
345 if (item == value) {
345 found = true;
346 found = true;
346 return false;
347 return false;
347 }
348 }
348 });
349 });
349
350
350 if (!found) {
351 if (!found) {
351 $(obj).remove();
352 $(obj).remove();
352 }
353 }
353 });
354 });
354
355
355 var description = this.model.get('description');
356 var description = this.model.get('description');
356 if (description.length === 0) {
357 if (description.length === 0) {
357 this.$label.hide();
358 this.$label.hide();
358 } else {
359 } else {
359 this.$label.text();
360 this.$label.text();
360 this.typeset(this.$label, description);
361 this.typeset(this.$label, description);
361 this.$label.show();
362 this.$label.show();
362 }
363 }
363 }
364 }
364 return ToggleButtonsView.__super__.update.apply(this);
365 return ToggleButtonsView.__super__.update.apply(this);
365 },
366 },
366
367
367 update_attr: function(name, value) {
368 update_attr: function(name, value) {
368 /**
369 /**
369 * Set a css attr of the widget view.
370 * Set a css attr of the widget view.
370 */
371 */
371 if (name == 'padding' || name == 'margin') {
372 if (name == 'padding' || name == 'margin') {
372 this.$el.css(name, value);
373 this.$el.css(name, value);
373 } else {
374 } else {
374 this._css_state[name] = value;
375 this._css_state[name] = value;
375 this.update_style_traits();
376 this.update_style_traits();
376 }
377 }
377 },
378 },
378
379
379 update_style_traits: function(button) {
380 update_style_traits: function(button) {
380 for (var name in this._css_state) {
381 for (var name in this._css_state) {
381 if (this._css_state.hasOwnProperty(name)) {
382 if (this._css_state.hasOwnProperty(name)) {
382 if (name == 'margin') {
383 if (name == 'margin') {
383 this.$buttongroup.css(name, this._css_state[name]);
384 this.$buttongroup.css(name, this._css_state[name]);
384 } else if (name != 'width') {
385 } else if (name != 'width') {
385 if (button) {
386 if (button) {
386 button.css(name, this._css_state[name]);
387 button.css(name, this._css_state[name]);
387 } else {
388 } else {
388 this.$buttongroup.find('button').css(name, this._css_state[name]);
389 this.$buttongroup.find('button').css(name, this._css_state[name]);
389 }
390 }
390 }
391 }
391 }
392 }
392 }
393 }
393 },
394 },
394
395
395 update_button_style: function(previous_trait_value) {
396 update_button_style: function(previous_trait_value) {
396 var class_map = {
397 var class_map = {
397 primary: ['btn-primary'],
398 primary: ['btn-primary'],
398 success: ['btn-success'],
399 success: ['btn-success'],
399 info: ['btn-info'],
400 info: ['btn-info'],
400 warning: ['btn-warning'],
401 warning: ['btn-warning'],
401 danger: ['btn-danger']
402 danger: ['btn-danger']
402 };
403 };
403 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$buttongroup.find('button'));
404 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$buttongroup.find('button'));
404 },
405 },
405
406
406 handle_click: function (e) {
407 handle_click: function (e) {
407 /**
408 /**
408 * Handle when a value is clicked.
409 * Handle when a value is clicked.
409 *
410 *
410 * Calling model.set will trigger all of the other views of the
411 * Calling model.set will trigger all of the other views of the
411 * model to update.
412 * model to update.
412 */
413 */
413 this.model.set('value_name', $(e.target).attr('value'), {updated_view: this});
414 this.model.set('selected_label', $(e.target).attr('value'), {updated_view: this});
414 this.touch();
415 this.touch();
415 },
416 },
416 });
417 });
417
418
418
419
419 var SelectView = widget.DOMWidgetView.extend({
420 var SelectView = widget.DOMWidgetView.extend({
420 render : function(){
421 render : function(){
421 /**
422 /**
422 * Called when view is rendered.
423 * Called when view is rendered.
423 */
424 */
424 this.$el
425 this.$el
425 .addClass('widget-hbox widget-select');
426 .addClass('widget-hbox widget-select');
426 this.$label = $('<div />')
427 this.$label = $('<div />')
427 .appendTo(this.$el)
428 .appendTo(this.$el)
428 .addClass('widget-label')
429 .addClass('widget-label')
429 .hide();
430 .hide();
430 this.$listbox = $('<select />')
431 this.$listbox = $('<select />')
431 .addClass('widget-listbox form-control')
432 .addClass('widget-listbox form-control')
432 .attr('size', 6)
433 .attr('size', 6)
433 .appendTo(this.$el);
434 .appendTo(this.$el);
434 this.update();
435 this.update();
435 },
436 },
436
437
437 update : function(options){
438 update : function(options){
438 /**
439 /**
439 * Update the contents of this view
440 * Update the contents of this view
440 *
441 *
441 * Called when the model is changed. The model may have been
442 * Called when the model is changed. The model may have been
442 * changed by another view or by a state update from the back-end.
443 * changed by another view or by a state update from the back-end.
443 */
444 */
444 if (options === undefined || options.updated_view != this) {
445 if (options === undefined || options.updated_view != this) {
445 // Add missing items to the DOM.
446 // Add missing items to the DOM.
446 var items = this.model.get('_value_names');
447 var items = this.model.get('_options_labels');
447 var that = this;
448 var that = this;
448 _.each(items, function(item, index) {
449 _.each(items, function(item, index) {
449 var item_query = 'option[data-value="' + encodeURIComponent(item) + '"]';
450 var item_query = 'option[data-value="' + encodeURIComponent(item) + '"]';
450 if (that.$listbox.find(item_query).length === 0) {
451 if (that.$listbox.find(item_query).length === 0) {
451 $('<option />')
452 $('<option />')
452 .text(item)
453 .text(item)
453 .attr('data-value', encodeURIComponent(item))
454 .attr('data-value', encodeURIComponent(item))
454 .attr('value_name', item)
455 .attr('selected_label', item)
455 .appendTo(that.$listbox)
456 .appendTo(that.$listbox)
456 .on('click', $.proxy(that.handle_click, that));
457 .on('click', $.proxy(that.handle_click, that));
457 }
458 }
458 });
459 });
459
460
460 // Select the correct element
461 // Select the correct element
461 this.$listbox.val(this.model.get('value_name'));
462 this.$listbox.val(this.model.get('selected_label'));
462
463
463 // Disable listbox if needed
464 // Disable listbox if needed
464 var disabled = this.model.get('disabled');
465 var disabled = this.model.get('disabled');
465 this.$listbox.prop('disabled', disabled);
466 this.$listbox.prop('disabled', disabled);
466
467
467 // Remove items that no longer exist.
468 // Remove items that no longer exist.
468 this.$listbox.find('option').each(function(i, obj) {
469 this.$listbox.find('option').each(function(i, obj) {
469 var value = $(obj).text();
470 var value = $(obj).text();
470 var found = false;
471 var found = false;
471 _.each(items, function(item, index) {
472 _.each(items, function(item, index) {
472 if (item == value) {
473 if (item == value) {
473 found = true;
474 found = true;
474 return false;
475 return false;
475 }
476 }
476 });
477 });
477
478
478 if (!found) {
479 if (!found) {
479 $(obj).remove();
480 $(obj).remove();
480 }
481 }
481 });
482 });
482
483
483 var description = this.model.get('description');
484 var description = this.model.get('description');
484 if (description.length === 0) {
485 if (description.length === 0) {
485 this.$label.hide();
486 this.$label.hide();
486 } else {
487 } else {
487 this.typeset(this.$label, description);
488 this.typeset(this.$label, description);
488 this.$label.show();
489 this.$label.show();
489 }
490 }
490 }
491 }
491 return SelectView.__super__.update.apply(this);
492 return SelectView.__super__.update.apply(this);
492 },
493 },
493
494
494 update_attr: function(name, value) {
495 update_attr: function(name, value) {
495 /**
496 /**
496 * Set a css attr of the widget view.
497 * Set a css attr of the widget view.
497 */
498 */
498 if (name == 'padding' || name == 'margin') {
499 if (name == 'padding' || name == 'margin') {
499 this.$el.css(name, value);
500 this.$el.css(name, value);
500 } else {
501 } else {
501 this.$listbox.css(name, value);
502 this.$listbox.css(name, value);
502 }
503 }
503 },
504 },
504
505
505 handle_click: function (e) {
506 handle_click: function (e) {
506 /**
507 /**
507 * Handle when a value is clicked.
508 * Handle when a value is clicked.
508 *
509 *
509 * Calling model.set will trigger all of the other views of the
510 * Calling model.set will trigger all of the other views of the
510 * model to update.
511 * model to update.
511 */
512 */
512 this.model.set('value_name', $(e.target).text(), {updated_view: this});
513 this.model.set('selected_label', $(e.target).text(), {updated_view: this});
513 this.touch();
514 this.touch();
514 },
515 },
516 });
517 var SelectMultipleView = SelectView.extend({
518 render: function(){
519 SelectMultipleView.__super__.render.apply(this);
520 this.$el.removeClass('widget-select')
521 .addClass('widget-select-multiple');
522 this.$listbox.attr('multiple', true)
523 .on('input', $.proxy(this.handle_click, this));
524 return this;
525 },
526
527 update: function(){
528 SelectMultipleView.__super__.update.apply(this, arguments);
529 this.$listbox.val(this.model.get('selected_labels'));
530 },
531
532 handle_click: function (e) {
533 // Handle when a value is clicked.
534
535 // Calling model.set will trigger all of the other views of the
536 // model to update.
537 this.model.set('selected_labels',
538 (this.$listbox.val() || []).slice(),
539 {updated_view: this});
540 this.touch();
541 },
515 });
542 });
516
543
517 return {
544 return {
518 'DropdownView': DropdownView,
545 'DropdownView': DropdownView,
519 'RadioButtonsView': RadioButtonsView,
546 'RadioButtonsView': RadioButtonsView,
520 'ToggleButtonsView': ToggleButtonsView,
547 'ToggleButtonsView': ToggleButtonsView,
521 'SelectView': SelectView,
548 'SelectView': SelectView,
549 'SelectMultipleView': SelectMultipleView,
522 };
550 };
523 });
551 });
@@ -1,148 +1,148 b''
1 // Test selection class
1 // Test selection class
2 casper.notebook_test(function () {
2 casper.notebook_test(function () {
3 index = this.append_cell(
3 index = this.append_cell(
4 'from IPython.html import widgets\n' +
4 'from IPython.html import widgets\n' +
5 'from IPython.display import display, clear_output\n' +
5 'from IPython.display import display, clear_output\n' +
6 'print("Success")');
6 'print("Success")');
7 this.execute_cell_then(index);
7 this.execute_cell_then(index);
8
8
9 var combo_selector = '.widget-area .widget-subarea .widget-hbox .btn-group .widget-combo-btn';
9 var combo_selector = '.widget-area .widget-subarea .widget-hbox .btn-group .widget-combo-btn';
10 var multibtn_selector = '.widget-area .widget-subarea .widget-hbox.widget-toggle-buttons .btn-group';
10 var multibtn_selector = '.widget-area .widget-subarea .widget-hbox.widget-toggle-buttons .btn-group';
11 var radio_selector = '.widget-area .widget-subarea .widget-hbox .widget-radio-box';
11 var radio_selector = '.widget-area .widget-subarea .widget-hbox .widget-radio-box';
12 var list_selector = '.widget-area .widget-subarea .widget-hbox .widget-listbox';
12 var list_selector = '.widget-area .widget-subarea .widget-hbox .widget-listbox';
13
13
14 var selection_index;
14 var selection_index;
15 var selection_values = 'abcd';
15 var selection_values = 'abcd';
16 var check_state = function(context, index, state){
16 var check_state = function(context, index, state){
17 if (0 <= index && index < selection_values.length) {
17 if (0 <= index && index < selection_values.length) {
18 var multibtn_state = context.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(' + (index + 1) + ')', 'hasClass', ['active']);
18 var multibtn_state = context.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(' + (index + 1) + ')', 'hasClass', ['active']);
19 var radio_state = context.cell_element_function(selection_index, radio_selector + ' .radio:nth-child(' + (index + 1) + ') input', 'prop', ['checked']);
19 var radio_state = context.cell_element_function(selection_index, radio_selector + ' .radio:nth-child(' + (index + 1) + ') input', 'prop', ['checked']);
20 var list_val = context.cell_element_function(selection_index, list_selector, 'val');
20 var list_val = context.cell_element_function(selection_index, list_selector, 'val');
21 var combo_val = context.cell_element_function(selection_index, combo_selector, 'html');
21 var combo_val = context.cell_element_function(selection_index, combo_selector, 'html');
22
22
23 var val = selection_values.charAt(index);
23 var val = selection_values.charAt(index);
24 var list_state = (val == list_val);
24 var list_state = (val == list_val);
25 var combo_state = (val == combo_val);
25 var combo_state = (val == combo_val);
26
26
27 return multibtn_state == state &&
27 return multibtn_state == state &&
28 radio_state == state &&
28 radio_state == state &&
29 list_state == state &&
29 list_state == state &&
30 combo_state == state;
30 combo_state == state;
31 }
31 }
32 return true;
32 return true;
33 };
33 };
34
34
35 var verify_selection = function(context, index){
35 var verify_selection = function(context, index){
36 for (var i = 0; i < selection_values.length; i++) {
36 for (var i = 0; i < selection_values.length; i++) {
37 if (!check_state(context, i, i==index)) {
37 if (!check_state(context, i, i==index)) {
38 return false;
38 return false;
39 }
39 }
40 }
40 }
41 return true;
41 return true;
42 };
42 };
43
43
44 //values=["' + selection_values + '"[i] for i in range(4)]
44 //values=["' + selection_values + '"[i] for i in range(4)]
45 selection_index = this.append_cell(
45 selection_index = this.append_cell(
46 'values=["' + selection_values + '"[i] for i in range(4)]\n' +
46 'options=["' + selection_values + '"[i] for i in range(4)]\n' +
47 'selection = [widgets.Dropdown(values=values),\n' +
47 'selection = [widgets.Dropdown(options=options),\n' +
48 ' widgets.ToggleButtons(values=values),\n' +
48 ' widgets.ToggleButtons(options=options),\n' +
49 ' widgets.RadioButtons(values=values),\n' +
49 ' widgets.RadioButtons(options=options),\n' +
50 ' widgets.Select(values=values)]\n' +
50 ' widgets.Select(options=options)]\n' +
51 '[display(selection[i]) for i in range(4)]\n' +
51 '[display(selection[i]) for i in range(4)]\n' +
52 'for widget in selection:\n' +
52 'for widget in selection:\n' +
53 ' def handle_change(name,old,new):\n' +
53 ' def handle_change(name,old,new):\n' +
54 ' for other_widget in selection:\n' +
54 ' for other_widget in selection:\n' +
55 ' other_widget.value = new\n' +
55 ' other_widget.value = new\n' +
56 ' widget.on_trait_change(handle_change, "value")\n' +
56 ' widget.on_trait_change(handle_change, "value")\n' +
57 'print("Success")\n');
57 'print("Success")\n');
58 this.execute_cell_then(selection_index, function(index){
58 this.execute_cell_then(selection_index, function(index){
59 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
59 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
60 'Create selection cell executed with correct output.');
60 'Create selection cell executed with correct output.');
61 });
61 });
62
62
63 // Wait for the widgets to actually display.
63 // Wait for the widgets to actually display.
64 this.wait_for_element(selection_index, combo_selector);
64 this.wait_for_element(selection_index, combo_selector);
65 this.wait_for_element(selection_index, multibtn_selector);
65 this.wait_for_element(selection_index, multibtn_selector);
66 this.wait_for_element(selection_index, radio_selector);
66 this.wait_for_element(selection_index, radio_selector);
67 this.wait_for_element(selection_index, list_selector);
67 this.wait_for_element(selection_index, list_selector);
68
68
69 // Continue with the tests.
69 // Continue with the tests.
70 this.then(function() {
70 this.then(function() {
71 this.test.assert(this.cell_element_exists(selection_index,
71 this.test.assert(this.cell_element_exists(selection_index,
72 '.widget-area .widget-subarea'),
72 '.widget-area .widget-subarea'),
73 'Widget subarea exists.');
73 'Widget subarea exists.');
74
74
75 this.test.assert(this.cell_element_exists(selection_index, combo_selector),
75 this.test.assert(this.cell_element_exists(selection_index, combo_selector),
76 'Widget combobox exists.');
76 'Widget combobox exists.');
77
77
78 this.test.assert(this.cell_element_exists(selection_index, multibtn_selector),
78 this.test.assert(this.cell_element_exists(selection_index, multibtn_selector),
79 'Widget multibutton exists.');
79 'Widget multibutton exists.');
80
80
81 this.test.assert(this.cell_element_exists(selection_index, radio_selector),
81 this.test.assert(this.cell_element_exists(selection_index, radio_selector),
82 'Widget radio buttons exists.');
82 'Widget radio buttons exists.');
83
83
84 this.test.assert(this.cell_element_exists(selection_index, list_selector),
84 this.test.assert(this.cell_element_exists(selection_index, list_selector),
85 'Widget list exists.');
85 'Widget list exists.');
86
86
87 // Verify that no items are selected.
87 // Verify that no items are selected.
88 this.test.assert(verify_selection(this, 0), 'Default first item selected.');
88 this.test.assert(verify_selection(this, 0), 'Default first item selected.');
89 });
89 });
90
90
91 index = this.append_cell(
91 index = this.append_cell(
92 'for widget in selection:\n' +
92 'for widget in selection:\n' +
93 ' widget.value = "a"\n' +
93 ' widget.value = "a"\n' +
94 'print("Success")\n');
94 'print("Success")\n');
95 this.execute_cell_then(index, function(index){
95 this.execute_cell_then(index, function(index){
96 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
96 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
97 'Python select item executed with correct output.');
97 'Python select item executed with correct output.');
98
98
99 // Verify that the first item is selected.
99 // Verify that the first item is selected.
100 this.test.assert(verify_selection(this, 0), 'Python selected');
100 this.test.assert(verify_selection(this, 0), 'Python selected');
101
101
102 // Verify that selecting a radio button updates all of the others.
102 // Verify that selecting a radio button updates all of the others.
103 this.cell_element_function(selection_index, radio_selector + ' .radio:nth-child(2) input', 'click');
103 this.cell_element_function(selection_index, radio_selector + ' .radio:nth-child(2) input', 'click');
104 });
104 });
105 this.wait_for_idle();
105 this.wait_for_idle();
106 this.then(function () {
106 this.then(function () {
107 this.test.assert(verify_selection(this, 1), 'Radio button selection updated view states correctly.');
107 this.test.assert(verify_selection(this, 1), 'Radio button selection updated view states correctly.');
108
108
109 // Verify that selecting a list option updates all of the others.
109 // Verify that selecting a list option updates all of the others.
110 this.cell_element_function(selection_index, list_selector + ' option:nth-child(3)', 'click');
110 this.cell_element_function(selection_index, list_selector + ' option:nth-child(3)', 'click');
111 });
111 });
112 this.wait_for_idle();
112 this.wait_for_idle();
113 this.then(function () {
113 this.then(function () {
114 this.test.assert(verify_selection(this, 2), 'List selection updated view states correctly.');
114 this.test.assert(verify_selection(this, 2), 'List selection updated view states correctly.');
115
115
116 // Verify that selecting a multibutton option updates all of the others.
116 // Verify that selecting a multibutton option updates all of the others.
117 // Bootstrap3 has changed the toggle button group behavior. Two clicks
117 // Bootstrap3 has changed the toggle button group behavior. Two clicks
118 // are required to actually select an item.
118 // are required to actually select an item.
119 this.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(4)', 'click');
119 this.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(4)', 'click');
120 this.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(4)', 'click');
120 this.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(4)', 'click');
121 });
121 });
122 this.wait_for_idle();
122 this.wait_for_idle();
123 this.then(function () {
123 this.then(function () {
124 this.test.assert(verify_selection(this, 3), 'Multibutton selection updated view states correctly.');
124 this.test.assert(verify_selection(this, 3), 'Multibutton selection updated view states correctly.');
125
125
126 // Verify that selecting a combobox option updates all of the others.
126 // Verify that selecting a combobox option updates all of the others.
127 this.cell_element_function(selection_index, '.widget-area .widget-subarea .widget-hbox .btn-group ul.dropdown-menu li:nth-child(3) a', 'click');
127 this.cell_element_function(selection_index, '.widget-area .widget-subarea .widget-hbox .btn-group ul.dropdown-menu li:nth-child(3) a', 'click');
128 });
128 });
129 this.wait_for_idle();
129 this.wait_for_idle();
130 this.then(function () {
130 this.then(function () {
131 this.test.assert(verify_selection(this, 2), 'Combobox selection updated view states correctly.');
131 this.test.assert(verify_selection(this, 2), 'Combobox selection updated view states correctly.');
132 });
132 });
133
133
134 this.wait_for_idle();
134 this.wait_for_idle();
135
135
136 index = this.append_cell(
136 index = this.append_cell(
137 'from copy import copy\n' +
137 'from copy import copy\n' +
138 'for widget in selection:\n' +
138 'for widget in selection:\n' +
139 ' d = copy(widget.values)\n' +
139 ' d = copy(widget.options)\n' +
140 ' d.append("z")\n' +
140 ' d.append("z")\n' +
141 ' widget.values = d\n' +
141 ' widget.options = d\n' +
142 'selection[0].value = "z"');
142 'selection[0].value = "z"');
143 this.execute_cell_then(index, function(index){
143 this.execute_cell_then(index, function(index){
144
144
145 // Verify that selecting a combobox option updates all of the others.
145 // Verify that selecting a combobox option updates all of the others.
146 this.test.assert(verify_selection(this, 4), 'Item added to selection widget.');
146 this.test.assert(verify_selection(this, 4), 'Item added to selection widget.');
147 });
147 });
148 }); No newline at end of file
148 });
@@ -1,38 +1,38 b''
1 from .widget import Widget, DOMWidget, CallbackDispatcher, register
1 from .widget import Widget, DOMWidget, CallbackDispatcher, register
2
2
3 from .widget_bool import Checkbox, ToggleButton
3 from .widget_bool import Checkbox, ToggleButton
4 from .widget_button import Button
4 from .widget_button import Button
5 from .widget_box import Box, FlexBox, HBox, VBox
5 from .widget_box import Box, FlexBox, HBox, VBox
6 from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider
6 from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider
7 from .widget_image import Image
7 from .widget_image import Image
8 from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider
8 from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider
9 from .widget_output import Output
9 from .widget_output import Output
10 from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select
10 from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select, SelectMultiple
11 from .widget_selectioncontainer import Tab, Accordion
11 from .widget_selectioncontainer import Tab, Accordion
12 from .widget_string import HTML, Latex, Text, Textarea
12 from .widget_string import HTML, Latex, Text, Textarea
13 from .interaction import interact, interactive, fixed, interact_manual
13 from .interaction import interact, interactive, fixed, interact_manual
14 from .widget_link import jslink, jsdlink
14 from .widget_link import jslink, jsdlink
15
15
16 # Deprecated classes
16 # Deprecated classes
17 from .widget_bool import CheckboxWidget, ToggleButtonWidget
17 from .widget_bool import CheckboxWidget, ToggleButtonWidget
18 from .widget_button import ButtonWidget
18 from .widget_button import ButtonWidget
19 from .widget_box import ContainerWidget
19 from .widget_box import ContainerWidget
20 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
20 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
21 from .widget_image import ImageWidget
21 from .widget_image import ImageWidget
22 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
22 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
23 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
23 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
24 from .widget_selectioncontainer import TabWidget, AccordionWidget
24 from .widget_selectioncontainer import TabWidget, AccordionWidget
25 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
25 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
26
26
27 # We use warn_explicit so we have very brief messages without file or line numbers.
27 # We use warn_explicit so we have very brief messages without file or line numbers.
28 # The concern is that file or line numbers will confuse the interactive user.
28 # The concern is that file or line numbers will confuse the interactive user.
29 # To ignore this warning, do:
29 # To ignore this warning, do:
30 #
30 #
31 # from warnings import filterwarnings
31 # from warnings import filterwarnings
32 # filterwarnings('ignore', module='IPython.html.widgets')
32 # filterwarnings('ignore', module='IPython.html.widgets')
33
33
34 from warnings import warn_explicit
34 from warnings import warn_explicit
35 __warningregistry__ = {}
35 __warningregistry__ = {}
36 warn_explicit("IPython widgets are experimental and may change in the future.",
36 warn_explicit("IPython widgets are experimental and may change in the future.",
37 FutureWarning, '', 0, module = 'IPython.html.widgets',
37 FutureWarning, '', 0, module = 'IPython.html.widgets',
38 registry = __warningregistry__, module_globals = globals)
38 registry = __warningregistry__, module_globals = globals)
@@ -1,349 +1,349 b''
1 """Interact with functions using widgets."""
1 """Interact with functions using widgets."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from __future__ import print_function
6 from __future__ import print_function
7
7
8 try: # Python >= 3.3
8 try: # Python >= 3.3
9 from inspect import signature, Parameter
9 from inspect import signature, Parameter
10 except ImportError:
10 except ImportError:
11 from IPython.utils.signatures import signature, Parameter
11 from IPython.utils.signatures import signature, Parameter
12 from inspect import getcallargs
12 from inspect import getcallargs
13
13
14 from IPython.core.getipython import get_ipython
14 from IPython.core.getipython import get_ipython
15 from IPython.html.widgets import (Widget, Text,
15 from IPython.html.widgets import (Widget, Text,
16 FloatSlider, IntSlider, Checkbox, Dropdown,
16 FloatSlider, IntSlider, Checkbox, Dropdown,
17 Box, Button, DOMWidget, Output)
17 Box, Button, DOMWidget, Output)
18 from IPython.display import display, clear_output
18 from IPython.display import display, clear_output
19 from IPython.utils.py3compat import string_types, unicode_type
19 from IPython.utils.py3compat import string_types, unicode_type
20 from IPython.utils.traitlets import HasTraits, Any, Unicode
20 from IPython.utils.traitlets import HasTraits, Any, Unicode
21
21
22 empty = Parameter.empty
22 empty = Parameter.empty
23
23
24
24
25 def _matches(o, pattern):
25 def _matches(o, pattern):
26 """Match a pattern of types in a sequence."""
26 """Match a pattern of types in a sequence."""
27 if not len(o) == len(pattern):
27 if not len(o) == len(pattern):
28 return False
28 return False
29 comps = zip(o,pattern)
29 comps = zip(o,pattern)
30 return all(isinstance(obj,kind) for obj,kind in comps)
30 return all(isinstance(obj,kind) for obj,kind in comps)
31
31
32
32
33 def _get_min_max_value(min, max, value=None, step=None):
33 def _get_min_max_value(min, max, value=None, step=None):
34 """Return min, max, value given input values with possible None."""
34 """Return min, max, value given input values with possible None."""
35 if value is None:
35 if value is None:
36 if not max > min:
36 if not max > min:
37 raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
37 raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
38 value = min + abs(min-max)/2
38 value = min + abs(min-max)/2
39 value = type(min)(value)
39 value = type(min)(value)
40 elif min is None and max is None:
40 elif min is None and max is None:
41 if value == 0.0:
41 if value == 0.0:
42 min, max, value = 0.0, 1.0, 0.5
42 min, max, value = 0.0, 1.0, 0.5
43 elif value == 0:
43 elif value == 0:
44 min, max, value = 0, 1, 0
44 min, max, value = 0, 1, 0
45 elif isinstance(value, (int, float)):
45 elif isinstance(value, (int, float)):
46 min, max = (-value, 3*value) if value > 0 else (3*value, -value)
46 min, max = (-value, 3*value) if value > 0 else (3*value, -value)
47 else:
47 else:
48 raise TypeError('expected a number, got: %r' % value)
48 raise TypeError('expected a number, got: %r' % value)
49 else:
49 else:
50 raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
50 raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
51 if step is not None:
51 if step is not None:
52 # ensure value is on a step
52 # ensure value is on a step
53 r = (value - min) % step
53 r = (value - min) % step
54 value = value - r
54 value = value - r
55 return min, max, value
55 return min, max, value
56
56
57 def _widget_abbrev_single_value(o):
57 def _widget_abbrev_single_value(o):
58 """Make widgets from single values, which can be used as parameter defaults."""
58 """Make widgets from single values, which can be used as parameter defaults."""
59 if isinstance(o, string_types):
59 if isinstance(o, string_types):
60 return Text(value=unicode_type(o))
60 return Text(value=unicode_type(o))
61 elif isinstance(o, dict):
61 elif isinstance(o, dict):
62 return Dropdown(values=o)
62 return Dropdown(options=o)
63 elif isinstance(o, bool):
63 elif isinstance(o, bool):
64 return Checkbox(value=o)
64 return Checkbox(value=o)
65 elif isinstance(o, float):
65 elif isinstance(o, float):
66 min, max, value = _get_min_max_value(None, None, o)
66 min, max, value = _get_min_max_value(None, None, o)
67 return FloatSlider(value=o, min=min, max=max)
67 return FloatSlider(value=o, min=min, max=max)
68 elif isinstance(o, int):
68 elif isinstance(o, int):
69 min, max, value = _get_min_max_value(None, None, o)
69 min, max, value = _get_min_max_value(None, None, o)
70 return IntSlider(value=o, min=min, max=max)
70 return IntSlider(value=o, min=min, max=max)
71 else:
71 else:
72 return None
72 return None
73
73
74 def _widget_abbrev(o):
74 def _widget_abbrev(o):
75 """Make widgets from abbreviations: single values, lists or tuples."""
75 """Make widgets from abbreviations: single values, lists or tuples."""
76 float_or_int = (float, int)
76 float_or_int = (float, int)
77 if isinstance(o, (list, tuple)):
77 if isinstance(o, (list, tuple)):
78 if o and all(isinstance(x, string_types) for x in o):
78 if o and all(isinstance(x, string_types) for x in o):
79 return Dropdown(values=[unicode_type(k) for k in o])
79 return Dropdown(options=[unicode_type(k) for k in o])
80 elif _matches(o, (float_or_int, float_or_int)):
80 elif _matches(o, (float_or_int, float_or_int)):
81 min, max, value = _get_min_max_value(o[0], o[1])
81 min, max, value = _get_min_max_value(o[0], o[1])
82 if all(isinstance(_, int) for _ in o):
82 if all(isinstance(_, int) for _ in o):
83 cls = IntSlider
83 cls = IntSlider
84 else:
84 else:
85 cls = FloatSlider
85 cls = FloatSlider
86 return cls(value=value, min=min, max=max)
86 return cls(value=value, min=min, max=max)
87 elif _matches(o, (float_or_int, float_or_int, float_or_int)):
87 elif _matches(o, (float_or_int, float_or_int, float_or_int)):
88 step = o[2]
88 step = o[2]
89 if step <= 0:
89 if step <= 0:
90 raise ValueError("step must be >= 0, not %r" % step)
90 raise ValueError("step must be >= 0, not %r" % step)
91 min, max, value = _get_min_max_value(o[0], o[1], step=step)
91 min, max, value = _get_min_max_value(o[0], o[1], step=step)
92 if all(isinstance(_, int) for _ in o):
92 if all(isinstance(_, int) for _ in o):
93 cls = IntSlider
93 cls = IntSlider
94 else:
94 else:
95 cls = FloatSlider
95 cls = FloatSlider
96 return cls(value=value, min=min, max=max, step=step)
96 return cls(value=value, min=min, max=max, step=step)
97 else:
97 else:
98 return _widget_abbrev_single_value(o)
98 return _widget_abbrev_single_value(o)
99
99
100 def _widget_from_abbrev(abbrev, default=empty):
100 def _widget_from_abbrev(abbrev, default=empty):
101 """Build a Widget instance given an abbreviation or Widget."""
101 """Build a Widget instance given an abbreviation or Widget."""
102 if isinstance(abbrev, Widget) or isinstance(abbrev, fixed):
102 if isinstance(abbrev, Widget) or isinstance(abbrev, fixed):
103 return abbrev
103 return abbrev
104
104
105 widget = _widget_abbrev(abbrev)
105 widget = _widget_abbrev(abbrev)
106 if default is not empty and isinstance(abbrev, (list, tuple, dict)):
106 if default is not empty and isinstance(abbrev, (list, tuple, dict)):
107 # if it's not a single-value abbreviation,
107 # if it's not a single-value abbreviation,
108 # set the initial value from the default
108 # set the initial value from the default
109 try:
109 try:
110 widget.value = default
110 widget.value = default
111 except Exception:
111 except Exception:
112 # ignore failure to set default
112 # ignore failure to set default
113 pass
113 pass
114 if widget is None:
114 if widget is None:
115 raise ValueError("%r cannot be transformed to a Widget" % (abbrev,))
115 raise ValueError("%r cannot be transformed to a Widget" % (abbrev,))
116 return widget
116 return widget
117
117
118 def _yield_abbreviations_for_parameter(param, kwargs):
118 def _yield_abbreviations_for_parameter(param, kwargs):
119 """Get an abbreviation for a function parameter."""
119 """Get an abbreviation for a function parameter."""
120 name = param.name
120 name = param.name
121 kind = param.kind
121 kind = param.kind
122 ann = param.annotation
122 ann = param.annotation
123 default = param.default
123 default = param.default
124 not_found = (name, empty, empty)
124 not_found = (name, empty, empty)
125 if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
125 if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
126 if name in kwargs:
126 if name in kwargs:
127 value = kwargs.pop(name)
127 value = kwargs.pop(name)
128 elif ann is not empty:
128 elif ann is not empty:
129 value = ann
129 value = ann
130 elif default is not empty:
130 elif default is not empty:
131 value = default
131 value = default
132 else:
132 else:
133 yield not_found
133 yield not_found
134 yield (name, value, default)
134 yield (name, value, default)
135 elif kind == Parameter.VAR_KEYWORD:
135 elif kind == Parameter.VAR_KEYWORD:
136 # In this case name=kwargs and we yield the items in kwargs with their keys.
136 # In this case name=kwargs and we yield the items in kwargs with their keys.
137 for k, v in kwargs.copy().items():
137 for k, v in kwargs.copy().items():
138 kwargs.pop(k)
138 kwargs.pop(k)
139 yield k, v, empty
139 yield k, v, empty
140
140
141 def _find_abbreviations(f, kwargs):
141 def _find_abbreviations(f, kwargs):
142 """Find the abbreviations for a function and kwargs passed to interact."""
142 """Find the abbreviations for a function and kwargs passed to interact."""
143 new_kwargs = []
143 new_kwargs = []
144 for param in signature(f).parameters.values():
144 for param in signature(f).parameters.values():
145 for name, value, default in _yield_abbreviations_for_parameter(param, kwargs):
145 for name, value, default in _yield_abbreviations_for_parameter(param, kwargs):
146 if value is empty:
146 if value is empty:
147 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
147 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
148 new_kwargs.append((name, value, default))
148 new_kwargs.append((name, value, default))
149 return new_kwargs
149 return new_kwargs
150
150
151 def _widgets_from_abbreviations(seq):
151 def _widgets_from_abbreviations(seq):
152 """Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
152 """Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
153 result = []
153 result = []
154 for name, abbrev, default in seq:
154 for name, abbrev, default in seq:
155 widget = _widget_from_abbrev(abbrev, default)
155 widget = _widget_from_abbrev(abbrev, default)
156 if not widget.description:
156 if not widget.description:
157 widget.description = name
157 widget.description = name
158 widget._kwarg = name
158 widget._kwarg = name
159 result.append(widget)
159 result.append(widget)
160 return result
160 return result
161
161
162 def interactive(__interact_f, **kwargs):
162 def interactive(__interact_f, **kwargs):
163 """
163 """
164 Builds a group of interactive widgets tied to a function and places the
164 Builds a group of interactive widgets tied to a function and places the
165 group into a Box container.
165 group into a Box container.
166
166
167 Returns
167 Returns
168 -------
168 -------
169 container : a Box instance containing multiple widgets
169 container : a Box instance containing multiple widgets
170
170
171 Parameters
171 Parameters
172 ----------
172 ----------
173 __interact_f : function
173 __interact_f : function
174 The function to which the interactive widgets are tied. The **kwargs
174 The function to which the interactive widgets are tied. The **kwargs
175 should match the function signature.
175 should match the function signature.
176 **kwargs : various, optional
176 **kwargs : various, optional
177 An interactive widget is created for each keyword argument that is a
177 An interactive widget is created for each keyword argument that is a
178 valid widget abbreviation.
178 valid widget abbreviation.
179 """
179 """
180 f = __interact_f
180 f = __interact_f
181 co = kwargs.pop('clear_output', True)
181 co = kwargs.pop('clear_output', True)
182 manual = kwargs.pop('__manual', False)
182 manual = kwargs.pop('__manual', False)
183 kwargs_widgets = []
183 kwargs_widgets = []
184 container = Box()
184 container = Box()
185 container.result = None
185 container.result = None
186 container.args = []
186 container.args = []
187 container.kwargs = dict()
187 container.kwargs = dict()
188 kwargs = kwargs.copy()
188 kwargs = kwargs.copy()
189
189
190 new_kwargs = _find_abbreviations(f, kwargs)
190 new_kwargs = _find_abbreviations(f, kwargs)
191 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
191 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
192 # that will lead to a valid call of the function. This protects against unspecified
192 # that will lead to a valid call of the function. This protects against unspecified
193 # and doubly-specified arguments.
193 # and doubly-specified arguments.
194 getcallargs(f, **{n:v for n,v,_ in new_kwargs})
194 getcallargs(f, **{n:v for n,v,_ in new_kwargs})
195 # Now build the widgets from the abbreviations.
195 # Now build the widgets from the abbreviations.
196 kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
196 kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
197
197
198 # This has to be done as an assignment, not using container.children.append,
198 # This has to be done as an assignment, not using container.children.append,
199 # so that traitlets notices the update. We skip any objects (such as fixed) that
199 # so that traitlets notices the update. We skip any objects (such as fixed) that
200 # are not DOMWidgets.
200 # are not DOMWidgets.
201 c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
201 c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
202
202
203 # If we are only to run the function on demand, add a button to request this
203 # If we are only to run the function on demand, add a button to request this
204 if manual:
204 if manual:
205 manual_button = Button(description="Run %s" % f.__name__)
205 manual_button = Button(description="Run %s" % f.__name__)
206 c.append(manual_button)
206 c.append(manual_button)
207
207
208 # Use an output widget to capture the output of interact.
208 # Use an output widget to capture the output of interact.
209 output = Output()
209 output = Output()
210 c.append(output)
210 c.append(output)
211 container.children = c
211 container.children = c
212
212
213 # Build the callback
213 # Build the callback
214 def call_f(name=None, old=None, new=None):
214 def call_f(name=None, old=None, new=None):
215 with output:
215 with output:
216 container.kwargs = {}
216 container.kwargs = {}
217 for widget in kwargs_widgets:
217 for widget in kwargs_widgets:
218 value = widget.value
218 value = widget.value
219 container.kwargs[widget._kwarg] = value
219 container.kwargs[widget._kwarg] = value
220 if co:
220 if co:
221 clear_output(wait=True)
221 clear_output(wait=True)
222 if manual:
222 if manual:
223 manual_button.disabled = True
223 manual_button.disabled = True
224 try:
224 try:
225 container.result = f(**container.kwargs)
225 container.result = f(**container.kwargs)
226 except Exception as e:
226 except Exception as e:
227 ip = get_ipython()
227 ip = get_ipython()
228 if ip is None:
228 if ip is None:
229 container.log.warn("Exception in interact callback: %s", e, exc_info=True)
229 container.log.warn("Exception in interact callback: %s", e, exc_info=True)
230 else:
230 else:
231 ip.showtraceback()
231 ip.showtraceback()
232 finally:
232 finally:
233 if manual:
233 if manual:
234 manual_button.disabled = False
234 manual_button.disabled = False
235
235
236 # Wire up the widgets
236 # Wire up the widgets
237 # If we are doing manual running, the callback is only triggered by the button
237 # If we are doing manual running, the callback is only triggered by the button
238 # Otherwise, it is triggered for every trait change received
238 # Otherwise, it is triggered for every trait change received
239 # On-demand running also suppresses running the function with the initial parameters
239 # On-demand running also suppresses running the function with the initial parameters
240 if manual:
240 if manual:
241 manual_button.on_click(call_f)
241 manual_button.on_click(call_f)
242 else:
242 else:
243 for widget in kwargs_widgets:
243 for widget in kwargs_widgets:
244 widget.on_trait_change(call_f, 'value')
244 widget.on_trait_change(call_f, 'value')
245
245
246 container.on_displayed(lambda _: call_f(None, None, None))
246 container.on_displayed(lambda _: call_f(None, None, None))
247
247
248 return container
248 return container
249
249
250 def interact(__interact_f=None, **kwargs):
250 def interact(__interact_f=None, **kwargs):
251 """
251 """
252 Displays interactive widgets which are tied to a function.
252 Displays interactive widgets which are tied to a function.
253 Expects the first argument to be a function. Parameters to this function are
253 Expects the first argument to be a function. Parameters to this function are
254 widget abbreviations passed in as keyword arguments (**kwargs). Can be used
254 widget abbreviations passed in as keyword arguments (**kwargs). Can be used
255 as a decorator (see examples).
255 as a decorator (see examples).
256
256
257 Returns
257 Returns
258 -------
258 -------
259 f : __interact_f with interactive widget attached to it.
259 f : __interact_f with interactive widget attached to it.
260
260
261 Parameters
261 Parameters
262 ----------
262 ----------
263 __interact_f : function
263 __interact_f : function
264 The function to which the interactive widgets are tied. The **kwargs
264 The function to which the interactive widgets are tied. The **kwargs
265 should match the function signature. Passed to :func:`interactive()`
265 should match the function signature. Passed to :func:`interactive()`
266 **kwargs : various, optional
266 **kwargs : various, optional
267 An interactive widget is created for each keyword argument that is a
267 An interactive widget is created for each keyword argument that is a
268 valid widget abbreviation. Passed to :func:`interactive()`
268 valid widget abbreviation. Passed to :func:`interactive()`
269
269
270 Examples
270 Examples
271 --------
271 --------
272 Renders an interactive text field that shows the greeting with the passed in
272 Renders an interactive text field that shows the greeting with the passed in
273 text.
273 text.
274
274
275 1. Invocation of interact as a function
275 1. Invocation of interact as a function
276 def greeting(text="World"):
276 def greeting(text="World"):
277 print "Hello {}".format(text)
277 print "Hello {}".format(text)
278 interact(greeting, text="IPython Widgets")
278 interact(greeting, text="IPython Widgets")
279
279
280 2. Invocation of interact as a decorator
280 2. Invocation of interact as a decorator
281 @interact
281 @interact
282 def greeting(text="World"):
282 def greeting(text="World"):
283 print "Hello {}".format(text)
283 print "Hello {}".format(text)
284
284
285 3. Invocation of interact as a decorator with named parameters
285 3. Invocation of interact as a decorator with named parameters
286 @interact(text="IPython Widgets")
286 @interact(text="IPython Widgets")
287 def greeting(text="World"):
287 def greeting(text="World"):
288 print "Hello {}".format(text)
288 print "Hello {}".format(text)
289
289
290 Renders an interactive slider widget and prints square of number.
290 Renders an interactive slider widget and prints square of number.
291
291
292 1. Invocation of interact as a function
292 1. Invocation of interact as a function
293 def square(num=1):
293 def square(num=1):
294 print "{} squared is {}".format(num, num*num)
294 print "{} squared is {}".format(num, num*num)
295 interact(square, num=5)
295 interact(square, num=5)
296
296
297 2. Invocation of interact as a decorator
297 2. Invocation of interact as a decorator
298 @interact
298 @interact
299 def square(num=2):
299 def square(num=2):
300 print "{} squared is {}".format(num, num*num)
300 print "{} squared is {}".format(num, num*num)
301
301
302 3. Invocation of interact as a decorator with named parameters
302 3. Invocation of interact as a decorator with named parameters
303 @interact(num=5)
303 @interact(num=5)
304 def square(num=2):
304 def square(num=2):
305 print "{} squared is {}".format(num, num*num)
305 print "{} squared is {}".format(num, num*num)
306 """
306 """
307 # positional arg support in: https://gist.github.com/8851331
307 # positional arg support in: https://gist.github.com/8851331
308 if __interact_f is not None:
308 if __interact_f is not None:
309 # This branch handles the cases 1 and 2
309 # This branch handles the cases 1 and 2
310 # 1. interact(f, **kwargs)
310 # 1. interact(f, **kwargs)
311 # 2. @interact
311 # 2. @interact
312 # def f(*args, **kwargs):
312 # def f(*args, **kwargs):
313 # ...
313 # ...
314 f = __interact_f
314 f = __interact_f
315 w = interactive(f, **kwargs)
315 w = interactive(f, **kwargs)
316 try:
316 try:
317 f.widget = w
317 f.widget = w
318 except AttributeError:
318 except AttributeError:
319 # some things (instancemethods) can't have attributes attached,
319 # some things (instancemethods) can't have attributes attached,
320 # so wrap in a lambda
320 # so wrap in a lambda
321 f = lambda *args, **kwargs: __interact_f(*args, **kwargs)
321 f = lambda *args, **kwargs: __interact_f(*args, **kwargs)
322 f.widget = w
322 f.widget = w
323 display(w)
323 display(w)
324 return f
324 return f
325 else:
325 else:
326 # This branch handles the case 3
326 # This branch handles the case 3
327 # @interact(a=30, b=40)
327 # @interact(a=30, b=40)
328 # def f(*args, **kwargs):
328 # def f(*args, **kwargs):
329 # ...
329 # ...
330 def dec(f):
330 def dec(f):
331 return interact(f, **kwargs)
331 return interact(f, **kwargs)
332 return dec
332 return dec
333
333
334 def interact_manual(__interact_f=None, **kwargs):
334 def interact_manual(__interact_f=None, **kwargs):
335 """interact_manual(f, **kwargs)
335 """interact_manual(f, **kwargs)
336
336
337 As `interact()`, generates widgets for each argument, but rather than running
337 As `interact()`, generates widgets for each argument, but rather than running
338 the function after each widget change, adds a "Run" button and waits for it
338 the function after each widget change, adds a "Run" button and waits for it
339 to be clicked. Useful if the function is long-running and has several
339 to be clicked. Useful if the function is long-running and has several
340 parameters to change.
340 parameters to change.
341 """
341 """
342 return interact(__interact_f, __manual=True, **kwargs)
342 return interact(__interact_f, __manual=True, **kwargs)
343
343
344 class fixed(HasTraits):
344 class fixed(HasTraits):
345 """A pseudo-widget whose value is fixed and never synced to the client."""
345 """A pseudo-widget whose value is fixed and never synced to the client."""
346 value = Any(help="Any Python object")
346 value = Any(help="Any Python object")
347 description = Unicode('', help="Any Python object")
347 description = Unicode('', help="Any Python object")
348 def __init__(self, value, **kwargs):
348 def __init__(self, value, **kwargs):
349 super(fixed, self).__init__(value=value, **kwargs)
349 super(fixed, self).__init__(value=value, **kwargs)
@@ -1,636 +1,692 b''
1 """Test interact and interactive."""
1 """Test interact and interactive."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from __future__ import print_function
6 from __future__ import print_function
7
7
8 from collections import OrderedDict
8 from collections import OrderedDict
9
9
10 import nose.tools as nt
10 import nose.tools as nt
11 import IPython.testing.tools as tt
11 import IPython.testing.tools as tt
12
12
13 from IPython.kernel.comm import Comm
13 from IPython.kernel.comm import Comm
14 from IPython.html import widgets
14 from IPython.html import widgets
15 from IPython.html.widgets import interact, interactive, Widget, interaction
15 from IPython.html.widgets import interact, interactive, Widget, interaction
16 from IPython.utils.py3compat import annotate
16 from IPython.utils.py3compat import annotate
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Utility stuff
19 # Utility stuff
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 class DummyComm(Comm):
22 class DummyComm(Comm):
23 comm_id = 'a-b-c-d'
23 comm_id = 'a-b-c-d'
24
24
25 def open(self, *args, **kwargs):
25 def open(self, *args, **kwargs):
26 pass
26 pass
27
27
28 def send(self, *args, **kwargs):
28 def send(self, *args, **kwargs):
29 pass
29 pass
30
30
31 def close(self, *args, **kwargs):
31 def close(self, *args, **kwargs):
32 pass
32 pass
33
33
34 _widget_attrs = {}
34 _widget_attrs = {}
35 displayed = []
35 displayed = []
36 undefined = object()
36 undefined = object()
37
37
38 def setup():
38 def setup():
39 _widget_attrs['_comm_default'] = getattr(Widget, '_comm_default', undefined)
39 _widget_attrs['_comm_default'] = getattr(Widget, '_comm_default', undefined)
40 Widget._comm_default = lambda self: DummyComm()
40 Widget._comm_default = lambda self: DummyComm()
41 _widget_attrs['_ipython_display_'] = Widget._ipython_display_
41 _widget_attrs['_ipython_display_'] = Widget._ipython_display_
42 def raise_not_implemented(*args, **kwargs):
42 def raise_not_implemented(*args, **kwargs):
43 raise NotImplementedError()
43 raise NotImplementedError()
44 Widget._ipython_display_ = raise_not_implemented
44 Widget._ipython_display_ = raise_not_implemented
45
45
46 def teardown():
46 def teardown():
47 for attr, value in _widget_attrs.items():
47 for attr, value in _widget_attrs.items():
48 if value is undefined:
48 if value is undefined:
49 delattr(Widget, attr)
49 delattr(Widget, attr)
50 else:
50 else:
51 setattr(Widget, attr, value)
51 setattr(Widget, attr, value)
52
52
53 def f(**kwargs):
53 def f(**kwargs):
54 pass
54 pass
55
55
56 def clear_display():
56 def clear_display():
57 global displayed
57 global displayed
58 displayed = []
58 displayed = []
59
59
60 def record_display(*args):
60 def record_display(*args):
61 displayed.extend(args)
61 displayed.extend(args)
62
62
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64 # Actual tests
64 # Actual tests
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66
66
67 def check_widget(w, **d):
67 def check_widget(w, **d):
68 """Check a single widget against a dict"""
68 """Check a single widget against a dict"""
69 for attr, expected in d.items():
69 for attr, expected in d.items():
70 if attr == 'cls':
70 if attr == 'cls':
71 nt.assert_is(w.__class__, expected)
71 nt.assert_is(w.__class__, expected)
72 else:
72 else:
73 value = getattr(w, attr)
73 value = getattr(w, attr)
74 nt.assert_equal(value, expected,
74 nt.assert_equal(value, expected,
75 "%s.%s = %r != %r" % (w.__class__.__name__, attr, value, expected)
75 "%s.%s = %r != %r" % (w.__class__.__name__, attr, value, expected)
76 )
76 )
77
77
78 def check_widgets(container, **to_check):
78 def check_widgets(container, **to_check):
79 """Check that widgets are created as expected"""
79 """Check that widgets are created as expected"""
80 # build a widget dictionary, so it matches
80 # build a widget dictionary, so it matches
81 widgets = {}
81 widgets = {}
82 for w in container.children:
82 for w in container.children:
83 if hasattr(w, 'description'):
83 if hasattr(w, 'description'):
84 widgets[w.description] = w
84 widgets[w.description] = w
85
85
86 for key, d in to_check.items():
86 for key, d in to_check.items():
87 nt.assert_in(key, widgets)
87 nt.assert_in(key, widgets)
88 check_widget(widgets[key], **d)
88 check_widget(widgets[key], **d)
89
89
90
90
91 def test_single_value_string():
91 def test_single_value_string():
92 a = u'hello'
92 a = u'hello'
93 c = interactive(f, a=a)
93 c = interactive(f, a=a)
94 w = c.children[0]
94 w = c.children[0]
95 check_widget(w,
95 check_widget(w,
96 cls=widgets.Text,
96 cls=widgets.Text,
97 description='a',
97 description='a',
98 value=a,
98 value=a,
99 )
99 )
100
100
101 def test_single_value_bool():
101 def test_single_value_bool():
102 for a in (True, False):
102 for a in (True, False):
103 c = interactive(f, a=a)
103 c = interactive(f, a=a)
104 w = c.children[0]
104 w = c.children[0]
105 check_widget(w,
105 check_widget(w,
106 cls=widgets.Checkbox,
106 cls=widgets.Checkbox,
107 description='a',
107 description='a',
108 value=a,
108 value=a,
109 )
109 )
110
110
111 def test_single_value_dict():
111 def test_single_value_dict():
112 for d in [
112 for d in [
113 dict(a=5),
113 dict(a=5),
114 dict(a=5, b='b', c=dict),
114 dict(a=5, b='b', c=dict),
115 ]:
115 ]:
116 c = interactive(f, d=d)
116 c = interactive(f, d=d)
117 w = c.children[0]
117 w = c.children[0]
118 check_widget(w,
118 check_widget(w,
119 cls=widgets.Dropdown,
119 cls=widgets.Dropdown,
120 description='d',
120 description='d',
121 values=d,
121 options=d,
122 value=next(iter(d.values())),
122 value=next(iter(d.values())),
123 )
123 )
124
124
125 def test_single_value_float():
125 def test_single_value_float():
126 for a in (2.25, 1.0, -3.5):
126 for a in (2.25, 1.0, -3.5):
127 c = interactive(f, a=a)
127 c = interactive(f, a=a)
128 w = c.children[0]
128 w = c.children[0]
129 check_widget(w,
129 check_widget(w,
130 cls=widgets.FloatSlider,
130 cls=widgets.FloatSlider,
131 description='a',
131 description='a',
132 value=a,
132 value=a,
133 min= -a if a > 0 else 3*a,
133 min= -a if a > 0 else 3*a,
134 max= 3*a if a > 0 else -a,
134 max= 3*a if a > 0 else -a,
135 step=0.1,
135 step=0.1,
136 readout=True,
136 readout=True,
137 )
137 )
138
138
139 def test_single_value_int():
139 def test_single_value_int():
140 for a in (1, 5, -3):
140 for a in (1, 5, -3):
141 c = interactive(f, a=a)
141 c = interactive(f, a=a)
142 nt.assert_equal(len(c.children), 2)
142 nt.assert_equal(len(c.children), 2)
143 w = c.children[0]
143 w = c.children[0]
144 check_widget(w,
144 check_widget(w,
145 cls=widgets.IntSlider,
145 cls=widgets.IntSlider,
146 description='a',
146 description='a',
147 value=a,
147 value=a,
148 min= -a if a > 0 else 3*a,
148 min= -a if a > 0 else 3*a,
149 max= 3*a if a > 0 else -a,
149 max= 3*a if a > 0 else -a,
150 step=1,
150 step=1,
151 readout=True,
151 readout=True,
152 )
152 )
153
153
154 def test_list_tuple_2_int():
154 def test_list_tuple_2_int():
155 with nt.assert_raises(ValueError):
155 with nt.assert_raises(ValueError):
156 c = interactive(f, tup=(1,1))
156 c = interactive(f, tup=(1,1))
157 with nt.assert_raises(ValueError):
157 with nt.assert_raises(ValueError):
158 c = interactive(f, tup=(1,-1))
158 c = interactive(f, tup=(1,-1))
159 for min, max in [ (0,1), (1,10), (1,2), (-5,5), (-20,-19) ]:
159 for min, max in [ (0,1), (1,10), (1,2), (-5,5), (-20,-19) ]:
160 c = interactive(f, tup=(min, max), lis=[min, max])
160 c = interactive(f, tup=(min, max), lis=[min, max])
161 nt.assert_equal(len(c.children), 3)
161 nt.assert_equal(len(c.children), 3)
162 d = dict(
162 d = dict(
163 cls=widgets.IntSlider,
163 cls=widgets.IntSlider,
164 min=min,
164 min=min,
165 max=max,
165 max=max,
166 step=1,
166 step=1,
167 readout=True,
167 readout=True,
168 )
168 )
169 check_widgets(c, tup=d, lis=d)
169 check_widgets(c, tup=d, lis=d)
170
170
171 def test_list_tuple_3_int():
171 def test_list_tuple_3_int():
172 with nt.assert_raises(ValueError):
172 with nt.assert_raises(ValueError):
173 c = interactive(f, tup=(1,2,0))
173 c = interactive(f, tup=(1,2,0))
174 with nt.assert_raises(ValueError):
174 with nt.assert_raises(ValueError):
175 c = interactive(f, tup=(1,2,-1))
175 c = interactive(f, tup=(1,2,-1))
176 for min, max, step in [ (0,2,1), (1,10,2), (1,100,2), (-5,5,4), (-100,-20,4) ]:
176 for min, max, step in [ (0,2,1), (1,10,2), (1,100,2), (-5,5,4), (-100,-20,4) ]:
177 c = interactive(f, tup=(min, max, step), lis=[min, max, step])
177 c = interactive(f, tup=(min, max, step), lis=[min, max, step])
178 nt.assert_equal(len(c.children), 3)
178 nt.assert_equal(len(c.children), 3)
179 d = dict(
179 d = dict(
180 cls=widgets.IntSlider,
180 cls=widgets.IntSlider,
181 min=min,
181 min=min,
182 max=max,
182 max=max,
183 step=step,
183 step=step,
184 readout=True,
184 readout=True,
185 )
185 )
186 check_widgets(c, tup=d, lis=d)
186 check_widgets(c, tup=d, lis=d)
187
187
188 def test_list_tuple_2_float():
188 def test_list_tuple_2_float():
189 with nt.assert_raises(ValueError):
189 with nt.assert_raises(ValueError):
190 c = interactive(f, tup=(1.0,1.0))
190 c = interactive(f, tup=(1.0,1.0))
191 with nt.assert_raises(ValueError):
191 with nt.assert_raises(ValueError):
192 c = interactive(f, tup=(0.5,-0.5))
192 c = interactive(f, tup=(0.5,-0.5))
193 for min, max in [ (0.5, 1.5), (1.1,10.2), (1,2.2), (-5.,5), (-20,-19.) ]:
193 for min, max in [ (0.5, 1.5), (1.1,10.2), (1,2.2), (-5.,5), (-20,-19.) ]:
194 c = interactive(f, tup=(min, max), lis=[min, max])
194 c = interactive(f, tup=(min, max), lis=[min, max])
195 nt.assert_equal(len(c.children), 3)
195 nt.assert_equal(len(c.children), 3)
196 d = dict(
196 d = dict(
197 cls=widgets.FloatSlider,
197 cls=widgets.FloatSlider,
198 min=min,
198 min=min,
199 max=max,
199 max=max,
200 step=.1,
200 step=.1,
201 readout=True,
201 readout=True,
202 )
202 )
203 check_widgets(c, tup=d, lis=d)
203 check_widgets(c, tup=d, lis=d)
204
204
205 def test_list_tuple_3_float():
205 def test_list_tuple_3_float():
206 with nt.assert_raises(ValueError):
206 with nt.assert_raises(ValueError):
207 c = interactive(f, tup=(1,2,0.0))
207 c = interactive(f, tup=(1,2,0.0))
208 with nt.assert_raises(ValueError):
208 with nt.assert_raises(ValueError):
209 c = interactive(f, tup=(-1,-2,1.))
209 c = interactive(f, tup=(-1,-2,1.))
210 with nt.assert_raises(ValueError):
210 with nt.assert_raises(ValueError):
211 c = interactive(f, tup=(1,2.,-1.))
211 c = interactive(f, tup=(1,2.,-1.))
212 for min, max, step in [ (0.,2,1), (1,10.,2), (1,100,2.), (-5.,5.,4), (-100,-20.,4.) ]:
212 for min, max, step in [ (0.,2,1), (1,10.,2), (1,100,2.), (-5.,5.,4), (-100,-20.,4.) ]:
213 c = interactive(f, tup=(min, max, step), lis=[min, max, step])
213 c = interactive(f, tup=(min, max, step), lis=[min, max, step])
214 nt.assert_equal(len(c.children), 3)
214 nt.assert_equal(len(c.children), 3)
215 d = dict(
215 d = dict(
216 cls=widgets.FloatSlider,
216 cls=widgets.FloatSlider,
217 min=min,
217 min=min,
218 max=max,
218 max=max,
219 step=step,
219 step=step,
220 readout=True,
220 readout=True,
221 )
221 )
222 check_widgets(c, tup=d, lis=d)
222 check_widgets(c, tup=d, lis=d)
223
223
224 def test_list_tuple_str():
224 def test_list_tuple_str():
225 values = ['hello', 'there', 'guy']
225 values = ['hello', 'there', 'guy']
226 first = values[0]
226 first = values[0]
227 c = interactive(f, tup=tuple(values), lis=list(values))
227 c = interactive(f, tup=tuple(values), lis=list(values))
228 nt.assert_equal(len(c.children), 3)
228 nt.assert_equal(len(c.children), 3)
229 d = dict(
229 d = dict(
230 cls=widgets.Dropdown,
230 cls=widgets.Dropdown,
231 value=first,
231 value=first,
232 values=values
232 options=values
233 )
233 )
234 check_widgets(c, tup=d, lis=d)
234 check_widgets(c, tup=d, lis=d)
235
235
236 def test_list_tuple_invalid():
236 def test_list_tuple_invalid():
237 for bad in [
237 for bad in [
238 (),
238 (),
239 (5, 'hi'),
239 (5, 'hi'),
240 ('hi', 5),
240 ('hi', 5),
241 ({},),
241 ({},),
242 (None,),
242 (None,),
243 ]:
243 ]:
244 with nt.assert_raises(ValueError):
244 with nt.assert_raises(ValueError):
245 print(bad) # because there is no custom message in assert_raises
245 print(bad) # because there is no custom message in assert_raises
246 c = interactive(f, tup=bad)
246 c = interactive(f, tup=bad)
247
247
248 def test_defaults():
248 def test_defaults():
249 @annotate(n=10)
249 @annotate(n=10)
250 def f(n, f=4.5, g=1):
250 def f(n, f=4.5, g=1):
251 pass
251 pass
252
252
253 c = interactive(f)
253 c = interactive(f)
254 check_widgets(c,
254 check_widgets(c,
255 n=dict(
255 n=dict(
256 cls=widgets.IntSlider,
256 cls=widgets.IntSlider,
257 value=10,
257 value=10,
258 ),
258 ),
259 f=dict(
259 f=dict(
260 cls=widgets.FloatSlider,
260 cls=widgets.FloatSlider,
261 value=4.5,
261 value=4.5,
262 ),
262 ),
263 g=dict(
263 g=dict(
264 cls=widgets.IntSlider,
264 cls=widgets.IntSlider,
265 value=1,
265 value=1,
266 ),
266 ),
267 )
267 )
268
268
269 def test_default_values():
269 def test_default_values():
270 @annotate(n=10, f=(0, 10.), g=5, h={'a': 1, 'b': 2}, j=['hi', 'there'])
270 @annotate(n=10, f=(0, 10.), g=5, h={'a': 1, 'b': 2}, j=['hi', 'there'])
271 def f(n, f=4.5, g=1, h=2, j='there'):
271 def f(n, f=4.5, g=1, h=2, j='there'):
272 pass
272 pass
273
273
274 c = interactive(f)
274 c = interactive(f)
275 check_widgets(c,
275 check_widgets(c,
276 n=dict(
276 n=dict(
277 cls=widgets.IntSlider,
277 cls=widgets.IntSlider,
278 value=10,
278 value=10,
279 ),
279 ),
280 f=dict(
280 f=dict(
281 cls=widgets.FloatSlider,
281 cls=widgets.FloatSlider,
282 value=4.5,
282 value=4.5,
283 ),
283 ),
284 g=dict(
284 g=dict(
285 cls=widgets.IntSlider,
285 cls=widgets.IntSlider,
286 value=5,
286 value=5,
287 ),
287 ),
288 h=dict(
288 h=dict(
289 cls=widgets.Dropdown,
289 cls=widgets.Dropdown,
290 values={'a': 1, 'b': 2},
290 options={'a': 1, 'b': 2},
291 value=2
291 value=2
292 ),
292 ),
293 j=dict(
293 j=dict(
294 cls=widgets.Dropdown,
294 cls=widgets.Dropdown,
295 values=['hi', 'there'],
295 options=['hi', 'there'],
296 value='there'
296 value='there'
297 ),
297 ),
298 )
298 )
299
299
300 def test_default_out_of_bounds():
300 def test_default_out_of_bounds():
301 @annotate(f=(0, 10.), h={'a': 1}, j=['hi', 'there'])
301 @annotate(f=(0, 10.), h={'a': 1}, j=['hi', 'there'])
302 def f(f='hi', h=5, j='other'):
302 def f(f='hi', h=5, j='other'):
303 pass
303 pass
304
304
305 c = interactive(f)
305 c = interactive(f)
306 check_widgets(c,
306 check_widgets(c,
307 f=dict(
307 f=dict(
308 cls=widgets.FloatSlider,
308 cls=widgets.FloatSlider,
309 value=5.,
309 value=5.,
310 ),
310 ),
311 h=dict(
311 h=dict(
312 cls=widgets.Dropdown,
312 cls=widgets.Dropdown,
313 values={'a': 1},
313 options={'a': 1},
314 value=1,
314 value=1,
315 ),
315 ),
316 j=dict(
316 j=dict(
317 cls=widgets.Dropdown,
317 cls=widgets.Dropdown,
318 values=['hi', 'there'],
318 options=['hi', 'there'],
319 value='hi',
319 value='hi',
320 ),
320 ),
321 )
321 )
322
322
323 def test_annotations():
323 def test_annotations():
324 @annotate(n=10, f=widgets.FloatText())
324 @annotate(n=10, f=widgets.FloatText())
325 def f(n, f):
325 def f(n, f):
326 pass
326 pass
327
327
328 c = interactive(f)
328 c = interactive(f)
329 check_widgets(c,
329 check_widgets(c,
330 n=dict(
330 n=dict(
331 cls=widgets.IntSlider,
331 cls=widgets.IntSlider,
332 value=10,
332 value=10,
333 ),
333 ),
334 f=dict(
334 f=dict(
335 cls=widgets.FloatText,
335 cls=widgets.FloatText,
336 ),
336 ),
337 )
337 )
338
338
339 def test_priority():
339 def test_priority():
340 @annotate(annotate='annotate', kwarg='annotate')
340 @annotate(annotate='annotate', kwarg='annotate')
341 def f(kwarg='default', annotate='default', default='default'):
341 def f(kwarg='default', annotate='default', default='default'):
342 pass
342 pass
343
343
344 c = interactive(f, kwarg='kwarg')
344 c = interactive(f, kwarg='kwarg')
345 check_widgets(c,
345 check_widgets(c,
346 kwarg=dict(
346 kwarg=dict(
347 cls=widgets.Text,
347 cls=widgets.Text,
348 value='kwarg',
348 value='kwarg',
349 ),
349 ),
350 annotate=dict(
350 annotate=dict(
351 cls=widgets.Text,
351 cls=widgets.Text,
352 value='annotate',
352 value='annotate',
353 ),
353 ),
354 )
354 )
355
355
356 @nt.with_setup(clear_display)
356 @nt.with_setup(clear_display)
357 def test_decorator_kwarg():
357 def test_decorator_kwarg():
358 with tt.monkeypatch(interaction, 'display', record_display):
358 with tt.monkeypatch(interaction, 'display', record_display):
359 @interact(a=5)
359 @interact(a=5)
360 def foo(a):
360 def foo(a):
361 pass
361 pass
362 nt.assert_equal(len(displayed), 1)
362 nt.assert_equal(len(displayed), 1)
363 w = displayed[0].children[0]
363 w = displayed[0].children[0]
364 check_widget(w,
364 check_widget(w,
365 cls=widgets.IntSlider,
365 cls=widgets.IntSlider,
366 value=5,
366 value=5,
367 )
367 )
368
368
369 @nt.with_setup(clear_display)
369 @nt.with_setup(clear_display)
370 def test_interact_instancemethod():
370 def test_interact_instancemethod():
371 class Foo(object):
371 class Foo(object):
372 def show(self, x):
372 def show(self, x):
373 print(x)
373 print(x)
374
374
375 f = Foo()
375 f = Foo()
376
376
377 with tt.monkeypatch(interaction, 'display', record_display):
377 with tt.monkeypatch(interaction, 'display', record_display):
378 g = interact(f.show, x=(1,10))
378 g = interact(f.show, x=(1,10))
379 nt.assert_equal(len(displayed), 1)
379 nt.assert_equal(len(displayed), 1)
380 w = displayed[0].children[0]
380 w = displayed[0].children[0]
381 check_widget(w,
381 check_widget(w,
382 cls=widgets.IntSlider,
382 cls=widgets.IntSlider,
383 value=5,
383 value=5,
384 )
384 )
385
385
386 @nt.with_setup(clear_display)
386 @nt.with_setup(clear_display)
387 def test_decorator_no_call():
387 def test_decorator_no_call():
388 with tt.monkeypatch(interaction, 'display', record_display):
388 with tt.monkeypatch(interaction, 'display', record_display):
389 @interact
389 @interact
390 def foo(a='default'):
390 def foo(a='default'):
391 pass
391 pass
392 nt.assert_equal(len(displayed), 1)
392 nt.assert_equal(len(displayed), 1)
393 w = displayed[0].children[0]
393 w = displayed[0].children[0]
394 check_widget(w,
394 check_widget(w,
395 cls=widgets.Text,
395 cls=widgets.Text,
396 value='default',
396 value='default',
397 )
397 )
398
398
399 @nt.with_setup(clear_display)
399 @nt.with_setup(clear_display)
400 def test_call_interact():
400 def test_call_interact():
401 def foo(a='default'):
401 def foo(a='default'):
402 pass
402 pass
403 with tt.monkeypatch(interaction, 'display', record_display):
403 with tt.monkeypatch(interaction, 'display', record_display):
404 ifoo = interact(foo)
404 ifoo = interact(foo)
405 nt.assert_equal(len(displayed), 1)
405 nt.assert_equal(len(displayed), 1)
406 w = displayed[0].children[0]
406 w = displayed[0].children[0]
407 check_widget(w,
407 check_widget(w,
408 cls=widgets.Text,
408 cls=widgets.Text,
409 value='default',
409 value='default',
410 )
410 )
411
411
412 @nt.with_setup(clear_display)
412 @nt.with_setup(clear_display)
413 def test_call_interact_kwargs():
413 def test_call_interact_kwargs():
414 def foo(a='default'):
414 def foo(a='default'):
415 pass
415 pass
416 with tt.monkeypatch(interaction, 'display', record_display):
416 with tt.monkeypatch(interaction, 'display', record_display):
417 ifoo = interact(foo, a=10)
417 ifoo = interact(foo, a=10)
418 nt.assert_equal(len(displayed), 1)
418 nt.assert_equal(len(displayed), 1)
419 w = displayed[0].children[0]
419 w = displayed[0].children[0]
420 check_widget(w,
420 check_widget(w,
421 cls=widgets.IntSlider,
421 cls=widgets.IntSlider,
422 value=10,
422 value=10,
423 )
423 )
424
424
425 @nt.with_setup(clear_display)
425 @nt.with_setup(clear_display)
426 def test_call_decorated_on_trait_change():
426 def test_call_decorated_on_trait_change():
427 """test calling @interact decorated functions"""
427 """test calling @interact decorated functions"""
428 d = {}
428 d = {}
429 with tt.monkeypatch(interaction, 'display', record_display):
429 with tt.monkeypatch(interaction, 'display', record_display):
430 @interact
430 @interact
431 def foo(a='default'):
431 def foo(a='default'):
432 d['a'] = a
432 d['a'] = a
433 return a
433 return a
434 nt.assert_equal(len(displayed), 1)
434 nt.assert_equal(len(displayed), 1)
435 w = displayed[0].children[0]
435 w = displayed[0].children[0]
436 check_widget(w,
436 check_widget(w,
437 cls=widgets.Text,
437 cls=widgets.Text,
438 value='default',
438 value='default',
439 )
439 )
440 # test calling the function directly
440 # test calling the function directly
441 a = foo('hello')
441 a = foo('hello')
442 nt.assert_equal(a, 'hello')
442 nt.assert_equal(a, 'hello')
443 nt.assert_equal(d['a'], 'hello')
443 nt.assert_equal(d['a'], 'hello')
444
444
445 # test that setting trait values calls the function
445 # test that setting trait values calls the function
446 w.value = 'called'
446 w.value = 'called'
447 nt.assert_equal(d['a'], 'called')
447 nt.assert_equal(d['a'], 'called')
448
448
449 @nt.with_setup(clear_display)
449 @nt.with_setup(clear_display)
450 def test_call_decorated_kwargs_on_trait_change():
450 def test_call_decorated_kwargs_on_trait_change():
451 """test calling @interact(foo=bar) decorated functions"""
451 """test calling @interact(foo=bar) decorated functions"""
452 d = {}
452 d = {}
453 with tt.monkeypatch(interaction, 'display', record_display):
453 with tt.monkeypatch(interaction, 'display', record_display):
454 @interact(a='kwarg')
454 @interact(a='kwarg')
455 def foo(a='default'):
455 def foo(a='default'):
456 d['a'] = a
456 d['a'] = a
457 return a
457 return a
458 nt.assert_equal(len(displayed), 1)
458 nt.assert_equal(len(displayed), 1)
459 w = displayed[0].children[0]
459 w = displayed[0].children[0]
460 check_widget(w,
460 check_widget(w,
461 cls=widgets.Text,
461 cls=widgets.Text,
462 value='kwarg',
462 value='kwarg',
463 )
463 )
464 # test calling the function directly
464 # test calling the function directly
465 a = foo('hello')
465 a = foo('hello')
466 nt.assert_equal(a, 'hello')
466 nt.assert_equal(a, 'hello')
467 nt.assert_equal(d['a'], 'hello')
467 nt.assert_equal(d['a'], 'hello')
468
468
469 # test that setting trait values calls the function
469 # test that setting trait values calls the function
470 w.value = 'called'
470 w.value = 'called'
471 nt.assert_equal(d['a'], 'called')
471 nt.assert_equal(d['a'], 'called')
472
472
473 def test_fixed():
473 def test_fixed():
474 c = interactive(f, a=widgets.fixed(5), b='text')
474 c = interactive(f, a=widgets.fixed(5), b='text')
475 nt.assert_equal(len(c.children), 2)
475 nt.assert_equal(len(c.children), 2)
476 w = c.children[0]
476 w = c.children[0]
477 check_widget(w,
477 check_widget(w,
478 cls=widgets.Text,
478 cls=widgets.Text,
479 value='text',
479 value='text',
480 description='b',
480 description='b',
481 )
481 )
482
482
483 def test_default_description():
483 def test_default_description():
484 c = interactive(f, b='text')
484 c = interactive(f, b='text')
485 w = c.children[0]
485 w = c.children[0]
486 check_widget(w,
486 check_widget(w,
487 cls=widgets.Text,
487 cls=widgets.Text,
488 value='text',
488 value='text',
489 description='b',
489 description='b',
490 )
490 )
491
491
492 def test_custom_description():
492 def test_custom_description():
493 d = {}
493 d = {}
494 def record_kwargs(**kwargs):
494 def record_kwargs(**kwargs):
495 d.clear()
495 d.clear()
496 d.update(kwargs)
496 d.update(kwargs)
497
497
498 c = interactive(record_kwargs, b=widgets.Text(value='text', description='foo'))
498 c = interactive(record_kwargs, b=widgets.Text(value='text', description='foo'))
499 w = c.children[0]
499 w = c.children[0]
500 check_widget(w,
500 check_widget(w,
501 cls=widgets.Text,
501 cls=widgets.Text,
502 value='text',
502 value='text',
503 description='foo',
503 description='foo',
504 )
504 )
505 w.value = 'different text'
505 w.value = 'different text'
506 nt.assert_equal(d, {'b': 'different text'})
506 nt.assert_equal(d, {'b': 'different text'})
507
507
508 def test_interact_manual_button():
508 def test_interact_manual_button():
509 c = interactive(f, __manual=True)
509 c = interactive(f, __manual=True)
510 w = c.children[0]
510 w = c.children[0]
511 check_widget(w, cls=widgets.Button)
511 check_widget(w, cls=widgets.Button)
512
512
513 def test_interact_manual_nocall():
513 def test_interact_manual_nocall():
514 callcount = 0
514 callcount = 0
515 def calltest(testarg):
515 def calltest(testarg):
516 callcount += 1
516 callcount += 1
517 c = interactive(calltest, testarg=5, __manual=True)
517 c = interactive(calltest, testarg=5, __manual=True)
518 c.children[0].value = 10
518 c.children[0].value = 10
519 nt.assert_equal(callcount, 0)
519 nt.assert_equal(callcount, 0)
520
520
521 def test_int_range_logic():
521 def test_int_range_logic():
522 irsw = widgets.IntRangeSlider
522 irsw = widgets.IntRangeSlider
523 w = irsw(value=(2, 4), min=0, max=6)
523 w = irsw(value=(2, 4), min=0, max=6)
524 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
524 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
525 w.value = (4, 2)
525 w.value = (4, 2)
526 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
526 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
527 w.value = (-1, 7)
527 w.value = (-1, 7)
528 check_widget(w, cls=irsw, value=(0, 6), min=0, max=6)
528 check_widget(w, cls=irsw, value=(0, 6), min=0, max=6)
529 w.min = 3
529 w.min = 3
530 check_widget(w, cls=irsw, value=(3, 6), min=3, max=6)
530 check_widget(w, cls=irsw, value=(3, 6), min=3, max=6)
531 w.max = 3
531 w.max = 3
532 check_widget(w, cls=irsw, value=(3, 3), min=3, max=3)
532 check_widget(w, cls=irsw, value=(3, 3), min=3, max=3)
533
533
534 w.min = 0
534 w.min = 0
535 w.max = 6
535 w.max = 6
536 w.lower = 2
536 w.lower = 2
537 w.upper = 4
537 w.upper = 4
538 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
538 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
539 w.value = (0, 1) #lower non-overlapping range
539 w.value = (0, 1) #lower non-overlapping range
540 check_widget(w, cls=irsw, value=(0, 1), min=0, max=6)
540 check_widget(w, cls=irsw, value=(0, 1), min=0, max=6)
541 w.value = (5, 6) #upper non-overlapping range
541 w.value = (5, 6) #upper non-overlapping range
542 check_widget(w, cls=irsw, value=(5, 6), min=0, max=6)
542 check_widget(w, cls=irsw, value=(5, 6), min=0, max=6)
543 w.value = (-1, 4) #semi out-of-range
543 w.value = (-1, 4) #semi out-of-range
544 check_widget(w, cls=irsw, value=(0, 4), min=0, max=6)
544 check_widget(w, cls=irsw, value=(0, 4), min=0, max=6)
545 w.lower = 2
545 w.lower = 2
546 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
546 check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
547 w.value = (-2, -1) #wholly out of range
547 w.value = (-2, -1) #wholly out of range
548 check_widget(w, cls=irsw, value=(0, 0), min=0, max=6)
548 check_widget(w, cls=irsw, value=(0, 0), min=0, max=6)
549 w.value = (7, 8)
549 w.value = (7, 8)
550 check_widget(w, cls=irsw, value=(6, 6), min=0, max=6)
550 check_widget(w, cls=irsw, value=(6, 6), min=0, max=6)
551
551
552 with nt.assert_raises(ValueError):
552 with nt.assert_raises(ValueError):
553 w.min = 7
553 w.min = 7
554 with nt.assert_raises(ValueError):
554 with nt.assert_raises(ValueError):
555 w.max = -1
555 w.max = -1
556 with nt.assert_raises(ValueError):
556 with nt.assert_raises(ValueError):
557 w.lower = 5
557 w.lower = 5
558 with nt.assert_raises(ValueError):
558 with nt.assert_raises(ValueError):
559 w.upper = 1
559 w.upper = 1
560
560
561 w = irsw(min=2, max=3)
561 w = irsw(min=2, max=3)
562 check_widget(w, min=2, max=3)
562 check_widget(w, min=2, max=3)
563 w = irsw(min=100, max=200)
563 w = irsw(min=100, max=200)
564 check_widget(w, lower=125, upper=175, value=(125, 175))
564 check_widget(w, lower=125, upper=175, value=(125, 175))
565
565
566 with nt.assert_raises(ValueError):
566 with nt.assert_raises(ValueError):
567 irsw(value=(2, 4), lower=3)
567 irsw(value=(2, 4), lower=3)
568 with nt.assert_raises(ValueError):
568 with nt.assert_raises(ValueError):
569 irsw(value=(2, 4), upper=3)
569 irsw(value=(2, 4), upper=3)
570 with nt.assert_raises(ValueError):
570 with nt.assert_raises(ValueError):
571 irsw(value=(2, 4), lower=3, upper=3)
571 irsw(value=(2, 4), lower=3, upper=3)
572 with nt.assert_raises(ValueError):
572 with nt.assert_raises(ValueError):
573 irsw(min=2, max=1)
573 irsw(min=2, max=1)
574 with nt.assert_raises(ValueError):
574 with nt.assert_raises(ValueError):
575 irsw(lower=5)
575 irsw(lower=5)
576 with nt.assert_raises(ValueError):
576 with nt.assert_raises(ValueError):
577 irsw(upper=5)
577 irsw(upper=5)
578
578
579
579
580 def test_float_range_logic():
580 def test_float_range_logic():
581 frsw = widgets.FloatRangeSlider
581 frsw = widgets.FloatRangeSlider
582 w = frsw(value=(.2, .4), min=0., max=.6)
582 w = frsw(value=(.2, .4), min=0., max=.6)
583 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
583 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
584 w.value = (.4, .2)
584 w.value = (.4, .2)
585 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
585 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
586 w.value = (-.1, .7)
586 w.value = (-.1, .7)
587 check_widget(w, cls=frsw, value=(0., .6), min=0., max=.6)
587 check_widget(w, cls=frsw, value=(0., .6), min=0., max=.6)
588 w.min = .3
588 w.min = .3
589 check_widget(w, cls=frsw, value=(.3, .6), min=.3, max=.6)
589 check_widget(w, cls=frsw, value=(.3, .6), min=.3, max=.6)
590 w.max = .3
590 w.max = .3
591 check_widget(w, cls=frsw, value=(.3, .3), min=.3, max=.3)
591 check_widget(w, cls=frsw, value=(.3, .3), min=.3, max=.3)
592
592
593 w.min = 0.
593 w.min = 0.
594 w.max = .6
594 w.max = .6
595 w.lower = .2
595 w.lower = .2
596 w.upper = .4
596 w.upper = .4
597 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
597 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
598 w.value = (0., .1) #lower non-overlapping range
598 w.value = (0., .1) #lower non-overlapping range
599 check_widget(w, cls=frsw, value=(0., .1), min=0., max=.6)
599 check_widget(w, cls=frsw, value=(0., .1), min=0., max=.6)
600 w.value = (.5, .6) #upper non-overlapping range
600 w.value = (.5, .6) #upper non-overlapping range
601 check_widget(w, cls=frsw, value=(.5, .6), min=0., max=.6)
601 check_widget(w, cls=frsw, value=(.5, .6), min=0., max=.6)
602 w.value = (-.1, .4) #semi out-of-range
602 w.value = (-.1, .4) #semi out-of-range
603 check_widget(w, cls=frsw, value=(0., .4), min=0., max=.6)
603 check_widget(w, cls=frsw, value=(0., .4), min=0., max=.6)
604 w.lower = .2
604 w.lower = .2
605 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
605 check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
606 w.value = (-.2, -.1) #wholly out of range
606 w.value = (-.2, -.1) #wholly out of range
607 check_widget(w, cls=frsw, value=(0., 0.), min=0., max=.6)
607 check_widget(w, cls=frsw, value=(0., 0.), min=0., max=.6)
608 w.value = (.7, .8)
608 w.value = (.7, .8)
609 check_widget(w, cls=frsw, value=(.6, .6), min=.0, max=.6)
609 check_widget(w, cls=frsw, value=(.6, .6), min=.0, max=.6)
610
610
611 with nt.assert_raises(ValueError):
611 with nt.assert_raises(ValueError):
612 w.min = .7
612 w.min = .7
613 with nt.assert_raises(ValueError):
613 with nt.assert_raises(ValueError):
614 w.max = -.1
614 w.max = -.1
615 with nt.assert_raises(ValueError):
615 with nt.assert_raises(ValueError):
616 w.lower = .5
616 w.lower = .5
617 with nt.assert_raises(ValueError):
617 with nt.assert_raises(ValueError):
618 w.upper = .1
618 w.upper = .1
619
619
620 w = frsw(min=2, max=3)
620 w = frsw(min=2, max=3)
621 check_widget(w, min=2, max=3)
621 check_widget(w, min=2, max=3)
622 w = frsw(min=1., max=2.)
622 w = frsw(min=1., max=2.)
623 check_widget(w, lower=1.25, upper=1.75, value=(1.25, 1.75))
623 check_widget(w, lower=1.25, upper=1.75, value=(1.25, 1.75))
624
624
625 with nt.assert_raises(ValueError):
625 with nt.assert_raises(ValueError):
626 frsw(value=(2, 4), lower=3)
626 frsw(value=(2, 4), lower=3)
627 with nt.assert_raises(ValueError):
627 with nt.assert_raises(ValueError):
628 frsw(value=(2, 4), upper=3)
628 frsw(value=(2, 4), upper=3)
629 with nt.assert_raises(ValueError):
629 with nt.assert_raises(ValueError):
630 frsw(value=(2, 4), lower=3, upper=3)
630 frsw(value=(2, 4), lower=3, upper=3)
631 with nt.assert_raises(ValueError):
631 with nt.assert_raises(ValueError):
632 frsw(min=.2, max=.1)
632 frsw(min=.2, max=.1)
633 with nt.assert_raises(ValueError):
633 with nt.assert_raises(ValueError):
634 frsw(lower=5)
634 frsw(lower=5)
635 with nt.assert_raises(ValueError):
635 with nt.assert_raises(ValueError):
636 frsw(upper=5)
636 frsw(upper=5)
637
638
639 def test_multiple_selection():
640 smw = widgets.SelectMultiple
641
642 # degenerate multiple select
643 w = smw()
644 check_widget(w, value=tuple(), options=None, selected_labels=tuple())
645
646 # don't accept random other value when no options
647 with nt.assert_raises(KeyError):
648 w.value = (2,)
649 check_widget(w, value=tuple(), selected_labels=tuple())
650
651 # basic multiple select
652 w = smw(options=[(1, 1)], value=[1])
653 check_widget(w, cls=smw, value=(1,), options=[(1, 1)])
654
655 # don't accept random other value
656 with nt.assert_raises(KeyError):
657 w.value = w.value + (2,)
658 check_widget(w, value=(1,), selected_labels=(1,))
659
660 # change options
661 w.options = w.options + [(2, 2)]
662 check_widget(w, options=[(1, 1), (2,2)])
663
664 # change value
665 w.value = w.value + (2,)
666 check_widget(w, value=(1, 2), selected_labels=(1, 2))
667
668 # change value name
669 w.selected_labels = (1,)
670 check_widget(w, value=(1,))
671
672 # don't accept random other names when no options
673 with nt.assert_raises(KeyError):
674 w.selected_labels = (3,)
675 check_widget(w, value=(1,))
676
677 # don't accept selected_label (from superclass)
678 with nt.assert_raises(AttributeError):
679 w.selected_label = 3
680
681 # don't return selected_label (from superclass)
682 with nt.assert_raises(AttributeError):
683 print(w.selected_label)
684
685 # dict style
686 w.options = {1: 1}
687 check_widget(w, options={1: 1})
688
689 # updating
690 with nt.assert_raises(KeyError):
691 w.value = (2,)
692 check_widget(w, options={1: 1})
@@ -1,172 +1,238 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, register
20 from .widget import DOMWidget, register
21 from IPython.utils.traitlets import (
21 from IPython.utils.traitlets import (
22 Unicode, Bool, Any, Dict, TraitError, CaselessStrEnum, Tuple
22 Unicode, Bool, Any, Dict, TraitError, CaselessStrEnum, Tuple
23 )
23 )
24 from IPython.utils.py3compat import unicode_type
24 from IPython.utils.py3compat import unicode_type
25 from IPython.utils.warn import DeprecatedClass
25 from IPython.utils.warn import DeprecatedClass
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # SelectionWidget
28 # SelectionWidget
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 class _Selection(DOMWidget):
30 class _Selection(DOMWidget):
31 """Base class for Selection widgets
31 """Base class for Selection widgets
32
32
33 ``values`` can be specified as a list or dict. If given as a list,
33 ``options`` can be specified as a list or dict. If given as a list,
34 it will be transformed to a dict of the form ``{str(value):value}``.
34 it will be transformed to a dict of the form ``{str(value):value}``.
35 """
35 """
36
36
37 value = Any(help="Selected value")
37 value = Any(help="Selected value")
38 value_name = Unicode(help="The name of the selected value", sync=True)
38 selected_label = Unicode(help="The label of the selected value", sync=True)
39 values = Any(help="""List of (key, value) tuples or dict of values that the
39 options = Any(help="""List of (key, value) tuples or dict of values that the
40 user can select.
40 user can select.
41
41
42 The keys of this list are the strings that will be displayed in the UI,
42 The keys of this list are the strings that will be displayed in the UI,
43 representing the actual Python choices.
43 representing the actual Python choices.
44
44
45 The keys of this list are also available as _value_names.
45 The keys of this list are also available as _options_labels.
46 """)
46 """)
47
47
48 _values_dict = Dict()
48 _options_dict = Dict()
49 _value_names = Tuple(sync=True)
49 _options_labels = Tuple(sync=True)
50 _value_values = Tuple()
50 _options_values = Tuple()
51
51
52 disabled = Bool(False, help="Enable or disable user changes", sync=True)
52 disabled = Bool(False, help="Enable or disable user changes", sync=True)
53 description = Unicode(help="Description of the value this widget represents", sync=True)
53 description = Unicode(help="Description of the value this widget represents", sync=True)
54
54
55 def __init__(self, *args, **kwargs):
55 def __init__(self, *args, **kwargs):
56 self.value_lock = Lock()
56 self.value_lock = Lock()
57 self.values_lock = Lock()
57 self.options_lock = Lock()
58 self.on_trait_change(self._values_readonly_changed, ['_values_dict', '_value_names', '_value_values', '_values'])
58 self.on_trait_change(self._options_readonly_changed, ['_options_dict', '_options_labels', '_options_values', '_options'])
59 if 'values' in kwargs:
59 if 'options' in kwargs:
60 self.values = kwargs.pop('values')
60 self.options = kwargs.pop('options')
61 DOMWidget.__init__(self, *args, **kwargs)
61 DOMWidget.__init__(self, *args, **kwargs)
62 self._value_in_values()
62 self._value_in_options()
63
63
64 def _make_values(self, x):
64 def _make_options(self, x):
65 # If x is a dict, convert it to list format.
65 # If x is a dict, convert it to list format.
66 if isinstance(x, (OrderedDict, dict)):
66 if isinstance(x, (OrderedDict, dict)):
67 return [(k, v) for k, v in x.items()]
67 return [(k, v) for k, v in x.items()]
68
68
69 # Make sure x is a list or tuple.
69 # Make sure x is a list or tuple.
70 if not isinstance(x, (list, tuple)):
70 if not isinstance(x, (list, tuple)):
71 raise ValueError('x')
71 raise ValueError('x')
72
72
73 # If x is an ordinary list, use the values as names.
73 # If x is an ordinary list, use the option values as names.
74 for y in x:
74 for y in x:
75 if not isinstance(y, (list, tuple)) or len(y) < 2:
75 if not isinstance(y, (list, tuple)) or len(y) < 2:
76 return [(i, i) for i in x]
76 return [(i, i) for i in x]
77
77
78 # Value is already in the correct format.
78 # Value is already in the correct format.
79 return x
79 return x
80
80
81 def _values_changed(self, name, old, new):
81 def _options_changed(self, name, old, new):
82 """Handles when the values tuple has been changed.
82 """Handles when the options tuple has been changed.
83
83
84 Setting values implies setting value names from the keys of the dict.
84 Setting options implies setting option labels from the keys of the dict.
85 """
85 """
86 if self.values_lock.acquire(False):
86 if self.options_lock.acquire(False):
87 try:
87 try:
88 self.values = new
88 self.options = new
89
89
90 values = self._make_values(new)
90 options = self._make_options(new)
91 self._values_dict = {i[0]: i[1] for i in values}
91 self._options_dict = {i[0]: i[1] for i in options}
92 self._value_names = [i[0] for i in values]
92 self._options_labels = [i[0] for i in options]
93 self._value_values = [i[1] for i in values]
93 self._options_values = [i[1] for i in options]
94 self._value_in_values()
94 self._value_in_options()
95 finally:
95 finally:
96 self.values_lock.release()
96 self.options_lock.release()
97
97
98 def _value_in_values(self):
98 def _value_in_options(self):
99 # ensure that the chosen value is one of the choices
99 # ensure that the chosen value is one of the choices
100 if self._value_values:
101 if self.value not in self._value_values:
102 self.value = next(iter(self._value_values))
103
100
104 def _values_readonly_changed(self, name, old, new):
101 if self._options_values:
105 if not self.values_lock.locked():
102 if self.value not in self._options_values:
106 raise TraitError("`.%s` is a read-only trait. Use the `.values` tuple instead." % name)
103 self.value = next(iter(self._options_values))
107
104
105 def _options_readonly_changed(self, name, old, new):
106 if not self.options_lock.locked():
107 raise TraitError("`.%s` is a read-only trait. Use the `.options` tuple instead." % name)
108 def _value_changed(self, name, old, new):
108 def _value_changed(self, name, old, new):
109 """Called when value has been changed"""
109 """Called when value has been changed"""
110 if self.value_lock.acquire(False):
110 if self.value_lock.acquire(False):
111 try:
111 try:
112 # Reverse dictionary lookup for the value name
112 # Reverse dictionary lookup for the value name
113 for k,v in self._values_dict.items():
113 for k, v in self._options_dict.items():
114 if new == v:
114 if new == v:
115 # set the selected value name
115 # set the selected value name
116 self.value_name = k
116 self.selected_label = k
117 return
117 return
118 # undo the change, and raise KeyError
118 # undo the change, and raise KeyError
119 self.value = old
119 self.value = old
120 raise KeyError(new)
120 raise KeyError(new)
121 finally:
121 finally:
122 self.value_lock.release()
122 self.value_lock.release()
123
123
124 def _value_name_changed(self, name, old, new):
124 def _selected_label_changed(self, name, old, new):
125 """Called when the value name has been changed (typically by the frontend)."""
125 """Called when the value name has been changed (typically by the frontend)."""
126 if self.value_lock.acquire(False):
126 if self.value_lock.acquire(False):
127 try:
127 try:
128 self.value = self._values_dict[new]
128 self.value = self._options_dict[new]
129 finally:
130 self.value_lock.release()
131
132
133 class _MultipleSelection(_Selection):
134 """Base class for MultipleSelection widgets.
135
136 As with ``_Selection``, ``options`` can be specified as a list or dict. If
137 given as a list, it will be transformed to a dict of the form
138 ``{str(value): value}``.
139
140 Despite their names, ``value`` (and ``selected_label``) will be tuples, even
141 if only a single option is selected.
142 """
143
144 value = Tuple(help="Selected values")
145 selected_labels = Tuple(help="The labels of the selected options",
146 sync=True)
147
148 @property
149 def selected_label(self):
150 raise AttributeError(
151 "Does not support selected_label, use selected_labels")
152
153 def _value_in_options(self):
154 # ensure that the chosen value is one of the choices
155 if self.options:
156 old_value = self.value or []
157 new_value = []
158 for value in old_value:
159 if value in self._options_dict.values():
160 new_value.append(value)
161 if new_value:
162 self.value = new_value
163 else:
164 self.value = [next(iter(self._options_dict.values()))]
165
166 def _value_changed(self, name, old, new):
167 """Called when value has been changed"""
168 if self.value_lock.acquire(False):
169 try:
170 self.selected_labels = [
171 self._options_labels[self._options_values.index(v)]
172 for v in new
173 ]
174 except:
175 self.value = old
176 raise KeyError(new)
177 finally:
178 self.value_lock.release()
179
180 def _selected_labels_changed(self, name, old, new):
181 """Called when the selected label has been changed (typically by the
182 frontend)."""
183 if self.value_lock.acquire(False):
184 try:
185 self.value = [self._options_dict[name] for name in new]
129 finally:
186 finally:
130 self.value_lock.release()
187 self.value_lock.release()
131
188
132
189
133 @register('IPython.ToggleButtons')
190 @register('IPython.ToggleButtons')
134 class ToggleButtons(_Selection):
191 class ToggleButtons(_Selection):
135 """Group of toggle buttons that represent an enumeration. Only one toggle
192 """Group of toggle buttons that represent an enumeration. Only one toggle
136 button can be toggled at any point in time."""
193 button can be toggled at any point in time."""
137 _view_name = Unicode('ToggleButtonsView', sync=True)
194 _view_name = Unicode('ToggleButtonsView', sync=True)
138
195
139 button_style = CaselessStrEnum(
196 button_style = CaselessStrEnum(
140 values=['primary', 'success', 'info', 'warning', 'danger', ''],
197 values=['primary', 'success', 'info', 'warning', 'danger', ''],
141 default_value='', allow_none=True, sync=True, help="""Use a
198 default_value='', allow_none=True, sync=True, help="""Use a
142 predefined styling for the buttons.""")
199 predefined styling for the buttons.""")
143
200
144 @register('IPython.Dropdown')
201 @register('IPython.Dropdown')
145 class Dropdown(_Selection):
202 class Dropdown(_Selection):
146 """Allows you to select a single item from a dropdown."""
203 """Allows you to select a single item from a dropdown."""
147 _view_name = Unicode('DropdownView', sync=True)
204 _view_name = Unicode('DropdownView', sync=True)
148
205
149 button_style = CaselessStrEnum(
206 button_style = CaselessStrEnum(
150 values=['primary', 'success', 'info', 'warning', 'danger', ''],
207 values=['primary', 'success', 'info', 'warning', 'danger', ''],
151 default_value='', allow_none=True, sync=True, help="""Use a
208 default_value='', allow_none=True, sync=True, help="""Use a
152 predefined styling for the buttons.""")
209 predefined styling for the buttons.""")
153
210
154 @register('IPython.RadioButtons')
211 @register('IPython.RadioButtons')
155 class RadioButtons(_Selection):
212 class RadioButtons(_Selection):
156 """Group of radio buttons that represent an enumeration. Only one radio
213 """Group of radio buttons that represent an enumeration. Only one radio
157 button can be toggled at any point in time."""
214 button can be toggled at any point in time."""
158 _view_name = Unicode('RadioButtonsView', sync=True)
215 _view_name = Unicode('RadioButtonsView', sync=True)
159
216
160
217
161
218
162 @register('IPython.Select')
219 @register('IPython.Select')
163 class Select(_Selection):
220 class Select(_Selection):
164 """Listbox that only allows one item to be selected at any given time."""
221 """Listbox that only allows one item to be selected at any given time."""
165 _view_name = Unicode('SelectView', sync=True)
222 _view_name = Unicode('SelectView', sync=True)
166
223
167
224
225 @register('IPython.SelectMultiple')
226 class SelectMultiple(_MultipleSelection):
227 """Listbox that allows many items to be selected at any given time.
228 Despite their names, inherited from ``_Selection``, the currently chosen
229 option values, ``value``, or their labels, ``selected_labels`` must both be
230 updated with a list-like object."""
231 _view_name = Unicode('SelectMultipleView', sync=True)
232
233
168 # Remove in IPython 4.0
234 # Remove in IPython 4.0
169 ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
235 ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
170 DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
236 DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
171 RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget')
237 RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget')
172 SelectWidget = DeprecatedClass(Select, 'SelectWidget')
238 SelectWidget = DeprecatedClass(Select, 'SelectWidget')
@@ -1,176 +1,176 b''
1 {
1 {
2 "cells": [
2 "cells": [
3 {
3 {
4 "cell_type": "code",
4 "cell_type": "code",
5 "execution_count": null,
5 "execution_count": null,
6 "metadata": {
6 "metadata": {
7 "collapsed": false
7 "collapsed": false
8 },
8 },
9 "outputs": [],
9 "outputs": [],
10 "source": [
10 "source": [
11 "# Widget related imports\n",
11 "# Widget related imports\n",
12 "from IPython.html import widgets\n",
12 "from IPython.html import widgets\n",
13 "from IPython.display import display, clear_output, Javascript\n",
13 "from IPython.display import display, clear_output, Javascript\n",
14 "from IPython.utils.traitlets import Unicode\n",
14 "from IPython.utils.traitlets import Unicode\n",
15 "\n",
15 "\n",
16 "# nbconvert related imports\n",
16 "# nbconvert related imports\n",
17 "from IPython.nbconvert import get_export_names, export_by_name\n",
17 "from IPython.nbconvert import get_export_names, export_by_name\n",
18 "from IPython.nbconvert.writers import FilesWriter\n",
18 "from IPython.nbconvert.writers import FilesWriter\n",
19 "from IPython.nbformat import current\n",
19 "from IPython.nbformat import current\n",
20 "from IPython.nbconvert.utils.exceptions import ConversionException"
20 "from IPython.nbconvert.utils.exceptions import ConversionException"
21 ]
21 ]
22 },
22 },
23 {
23 {
24 "cell_type": "markdown",
24 "cell_type": "markdown",
25 "metadata": {},
25 "metadata": {},
26 "source": [
26 "source": [
27 "Create a text Widget without displaying it. The widget will be used to store the notebook's name which is otherwise only available in the front-end."
27 "Create a text Widget without displaying it. The widget will be used to store the notebook's name which is otherwise only available in the front-end."
28 ]
28 ]
29 },
29 },
30 {
30 {
31 "cell_type": "code",
31 "cell_type": "code",
32 "execution_count": null,
32 "execution_count": null,
33 "metadata": {
33 "metadata": {
34 "collapsed": false
34 "collapsed": false
35 },
35 },
36 "outputs": [],
36 "outputs": [],
37 "source": [
37 "source": [
38 "notebook_name = widgets.Text()"
38 "notebook_name = widgets.Text()"
39 ]
39 ]
40 },
40 },
41 {
41 {
42 "cell_type": "markdown",
42 "cell_type": "markdown",
43 "metadata": {},
43 "metadata": {},
44 "source": [
44 "source": [
45 "Get the current notebook's name by pushing JavaScript to the browser that sets the notebook name in a string widget."
45 "Get the current notebook's name by pushing JavaScript to the browser that sets the notebook name in a string widget."
46 ]
46 ]
47 },
47 },
48 {
48 {
49 "cell_type": "code",
49 "cell_type": "code",
50 "execution_count": null,
50 "execution_count": null,
51 "metadata": {
51 "metadata": {
52 "collapsed": false
52 "collapsed": false
53 },
53 },
54 "outputs": [],
54 "outputs": [],
55 "source": [
55 "source": [
56 "js = \"\"\"var model = IPython.notebook.kernel.widget_manager.get_model('{model_id}');\n",
56 "js = \"\"\"var model = IPython.notebook.kernel.widget_manager.get_model('{model_id}');\n",
57 "model.set('value', IPython.notebook.notebook_name);\n",
57 "model.set('value', IPython.notebook.notebook_name);\n",
58 "model.save();\"\"\".format(model_id=notebook_name.model_id)\n",
58 "model.save();\"\"\".format(model_id=notebook_name.model_id)\n",
59 "display(Javascript(data=js))"
59 "display(Javascript(data=js))"
60 ]
60 ]
61 },
61 },
62 {
62 {
63 "cell_type": "code",
63 "cell_type": "code",
64 "execution_count": null,
64 "execution_count": null,
65 "metadata": {
65 "metadata": {
66 "collapsed": false
66 "collapsed": false
67 },
67 },
68 "outputs": [],
68 "outputs": [],
69 "source": [
69 "source": [
70 "filename = notebook_name.value\n",
70 "filename = notebook_name.value\n",
71 "filename"
71 "filename"
72 ]
72 ]
73 },
73 },
74 {
74 {
75 "cell_type": "markdown",
75 "cell_type": "markdown",
76 "metadata": {},
76 "metadata": {},
77 "source": [
77 "source": [
78 "Create the widget that will allow the user to Export the current notebook."
78 "Create the widget that will allow the user to Export the current notebook."
79 ]
79 ]
80 },
80 },
81 {
81 {
82 "cell_type": "code",
82 "cell_type": "code",
83 "execution_count": null,
83 "execution_count": null,
84 "metadata": {
84 "metadata": {
85 "collapsed": false
85 "collapsed": false
86 },
86 },
87 "outputs": [],
87 "outputs": [],
88 "source": [
88 "source": [
89 "exporter_names = widgets.Dropdown(values=get_export_names(), value='html')\n",
89 "exporter_names = widgets.Dropdown(options=get_export_names(), value='html')\n",
90 "export_button = widgets.Button(description=\"Export\")\n",
90 "export_button = widgets.Button(description=\"Export\")\n",
91 "download_link = widgets.HTML(visible=False)"
91 "download_link = widgets.HTML(visible=False)"
92 ]
92 ]
93 },
93 },
94 {
94 {
95 "cell_type": "markdown",
95 "cell_type": "markdown",
96 "metadata": {},
96 "metadata": {},
97 "source": [
97 "source": [
98 "Export the notebook when the export button is clicked."
98 "Export the notebook when the export button is clicked."
99 ]
99 ]
100 },
100 },
101 {
101 {
102 "cell_type": "code",
102 "cell_type": "code",
103 "execution_count": null,
103 "execution_count": null,
104 "metadata": {
104 "metadata": {
105 "collapsed": false
105 "collapsed": false
106 },
106 },
107 "outputs": [],
107 "outputs": [],
108 "source": [
108 "source": [
109 "file_writer = FilesWriter()\n",
109 "file_writer = FilesWriter()\n",
110 "\n",
110 "\n",
111 "def export(name, nb):\n",
111 "def export(name, nb):\n",
112 " \n",
112 " \n",
113 " # Get a unique key for the notebook and set it in the resources object.\n",
113 " # Get a unique key for the notebook and set it in the resources object.\n",
114 " notebook_name = name[:name.rfind('.')]\n",
114 " notebook_name = name[:name.rfind('.')]\n",
115 " resources = {}\n",
115 " resources = {}\n",
116 " resources['unique_key'] = notebook_name\n",
116 " resources['unique_key'] = notebook_name\n",
117 " resources['output_files_dir'] = '%s_files' % notebook_name\n",
117 " resources['output_files_dir'] = '%s_files' % notebook_name\n",
118 "\n",
118 "\n",
119 " # Try to export\n",
119 " # Try to export\n",
120 " try:\n",
120 " try:\n",
121 " output, resources = export_by_name(exporter_names.value, nb)\n",
121 " output, resources = export_by_name(exporter_names.value, nb)\n",
122 " except ConversionException as e:\n",
122 " except ConversionException as e:\n",
123 " download_link.value = \"<br>Could not export notebook!\"\n",
123 " download_link.value = \"<br>Could not export notebook!\"\n",
124 " else:\n",
124 " else:\n",
125 " write_results = file_writer.write(output, resources, notebook_name=notebook_name)\n",
125 " write_results = file_writer.write(output, resources, notebook_name=notebook_name)\n",
126 " \n",
126 " \n",
127 " download_link.value = \"<br>Results: <a href='files/{filename}'><i>\\\"{filename}\\\"</i></a>\".format(filename=write_results)\n",
127 " download_link.value = \"<br>Results: <a href='files/{filename}'><i>\\\"{filename}\\\"</i></a>\".format(filename=write_results)\n",
128 " download_link.visible = True\n",
128 " download_link.visible = True\n",
129 " \n",
129 " \n",
130 "def handle_export(widget):\n",
130 "def handle_export(widget):\n",
131 " with open(filename, 'r') as f:\n",
131 " with open(filename, 'r') as f:\n",
132 " export(filename, current.read(f, 'json'))\n",
132 " export(filename, current.read(f, 'json'))\n",
133 "export_button.on_click(handle_export)"
133 "export_button.on_click(handle_export)"
134 ]
134 ]
135 },
135 },
136 {
136 {
137 "cell_type": "markdown",
137 "cell_type": "markdown",
138 "metadata": {},
138 "metadata": {},
139 "source": [
139 "source": [
140 "Display the controls."
140 "Display the controls."
141 ]
141 ]
142 },
142 },
143 {
143 {
144 "cell_type": "code",
144 "cell_type": "code",
145 "execution_count": null,
145 "execution_count": null,
146 "metadata": {
146 "metadata": {
147 "collapsed": false
147 "collapsed": false
148 },
148 },
149 "outputs": [],
149 "outputs": [],
150 "source": [
150 "source": [
151 "display(exporter_names, export_button, download_link)"
151 "display(exporter_names, export_button, download_link)"
152 ]
152 ]
153 }
153 }
154 ],
154 ],
155 "metadata": {
155 "metadata": {
156 "kernelspec": {
156 "kernelspec": {
157 "display_name": "Python 3",
157 "display_name": "Python 3",
158 "language": "python",
158 "language": "python",
159 "name": "python3"
159 "name": "python3"
160 },
160 },
161 "language_info": {
161 "language_info": {
162 "codemirror_mode": {
162 "codemirror_mode": {
163 "name": "ipython",
163 "name": "ipython",
164 "version": 3
164 "version": 3
165 },
165 },
166 "file_extension": ".py",
166 "file_extension": ".py",
167 "mimetype": "text/x-python",
167 "mimetype": "text/x-python",
168 "name": "python",
168 "name": "python",
169 "nbconvert_exporter": "python",
169 "nbconvert_exporter": "python",
170 "pygments_lexer": "ipython3",
170 "pygments_lexer": "ipython3",
171 "version": "3.4.2"
171 "version": "3.4.2"
172 }
172 }
173 },
173 },
174 "nbformat": 4,
174 "nbformat": 4,
175 "nbformat_minor": 0
175 "nbformat_minor": 0
176 }
176 }
@@ -1,587 +1,621 b''
1 {
1 {
2 "cells": [
2 "cells": [
3 {
3 {
4 "cell_type": "markdown",
4 "cell_type": "markdown",
5 "metadata": {},
5 "metadata": {},
6 "source": [
6 "source": [
7 "[Index](Index.ipynb) - [Back](Widget Basics.ipynb) - [Next](Widget Events.ipynb)"
7 "[Index](Index.ipynb) - [Back](Widget Basics.ipynb) - [Next](Widget Events.ipynb)"
8 ]
8 ]
9 },
9 },
10 {
10 {
11 "cell_type": "markdown",
11 "cell_type": "markdown",
12 "metadata": {},
12 "metadata": {},
13 "source": [
13 "source": [
14 "# Widget List"
14 "# Widget List"
15 ]
15 ]
16 },
16 },
17 {
17 {
18 "cell_type": "markdown",
18 "cell_type": "markdown",
19 "metadata": {},
19 "metadata": {},
20 "source": [
20 "source": [
21 "## Complete list"
21 "## Complete list"
22 ]
22 ]
23 },
23 },
24 {
24 {
25 "cell_type": "markdown",
25 "cell_type": "markdown",
26 "metadata": {
26 "metadata": {
27 "slideshow": {
27 "slideshow": {
28 "slide_type": "slide"
28 "slide_type": "slide"
29 }
29 }
30 },
30 },
31 "source": [
31 "source": [
32 "For a complete list of the widgets available to you, you can list the classes in the widget namespace (as seen below). `Widget` and `DOMWidget`, not listed below, are base classes."
32 "For a complete list of the widgets available to you, you can list the classes in the widget namespace (as seen below). `Widget` and `DOMWidget`, not listed below, are base classes."
33 ]
33 ]
34 },
34 },
35 {
35 {
36 "cell_type": "code",
36 "cell_type": "code",
37 "execution_count": null,
37 "execution_count": null,
38 "metadata": {
38 "metadata": {
39 "collapsed": false
39 "collapsed": false
40 },
40 },
41 "outputs": [],
41 "outputs": [],
42 "source": [
42 "source": [
43 "from IPython.html import widgets\n",
43 "from IPython.html import widgets\n",
44 "[n for n in dir(widgets) if not n.endswith('Widget') and n[0] == n[0].upper() and not n[0] == '_']"
44 "[n for n in dir(widgets) if not n.endswith('Widget') and n[0] == n[0].upper() and not n[0] == '_']"
45 ]
45 ]
46 },
46 },
47 {
47 {
48 "cell_type": "markdown",
48 "cell_type": "markdown",
49 "metadata": {
49 "metadata": {
50 "slideshow": {
50 "slideshow": {
51 "slide_type": "slide"
51 "slide_type": "slide"
52 }
52 }
53 },
53 },
54 "source": [
54 "source": [
55 "## Numeric widgets"
55 "## Numeric widgets"
56 ]
56 ]
57 },
57 },
58 {
58 {
59 "cell_type": "markdown",
59 "cell_type": "markdown",
60 "metadata": {},
60 "metadata": {},
61 "source": [
61 "source": [
62 "There are 8 widgets distributed with IPython that are designed to display numeric values. Widgets exist for displaying integers and floats, both bounded and unbounded. The integer widgets share a similar naming scheme to their floating point counterparts. By replacing `Float` with `Int` in the widget name, you can find the Integer equivalent."
62 "There are 8 widgets distributed with IPython that are designed to display numeric values. Widgets exist for displaying integers and floats, both bounded and unbounded. The integer widgets share a similar naming scheme to their floating point counterparts. By replacing `Float` with `Int` in the widget name, you can find the Integer equivalent."
63 ]
63 ]
64 },
64 },
65 {
65 {
66 "cell_type": "markdown",
66 "cell_type": "markdown",
67 "metadata": {
67 "metadata": {
68 "slideshow": {
68 "slideshow": {
69 "slide_type": "slide"
69 "slide_type": "slide"
70 }
70 }
71 },
71 },
72 "source": [
72 "source": [
73 "### FloatSlider"
73 "### FloatSlider"
74 ]
74 ]
75 },
75 },
76 {
76 {
77 "cell_type": "code",
77 "cell_type": "code",
78 "execution_count": null,
78 "execution_count": null,
79 "metadata": {
79 "metadata": {
80 "collapsed": false
80 "collapsed": false
81 },
81 },
82 "outputs": [],
82 "outputs": [],
83 "source": [
83 "source": [
84 "widgets.FloatSlider(\n",
84 "widgets.FloatSlider(\n",
85 " value=7.5,\n",
85 " value=7.5,\n",
86 " min=5.0,\n",
86 " min=5.0,\n",
87 " max=10.0,\n",
87 " max=10.0,\n",
88 " step=0.1,\n",
88 " step=0.1,\n",
89 " description='Test:',\n",
89 " description='Test:',\n",
90 ")"
90 ")"
91 ]
91 ]
92 },
92 },
93 {
93 {
94 "cell_type": "markdown",
94 "cell_type": "markdown",
95 "metadata": {},
95 "metadata": {},
96 "source": [
96 "source": [
97 "Sliders can also be **displayed vertically**."
97 "Sliders can also be **displayed vertically**."
98 ]
98 ]
99 },
99 },
100 {
100 {
101 "cell_type": "code",
101 "cell_type": "code",
102 "execution_count": null,
102 "execution_count": null,
103 "metadata": {
103 "metadata": {
104 "collapsed": false
104 "collapsed": false
105 },
105 },
106 "outputs": [],
106 "outputs": [],
107 "source": [
107 "source": [
108 "widgets.FloatSlider(\n",
108 "widgets.FloatSlider(\n",
109 " value=7.5,\n",
109 " value=7.5,\n",
110 " min=5.0,\n",
110 " min=5.0,\n",
111 " max=10.0,\n",
111 " max=10.0,\n",
112 " step=0.1,\n",
112 " step=0.1,\n",
113 " description='Test',\n",
113 " description='Test',\n",
114 " orientation='vertical',\n",
114 " orientation='vertical',\n",
115 ")"
115 ")"
116 ]
116 ]
117 },
117 },
118 {
118 {
119 "cell_type": "markdown",
119 "cell_type": "markdown",
120 "metadata": {
120 "metadata": {
121 "slideshow": {
121 "slideshow": {
122 "slide_type": "slide"
122 "slide_type": "slide"
123 }
123 }
124 },
124 },
125 "source": [
125 "source": [
126 "### FloatProgress"
126 "### FloatProgress"
127 ]
127 ]
128 },
128 },
129 {
129 {
130 "cell_type": "code",
130 "cell_type": "code",
131 "execution_count": null,
131 "execution_count": null,
132 "metadata": {
132 "metadata": {
133 "collapsed": false
133 "collapsed": false
134 },
134 },
135 "outputs": [],
135 "outputs": [],
136 "source": [
136 "source": [
137 "widgets.FloatProgress(\n",
137 "widgets.FloatProgress(\n",
138 " value=7.5,\n",
138 " value=7.5,\n",
139 " min=5.0,\n",
139 " min=5.0,\n",
140 " max=10.0,\n",
140 " max=10.0,\n",
141 " step=0.1,\n",
141 " step=0.1,\n",
142 " description='Loading:',\n",
142 " description='Loading:',\n",
143 ")"
143 ")"
144 ]
144 ]
145 },
145 },
146 {
146 {
147 "cell_type": "markdown",
147 "cell_type": "markdown",
148 "metadata": {
148 "metadata": {
149 "slideshow": {
149 "slideshow": {
150 "slide_type": "slide"
150 "slide_type": "slide"
151 }
151 }
152 },
152 },
153 "source": [
153 "source": [
154 "### BoundedFloatText"
154 "### BoundedFloatText"
155 ]
155 ]
156 },
156 },
157 {
157 {
158 "cell_type": "code",
158 "cell_type": "code",
159 "execution_count": null,
159 "execution_count": null,
160 "metadata": {
160 "metadata": {
161 "collapsed": false
161 "collapsed": false
162 },
162 },
163 "outputs": [],
163 "outputs": [],
164 "source": [
164 "source": [
165 "widgets.BoundedFloatText(\n",
165 "widgets.BoundedFloatText(\n",
166 " value=7.5,\n",
166 " value=7.5,\n",
167 " min=5.0,\n",
167 " min=5.0,\n",
168 " max=10.0,\n",
168 " max=10.0,\n",
169 " description='Text:',\n",
169 " description='Text:',\n",
170 ")"
170 ")"
171 ]
171 ]
172 },
172 },
173 {
173 {
174 "cell_type": "markdown",
174 "cell_type": "markdown",
175 "metadata": {
175 "metadata": {
176 "slideshow": {
176 "slideshow": {
177 "slide_type": "slide"
177 "slide_type": "slide"
178 }
178 }
179 },
179 },
180 "source": [
180 "source": [
181 "### FloatText"
181 "### FloatText"
182 ]
182 ]
183 },
183 },
184 {
184 {
185 "cell_type": "code",
185 "cell_type": "code",
186 "execution_count": null,
186 "execution_count": null,
187 "metadata": {
187 "metadata": {
188 "collapsed": false
188 "collapsed": false
189 },
189 },
190 "outputs": [],
190 "outputs": [],
191 "source": [
191 "source": [
192 "widgets.FloatText(\n",
192 "widgets.FloatText(\n",
193 " value=7.5,\n",
193 " value=7.5,\n",
194 " description='Any:',\n",
194 " description='Any:',\n",
195 ")"
195 ")"
196 ]
196 ]
197 },
197 },
198 {
198 {
199 "cell_type": "markdown",
199 "cell_type": "markdown",
200 "metadata": {
200 "metadata": {
201 "slideshow": {
201 "slideshow": {
202 "slide_type": "slide"
202 "slide_type": "slide"
203 }
203 }
204 },
204 },
205 "source": [
205 "source": [
206 "## Boolean widgets"
206 "## Boolean widgets"
207 ]
207 ]
208 },
208 },
209 {
209 {
210 "cell_type": "markdown",
210 "cell_type": "markdown",
211 "metadata": {},
211 "metadata": {},
212 "source": [
212 "source": [
213 "There are two widgets that are designed to display a boolean value."
213 "There are two widgets that are designed to display a boolean value."
214 ]
214 ]
215 },
215 },
216 {
216 {
217 "cell_type": "markdown",
217 "cell_type": "markdown",
218 "metadata": {},
218 "metadata": {},
219 "source": [
219 "source": [
220 "### ToggleButton"
220 "### ToggleButton"
221 ]
221 ]
222 },
222 },
223 {
223 {
224 "cell_type": "code",
224 "cell_type": "code",
225 "execution_count": null,
225 "execution_count": null,
226 "metadata": {
226 "metadata": {
227 "collapsed": false
227 "collapsed": false
228 },
228 },
229 "outputs": [],
229 "outputs": [],
230 "source": [
230 "source": [
231 "widgets.ToggleButton(\n",
231 "widgets.ToggleButton(\n",
232 " description='Click me',\n",
232 " description='Click me',\n",
233 " value=False,\n",
233 " value=False,\n",
234 ")"
234 ")"
235 ]
235 ]
236 },
236 },
237 {
237 {
238 "cell_type": "markdown",
238 "cell_type": "markdown",
239 "metadata": {
239 "metadata": {
240 "slideshow": {
240 "slideshow": {
241 "slide_type": "slide"
241 "slide_type": "slide"
242 }
242 }
243 },
243 },
244 "source": [
244 "source": [
245 "### Checkbox"
245 "### Checkbox"
246 ]
246 ]
247 },
247 },
248 {
248 {
249 "cell_type": "code",
249 "cell_type": "code",
250 "execution_count": null,
250 "execution_count": null,
251 "metadata": {
251 "metadata": {
252 "collapsed": false
252 "collapsed": false
253 },
253 },
254 "outputs": [],
254 "outputs": [],
255 "source": [
255 "source": [
256 "widgets.Checkbox(\n",
256 "widgets.Checkbox(\n",
257 " description='Check me',\n",
257 " description='Check me',\n",
258 " value=True,\n",
258 " value=True,\n",
259 ")"
259 ")"
260 ]
260 ]
261 },
261 },
262 {
262 {
263 "cell_type": "markdown",
263 "cell_type": "markdown",
264 "metadata": {
264 "metadata": {
265 "slideshow": {
265 "slideshow": {
266 "slide_type": "slide"
266 "slide_type": "slide"
267 }
267 }
268 },
268 },
269 "source": [
269 "source": [
270 "## Selection widgets"
270 "## Selection widgets"
271 ]
271 ]
272 },
272 },
273 {
273 {
274 "cell_type": "markdown",
274 "cell_type": "markdown",
275 "metadata": {},
275 "metadata": {},
276 "source": [
276 "source": [
277 "There are four widgets that can be used to display single selection lists. All four inherit from the same base class. You can specify the **enumeration of selectables by passing a list**. You can **also specify the enumeration as a dictionary**, in which case the **keys will be used as the item displayed** in the list and the corresponding **value will be returned** when an item is selected."
277 "There are four widgets that can be used to display single selection lists, and one that can be used to display multiple selection lists. All inherit from the same base class. You can specify the **enumeration of selectable options by passing a list**. You can **also specify the enumeration as a dictionary**, in which case the **keys will be used as the item displayed** in the list and the corresponding **value will be returned** when an item is selected."
278 ]
278 ]
279 },
279 },
280 {
280 {
281 "cell_type": "markdown",
281 "cell_type": "markdown",
282 "metadata": {
282 "metadata": {
283 "slideshow": {
283 "slideshow": {
284 "slide_type": "slide"
284 "slide_type": "slide"
285 }
285 }
286 },
286 },
287 "source": [
287 "source": [
288 "### Dropdown"
288 "### Dropdown"
289 ]
289 ]
290 },
290 },
291 {
291 {
292 "cell_type": "code",
292 "cell_type": "code",
293 "execution_count": null,
293 "execution_count": null,
294 "metadata": {
294 "metadata": {
295 "collapsed": false
295 "collapsed": false
296 },
296 },
297 "outputs": [],
297 "outputs": [],
298 "source": [
298 "source": [
299 "from IPython.display import display\n",
299 "from IPython.display import display\n",
300 "w = widgets.Dropdown(\n",
300 "w = widgets.Dropdown(\n",
301 " values=['1', '2', '3'],\n",
301 " options=['1', '2', '3'],\n",
302 " value='2',\n",
302 " value='2',\n",
303 " description='Number:',\n",
303 " description='Number:',\n",
304 ")\n",
304 ")\n",
305 "display(w)"
305 "display(w)"
306 ]
306 ]
307 },
307 },
308 {
308 {
309 "cell_type": "code",
309 "cell_type": "code",
310 "execution_count": null,
310 "execution_count": null,
311 "metadata": {
311 "metadata": {
312 "collapsed": false
312 "collapsed": false
313 },
313 },
314 "outputs": [],
314 "outputs": [],
315 "source": [
315 "source": [
316 "w.value"
316 "w.value"
317 ]
317 ]
318 },
318 },
319 {
319 {
320 "cell_type": "markdown",
320 "cell_type": "markdown",
321 "metadata": {},
321 "metadata": {},
322 "source": [
322 "source": [
323 "The following is also valid:"
323 "The following is also valid:"
324 ]
324 ]
325 },
325 },
326 {
326 {
327 "cell_type": "code",
327 "cell_type": "code",
328 "execution_count": null,
328 "execution_count": null,
329 "metadata": {
329 "metadata": {
330 "collapsed": false
330 "collapsed": false
331 },
331 },
332 "outputs": [],
332 "outputs": [],
333 "source": [
333 "source": [
334 "w = widgets.Dropdown(\n",
334 "w = widgets.Dropdown(\n",
335 " values={'One': 1, 'Two': 2, 'Three': 3},\n",
335 " options={'One': 1, 'Two': 2, 'Three': 3},\n",
336 " value=2,\n",
336 " value=2,\n",
337 " description='Number:',\n",
337 " description='Number:',\n",
338 ")\n",
338 ")\n",
339 "display(w)"
339 "display(w)"
340 ]
340 ]
341 },
341 },
342 {
342 {
343 "cell_type": "code",
343 "cell_type": "code",
344 "execution_count": null,
344 "execution_count": null,
345 "metadata": {
345 "metadata": {
346 "collapsed": false
346 "collapsed": false
347 },
347 },
348 "outputs": [],
348 "outputs": [],
349 "source": [
349 "source": [
350 "w.value"
350 "w.value"
351 ]
351 ]
352 },
352 },
353 {
353 {
354 "cell_type": "markdown",
354 "cell_type": "markdown",
355 "metadata": {
355 "metadata": {
356 "slideshow": {
356 "slideshow": {
357 "slide_type": "slide"
357 "slide_type": "slide"
358 }
358 }
359 },
359 },
360 "source": [
360 "source": [
361 "### RadioButtons"
361 "### RadioButtons"
362 ]
362 ]
363 },
363 },
364 {
364 {
365 "cell_type": "code",
365 "cell_type": "code",
366 "execution_count": null,
366 "execution_count": null,
367 "metadata": {
367 "metadata": {
368 "collapsed": false
368 "collapsed": false
369 },
369 },
370 "outputs": [],
370 "outputs": [],
371 "source": [
371 "source": [
372 "widgets.RadioButtons(\n",
372 "widgets.RadioButtons(\n",
373 " description='Pizza topping:',\n",
373 " description='Pizza topping:',\n",
374 " values=['pepperoni', 'pineapple', 'anchovies'],\n",
374 " options=['pepperoni', 'pineapple', 'anchovies'],\n",
375 ")"
375 ")"
376 ]
376 ]
377 },
377 },
378 {
378 {
379 "cell_type": "markdown",
379 "cell_type": "markdown",
380 "metadata": {
380 "metadata": {
381 "slideshow": {
381 "slideshow": {
382 "slide_type": "slide"
382 "slide_type": "slide"
383 }
383 }
384 },
384 },
385 "source": [
385 "source": [
386 "### Select"
386 "### Select"
387 ]
387 ]
388 },
388 },
389 {
389 {
390 "cell_type": "code",
390 "cell_type": "code",
391 "execution_count": null,
391 "execution_count": null,
392 "metadata": {
392 "metadata": {
393 "collapsed": false
393 "collapsed": false
394 },
394 },
395 "outputs": [],
395 "outputs": [],
396 "source": [
396 "source": [
397 "widgets.Select(\n",
397 "widgets.Select(\n",
398 " description='OS:',\n",
398 " description='OS:',\n",
399 " values=['Linux', 'Windows', 'OSX'],\n",
399 " options=['Linux', 'Windows', 'OSX'],\n",
400 ")"
400 ")"
401 ]
401 ]
402 },
402 },
403 {
403 {
404 "cell_type": "markdown",
404 "cell_type": "markdown",
405 "metadata": {
405 "metadata": {
406 "slideshow": {
406 "slideshow": {
407 "slide_type": "slide"
407 "slide_type": "slide"
408 }
408 }
409 },
409 },
410 "source": [
410 "source": [
411 "### ToggleButtons"
411 "### ToggleButtons"
412 ]
412 ]
413 },
413 },
414 {
414 {
415 "cell_type": "code",
415 "cell_type": "code",
416 "execution_count": null,
416 "execution_count": null,
417 "metadata": {
417 "metadata": {
418 "collapsed": false
418 "collapsed": false
419 },
419 },
420 "outputs": [],
420 "outputs": [],
421 "source": [
421 "source": [
422 "widgets.ToggleButtons(\n",
422 "widgets.ToggleButtons(\n",
423 " description='Speed:',\n",
423 " description='Speed:',\n",
424 " values=['Slow', 'Regular', 'Fast'],\n",
424 " options=['Slow', 'Regular', 'Fast'],\n",
425 ")"
425 ")"
426 ]
426 ]
427 },
427 },
428 {
428 {
429 "cell_type": "markdown",
429 "cell_type": "markdown",
430 "metadata": {},
431 "source": [
432 "### SelectMultiple\n",
433 "Multiple values can be selected with <kbd>shift</kbd> and <kbd>ctrl</kbd> pressed and mouse clicks or arrow keys."
434 ]
435 },
436 {
437 "cell_type": "code",
438 "execution_count": null,
439 "metadata": {
440 "collapsed": true
441 },
442 "outputs": [],
443 "source": [
444 "w = widgets.SelectMultiple(\n",
445 " description=\"Fruits\",\n",
446 " options=['Apples', 'Oranges', 'Pears']\n",
447 ")\n",
448 "display(w)"
449 ]
450 },
451 {
452 "cell_type": "code",
453 "execution_count": null,
454 "metadata": {
455 "collapsed": false
456 },
457 "outputs": [],
458 "source": [
459 "w.value"
460 ]
461 },
462 {
463 "cell_type": "markdown",
430 "metadata": {
464 "metadata": {
431 "slideshow": {
465 "slideshow": {
432 "slide_type": "slide"
466 "slide_type": "slide"
433 }
467 }
434 },
468 },
435 "source": [
469 "source": [
436 "## String widgets"
470 "## String widgets"
437 ]
471 ]
438 },
472 },
439 {
473 {
440 "cell_type": "markdown",
474 "cell_type": "markdown",
441 "metadata": {},
475 "metadata": {},
442 "source": [
476 "source": [
443 "There are 4 widgets that can be used to display a string value. Of those, the **`Text` and `Textarea` widgets accept input**. The **`Latex` and `HTML` widgets display the string** as either Latex or HTML respectively, but **do not accept input**."
477 "There are 4 widgets that can be used to display a string value. Of those, the **`Text` and `Textarea` widgets accept input**. The **`Latex` and `HTML` widgets display the string** as either Latex or HTML respectively, but **do not accept input**."
444 ]
478 ]
445 },
479 },
446 {
480 {
447 "cell_type": "markdown",
481 "cell_type": "markdown",
448 "metadata": {
482 "metadata": {
449 "slideshow": {
483 "slideshow": {
450 "slide_type": "slide"
484 "slide_type": "slide"
451 }
485 }
452 },
486 },
453 "source": [
487 "source": [
454 "### Text"
488 "### Text"
455 ]
489 ]
456 },
490 },
457 {
491 {
458 "cell_type": "code",
492 "cell_type": "code",
459 "execution_count": null,
493 "execution_count": null,
460 "metadata": {
494 "metadata": {
461 "collapsed": false
495 "collapsed": false
462 },
496 },
463 "outputs": [],
497 "outputs": [],
464 "source": [
498 "source": [
465 "widgets.Text(\n",
499 "widgets.Text(\n",
466 " description='String:',\n",
500 " description='String:',\n",
467 " value='Hello World',\n",
501 " value='Hello World',\n",
468 ")"
502 ")"
469 ]
503 ]
470 },
504 },
471 {
505 {
472 "cell_type": "markdown",
506 "cell_type": "markdown",
473 "metadata": {},
507 "metadata": {},
474 "source": [
508 "source": [
475 "### Textarea"
509 "### Textarea"
476 ]
510 ]
477 },
511 },
478 {
512 {
479 "cell_type": "code",
513 "cell_type": "code",
480 "execution_count": null,
514 "execution_count": null,
481 "metadata": {
515 "metadata": {
482 "collapsed": false
516 "collapsed": false
483 },
517 },
484 "outputs": [],
518 "outputs": [],
485 "source": [
519 "source": [
486 "widgets.Textarea(\n",
520 "widgets.Textarea(\n",
487 " description='String:',\n",
521 " description='String:',\n",
488 " value='Hello World',\n",
522 " value='Hello World',\n",
489 ")"
523 ")"
490 ]
524 ]
491 },
525 },
492 {
526 {
493 "cell_type": "markdown",
527 "cell_type": "markdown",
494 "metadata": {
528 "metadata": {
495 "slideshow": {
529 "slideshow": {
496 "slide_type": "slide"
530 "slide_type": "slide"
497 }
531 }
498 },
532 },
499 "source": [
533 "source": [
500 "### Latex"
534 "### Latex"
501 ]
535 ]
502 },
536 },
503 {
537 {
504 "cell_type": "code",
538 "cell_type": "code",
505 "execution_count": null,
539 "execution_count": null,
506 "metadata": {
540 "metadata": {
507 "collapsed": false
541 "collapsed": false
508 },
542 },
509 "outputs": [],
543 "outputs": [],
510 "source": [
544 "source": [
511 "widgets.Latex(\n",
545 "widgets.Latex(\n",
512 " value=\"$$\\\\frac{n!}{k!(n-k)!} = \\\\binom{n}{k}$$\",\n",
546 " value=\"$$\\\\frac{n!}{k!(n-k)!} = \\\\binom{n}{k}$$\",\n",
513 ")"
547 ")"
514 ]
548 ]
515 },
549 },
516 {
550 {
517 "cell_type": "markdown",
551 "cell_type": "markdown",
518 "metadata": {},
552 "metadata": {},
519 "source": [
553 "source": [
520 "### HTML"
554 "### HTML"
521 ]
555 ]
522 },
556 },
523 {
557 {
524 "cell_type": "code",
558 "cell_type": "code",
525 "execution_count": null,
559 "execution_count": null,
526 "metadata": {
560 "metadata": {
527 "collapsed": false
561 "collapsed": false
528 },
562 },
529 "outputs": [],
563 "outputs": [],
530 "source": [
564 "source": [
531 "widgets.HTML(\n",
565 "widgets.HTML(\n",
532 " value=\"Hello <b>World</b>\"\n",
566 " value=\"Hello <b>World</b>\"\n",
533 ")"
567 ")"
534 ]
568 ]
535 },
569 },
536 {
570 {
537 "cell_type": "markdown",
571 "cell_type": "markdown",
538 "metadata": {
572 "metadata": {
539 "slideshow": {
573 "slideshow": {
540 "slide_type": "slide"
574 "slide_type": "slide"
541 }
575 }
542 },
576 },
543 "source": [
577 "source": [
544 "## Button"
578 "## Button"
545 ]
579 ]
546 },
580 },
547 {
581 {
548 "cell_type": "code",
582 "cell_type": "code",
549 "execution_count": null,
583 "execution_count": null,
550 "metadata": {
584 "metadata": {
551 "collapsed": false
585 "collapsed": false
552 },
586 },
553 "outputs": [],
587 "outputs": [],
554 "source": [
588 "source": [
555 "widgets.Button(description='Click me')"
589 "widgets.Button(description='Click me')"
556 ]
590 ]
557 },
591 },
558 {
592 {
559 "cell_type": "markdown",
593 "cell_type": "markdown",
560 "metadata": {},
594 "metadata": {},
561 "source": [
595 "source": [
562 "[Index](Index.ipynb) - [Back](Widget Basics.ipynb) - [Next](Widget Events.ipynb)"
596 "[Index](Index.ipynb) - [Back](Widget Basics.ipynb) - [Next](Widget Events.ipynb)"
563 ]
597 ]
564 }
598 }
565 ],
599 ],
566 "metadata": {
600 "metadata": {
567 "kernelspec": {
601 "kernelspec": {
568 "display_name": "Python 3",
602 "display_name": "Python 3",
569 "language": "python",
603 "language": "python",
570 "name": "python3"
604 "name": "python3"
571 },
605 },
572 "language_info": {
606 "language_info": {
573 "codemirror_mode": {
607 "codemirror_mode": {
574 "name": "ipython",
608 "name": "ipython",
575 "version": 3
609 "version": 3
576 },
610 },
577 "file_extension": ".py",
611 "file_extension": ".py",
578 "mimetype": "text/x-python",
612 "mimetype": "text/x-python",
579 "name": "python",
613 "name": "python",
580 "nbconvert_exporter": "python",
614 "nbconvert_exporter": "python",
581 "pygments_lexer": "ipython3",
615 "pygments_lexer": "ipython3",
582 "version": "3.4.2"
616 "version": "3.4.2"
583 }
617 }
584 },
618 },
585 "nbformat": 4,
619 "nbformat": 4,
586 "nbformat_minor": 0
620 "nbformat_minor": 0
587 }
621 }
@@ -1,585 +1,585 b''
1 {
1 {
2 "cells": [
2 "cells": [
3 {
3 {
4 "cell_type": "markdown",
4 "cell_type": "markdown",
5 "metadata": {},
5 "metadata": {},
6 "source": [
6 "source": [
7 "[Index](Index.ipynb) - [Back](Widget Events.ipynb) - [Next](Custom Widget - Hello World.ipynb)"
7 "[Index](Index.ipynb) - [Back](Widget Events.ipynb) - [Next](Custom Widget - Hello World.ipynb)"
8 ]
8 ]
9 },
9 },
10 {
10 {
11 "cell_type": "code",
11 "cell_type": "code",
12 "execution_count": null,
12 "execution_count": null,
13 "metadata": {
13 "metadata": {
14 "collapsed": false
14 "collapsed": false
15 },
15 },
16 "outputs": [],
16 "outputs": [],
17 "source": [
17 "source": [
18 "%%html\n",
18 "%%html\n",
19 "<style>\n",
19 "<style>\n",
20 ".example-container { background: #999999; padding: 2px; min-height: 100px; }\n",
20 ".example-container { background: #999999; padding: 2px; min-height: 100px; }\n",
21 ".example-container.sm { min-height: 50px; }\n",
21 ".example-container.sm { min-height: 50px; }\n",
22 ".example-box { background: #9999FF; width: 50px; height: 50px; text-align: center; vertical-align: middle; color: white; font-weight: bold; margin: 2px;}\n",
22 ".example-box { background: #9999FF; width: 50px; height: 50px; text-align: center; vertical-align: middle; color: white; font-weight: bold; margin: 2px;}\n",
23 ".example-box.med { width: 65px; height: 65px; } \n",
23 ".example-box.med { width: 65px; height: 65px; } \n",
24 ".example-box.lrg { width: 80px; height: 80px; } \n",
24 ".example-box.lrg { width: 80px; height: 80px; } \n",
25 "</style>"
25 "</style>"
26 ]
26 ]
27 },
27 },
28 {
28 {
29 "cell_type": "code",
29 "cell_type": "code",
30 "execution_count": null,
30 "execution_count": null,
31 "metadata": {
31 "metadata": {
32 "collapsed": false
32 "collapsed": false
33 },
33 },
34 "outputs": [],
34 "outputs": [],
35 "source": [
35 "source": [
36 "from IPython.html import widgets\n",
36 "from IPython.html import widgets\n",
37 "from IPython.display import display"
37 "from IPython.display import display"
38 ]
38 ]
39 },
39 },
40 {
40 {
41 "cell_type": "markdown",
41 "cell_type": "markdown",
42 "metadata": {
42 "metadata": {
43 "slideshow": {
43 "slideshow": {
44 "slide_type": "slide"
44 "slide_type": "slide"
45 }
45 }
46 },
46 },
47 "source": [
47 "source": [
48 "# Widget Styling"
48 "# Widget Styling"
49 ]
49 ]
50 },
50 },
51 {
51 {
52 "cell_type": "markdown",
52 "cell_type": "markdown",
53 "metadata": {},
53 "metadata": {},
54 "source": [
54 "source": [
55 "## Basic styling"
55 "## Basic styling"
56 ]
56 ]
57 },
57 },
58 {
58 {
59 "cell_type": "markdown",
59 "cell_type": "markdown",
60 "metadata": {},
60 "metadata": {},
61 "source": [
61 "source": [
62 "The widgets distributed with IPython can be styled by setting the following traits:\n",
62 "The widgets distributed with IPython can be styled by setting the following traits:\n",
63 "\n",
63 "\n",
64 "- width \n",
64 "- width \n",
65 "- height \n",
65 "- height \n",
66 "- fore_color \n",
66 "- fore_color \n",
67 "- back_color \n",
67 "- back_color \n",
68 "- border_color \n",
68 "- border_color \n",
69 "- border_width \n",
69 "- border_width \n",
70 "- border_style \n",
70 "- border_style \n",
71 "- font_style \n",
71 "- font_style \n",
72 "- font_weight \n",
72 "- font_weight \n",
73 "- font_size \n",
73 "- font_size \n",
74 "- font_family \n",
74 "- font_family \n",
75 "\n",
75 "\n",
76 "The example below shows how a `Button` widget can be styled:"
76 "The example below shows how a `Button` widget can be styled:"
77 ]
77 ]
78 },
78 },
79 {
79 {
80 "cell_type": "code",
80 "cell_type": "code",
81 "execution_count": null,
81 "execution_count": null,
82 "metadata": {
82 "metadata": {
83 "collapsed": false
83 "collapsed": false
84 },
84 },
85 "outputs": [],
85 "outputs": [],
86 "source": [
86 "source": [
87 "button = widgets.Button(\n",
87 "button = widgets.Button(\n",
88 " description='Hello World!',\n",
88 " description='Hello World!',\n",
89 " width=100, # Integers are interpreted as pixel measurements.\n",
89 " width=100, # Integers are interpreted as pixel measurements.\n",
90 " height='2em', # em is valid HTML unit of measurement.\n",
90 " height='2em', # em is valid HTML unit of measurement.\n",
91 " color='lime', # Colors can be set by name,\n",
91 " color='lime', # Colors can be set by name,\n",
92 " background_color='#0022FF', # and also by color code.\n",
92 " background_color='#0022FF', # and also by color code.\n",
93 " border_color='red')\n",
93 " border_color='red')\n",
94 "display(button)"
94 "display(button)"
95 ]
95 ]
96 },
96 },
97 {
97 {
98 "cell_type": "markdown",
98 "cell_type": "markdown",
99 "metadata": {
99 "metadata": {
100 "slideshow": {
100 "slideshow": {
101 "slide_type": "slide"
101 "slide_type": "slide"
102 }
102 }
103 },
103 },
104 "source": [
104 "source": [
105 "## Parent/child relationships"
105 "## Parent/child relationships"
106 ]
106 ]
107 },
107 },
108 {
108 {
109 "cell_type": "markdown",
109 "cell_type": "markdown",
110 "metadata": {},
110 "metadata": {},
111 "source": [
111 "source": [
112 "To display widget A inside widget B, widget A must be a child of widget B. Widgets that can contain other widgets have a **`children` attribute**. This attribute can be **set via a keyword argument** in the widget's constructor **or after construction**. Calling display on an **object with children automatically displays those children**, too."
112 "To display widget A inside widget B, widget A must be a child of widget B. Widgets that can contain other widgets have a **`children` attribute**. This attribute can be **set via a keyword argument** in the widget's constructor **or after construction**. Calling display on an **object with children automatically displays those children**, too."
113 ]
113 ]
114 },
114 },
115 {
115 {
116 "cell_type": "code",
116 "cell_type": "code",
117 "execution_count": null,
117 "execution_count": null,
118 "metadata": {
118 "metadata": {
119 "collapsed": false
119 "collapsed": false
120 },
120 },
121 "outputs": [],
121 "outputs": [],
122 "source": [
122 "source": [
123 "from IPython.display import display\n",
123 "from IPython.display import display\n",
124 "\n",
124 "\n",
125 "float_range = widgets.FloatSlider()\n",
125 "float_range = widgets.FloatSlider()\n",
126 "string = widgets.Text(value='hi')\n",
126 "string = widgets.Text(value='hi')\n",
127 "container = widgets.Box(children=[float_range, string])\n",
127 "container = widgets.Box(children=[float_range, string])\n",
128 "\n",
128 "\n",
129 "container.border_color = 'red'\n",
129 "container.border_color = 'red'\n",
130 "container.border_style = 'dotted'\n",
130 "container.border_style = 'dotted'\n",
131 "container.border_width = 3\n",
131 "container.border_width = 3\n",
132 "display(container) # Displays the `container` and all of it's children."
132 "display(container) # Displays the `container` and all of it's children."
133 ]
133 ]
134 },
134 },
135 {
135 {
136 "cell_type": "markdown",
136 "cell_type": "markdown",
137 "metadata": {},
137 "metadata": {},
138 "source": [
138 "source": [
139 "### After the parent is displayed"
139 "### After the parent is displayed"
140 ]
140 ]
141 },
141 },
142 {
142 {
143 "cell_type": "markdown",
143 "cell_type": "markdown",
144 "metadata": {
144 "metadata": {
145 "slideshow": {
145 "slideshow": {
146 "slide_type": "slide"
146 "slide_type": "slide"
147 }
147 }
148 },
148 },
149 "source": [
149 "source": [
150 "Children **can be added to parents** after the parent has been displayed. The **parent is responsible for rendering its children**."
150 "Children **can be added to parents** after the parent has been displayed. The **parent is responsible for rendering its children**."
151 ]
151 ]
152 },
152 },
153 {
153 {
154 "cell_type": "code",
154 "cell_type": "code",
155 "execution_count": null,
155 "execution_count": null,
156 "metadata": {
156 "metadata": {
157 "collapsed": false
157 "collapsed": false
158 },
158 },
159 "outputs": [],
159 "outputs": [],
160 "source": [
160 "source": [
161 "container = widgets.Box()\n",
161 "container = widgets.Box()\n",
162 "container.border_color = 'red'\n",
162 "container.border_color = 'red'\n",
163 "container.border_style = 'dotted'\n",
163 "container.border_style = 'dotted'\n",
164 "container.border_width = 3\n",
164 "container.border_width = 3\n",
165 "display(container)\n",
165 "display(container)\n",
166 "\n",
166 "\n",
167 "int_range = widgets.IntSlider()\n",
167 "int_range = widgets.IntSlider()\n",
168 "container.children=[int_range]"
168 "container.children=[int_range]"
169 ]
169 ]
170 },
170 },
171 {
171 {
172 "cell_type": "markdown",
172 "cell_type": "markdown",
173 "metadata": {
173 "metadata": {
174 "slideshow": {
174 "slideshow": {
175 "slide_type": "slide"
175 "slide_type": "slide"
176 }
176 }
177 },
177 },
178 "source": [
178 "source": [
179 "## Fancy boxes"
179 "## Fancy boxes"
180 ]
180 ]
181 },
181 },
182 {
182 {
183 "cell_type": "markdown",
183 "cell_type": "markdown",
184 "metadata": {},
184 "metadata": {},
185 "source": [
185 "source": [
186 "If you need to display a more complicated set of widgets, there are **specialized containers** that you can use. To display **multiple sets of widgets**, you can use an **`Accordion` or a `Tab` in combination with one `Box` per set of widgets** (as seen below). The \"pages\" of these widgets are their children. To set the titles of the pages, one can **call `set_title`**."
186 "If you need to display a more complicated set of widgets, there are **specialized containers** that you can use. To display **multiple sets of widgets**, you can use an **`Accordion` or a `Tab` in combination with one `Box` per set of widgets** (as seen below). The \"pages\" of these widgets are their children. To set the titles of the pages, one can **call `set_title`**."
187 ]
187 ]
188 },
188 },
189 {
189 {
190 "cell_type": "markdown",
190 "cell_type": "markdown",
191 "metadata": {},
191 "metadata": {},
192 "source": [
192 "source": [
193 "### Accordion"
193 "### Accordion"
194 ]
194 ]
195 },
195 },
196 {
196 {
197 "cell_type": "code",
197 "cell_type": "code",
198 "execution_count": null,
198 "execution_count": null,
199 "metadata": {
199 "metadata": {
200 "collapsed": false
200 "collapsed": false
201 },
201 },
202 "outputs": [],
202 "outputs": [],
203 "source": [
203 "source": [
204 "name1 = widgets.Text(description='Location:')\n",
204 "name1 = widgets.Text(description='Location:')\n",
205 "zip1 = widgets.BoundedIntText(description='Zip:', min=0, max=99999)\n",
205 "zip1 = widgets.BoundedIntText(description='Zip:', min=0, max=99999)\n",
206 "page1 = widgets.Box(children=[name1, zip1])\n",
206 "page1 = widgets.Box(children=[name1, zip1])\n",
207 "\n",
207 "\n",
208 "name2 = widgets.Text(description='Location:')\n",
208 "name2 = widgets.Text(description='Location:')\n",
209 "zip2 = widgets.BoundedIntText(description='Zip:', min=0, max=99999)\n",
209 "zip2 = widgets.BoundedIntText(description='Zip:', min=0, max=99999)\n",
210 "page2 = widgets.Box(children=[name2, zip2])\n",
210 "page2 = widgets.Box(children=[name2, zip2])\n",
211 "\n",
211 "\n",
212 "accord = widgets.Accordion(children=[page1, page2])\n",
212 "accord = widgets.Accordion(children=[page1, page2])\n",
213 "display(accord)\n",
213 "display(accord)\n",
214 "\n",
214 "\n",
215 "accord.set_title(0, 'From')\n",
215 "accord.set_title(0, 'From')\n",
216 "accord.set_title(1, 'To')"
216 "accord.set_title(1, 'To')"
217 ]
217 ]
218 },
218 },
219 {
219 {
220 "cell_type": "markdown",
220 "cell_type": "markdown",
221 "metadata": {
221 "metadata": {
222 "slideshow": {
222 "slideshow": {
223 "slide_type": "slide"
223 "slide_type": "slide"
224 }
224 }
225 },
225 },
226 "source": [
226 "source": [
227 "### TabWidget"
227 "### TabWidget"
228 ]
228 ]
229 },
229 },
230 {
230 {
231 "cell_type": "code",
231 "cell_type": "code",
232 "execution_count": null,
232 "execution_count": null,
233 "metadata": {
233 "metadata": {
234 "collapsed": false
234 "collapsed": false
235 },
235 },
236 "outputs": [],
236 "outputs": [],
237 "source": [
237 "source": [
238 "name = widgets.Text(description='Name:')\n",
238 "name = widgets.Text(description='Name:')\n",
239 "color = widgets.Dropdown(description='Color:', values=['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'])\n",
239 "color = widgets.Dropdown(description='Color:', options=['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'])\n",
240 "page1 = widgets.Box(children=[name, color])\n",
240 "page1 = widgets.Box(children=[name, color])\n",
241 "\n",
241 "\n",
242 "age = widgets.IntSlider(description='Age:', min=0, max=120, value=50)\n",
242 "age = widgets.IntSlider(description='Age:', min=0, max=120, value=50)\n",
243 "gender = widgets.RadioButtons(description='Gender:', values=['male', 'female'])\n",
243 "gender = widgets.RadioButtons(description='Gender:', options=['male', 'female'])\n",
244 "page2 = widgets.Box(children=[age, gender])\n",
244 "page2 = widgets.Box(children=[age, gender])\n",
245 "\n",
245 "\n",
246 "tabs = widgets.Tab(children=[page1, page2])\n",
246 "tabs = widgets.Tab(children=[page1, page2])\n",
247 "display(tabs)\n",
247 "display(tabs)\n",
248 "\n",
248 "\n",
249 "tabs.set_title(0, 'Name')\n",
249 "tabs.set_title(0, 'Name')\n",
250 "tabs.set_title(1, 'Details')"
250 "tabs.set_title(1, 'Details')"
251 ]
251 ]
252 },
252 },
253 {
253 {
254 "cell_type": "markdown",
254 "cell_type": "markdown",
255 "metadata": {
255 "metadata": {
256 "slideshow": {
256 "slideshow": {
257 "slide_type": "slide"
257 "slide_type": "slide"
258 }
258 }
259 },
259 },
260 "source": [
260 "source": [
261 "# Alignment"
261 "# Alignment"
262 ]
262 ]
263 },
263 },
264 {
264 {
265 "cell_type": "markdown",
265 "cell_type": "markdown",
266 "metadata": {},
266 "metadata": {},
267 "source": [
267 "source": [
268 "Most widgets have a **`description` attribute**, which allows a label for the widget to be defined.\n",
268 "Most widgets have a **`description` attribute**, which allows a label for the widget to be defined.\n",
269 "The label of the widget **has a fixed minimum width**.\n",
269 "The label of the widget **has a fixed minimum width**.\n",
270 "The text of the label is **always right aligned and the widget is left aligned**:"
270 "The text of the label is **always right aligned and the widget is left aligned**:"
271 ]
271 ]
272 },
272 },
273 {
273 {
274 "cell_type": "code",
274 "cell_type": "code",
275 "execution_count": null,
275 "execution_count": null,
276 "metadata": {
276 "metadata": {
277 "collapsed": false
277 "collapsed": false
278 },
278 },
279 "outputs": [],
279 "outputs": [],
280 "source": [
280 "source": [
281 "display(widgets.Text(description=\"a:\"))\n",
281 "display(widgets.Text(description=\"a:\"))\n",
282 "display(widgets.Text(description=\"aa:\"))\n",
282 "display(widgets.Text(description=\"aa:\"))\n",
283 "display(widgets.Text(description=\"aaa:\"))"
283 "display(widgets.Text(description=\"aaa:\"))"
284 ]
284 ]
285 },
285 },
286 {
286 {
287 "cell_type": "markdown",
287 "cell_type": "markdown",
288 "metadata": {
288 "metadata": {
289 "slideshow": {
289 "slideshow": {
290 "slide_type": "slide"
290 "slide_type": "slide"
291 }
291 }
292 },
292 },
293 "source": [
293 "source": [
294 "If a **label is longer** than the minimum width, the **widget is shifted to the right**:"
294 "If a **label is longer** than the minimum width, the **widget is shifted to the right**:"
295 ]
295 ]
296 },
296 },
297 {
297 {
298 "cell_type": "code",
298 "cell_type": "code",
299 "execution_count": null,
299 "execution_count": null,
300 "metadata": {
300 "metadata": {
301 "collapsed": false
301 "collapsed": false
302 },
302 },
303 "outputs": [],
303 "outputs": [],
304 "source": [
304 "source": [
305 "display(widgets.Text(description=\"a:\"))\n",
305 "display(widgets.Text(description=\"a:\"))\n",
306 "display(widgets.Text(description=\"aa:\"))\n",
306 "display(widgets.Text(description=\"aa:\"))\n",
307 "display(widgets.Text(description=\"aaa:\"))\n",
307 "display(widgets.Text(description=\"aaa:\"))\n",
308 "display(widgets.Text(description=\"aaaaaaaaaaaaaaaaaa:\"))"
308 "display(widgets.Text(description=\"aaaaaaaaaaaaaaaaaa:\"))"
309 ]
309 ]
310 },
310 },
311 {
311 {
312 "cell_type": "markdown",
312 "cell_type": "markdown",
313 "metadata": {
313 "metadata": {
314 "slideshow": {
314 "slideshow": {
315 "slide_type": "slide"
315 "slide_type": "slide"
316 }
316 }
317 },
317 },
318 "source": [
318 "source": [
319 "If a `description` is **not set** for the widget, the **label is not displayed**:"
319 "If a `description` is **not set** for the widget, the **label is not displayed**:"
320 ]
320 ]
321 },
321 },
322 {
322 {
323 "cell_type": "code",
323 "cell_type": "code",
324 "execution_count": null,
324 "execution_count": null,
325 "metadata": {
325 "metadata": {
326 "collapsed": false
326 "collapsed": false
327 },
327 },
328 "outputs": [],
328 "outputs": [],
329 "source": [
329 "source": [
330 "display(widgets.Text(description=\"a:\"))\n",
330 "display(widgets.Text(description=\"a:\"))\n",
331 "display(widgets.Text(description=\"aa:\"))\n",
331 "display(widgets.Text(description=\"aa:\"))\n",
332 "display(widgets.Text(description=\"aaa:\"))\n",
332 "display(widgets.Text(description=\"aaa:\"))\n",
333 "display(widgets.Text())"
333 "display(widgets.Text())"
334 ]
334 ]
335 },
335 },
336 {
336 {
337 "cell_type": "markdown",
337 "cell_type": "markdown",
338 "metadata": {
338 "metadata": {
339 "slideshow": {
339 "slideshow": {
340 "slide_type": "slide"
340 "slide_type": "slide"
341 }
341 }
342 },
342 },
343 "source": [
343 "source": [
344 "## Flex boxes"
344 "## Flex boxes"
345 ]
345 ]
346 },
346 },
347 {
347 {
348 "cell_type": "markdown",
348 "cell_type": "markdown",
349 "metadata": {},
349 "metadata": {},
350 "source": [
350 "source": [
351 "Widgets can be aligned using the `FlexBox`, `HBox`, and `VBox` widgets."
351 "Widgets can be aligned using the `FlexBox`, `HBox`, and `VBox` widgets."
352 ]
352 ]
353 },
353 },
354 {
354 {
355 "cell_type": "markdown",
355 "cell_type": "markdown",
356 "metadata": {
356 "metadata": {
357 "slideshow": {
357 "slideshow": {
358 "slide_type": "slide"
358 "slide_type": "slide"
359 }
359 }
360 },
360 },
361 "source": [
361 "source": [
362 "### Application to widgets"
362 "### Application to widgets"
363 ]
363 ]
364 },
364 },
365 {
365 {
366 "cell_type": "markdown",
366 "cell_type": "markdown",
367 "metadata": {},
367 "metadata": {},
368 "source": [
368 "source": [
369 "Widgets display vertically by default:"
369 "Widgets display vertically by default:"
370 ]
370 ]
371 },
371 },
372 {
372 {
373 "cell_type": "code",
373 "cell_type": "code",
374 "execution_count": null,
374 "execution_count": null,
375 "metadata": {
375 "metadata": {
376 "collapsed": false
376 "collapsed": false
377 },
377 },
378 "outputs": [],
378 "outputs": [],
379 "source": [
379 "source": [
380 "buttons = [widgets.Button(description=str(i)) for i in range(3)]\n",
380 "buttons = [widgets.Button(description=str(i)) for i in range(3)]\n",
381 "display(*buttons)"
381 "display(*buttons)"
382 ]
382 ]
383 },
383 },
384 {
384 {
385 "cell_type": "markdown",
385 "cell_type": "markdown",
386 "metadata": {
386 "metadata": {
387 "slideshow": {
387 "slideshow": {
388 "slide_type": "slide"
388 "slide_type": "slide"
389 }
389 }
390 },
390 },
391 "source": [
391 "source": [
392 "### Using hbox"
392 "### Using hbox"
393 ]
393 ]
394 },
394 },
395 {
395 {
396 "cell_type": "markdown",
396 "cell_type": "markdown",
397 "metadata": {},
397 "metadata": {},
398 "source": [
398 "source": [
399 "To make widgets display horizontally, you need to **child them to a `HBox` widget**."
399 "To make widgets display horizontally, you need to **child them to a `HBox` widget**."
400 ]
400 ]
401 },
401 },
402 {
402 {
403 "cell_type": "code",
403 "cell_type": "code",
404 "execution_count": null,
404 "execution_count": null,
405 "metadata": {
405 "metadata": {
406 "collapsed": false
406 "collapsed": false
407 },
407 },
408 "outputs": [],
408 "outputs": [],
409 "source": [
409 "source": [
410 "container = widgets.HBox(children=buttons)\n",
410 "container = widgets.HBox(children=buttons)\n",
411 "display(container)"
411 "display(container)"
412 ]
412 ]
413 },
413 },
414 {
414 {
415 "cell_type": "markdown",
415 "cell_type": "markdown",
416 "metadata": {},
416 "metadata": {},
417 "source": [
417 "source": [
418 "By setting the width of the container to 100% and its `pack` to `center`, you can center the buttons."
418 "By setting the width of the container to 100% and its `pack` to `center`, you can center the buttons."
419 ]
419 ]
420 },
420 },
421 {
421 {
422 "cell_type": "code",
422 "cell_type": "code",
423 "execution_count": null,
423 "execution_count": null,
424 "metadata": {
424 "metadata": {
425 "collapsed": false
425 "collapsed": false
426 },
426 },
427 "outputs": [],
427 "outputs": [],
428 "source": [
428 "source": [
429 "container.width = '100%'\n",
429 "container.width = '100%'\n",
430 "container.pack = 'center'"
430 "container.pack = 'center'"
431 ]
431 ]
432 },
432 },
433 {
433 {
434 "cell_type": "markdown",
434 "cell_type": "markdown",
435 "metadata": {
435 "metadata": {
436 "slideshow": {
436 "slideshow": {
437 "slide_type": "slide"
437 "slide_type": "slide"
438 }
438 }
439 },
439 },
440 "source": [
440 "source": [
441 "## Visibility"
441 "## Visibility"
442 ]
442 ]
443 },
443 },
444 {
444 {
445 "cell_type": "markdown",
445 "cell_type": "markdown",
446 "metadata": {},
446 "metadata": {},
447 "source": [
447 "source": [
448 "Sometimes it is necessary to **hide or show widgets** in place, **without having to re-display** the widget.\n",
448 "Sometimes it is necessary to **hide or show widgets** in place, **without having to re-display** the widget.\n",
449 "The `visible` property of widgets can be used to hide or show **widgets that have already been displayed** (as seen below). The `visible` property can be:\n",
449 "The `visible` property of widgets can be used to hide or show **widgets that have already been displayed** (as seen below). The `visible` property can be:\n",
450 "* `True` - the widget is displayed\n",
450 "* `True` - the widget is displayed\n",
451 "* `False` - the widget is hidden, and the empty space where the widget would be is collapsed\n",
451 "* `False` - the widget is hidden, and the empty space where the widget would be is collapsed\n",
452 "* `None` - the widget is hidden, and the empty space where the widget would be is shown"
452 "* `None` - the widget is hidden, and the empty space where the widget would be is shown"
453 ]
453 ]
454 },
454 },
455 {
455 {
456 "cell_type": "code",
456 "cell_type": "code",
457 "execution_count": null,
457 "execution_count": null,
458 "metadata": {
458 "metadata": {
459 "collapsed": false
459 "collapsed": false
460 },
460 },
461 "outputs": [],
461 "outputs": [],
462 "source": [
462 "source": [
463 "w1 = widgets.Latex(value=\"First line\")\n",
463 "w1 = widgets.Latex(value=\"First line\")\n",
464 "w2 = widgets.Latex(value=\"Second line\")\n",
464 "w2 = widgets.Latex(value=\"Second line\")\n",
465 "w3 = widgets.Latex(value=\"Third line\")\n",
465 "w3 = widgets.Latex(value=\"Third line\")\n",
466 "display(w1, w2, w3)"
466 "display(w1, w2, w3)"
467 ]
467 ]
468 },
468 },
469 {
469 {
470 "cell_type": "code",
470 "cell_type": "code",
471 "execution_count": null,
471 "execution_count": null,
472 "metadata": {
472 "metadata": {
473 "collapsed": true
473 "collapsed": true
474 },
474 },
475 "outputs": [],
475 "outputs": [],
476 "source": [
476 "source": [
477 "w2.visible=None"
477 "w2.visible=None"
478 ]
478 ]
479 },
479 },
480 {
480 {
481 "cell_type": "code",
481 "cell_type": "code",
482 "execution_count": null,
482 "execution_count": null,
483 "metadata": {
483 "metadata": {
484 "collapsed": false
484 "collapsed": false
485 },
485 },
486 "outputs": [],
486 "outputs": [],
487 "source": [
487 "source": [
488 "w2.visible=False"
488 "w2.visible=False"
489 ]
489 ]
490 },
490 },
491 {
491 {
492 "cell_type": "code",
492 "cell_type": "code",
493 "execution_count": null,
493 "execution_count": null,
494 "metadata": {
494 "metadata": {
495 "collapsed": false
495 "collapsed": false
496 },
496 },
497 "outputs": [],
497 "outputs": [],
498 "source": [
498 "source": [
499 "w2.visible=True"
499 "w2.visible=True"
500 ]
500 ]
501 },
501 },
502 {
502 {
503 "cell_type": "markdown",
503 "cell_type": "markdown",
504 "metadata": {
504 "metadata": {
505 "slideshow": {
505 "slideshow": {
506 "slide_type": "slide"
506 "slide_type": "slide"
507 }
507 }
508 },
508 },
509 "source": [
509 "source": [
510 "### Another example"
510 "### Another example"
511 ]
511 ]
512 },
512 },
513 {
513 {
514 "cell_type": "markdown",
514 "cell_type": "markdown",
515 "metadata": {},
515 "metadata": {},
516 "source": [
516 "source": [
517 "In the example below, a form is rendered, which conditionally displays widgets depending on the state of other widgets. Try toggling the student checkbox."
517 "In the example below, a form is rendered, which conditionally displays widgets depending on the state of other widgets. Try toggling the student checkbox."
518 ]
518 ]
519 },
519 },
520 {
520 {
521 "cell_type": "code",
521 "cell_type": "code",
522 "execution_count": null,
522 "execution_count": null,
523 "metadata": {
523 "metadata": {
524 "collapsed": false
524 "collapsed": false
525 },
525 },
526 "outputs": [],
526 "outputs": [],
527 "source": [
527 "source": [
528 "form = widgets.VBox()\n",
528 "form = widgets.VBox()\n",
529 "first = widgets.Text(description=\"First Name:\")\n",
529 "first = widgets.Text(description=\"First Name:\")\n",
530 "last = widgets.Text(description=\"Last Name:\")\n",
530 "last = widgets.Text(description=\"Last Name:\")\n",
531 "\n",
531 "\n",
532 "student = widgets.Checkbox(description=\"Student:\", value=False)\n",
532 "student = widgets.Checkbox(description=\"Student:\", value=False)\n",
533 "school_info = widgets.VBox(visible=False, children=[\n",
533 "school_info = widgets.VBox(visible=False, children=[\n",
534 " widgets.Text(description=\"School:\"),\n",
534 " widgets.Text(description=\"School:\"),\n",
535 " widgets.IntText(description=\"Grade:\", min=0, max=12)\n",
535 " widgets.IntText(description=\"Grade:\", min=0, max=12)\n",
536 " ])\n",
536 " ])\n",
537 "\n",
537 "\n",
538 "pet = widgets.Text(description=\"Pet's Name:\")\n",
538 "pet = widgets.Text(description=\"Pet's Name:\")\n",
539 "form.children = [first, last, student, school_info, pet]\n",
539 "form.children = [first, last, student, school_info, pet]\n",
540 "display(form)\n",
540 "display(form)\n",
541 "\n",
541 "\n",
542 "def on_student_toggle(name, value):\n",
542 "def on_student_toggle(name, value):\n",
543 " if value:\n",
543 " if value:\n",
544 " school_info.visible = True\n",
544 " school_info.visible = True\n",
545 " else:\n",
545 " else:\n",
546 " school_info.visible = False\n",
546 " school_info.visible = False\n",
547 "student.on_trait_change(on_student_toggle, 'value')\n"
547 "student.on_trait_change(on_student_toggle, 'value')\n"
548 ]
548 ]
549 },
549 },
550 {
550 {
551 "cell_type": "markdown",
551 "cell_type": "markdown",
552 "metadata": {},
552 "metadata": {},
553 "source": [
553 "source": [
554 "[Index](Index.ipynb) - [Back](Widget Events.ipynb) - [Next](Custom Widget - Hello World.ipynb)"
554 "[Index](Index.ipynb) - [Back](Widget Events.ipynb) - [Next](Custom Widget - Hello World.ipynb)"
555 ]
555 ]
556 }
556 }
557 ],
557 ],
558 "metadata": {
558 "metadata": {
559 "cell_tags": [
559 "cell_tags": [
560 [
560 [
561 "<None>",
561 "<None>",
562 null
562 null
563 ]
563 ]
564 ],
564 ],
565 "kernelspec": {
565 "kernelspec": {
566 "display_name": "Python 3",
566 "display_name": "Python 3",
567 "language": "python",
567 "language": "python",
568 "name": "python3"
568 "name": "python3"
569 },
569 },
570 "language_info": {
570 "language_info": {
571 "codemirror_mode": {
571 "codemirror_mode": {
572 "name": "ipython",
572 "name": "ipython",
573 "version": 3
573 "version": 3
574 },
574 },
575 "file_extension": ".py",
575 "file_extension": ".py",
576 "mimetype": "text/x-python",
576 "mimetype": "text/x-python",
577 "name": "python",
577 "name": "python",
578 "nbconvert_exporter": "python",
578 "nbconvert_exporter": "python",
579 "pygments_lexer": "ipython3",
579 "pygments_lexer": "ipython3",
580 "version": "3.4.2"
580 "version": "3.4.2"
581 }
581 }
582 },
582 },
583 "nbformat": 4,
583 "nbformat": 4,
584 "nbformat_minor": 0
584 "nbformat_minor": 0
585 }
585 }
General Comments 0
You need to be logged in to leave comments. Login now