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