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