##// END OF EJS Templates
forcing value to be in values
Sylvain Corlay -
Show More
@@ -1,149 +1,153 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
21 21 from IPython.utils.traitlets import Unicode, List, Bool, Any, Dict, TraitError, CaselessStrEnum
22 22 from IPython.utils.py3compat import unicode_type
23 23 from IPython.utils.warn import DeprecatedClass
24 24
25 25 #-----------------------------------------------------------------------------
26 26 # SelectionWidget
27 27 #-----------------------------------------------------------------------------
28 28 class _Selection(DOMWidget):
29 29 """Base class for Selection widgets
30 30
31 31 ``values`` can be specified as a list or dict. If given as a list,
32 32 it will be transformed to a dict of the form ``{str(value):value}``.
33 33 """
34 34
35 35 value = Any(help="Selected value")
36 36 values = Dict(help="""Dictionary of {name: value} the user can select.
37 37
38 38 The keys of this dictionary are the strings that will be displayed in the UI,
39 39 representing the actual Python choices.
40 40
41 41 The keys of this dictionary are also available as value_names.
42 42 """)
43 43 value_name = Unicode(help="The name of the selected value", sync=True)
44 44 value_names = List(Unicode, help="""Read-only list of names for each value.
45 45
46 46 If values is specified as a list, this is the string representation of each element.
47 47 Otherwise, it is the keys of the values dictionary.
48 48
49 49 These strings are used to display the choices in the front-end.""", sync=True)
50 50 disabled = Bool(False, help="Enable or disable user changes", sync=True)
51 51 description = Unicode(help="Description of the value this widget represents", sync=True)
52 52
53
53
54 54 def __init__(self, *args, **kwargs):
55 55 self.value_lock = Lock()
56 56 self._in_values_changed = False
57 57 if 'values' in kwargs:
58 58 values = kwargs['values']
59 59 # convert list values to an dict of {str(v):v}
60 60 if isinstance(values, list):
61 61 # preserve list order with an OrderedDict
62 62 kwargs['values'] = OrderedDict((unicode_type(v), v) for v in values)
63 63 # python3.3 turned on hash randomization by default - this means that sometimes, randomly
64 64 # we try to set value before setting values, due to dictionary ordering. To fix this, force
65 65 # the setting of self.values right now, before anything else runs
66 66 self.values = kwargs.pop('values')
67 67 DOMWidget.__init__(self, *args, **kwargs)
68 self._value_in_values()
68 69
69 70 def _values_changed(self, name, old, new):
70 71 """Handles when the values dict has been changed.
71 72
72 73 Setting values implies setting value names from the keys of the dict.
73 74 """
74 75 self._in_values_changed = True
75 76 try:
76 77 self.value_names = list(new.keys())
77 78 finally:
78 79 self._in_values_changed = False
79
80 self._value_in_values()
81
82 def _value_in_values(self):
80 83 # ensure that the chosen value is one of the choices
81 if self.value not in new.values():
82 self.value = next(iter(new.values()))
84 if self.values:
85 if self.value not in self.values.values():
86 self.value = next(iter(self.values.values()))
83 87
84 88 def _value_names_changed(self, name, old, new):
85 89 if not self._in_values_changed:
86 90 raise TraitError("value_names is a read-only proxy to values.keys(). Use the values dict instead.")
87 91
88 92 def _value_changed(self, name, old, new):
89 93 """Called when value has been changed"""
90 94 if self.value_lock.acquire(False):
91 95 try:
92 96 # Reverse dictionary lookup for the value name
93 97 for k,v in self.values.items():
94 98 if new == v:
95 99 # set the selected value name
96 100 self.value_name = k
97 101 return
98 102 # undo the change, and raise KeyError
99 103 self.value = old
100 104 raise KeyError(new)
101 105 finally:
102 106 self.value_lock.release()
103 107
104 108 def _value_name_changed(self, name, old, new):
105 109 """Called when the value name has been changed (typically by the frontend)."""
106 110 if self.value_lock.acquire(False):
107 111 try:
108 112 self.value = self.values[new]
109 113 finally:
110 114 self.value_lock.release()
111 115
112 116
113 117 class ToggleButtons(_Selection):
114 118 """Group of toggle buttons that represent an enumeration. Only one toggle
115 119 button can be toggled at any point in time."""
116 120 _view_name = Unicode('ToggleButtonsView', sync=True)
117 121
118 122 button_style = CaselessStrEnum(
119 123 values=['primary', 'success', 'info', 'warning', 'danger', ''],
120 124 default_value='', allow_none=True, sync=True, help="""Use a
121 125 predefined styling for the buttons.""")
122 126
123 127
124 128 class Dropdown(_Selection):
125 129 """Allows you to select a single item from a dropdown."""
126 130 _view_name = Unicode('DropdownView', sync=True)
127 131
128 132 button_style = CaselessStrEnum(
129 133 values=['primary', 'success', 'info', 'warning', 'danger', ''],
130 134 default_value='', allow_none=True, sync=True, help="""Use a
131 135 predefined styling for the buttons.""")
132 136
133 137
134 138 class RadioButtons(_Selection):
135 139 """Group of radio buttons that represent an enumeration. Only one radio
136 140 button can be toggled at any point in time."""
137 141 _view_name = Unicode('RadioButtonsView', sync=True)
138 142
139 143
140 144 class Select(_Selection):
141 145 """Listbox that only allows one item to be selected at any given time."""
142 146 _view_name = Unicode('SelectView', sync=True)
143 147
144 148
145 149 # Remove in IPython 4.0
146 150 ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
147 151 DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
148 152 RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget')
149 153 SelectWidget = DeprecatedClass(Select, 'SelectWidget')
General Comments 0
You need to be logged in to leave comments. Login now