##// END OF EJS Templates
Don't magically convert values to a list of tuples.
Jonathan Frederic -
Show More
@@ -1,169 +1,172 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 ``values`` 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 value_name = Unicode(help="The name of the selected value", sync=True)
39 values = Any(sync=True, help="""List of (key, value) tuples the user can select.
39 values = Any(help="""List of (key, value) tuples or dict of values that the
40 user can select.
40
41
41 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,
42 representing the actual Python choices.
43 representing the actual Python choices.
43
44
44 The keys of this list are also available as value_names.
45 The keys of this list are also available as value_names.
45 """)
46 """)
46
47
47 values_dict = Dict()
48 values_dict = Dict()
48 values_names = Tuple(sync=True)
49 values_names = Tuple(sync=True)
49 values_values = Tuple()
50 values_values = Tuple()
50
51
51 disabled = Bool(False, help="Enable or disable user changes", sync=True)
52 disabled = Bool(False, help="Enable or disable user changes", sync=True)
52 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)
53
54
54 def __init__(self, *args, **kwargs):
55 def __init__(self, *args, **kwargs):
55 self.value_lock = Lock()
56 self.value_lock = Lock()
56 self.values_lock = Lock()
57 self.values_lock = Lock()
57 self.on_trait_change(self._values_readonly_changed, ['values_dict', 'values_names', 'values_values', '_values'])
58 self.on_trait_change(self._values_readonly_changed, ['values_dict', 'values_names', 'values_values', '_values'])
58 if 'values' in kwargs:
59 if 'values' in kwargs:
59 self.values = kwargs.pop('values')
60 self.values = kwargs.pop('values')
60 DOMWidget.__init__(self, *args, **kwargs)
61 DOMWidget.__init__(self, *args, **kwargs)
61 self._value_in_values()
62 self._value_in_values()
62
63
63 def _make_values(self, x):
64 def _make_values(self, x):
64 # If x is a dict, convert it to list format.
65 # If x is a dict, convert it to list format.
65 if isinstance(x, (OrderedDict, dict)):
66 if isinstance(x, (OrderedDict, dict)):
66 return [(k, v) for k, v in x.items()]
67 return [(k, v) for k, v in x.items()]
67
68
68 # Make sure x is a list or tuple.
69 # Make sure x is a list or tuple.
69 if not isinstance(x, (list, tuple)):
70 if not isinstance(x, (list, tuple)):
70 raise ValueError('x')
71 raise ValueError('x')
71
72
72 # If x is an ordinary list, use the values as names.
73 # If x is an ordinary list, use the values as names.
73 for y in x:
74 for y in x:
74 if not isinstance(y, (list, tuple)) or len(y) < 2:
75 if not isinstance(y, (list, tuple)) or len(y) < 2:
75 return [(i, i) for i in x]
76 return [(i, i) for i in x]
76
77
77 # Value is already in the correct format.
78 # Value is already in the correct format.
78 return x
79 return x
79
80
80 def _values_changed(self, name, old, new):
81 def _values_changed(self, name, old, new):
81 """Handles when the values tuple has been changed.
82 """Handles when the values tuple has been changed.
82
83
83 Setting values implies setting value names from the keys of the dict.
84 Setting values implies setting value names from the keys of the dict.
84 """
85 """
85 if self.values_lock.acquire(False):
86 if self.values_lock.acquire(False):
86 try:
87 try:
87 self.values = self._make_values(new)
88 self.values = new
88 self.values_dict = {i[0]: i[1] for i in self.values}
89
89 self.values_names = [i[0] for i in self.values]
90 values = self._make_values(new)
90 self.values_values = [i[1] for i in self.values]
91 self.values_dict = {i[0]: i[1] for i in values}
92 self.values_names = [i[0] for i in values]
93 self.values_values = [i[1] for i in values]
91 self._value_in_values()
94 self._value_in_values()
92 finally:
95 finally:
93 self.values_lock.release()
96 self.values_lock.release()
94
97
95 def _value_in_values(self):
98 def _value_in_values(self):
96 # ensure that the chosen value is one of the choices
99 # ensure that the chosen value is one of the choices
97 if self.values_values:
100 if self.values_values:
98 if self.value not in self.values_values:
101 if self.value not in self.values_values:
99 self.value = next(iter(self.values_values))
102 self.value = next(iter(self.values_values))
100
103
101 def _values_readonly_changed(self, name, old, new):
104 def _values_readonly_changed(self, name, old, new):
102 if not self.values_lock.locked():
105 if not self.values_lock.locked():
103 raise TraitError("`.%s` is a read-only trait. Use the `.values` tuple instead." % name)
106 raise TraitError("`.%s` is a read-only trait. Use the `.values` tuple instead." % name)
104
107
105 def _value_changed(self, name, old, new):
108 def _value_changed(self, name, old, new):
106 """Called when value has been changed"""
109 """Called when value has been changed"""
107 if self.value_lock.acquire(False):
110 if self.value_lock.acquire(False):
108 try:
111 try:
109 # Reverse dictionary lookup for the value name
112 # Reverse dictionary lookup for the value name
110 for k,v in self.values_dict.items():
113 for k,v in self.values_dict.items():
111 if new == v:
114 if new == v:
112 # set the selected value name
115 # set the selected value name
113 self.value_name = k
116 self.value_name = k
114 return
117 return
115 # undo the change, and raise KeyError
118 # undo the change, and raise KeyError
116 self.value = old
119 self.value = old
117 raise KeyError(new)
120 raise KeyError(new)
118 finally:
121 finally:
119 self.value_lock.release()
122 self.value_lock.release()
120
123
121 def _value_name_changed(self, name, old, new):
124 def _value_name_changed(self, name, old, new):
122 """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)."""
123 if self.value_lock.acquire(False):
126 if self.value_lock.acquire(False):
124 try:
127 try:
125 self.value = self.values_dict[new]
128 self.value = self.values_dict[new]
126 finally:
129 finally:
127 self.value_lock.release()
130 self.value_lock.release()
128
131
129
132
130 @register('IPython.ToggleButtons')
133 @register('IPython.ToggleButtons')
131 class ToggleButtons(_Selection):
134 class ToggleButtons(_Selection):
132 """Group of toggle buttons that represent an enumeration. Only one toggle
135 """Group of toggle buttons that represent an enumeration. Only one toggle
133 button can be toggled at any point in time."""
136 button can be toggled at any point in time."""
134 _view_name = Unicode('ToggleButtonsView', sync=True)
137 _view_name = Unicode('ToggleButtonsView', sync=True)
135
138
136 button_style = CaselessStrEnum(
139 button_style = CaselessStrEnum(
137 values=['primary', 'success', 'info', 'warning', 'danger', ''],
140 values=['primary', 'success', 'info', 'warning', 'danger', ''],
138 default_value='', allow_none=True, sync=True, help="""Use a
141 default_value='', allow_none=True, sync=True, help="""Use a
139 predefined styling for the buttons.""")
142 predefined styling for the buttons.""")
140
143
141 @register('IPython.Dropdown')
144 @register('IPython.Dropdown')
142 class Dropdown(_Selection):
145 class Dropdown(_Selection):
143 """Allows you to select a single item from a dropdown."""
146 """Allows you to select a single item from a dropdown."""
144 _view_name = Unicode('DropdownView', sync=True)
147 _view_name = Unicode('DropdownView', sync=True)
145
148
146 button_style = CaselessStrEnum(
149 button_style = CaselessStrEnum(
147 values=['primary', 'success', 'info', 'warning', 'danger', ''],
150 values=['primary', 'success', 'info', 'warning', 'danger', ''],
148 default_value='', allow_none=True, sync=True, help="""Use a
151 default_value='', allow_none=True, sync=True, help="""Use a
149 predefined styling for the buttons.""")
152 predefined styling for the buttons.""")
150
153
151 @register('IPython.RadioButtons')
154 @register('IPython.RadioButtons')
152 class RadioButtons(_Selection):
155 class RadioButtons(_Selection):
153 """Group of radio buttons that represent an enumeration. Only one radio
156 """Group of radio buttons that represent an enumeration. Only one radio
154 button can be toggled at any point in time."""
157 button can be toggled at any point in time."""
155 _view_name = Unicode('RadioButtonsView', sync=True)
158 _view_name = Unicode('RadioButtonsView', sync=True)
156
159
157
160
158
161
159 @register('IPython.Select')
162 @register('IPython.Select')
160 class Select(_Selection):
163 class Select(_Selection):
161 """Listbox that only allows one item to be selected at any given time."""
164 """Listbox that only allows one item to be selected at any given time."""
162 _view_name = Unicode('SelectView', sync=True)
165 _view_name = Unicode('SelectView', sync=True)
163
166
164
167
165 # Remove in IPython 4.0
168 # Remove in IPython 4.0
166 ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
169 ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
167 DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
170 DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
168 RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget')
171 RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget')
169 SelectWidget = DeprecatedClass(Select, 'SelectWidget')
172 SelectWidget = DeprecatedClass(Select, 'SelectWidget')
General Comments 0
You need to be logged in to leave comments. Login now