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