##// END OF EJS Templates
Something
Jonathan Frederic -
Show More
@@ -33,68 +33,81 class _Selection(DOMWidget):
33 """
33 """
34
34
35 value = Any(help="Selected value")
35 value = Any(help="Selected value")
36 values = Dict(help="""Dictionary of {name: value} the user can select.
36 value_name = Unicode(help="The name of the selected value", sync=True)
37 values = Any(sync=True, help="""List of (key, value) tuples the user can select.
37
38
38 The keys of this dictionary are the strings that will be displayed in the UI,
39 The keys of this list are the strings that will be displayed in the UI,
39 representing the actual Python choices.
40 representing the actual Python choices.
40
41
41 The keys of this dictionary are also available as value_names.
42 The keys of this list are also available as value_names.
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 values_dict = Dict()
45
46 values_names = Tuple()
46 If values is specified as a list, this is the string representation of each element.
47 values_values = Tuple()
47 Otherwise, it is the keys of the values dictionary.
48
48
49 These strings are used to display the choices in the front-end.""", sync=True)
50 disabled = Bool(False, help="Enable or disable user changes", sync=True)
49 disabled = Bool(False, help="Enable or disable user changes", sync=True)
51 description = Unicode(help="Description of the value this widget represents", sync=True)
50 description = Unicode(help="Description of the value this widget represents", sync=True)
52
51
53
54 def __init__(self, *args, **kwargs):
52 def __init__(self, *args, **kwargs):
55 self.value_lock = Lock()
53 self.value_lock = Lock()
56 self._in_values_changed = False
54 self.values_lock = Lock()
55 self.on_trait_change(self._values_readonly_changed, ['values_dict', 'values_names', 'values_values', '_values'])
57 if 'values' in kwargs:
56 if 'values' in kwargs:
58 values = kwargs['values']
59 # convert list values to an dict of {str(v):v}
60 if isinstance(values, list):
61 # preserve list order with an OrderedDict
62 kwargs['values'] = OrderedDict((unicode_type(v), v) for v in values)
63 # python3.3 turned on hash randomization by default - this means that sometimes, randomly
64 # we try to set value before setting values, due to dictionary ordering. To fix this, force
65 # the setting of self.values right now, before anything else runs
66 self.values = kwargs.pop('values')
57 self.values = kwargs.pop('values')
67 DOMWidget.__init__(self, *args, **kwargs)
58 DOMWidget.__init__(self, *args, **kwargs)
68 self._value_in_values()
59 self._value_in_values()
69
60
61 def _make_values(self, x):
62 # If x is a dict, convert it to list format.
63 if isinstance(x, (OrderedDict, dict)):
64 return [(k, v) for k, v in x.items()]
65
66 # Make sure x is a list or tuple.
67 if not isinstance(x, (list, tuple)):
68 raise ValueError('x')
69
70 # If x is an ordinary list, use the values as names.
71 for y in x:
72 if not isinstance(y, (list, tuple)) or len(y) < 2:
73 return [(i, i) for i in x]
74
75 # Value is already in the correct format.
76 return x
77
70 def _values_changed(self, name, old, new):
78 def _values_changed(self, name, old, new):
71 """Handles when the values dict has been changed.
79 """Handles when the values tuple has been changed.
72
80
73 Setting values implies setting value names from the keys of the dict.
81 Setting values implies setting value names from the keys of the dict.
74 """
82 """
75 self._in_values_changed = True
83 if self.values_lock.acquire(False):
76 try:
84 try:
77 self.value_names = list(new.keys())
85
78 finally:
79 self._in_values_changed = False
80 self._value_in_values()
81
86
87 self.values = self._make_values(x)
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]
91 self._value_in_values()
92
82 def _value_in_values(self):
93 def _value_in_values(self):
83 # ensure that the chosen value is one of the choices
94 # ensure that the chosen value is one of the choices
84 if self.values:
95 if self.values_values:
85 if self.value not in self.values.values():
96 if self.value not in self.values_values:
86 self.value = next(iter(self.values.values()))
97 self.value = next(iter(self.values_values))
87
98
88 def _value_names_changed(self, name, old, new):
99 def _values_readonly_changed(self, name, old, new):
89 if not self._in_values_changed:
100 if not self.values_lock.acquire(False):
90 raise TraitError("value_names is a read-only proxy to values.keys(). Use the values dict instead.")
101 raise TraitError("`.%s` is a read-only trait. Use the `.values` tuple instead." % name)
102 else:
103 self.values_lock.release()
91
104
92 def _value_changed(self, name, old, new):
105 def _value_changed(self, name, old, new):
93 """Called when value has been changed"""
106 """Called when value has been changed"""
94 if self.value_lock.acquire(False):
107 if self.value_lock.acquire(False):
95 try:
108 try:
96 # Reverse dictionary lookup for the value name
109 # Reverse dictionary lookup for the value name
97 for k,v in self.values.items():
110 for k,v in self.values_dict.items():
98 if new == v:
111 if new == v:
99 # set the selected value name
112 # set the selected value name
100 self.value_name = k
113 self.value_name = k
@@ -109,7 +122,7 class _Selection(DOMWidget):
109 """Called when the value name has been changed (typically by the frontend)."""
122 """Called when the value name has been changed (typically by the frontend)."""
110 if self.value_lock.acquire(False):
123 if self.value_lock.acquire(False):
111 try:
124 try:
112 self.value = self.values[new]
125 self.value = self.values_dict[new]
113 finally:
126 finally:
114 self.value_lock.release()
127 self.value_lock.release()
115
128
General Comments 0
You need to be logged in to leave comments. Login now