##// END OF EJS Templates
Merge pull request #5012 from minrk/selection-dict...
Brian E. Granger -
r15066:a4c95005 merge
parent child Browse files
Show More
@@ -1,376 +1,376 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // SelectionWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(WidgetManager){
18 18
19 19 var DropdownView = IPython.DOMWidgetView.extend({
20 20 render : function(){
21 21 // Called when view is rendered.
22 22 this.$el
23 23 .addClass('widget-hbox-single');
24 24 this.$label = $('<div />')
25 25 .appendTo(this.$el)
26 26 .addClass('widget-hlabel')
27 27 .hide();
28 28 this.$buttongroup = $('<div />')
29 29 .addClass('widget_item')
30 30 .addClass('btn-group')
31 31 .appendTo(this.$el);
32 32 this.$el_to_style = this.$buttongroup; // Set default element to style
33 33 this.$droplabel = $('<button />')
34 34 .addClass('btn')
35 35 .addClass('widget-combo-btn')
36 36 .text(' ')
37 37 .appendTo(this.$buttongroup);
38 38 this.$dropbutton = $('<button />')
39 39 .addClass('btn')
40 40 .addClass('dropdown-toggle')
41 41 .addClass('widget-combo-carrot-btn')
42 42 .attr('data-toggle', 'dropdown')
43 43 .append($('<span />').addClass("caret"))
44 44 .appendTo(this.$buttongroup);
45 45 this.$droplist = $('<ul />')
46 46 .addClass('dropdown-menu')
47 47 .appendTo(this.$buttongroup);
48 48
49 49 // Set defaults.
50 50 this.update();
51 51 },
52 52
53 53 update : function(options){
54 54 // Update the contents of this view
55 55 //
56 56 // Called when the model is changed. The model may have been
57 57 // changed by another view or by a state update from the back-end.
58 58
59 59 if (options === undefined || options.updated_view != this) {
60 var selected_item_text = this.model.get('_value');
60 var selected_item_text = this.model.get('value_name');
61 61 if (selected_item_text.length === 0) {
62 62 this.$droplabel.text(' ');
63 63 } else {
64 64 this.$droplabel.text(selected_item_text);
65 65 }
66 66
67 var items = this.model.get('labels');
67 var items = this.model.get('value_names');
68 68 var $replace_droplist = $('<ul />')
69 69 .addClass('dropdown-menu');
70 70 var that = this;
71 71 _.each(items, function(item, i) {
72 72 var item_button = $('<a href="#"/>')
73 73 .text(item)
74 74 .on('click', $.proxy(that.handle_click, that));
75 75 $replace_droplist.append($('<li />').append(item_button));
76 76 });
77 77
78 78 this.$droplist.replaceWith($replace_droplist);
79 79 this.$droplist.remove();
80 80 this.$droplist = $replace_droplist;
81 81
82 82 if (this.model.get('disabled')) {
83 83 this.$buttongroup.attr('disabled','disabled');
84 84 this.$droplabel.attr('disabled','disabled');
85 85 this.$dropbutton.attr('disabled','disabled');
86 86 this.$droplist.attr('disabled','disabled');
87 87 } else {
88 88 this.$buttongroup.removeAttr('disabled');
89 89 this.$droplabel.removeAttr('disabled');
90 90 this.$dropbutton.removeAttr('disabled');
91 91 this.$droplist.removeAttr('disabled');
92 92 }
93 93
94 94 var description = this.model.get('description');
95 95 if (description.length === 0) {
96 96 this.$label.hide();
97 97 } else {
98 98 this.$label.text(description);
99 99 this.$label.show();
100 100 }
101 101 }
102 102 return DropdownView.__super__.update.apply(this);
103 103 },
104 104
105 105 handle_click: function (e) {
106 106 // Handle when a value is clicked.
107 107
108 108 // Calling model.set will trigger all of the other views of the
109 109 // model to update.
110 this.model.set('_value', $(e.target).text(), {updated_view: this});
110 this.model.set('value_name', $(e.target).text(), {updated_view: this});
111 111 this.touch();
112 112 },
113 113
114 114 });
115 115 WidgetManager.register_widget_view('DropdownView', DropdownView);
116 116
117 117
118 118 var RadioButtonsView = IPython.DOMWidgetView.extend({
119 119 render : function(){
120 120 // Called when view is rendered.
121 121 this.$el
122 122 .addClass('widget-hbox');
123 123 this.$label = $('<div />')
124 124 .appendTo(this.$el)
125 125 .addClass('widget-hlabel')
126 126 .hide();
127 127 this.$container = $('<div />')
128 128 .appendTo(this.$el)
129 129 .addClass('widget-container')
130 130 .addClass('vbox');
131 131 this.$el_to_style = this.$container; // Set default element to style
132 132 this.update();
133 133 },
134 134
135 135 update : function(options){
136 136 // Update the contents of this view
137 137 //
138 138 // Called when the model is changed. The model may have been
139 139 // changed by another view or by a state update from the back-end.
140 140 if (options === undefined || options.updated_view != this) {
141 141 // Add missing items to the DOM.
142 var items = this.model.get('labels');
142 var items = this.model.get('value_names');
143 143 var disabled = this.model.get('disabled');
144 144 var that = this;
145 145 _.each(items, function(item, index) {
146 146 var item_query = ' :input[value="' + item + '"]';
147 147 if (that.$el.find(item_query).length === 0) {
148 148 var $label = $('<label />')
149 149 .addClass('radio')
150 150 .text(item)
151 151 .appendTo(that.$container);
152 152
153 153 $('<input />')
154 154 .attr('type', 'radio')
155 155 .addClass(that.model)
156 156 .val(item)
157 157 .prependTo($label)
158 158 .on('click', $.proxy(that.handle_click, that));
159 159 }
160 160
161 161 var $item_element = that.$container.find(item_query);
162 if (that.model.get('_value') == item) {
162 if (that.model.get('value_name') == item) {
163 163 $item_element.prop('checked', true);
164 164 } else {
165 165 $item_element.prop('checked', false);
166 166 }
167 167 $item_element.prop('disabled', disabled);
168 168 });
169 169
170 170 // Remove items that no longer exist.
171 171 this.$container.find('input').each(function(i, obj) {
172 172 var value = $(obj).val();
173 173 var found = false;
174 174 _.each(items, function(item, index) {
175 175 if (item == value) {
176 176 found = true;
177 177 return false;
178 178 }
179 179 });
180 180
181 181 if (!found) {
182 182 $(obj).parent().remove();
183 183 }
184 184 });
185 185
186 186 var description = this.model.get('description');
187 187 if (description.length === 0) {
188 188 this.$label.hide();
189 189 } else {
190 190 this.$label.text(description);
191 191 this.$label.show();
192 192 }
193 193 }
194 194 return RadioButtonsView.__super__.update.apply(this);
195 195 },
196 196
197 197 handle_click: function (e) {
198 198 // Handle when a value is clicked.
199 199
200 200 // Calling model.set will trigger all of the other views of the
201 201 // model to update.
202 this.model.set('_value', $(e.target).val(), {updated_view: this});
202 this.model.set('value_name', $(e.target).val(), {updated_view: this});
203 203 this.touch();
204 204 },
205 205 });
206 206 WidgetManager.register_widget_view('RadioButtonsView', RadioButtonsView);
207 207
208 208
209 var ToggleButtonsView = IPython.DOMWidgetView.extend({
209 var ToggleButtonsView = IPython.DOMWidgetView.extend({
210 210 render : function(){
211 211 // Called when view is rendered.
212 212 this.$el
213 213 .addClass('widget-hbox-single');
214 214 this.$label = $('<div />')
215 215 .appendTo(this.$el)
216 216 .addClass('widget-hlabel')
217 217 .hide();
218 218 this.$buttongroup = $('<div />')
219 219 .addClass('btn-group')
220 220 .attr('data-toggle', 'buttons-radio')
221 221 .appendTo(this.$el);
222 222 this.$el_to_style = this.$buttongroup; // Set default element to style
223 223 this.update();
224 224 },
225 225
226 226 update : function(options){
227 227 // Update the contents of this view
228 228 //
229 229 // Called when the model is changed. The model may have been
230 230 // changed by another view or by a state update from the back-end.
231 231 if (options === undefined || options.updated_view != this) {
232 232 // Add missing items to the DOM.
233 var items = this.model.get('labels');
233 var items = this.model.get('value_names');
234 234 var disabled = this.model.get('disabled');
235 235 var that = this;
236 236 _.each(items, function(item, index) {
237 237 var item_query = ' :contains("' + item + '")';
238 238 if (that.$buttongroup.find(item_query).length === 0) {
239 239 $('<button />')
240 240 .attr('type', 'button')
241 241 .addClass('btn')
242 242 .text(item)
243 243 .appendTo(that.$buttongroup)
244 244 .on('click', $.proxy(that.handle_click, that));
245 245 }
246 246
247 247 var $item_element = that.$buttongroup.find(item_query);
248 if (that.model.get('_value') == item) {
248 if (that.model.get('value_name') == item) {
249 249 $item_element.addClass('active');
250 250 } else {
251 251 $item_element.removeClass('active');
252 252 }
253 253 $item_element.prop('disabled', disabled);
254 254 });
255 255
256 256 // Remove items that no longer exist.
257 257 this.$buttongroup.find('button').each(function(i, obj) {
258 258 var value = $(obj).text();
259 259 var found = false;
260 260 _.each(items, function(item, index) {
261 261 if (item == value) {
262 262 found = true;
263 263 return false;
264 264 }
265 265 });
266 266
267 267 if (!found) {
268 268 $(obj).remove();
269 269 }
270 270 });
271 271
272 272 var description = this.model.get('description');
273 273 if (description.length === 0) {
274 274 this.$label.hide();
275 275 } else {
276 276 this.$label.text(description);
277 277 this.$label.show();
278 278 }
279 279 }
280 280 return ToggleButtonsView.__super__.update.apply(this);
281 281 },
282 282
283 283 handle_click: function (e) {
284 284 // Handle when a value is clicked.
285 285
286 286 // Calling model.set will trigger all of the other views of the
287 287 // model to update.
288 this.model.set('_value', $(e.target).text(), {updated_view: this});
288 this.model.set('value_name', $(e.target).text(), {updated_view: this});
289 289 this.touch();
290 290 },
291 291 });
292 292 WidgetManager.register_widget_view('ToggleButtonsView', ToggleButtonsView);
293 293
294 294
295 295 var SelectView = IPython.DOMWidgetView.extend({
296 296 render : function(){
297 297 // Called when view is rendered.
298 298 this.$el
299 299 .addClass('widget-hbox');
300 300 this.$label = $('<div />')
301 301 .appendTo(this.$el)
302 302 .addClass('widget-hlabel')
303 303 .hide();
304 304 this.$listbox = $('<select />')
305 305 .addClass('widget-listbox')
306 306 .attr('size', 6)
307 307 .appendTo(this.$el);
308 308 this.$el_to_style = this.$listbox; // Set default element to style
309 309 this.update();
310 310 },
311 311
312 312 update : function(options){
313 313 // Update the contents of this view
314 314 //
315 315 // Called when the model is changed. The model may have been
316 316 // changed by another view or by a state update from the back-end.
317 317 if (options === undefined || options.updated_view != this) {
318 318 // Add missing items to the DOM.
319 var items = this.model.get('labels');
319 var items = this.model.get('value_names');
320 320 var that = this;
321 321 _.each(items, function(item, index) {
322 322 var item_query = ' :contains("' + item + '")';
323 323 if (that.$listbox.find(item_query).length === 0) {
324 324 $('<option />')
325 325 .text(item)
326 .attr('_value', item)
326 .attr('value_name', item)
327 327 .appendTo(that.$listbox)
328 328 .on('click', $.proxy(that.handle_click, that));
329 329 }
330 330 });
331 331
332 332 // Select the correct element
333 this.$listbox.val(this.model.get('_value'));
333 this.$listbox.val(this.model.get('value_name'));
334 334
335 335 // Disable listbox if needed
336 336 var disabled = this.model.get('disabled');
337 337 this.$listbox.prop('disabled', disabled);
338 338
339 339 // Remove items that no longer exist.
340 340 this.$listbox.find('option').each(function(i, obj) {
341 341 var value = $(obj).text();
342 342 var found = false;
343 343 _.each(items, function(item, index) {
344 344 if (item == value) {
345 345 found = true;
346 346 return false;
347 347 }
348 348 });
349 349
350 350 if (!found) {
351 351 $(obj).remove();
352 352 }
353 353 });
354 354
355 355 var description = this.model.get('description');
356 356 if (description.length === 0) {
357 357 this.$label.hide();
358 358 } else {
359 359 this.$label.text(description);
360 360 this.$label.show();
361 361 }
362 362 }
363 363 return SelectView.__super__.update.apply(this);
364 364 },
365 365
366 366 handle_click: function (e) {
367 367 // Handle when a value is clicked.
368 368
369 369 // Calling model.set will trigger all of the other views of the
370 370 // model to update.
371 this.model.set('_value', $(e.target).text(), {updated_view: this});
371 this.model.set('value_name', $(e.target).text(), {updated_view: this});
372 372 this.touch();
373 373 },
374 374 });
375 375 WidgetManager.register_widget_view('SelectView', SelectView);
376 376 });
@@ -1,133 +1,135 b''
1 1 // Test selection class
2 2 casper.notebook_test(function () {
3 3 index = this.append_cell(
4 4 'from IPython.html import widgets\n' +
5 5 'from IPython.display import display, clear_output\n' +
6 6 'print("Success")');
7 7 this.execute_cell_then(index);
8 8
9 9 var combo_selector = '.widget-area .widget-subarea .widget-hbox-single .btn-group .widget-combo-btn';
10 10 var multibtn_selector = '.widget-area .widget-subarea .widget-hbox-single .btn-group[data-toggle="buttons-radio"]';
11 11 var radio_selector = '.widget-area .widget-subarea .widget-hbox .vbox';
12 12 var list_selector = '.widget-area .widget-subarea .widget-hbox .widget-listbox';
13 13
14 14 var selection_index;
15 15 var selection_values = 'abcd';
16 16 var check_state = function(context, index, state){
17 17 if (0 <= index && index < selection_values.length) {
18 18 var multibtn_state = context.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(' + (index + 1) + ')', 'hasClass', ['active']);
19 19 var radio_state = context.cell_element_function(selection_index, radio_selector + ' .radio:nth-child(' + (index + 1) + ') input', 'prop', ['checked']);
20 20 var list_val = context.cell_element_function(selection_index, list_selector, 'val');
21 21 var combo_val = context.cell_element_function(selection_index, combo_selector, 'html');
22 22
23 23 var val = selection_values.charAt(index);
24 24 var list_state = (val == list_val);
25 25 var combo_state = (val == combo_val);
26 26
27 27 return multibtn_state == state &&
28 28 radio_state == state &&
29 29 list_state == state &&
30 30 combo_state == state;
31 31 }
32 32 return true;
33 33 };
34 34
35 35 var verify_selection = function(context, index){
36 36 for (var i = 0; i < selection_values.length; i++) {
37 37 if (!check_state(context, i, i==index)) {
38 38 return false;
39 39 }
40 40 }
41 41 return true;
42 42 };
43 43
44 44 //values=["' + selection_values + '"[i] for i in range(4)]
45 45 selection_index = this.append_cell(
46 46 'values=["' + selection_values + '"[i] for i in range(4)]\n' +
47 47 'selection = [widgets.DropdownWidget(values=values),\n' +
48 48 ' widgets.ToggleButtonsWidget(values=values),\n' +
49 49 ' widgets.RadioButtonsWidget(values=values),\n' +
50 50 ' widgets.SelectWidget(values=values)]\n' +
51 51 '[display(selection[i]) for i in range(4)]\n' +
52 52 'for widget in selection:\n' +
53 53 ' def handle_change(name,old,new):\n' +
54 54 ' for other_widget in selection:\n' +
55 55 ' other_widget.value = new\n' +
56 56 ' widget.on_trait_change(handle_change, "value")\n' +
57 57 'print("Success")\n');
58 58 this.execute_cell_then(selection_index, function(index){
59 59 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
60 60 'Create selection cell executed with correct output.');
61 61
62 62 this.test.assert(this.cell_element_exists(index,
63 63 '.widget-area .widget-subarea'),
64 64 'Widget subarea exists.');
65 65
66 66 this.test.assert(this.cell_element_exists(index, combo_selector),
67 67 'Widget combobox exists.');
68 68
69 69 this.test.assert(this.cell_element_exists(index, multibtn_selector),
70 70 'Widget multibutton exists.');
71 71
72 72 this.test.assert(this.cell_element_exists(index, radio_selector),
73 73 'Widget radio buttons exists.');
74 74
75 75 this.test.assert(this.cell_element_exists(index, list_selector),
76 76 'Widget list exists.');
77 77
78 78 // Verify that no items are selected.
79 this.test.assert(verify_selection(this, -1), 'No items selected.');
79 this.test.assert(verify_selection(this, 0), 'Default first item selected.');
80 80 });
81 81
82 82 index = this.append_cell(
83 83 'for widget in selection:\n' +
84 84 ' widget.value = "a"\n' +
85 85 'print("Success")\n');
86 86 this.execute_cell_then(index, function(index){
87 87 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
88 88 'Python select item executed with correct output.');
89 89
90 90 // Verify that the first item is selected.
91 91 this.test.assert(verify_selection(this, 0), 'Python selected');
92 92
93 93 // Verify that selecting a radio button updates all of the others.
94 94 this.cell_element_function(selection_index, radio_selector + ' .radio:nth-child(2) input', 'click');
95 95 });
96 96 this.wait_for_idle();
97 97 this.then(function () {
98 98 this.test.assert(verify_selection(this, 1), 'Radio button selection updated view states correctly.');
99 99
100 100 // Verify that selecting a list option updates all of the others.
101 101 this.cell_element_function(selection_index, list_selector + ' option:nth-child(3)', 'click');
102 102 });
103 103 this.wait_for_idle();
104 104 this.then(function () {
105 105 this.test.assert(verify_selection(this, 2), 'List selection updated view states correctly.');
106 106
107 107 // Verify that selecting a multibutton option updates all of the others.
108 108 this.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(4)', 'click');
109 109 });
110 110 this.wait_for_idle();
111 111 this.then(function () {
112 112 this.test.assert(verify_selection(this, 3), 'Multibutton selection updated view states correctly.');
113 113
114 114 // Verify that selecting a combobox option updates all of the others.
115 115 this.cell_element_function(selection_index, '.widget-area .widget-subarea .widget-hbox-single .btn-group ul.dropdown-menu li:nth-child(3) a', 'click');
116 116 });
117 117 this.wait_for_idle();
118 118 this.then(function () {
119 119 this.test.assert(verify_selection(this, 2), 'Combobox selection updated view states correctly.');
120 120 });
121 121
122 122 this.wait_for_idle();
123 123
124 124 index = this.append_cell(
125 125 'for widget in selection:\n' +
126 ' widget.values = list(widget.values) + ["z"]\n' +
126 ' d = widget.values.copy()\n' +
127 ' d["z"] = "z"\n' +
128 ' widget.values = d\n' +
127 129 'selection[0].value = "z"');
128 130 this.execute_cell_then(index, function(index){
129 131
130 132 // Verify that selecting a combobox option updates all of the others.
131 133 this.test.assert(verify_selection(this, 4), 'Item added to selection widget.');
132 134 });
133 135 }); No newline at end of file
@@ -1,95 +1,119 b''
1 """SelectionWidget class.
1 """SelectionWidget classes.
2 2
3 3 Represents an enumeration using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16
17 from collections import OrderedDict
16 18 from threading import Lock
17 19
18 20 from .widget import DOMWidget
19 from IPython.utils.traitlets import Unicode, List, Bool, Any, Dict
21 from IPython.utils.traitlets import Unicode, List, Bool, Any, Dict, TraitError
22 from IPython.utils.py3compat import unicode_type
20 23
21 24 #-----------------------------------------------------------------------------
22 25 # SelectionWidget
23 26 #-----------------------------------------------------------------------------
24 27 class _SelectionWidget(DOMWidget):
25 value = Any(help="Selected value")
26 values = List(help="List of values the user can select")
27 labels = List(help="""List of string representations for each value.
28 These string representations are used to display the values in the
29 front-end.""", sync=True) # Only synced to the back-end.
28 """Base class for Selection widgets
29
30 ``values`` can be specified as a list or dict. If given as a list,
31 it will be transformed to a dict of the form ``{str(value):value}``.
32 """
33
34 value = Any(help="Selected value")
35 values = Dict(help="""Dictionary of {name: value} the user can select.
36
37 The keys of this dictionary are the strings that will be displayed in the UI,
38 representing the actual Python choices.
39
40 The keys of this dictionary are also available as value_names.
41 """)
42 value_name = Unicode(help="The name of the selected value", sync=True)
43 value_names = List(Unicode, help="""Read-only list of names for each value.
44
45 If values is specified as a list, this is the string representation of each element.
46 Otherwise, it is the keys of the values dictionary.
47
48 These strings are used to display the choices in the front-end.""", sync=True)
30 49 disabled = Bool(False, help="Enable or disable user changes", sync=True)
31 50 description = Unicode(help="Description of the value this widget represents", sync=True)
51
32 52
33 _value = Unicode(sync=True) # Bi-directionally synced.
34
35 def __init__(self, *pargs, **kwargs):
36 """Constructor"""
53 def __init__(self, *args, **kwargs):
37 54 self.value_lock = Lock()
38 self.on_trait_change(self._string_value_set, ['_value'])
39 DOMWidget.__init__(self, *pargs, **kwargs)
40
41 def _labels_changed(self, name=None, old=None, new=None):
42 """Handles when the value_names Dict has been changed.
43
44 This method sets the _reverse_value_names Dict to the inverse of the new
45 value for the value_names Dict."""
46 if len(new) != len(self.values):
47 raise TypeError('Labels list must be the same size as the values list.')
48
49 def _values_changed(self, name=None, old=None, new=None):
50 """Handles when the value_names Dict has been changed.
51
52 This method sets the _reverse_value_names Dict to the inverse of the new
53 value for the value_names Dict."""
54 if len(new) != len(self.labels):
55 self.labels = [(self.labels[i] if i < len(self.labels) else str(v)) for i, v in enumerate(new)]
55 self._in_values_changed = False
56 if 'values' in kwargs:
57 values = kwargs['values']
58 # convert list values to an dict of {str(v):v}
59 if isinstance(values, list):
60 # preserve list order with an OrderedDict
61 kwargs['values'] = OrderedDict((unicode_type(v), v) for v in values)
62 DOMWidget.__init__(self, *args, **kwargs)
63
64 def _values_changed(self, name, old, new):
65 """Handles when the values dict has been changed.
66
67 Setting values implies setting value names from the keys of the dict.
68 """
69 self._in_values_changed = True
70 try:
71 self.value_names = list(new.keys())
72 finally:
73 self._in_values_changed = False
74
75 # ensure that the chosen value is one of the choices
76 if self.value not in new.values():
77 self.value = next(iter(new.values()))
78
79 def _value_names_changed(self, name, old, new):
80 if not self._in_values_changed:
81 raise TraitError("value_names is a read-only proxy to values.keys(). Use the values dict instead.")
56 82
57 83 def _value_changed(self, name, old, new):
58 84 """Called when value has been changed"""
59 85 if self.value_lock.acquire(False):
60 86 try:
61 # Make sure the value is in the list of values.
62 if new in self.values:
63 # Set the string version of the value.
64 self._value = self.labels[self.values.index(new)]
65 else:
66 raise TypeError('Value must be a value in the values list.')
87 # Reverse dictionary lookup for the value name
88 for k,v in self.values.items():
89 if new == v:
90 # set the selected value name
91 self.value_name = k
92 return
93 raise KeyError(new)
67 94 finally:
68 95 self.value_lock.release()
69 96
70 def _string_value_set(self, name, old, new):
71 """Called when _value has been changed."""
97 def _value_name_changed(self, name, old, new):
98 """Called when the value name has been changed (typically by the frontend)."""
72 99 if self.value_lock.acquire(False):
73 100 try:
74 if new in self.labels:
75 self.value = self.values[self.labels.index(new)]
76 else:
77 self.value = None
101 self.value = self.values[new]
78 102 finally:
79 103 self.value_lock.release()
80 104
81 105
82 106 class ToggleButtonsWidget(_SelectionWidget):
83 107 _view_name = Unicode('ToggleButtonsView', sync=True)
84 108
85 109
86 110 class DropdownWidget(_SelectionWidget):
87 111 _view_name = Unicode('DropdownView', sync=True)
88 112
89 113
90 114 class RadioButtonsWidget(_SelectionWidget):
91 115 _view_name = Unicode('RadioButtonsView', sync=True)
92 116
93 117
94 118 class SelectWidget(_SelectionWidget):
95 119 _view_name = Unicode('SelectView', sync=True)
General Comments 0
You need to be logged in to leave comments. Login now