##// END OF EJS Templates
squashing the whitespace changes
Nicholas Bollweg -
Show More
@@ -5,8 +5,9 b' define(['
5 5 "widgets/js/widget",
6 6 "base/js/utils",
7 7 "jquery",
8 "underscore",
8 9 "bootstrap",
9 ], function(widget, utils, $){
10 ], function(widget, utils, $, _){
10 11
11 12 var DropdownView = widget.DOMWidgetView.extend({
12 13 render : function(){
@@ -57,14 +58,14 b' define(['
57 58 */
58 59
59 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 62 if (selected_item_text.trim().length === 0) {
62 63 this.$droplabel.html(" ");
63 64 } else {
64 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 69 var $replace_droplist = $('<ul />')
69 70 .addClass('dropdown-menu');
70 71 // Copy the style
@@ -150,7 +151,7 b' define(['
150 151 * Calling model.set will trigger all of the other views of the
151 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 155 this.touch();
155 156
156 157 // Manually hide the droplist.
@@ -188,7 +189,7 b' define(['
188 189 */
189 190 if (options === undefined || options.updated_view != this) {
190 191 // Add missing items to the DOM.
191 var items = this.model.get('_value_names');
192 var items = this.model.get('_options_labels');
192 193 var disabled = this.model.get('disabled');
193 194 var that = this;
194 195 _.each(items, function(item, index) {
@@ -209,7 +210,7 b' define(['
209 210 }
210 211
211 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 214 $item_element.prop('checked', true);
214 215 } else {
215 216 $item_element.prop('checked', false);
@@ -263,7 +264,7 b' define(['
263 264 * Calling model.set will trigger all of the other views of the
264 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 268 this.touch();
268 269 },
269 270 });
@@ -305,7 +306,7 b' define(['
305 306 */
306 307 if (options === undefined || options.updated_view != this) {
307 308 // Add missing items to the DOM.
308 var items = this.model.get('_value_names');
309 var items = this.model.get('_options_labels');
309 310 var disabled = this.model.get('disabled');
310 311 var that = this;
311 312 var item_html;
@@ -328,7 +329,7 b' define(['
328 329 .on('click', $.proxy(that.handle_click, that));
329 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 333 $item_element.addClass('active');
333 334 } else {
334 335 $item_element.removeClass('active');
@@ -410,7 +411,7 b' define(['
410 411 * Calling model.set will trigger all of the other views of the
411 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 415 this.touch();
415 416 },
416 417 });
@@ -443,7 +444,7 b' define(['
443 444 */
444 445 if (options === undefined || options.updated_view != this) {
445 446 // Add missing items to the DOM.
446 var items = this.model.get('_value_names');
447 var items = this.model.get('_options_labels');
447 448 var that = this;
448 449 _.each(items, function(item, index) {
449 450 var item_query = 'option[data-value="' + encodeURIComponent(item) + '"]';
@@ -451,14 +452,14 b' define(['
451 452 $('<option />')
452 453 .text(item)
453 454 .attr('data-value', encodeURIComponent(item))
454 .attr('value_name', item)
455 .attr('selected_label', item)
455 456 .appendTo(that.$listbox)
456 457 .on('click', $.proxy(that.handle_click, that));
457 458 }
458 459 });
459 460
460 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 464 // Disable listbox if needed
464 465 var disabled = this.model.get('disabled');
@@ -509,7 +510,33 b' define(['
509 510 * Calling model.set will trigger all of the other views of the
510 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});
514 this.touch();
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});
513 540 this.touch();
514 541 },
515 542 });
@@ -519,5 +546,6 b' define(['
519 546 'RadioButtonsView': RadioButtonsView,
520 547 'ToggleButtonsView': ToggleButtonsView,
521 548 'SelectView': SelectView,
549 'SelectMultipleView': SelectMultipleView,
522 550 };
523 551 });
@@ -43,11 +43,11 b' casper.notebook_test(function () {'
43 43
44 44 //values=["' + selection_values + '"[i] for i in range(4)]
45 45 selection_index = this.append_cell(
46 'values=["' + selection_values + '"[i] for i in range(4)]\n' +
47 'selection = [widgets.Dropdown(values=values),\n' +
48 ' widgets.ToggleButtons(values=values),\n' +
49 ' widgets.RadioButtons(values=values),\n' +
50 ' widgets.Select(values=values)]\n' +
46 'options=["' + selection_values + '"[i] for i in range(4)]\n' +
47 'selection = [widgets.Dropdown(options=options),\n' +
48 ' widgets.ToggleButtons(options=options),\n' +
49 ' widgets.RadioButtons(options=options),\n' +
50 ' widgets.Select(options=options)]\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' +
@@ -136,9 +136,9 b' casper.notebook_test(function () {'
136 136 index = this.append_cell(
137 137 'from copy import copy\n' +
138 138 'for widget in selection:\n' +
139 ' d = copy(widget.values)\n' +
139 ' d = copy(widget.options)\n' +
140 140 ' d.append("z")\n' +
141 ' widget.values = d\n' +
141 ' widget.options = d\n' +
142 142 'selection[0].value = "z"');
143 143 this.execute_cell_then(index, function(index){
144 144
@@ -7,7 +7,7 b' from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgres'
7 7 from .widget_image import Image
8 8 from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider
9 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 11 from .widget_selectioncontainer import Tab, Accordion
12 12 from .widget_string import HTML, Latex, Text, Textarea
13 13 from .interaction import interact, interactive, fixed, interact_manual
@@ -59,7 +59,7 b' def _widget_abbrev_single_value(o):'
59 59 if isinstance(o, string_types):
60 60 return Text(value=unicode_type(o))
61 61 elif isinstance(o, dict):
62 return Dropdown(values=o)
62 return Dropdown(options=o)
63 63 elif isinstance(o, bool):
64 64 return Checkbox(value=o)
65 65 elif isinstance(o, float):
@@ -76,7 +76,7 b' def _widget_abbrev(o):'
76 76 float_or_int = (float, int)
77 77 if isinstance(o, (list, tuple)):
78 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 80 elif _matches(o, (float_or_int, float_or_int)):
81 81 min, max, value = _get_min_max_value(o[0], o[1])
82 82 if all(isinstance(_, int) for _ in o):
@@ -118,7 +118,7 b' def test_single_value_dict():'
118 118 check_widget(w,
119 119 cls=widgets.Dropdown,
120 120 description='d',
121 values=d,
121 options=d,
122 122 value=next(iter(d.values())),
123 123 )
124 124
@@ -229,7 +229,7 b' def test_list_tuple_str():'
229 229 d = dict(
230 230 cls=widgets.Dropdown,
231 231 value=first,
232 values=values
232 options=values
233 233 )
234 234 check_widgets(c, tup=d, lis=d)
235 235
@@ -287,12 +287,12 b' def test_default_values():'
287 287 ),
288 288 h=dict(
289 289 cls=widgets.Dropdown,
290 values={'a': 1, 'b': 2},
290 options={'a': 1, 'b': 2},
291 291 value=2
292 292 ),
293 293 j=dict(
294 294 cls=widgets.Dropdown,
295 values=['hi', 'there'],
295 options=['hi', 'there'],
296 296 value='there'
297 297 ),
298 298 )
@@ -310,12 +310,12 b' def test_default_out_of_bounds():'
310 310 ),
311 311 h=dict(
312 312 cls=widgets.Dropdown,
313 values={'a': 1},
313 options={'a': 1},
314 314 value=1,
315 315 ),
316 316 j=dict(
317 317 cls=widgets.Dropdown,
318 values=['hi', 'there'],
318 options=['hi', 'there'],
319 319 value='hi',
320 320 ),
321 321 )
@@ -634,3 +634,59 b' def test_float_range_logic():'
634 634 frsw(lower=5)
635 635 with nt.assert_raises(ValueError):
636 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})
@@ -30,38 +30,38 b' from IPython.utils.warn import DeprecatedClass'
30 30 class _Selection(DOMWidget):
31 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 34 it will be transformed to a dict of the form ``{str(value):value}``.
35 35 """
36 36
37 37 value = Any(help="Selected value")
38 value_name = Unicode(help="The name of the selected value", sync=True)
39 values = Any(help="""List of (key, value) tuples or dict of values that the
38 selected_label = Unicode(help="The label of the selected value", sync=True)
39 options = Any(help="""List of (key, value) tuples or dict of values that the
40 40 user can select.
41 41
42 42 The keys of this list are the strings that will be displayed in the UI,
43 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()
49 _value_names = Tuple(sync=True)
50 _value_values = Tuple()
48 _options_dict = Dict()
49 _options_labels = Tuple(sync=True)
50 _options_values = Tuple()
51 51
52 52 disabled = Bool(False, help="Enable or disable user changes", sync=True)
53 53 description = Unicode(help="Description of the value this widget represents", sync=True)
54 54
55 55 def __init__(self, *args, **kwargs):
56 56 self.value_lock = Lock()
57 self.values_lock = Lock()
58 self.on_trait_change(self._values_readonly_changed, ['_values_dict', '_value_names', '_value_values', '_values'])
59 if 'values' in kwargs:
60 self.values = kwargs.pop('values')
57 self.options_lock = Lock()
58 self.on_trait_change(self._options_readonly_changed, ['_options_dict', '_options_labels', '_options_values', '_options'])
59 if 'options' in kwargs:
60 self.options = kwargs.pop('options')
61 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 65 # If x is a dict, convert it to list format.
66 66 if isinstance(x, (OrderedDict, dict)):
67 67 return [(k, v) for k, v in x.items()]
@@ -70,7 +70,7 b' class _Selection(DOMWidget):'
70 70 if not isinstance(x, (list, tuple)):
71 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 74 for y in x:
75 75 if not isinstance(y, (list, tuple)) or len(y) < 2:
76 76 return [(i, i) for i in x]
@@ -78,42 +78,42 b' class _Selection(DOMWidget):'
78 78 # Value is already in the correct format.
79 79 return x
80 80
81 def _values_changed(self, name, old, new):
82 """Handles when the values tuple has been changed.
81 def _options_changed(self, name, old, new):
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 87 try:
88 self.values = new
88 self.options = new
89 89
90 values = self._make_values(new)
91 self._values_dict = {i[0]: i[1] for i in values}
92 self._value_names = [i[0] for i in values]
93 self._value_values = [i[1] for i in values]
94 self._value_in_values()
90 options = self._make_options(new)
91 self._options_dict = {i[0]: i[1] for i in options}
92 self._options_labels = [i[0] for i in options]
93 self._options_values = [i[1] for i in options]
94 self._value_in_options()
95 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 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):
105 if not self.values_lock.locked():
106 raise TraitError("`.%s` is a read-only trait. Use the `.values` tuple instead." % name)
101 if self._options_values:
102 if self.value not in self._options_values:
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 108 def _value_changed(self, name, old, new):
109 109 """Called when value has been changed"""
110 110 if self.value_lock.acquire(False):
111 111 try:
112 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 114 if new == v:
115 115 # set the selected value name
116 self.value_name = k
116 self.selected_label = k
117 117 return
118 118 # undo the change, and raise KeyError
119 119 self.value = old
@@ -121,11 +121,68 b' class _Selection(DOMWidget):'
121 121 finally:
122 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 125 """Called when the value name has been changed (typically by the frontend)."""
126 126 if self.value_lock.acquire(False):
127 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 186 finally:
130 187 self.value_lock.release()
131 188
@@ -165,6 +222,15 b' class Select(_Selection):'
165 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 234 # Remove in IPython 4.0
169 235 ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
170 236 DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
@@ -86,7 +86,7 b''
86 86 },
87 87 "outputs": [],
88 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 90 "export_button = widgets.Button(description=\"Export\")\n",
91 91 "download_link = widgets.HTML(visible=False)"
92 92 ]
@@ -274,7 +274,7 b''
274 274 "cell_type": "markdown",
275 275 "metadata": {},
276 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 {
@@ -298,7 +298,7 b''
298 298 "source": [
299 299 "from IPython.display import display\n",
300 300 "w = widgets.Dropdown(\n",
301 " values=['1', '2', '3'],\n",
301 " options=['1', '2', '3'],\n",
302 302 " value='2',\n",
303 303 " description='Number:',\n",
304 304 ")\n",
@@ -332,7 +332,7 b''
332 332 "outputs": [],
333 333 "source": [
334 334 "w = widgets.Dropdown(\n",
335 " values={'One': 1, 'Two': 2, 'Three': 3},\n",
335 " options={'One': 1, 'Two': 2, 'Three': 3},\n",
336 336 " value=2,\n",
337 337 " description='Number:',\n",
338 338 ")\n",
@@ -371,7 +371,7 b''
371 371 "source": [
372 372 "widgets.RadioButtons(\n",
373 373 " description='Pizza topping:',\n",
374 " values=['pepperoni', 'pineapple', 'anchovies'],\n",
374 " options=['pepperoni', 'pineapple', 'anchovies'],\n",
375 375 ")"
376 376 ]
377 377 },
@@ -396,7 +396,7 b''
396 396 "source": [
397 397 "widgets.Select(\n",
398 398 " description='OS:',\n",
399 " values=['Linux', 'Windows', 'OSX'],\n",
399 " options=['Linux', 'Windows', 'OSX'],\n",
400 400 ")"
401 401 ]
402 402 },
@@ -421,12 +421,46 b''
421 421 "source": [
422 422 "widgets.ToggleButtons(\n",
423 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 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 464 "metadata": {
431 465 "slideshow": {
432 466 "slide_type": "slide"
@@ -236,11 +236,11 b''
236 236 "outputs": [],
237 237 "source": [
238 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 240 "page1 = widgets.Box(children=[name, color])\n",
241 241 "\n",
242 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 244 "page2 = widgets.Box(children=[age, gender])\n",
245 245 "\n",
246 246 "tabs = widgets.Tab(children=[page1, page2])\n",
General Comments 0
You need to be logged in to leave comments. Login now