widget_selection.py
169 lines
| 6.4 KiB
| text/x-python
|
PythonLexer
|
r17598 | """Selection classes. | ||
|
r14283 | |||
Represents an enumeration using a widget. | ||||
""" | ||||
#----------------------------------------------------------------------------- | ||||
# Copyright (c) 2013, the IPython Development Team. | ||||
# | ||||
# Distributed under the terms of the Modified BSD License. | ||||
# | ||||
# The full license is in the file COPYING.txt, distributed with this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
|
r15024 | |||
from collections import OrderedDict | ||||
|
r14698 | from threading import Lock | ||
|
r18531 | from .widget import DOMWidget, register | ||
|
r17729 | from IPython.utils.traitlets import Unicode, List, Bool, Any, Dict, TraitError, CaselessStrEnum | ||
|
r15024 | from IPython.utils.py3compat import unicode_type | ||
|
r17598 | from IPython.utils.warn import DeprecatedClass | ||
|
r14242 | |||
|
r14283 | #----------------------------------------------------------------------------- | ||
# SelectionWidget | ||||
#----------------------------------------------------------------------------- | ||||
|
r17598 | class _Selection(DOMWidget): | ||
|
r15024 | """Base class for Selection widgets | ||
``values`` can be specified as a list or dict. If given as a list, | ||||
it will be transformed to a dict of the form ``{str(value):value}``. | ||||
""" | ||||
value = Any(help="Selected value") | ||||
|
r19058 | value_name = Unicode(help="The name of the selected value", sync=True) | ||
values = Any(sync=True, help="""List of (key, value) tuples the user can select. | ||||
|
r15024 | |||
|
r19058 | The keys of this list are the strings that will be displayed in the UI, | ||
|
r15024 | representing the actual Python choices. | ||
|
r19058 | The keys of this list are also available as value_names. | ||
|
r15024 | """) | ||
|
r19058 | |||
values_dict = Dict() | ||||
values_names = Tuple() | ||||
values_values = Tuple() | ||||
|
r14588 | disabled = Bool(False, help="Enable or disable user changes", sync=True) | ||
description = Unicode(help="Description of the value this widget represents", sync=True) | ||||
|
r19058 | |||
|
r15024 | def __init__(self, *args, **kwargs): | ||
|
r14698 | self.value_lock = Lock() | ||
|
r19058 | self.values_lock = Lock() | ||
self.on_trait_change(self._values_readonly_changed, ['values_dict', 'values_names', 'values_values', '_values']) | ||||
|
r15024 | if 'values' in kwargs: | ||
|
r16196 | self.values = kwargs.pop('values') | ||
|
r15024 | DOMWidget.__init__(self, *args, **kwargs) | ||
|
r18125 | self._value_in_values() | ||
|
r15024 | |||
|
r19058 | def _make_values(self, x): | ||
# If x is a dict, convert it to list format. | ||||
if isinstance(x, (OrderedDict, dict)): | ||||
return [(k, v) for k, v in x.items()] | ||||
# Make sure x is a list or tuple. | ||||
if not isinstance(x, (list, tuple)): | ||||
raise ValueError('x') | ||||
# If x is an ordinary list, use the values as names. | ||||
for y in x: | ||||
if not isinstance(y, (list, tuple)) or len(y) < 2: | ||||
return [(i, i) for i in x] | ||||
# Value is already in the correct format. | ||||
return x | ||||
|
r15024 | def _values_changed(self, name, old, new): | ||
|
r19058 | """Handles when the values tuple has been changed. | ||
|
r14698 | |||
|
r15024 | Setting values implies setting value names from the keys of the dict. | ||
|
r19058 | """ | ||
if self.values_lock.acquire(False): | ||||
try: | ||||
|
r18125 | |||
|
r19058 | self.values = self._make_values(x) | ||
self.values_dict = {i[0]: i[1] for i in self.values} | ||||
self.values_names = [i[0] for i in self.values] | ||||
self.values_values = [i[1] for i in self.values] | ||||
self._value_in_values() | ||||
|
r18125 | def _value_in_values(self): | ||
|
r15046 | # ensure that the chosen value is one of the choices | ||
|
r19058 | if self.values_values: | ||
if self.value not in self.values_values: | ||||
self.value = next(iter(self.values_values)) | ||||
|
r15024 | |||
|
r19058 | def _values_readonly_changed(self, name, old, new): | ||
if not self.values_lock.acquire(False): | ||||
raise TraitError("`.%s` is a read-only trait. Use the `.values` tuple instead." % name) | ||||
else: | ||||
self.values_lock.release() | ||||
|
r14698 | |||
def _value_changed(self, name, old, new): | ||||
"""Called when value has been changed""" | ||||
if self.value_lock.acquire(False): | ||||
try: | ||||
|
r15046 | # Reverse dictionary lookup for the value name | ||
|
r19058 | for k,v in self.values_dict.items(): | ||
|
r15024 | if new == v: | ||
# set the selected value name | ||||
self.value_name = k | ||||
return | ||||
|
r15401 | # undo the change, and raise KeyError | ||
self.value = old | ||||
|
r15046 | raise KeyError(new) | ||
|
r14698 | finally: | ||
self.value_lock.release() | ||||
|
r15024 | def _value_name_changed(self, name, old, new): | ||
"""Called when the value name has been changed (typically by the frontend).""" | ||||
|
r14698 | if self.value_lock.acquire(False): | ||
try: | ||||
|
r19058 | self.value = self.values_dict[new] | ||
|
r14698 | finally: | ||
self.value_lock.release() | ||||
|
r14592 | |||
|
r18533 | @register('IPython.ToggleButtons') | ||
|
r17598 | class ToggleButtons(_Selection): | ||
|
r17602 | """Group of toggle buttons that represent an enumeration. Only one toggle | ||
button can be toggled at any point in time.""" | ||||
|
r14701 | _view_name = Unicode('ToggleButtonsView', sync=True) | ||
|
r14670 | |||
|
r17728 | button_style = CaselessStrEnum( | ||
values=['primary', 'success', 'info', 'warning', 'danger', ''], | ||||
default_value='', allow_none=True, sync=True, help="""Use a | ||||
predefined styling for the buttons.""") | ||||
|
r18533 | @register('IPython.Dropdown') | ||
|
r17598 | class Dropdown(_Selection): | ||
|
r17602 | """Allows you to select a single item from a dropdown.""" | ||
|
r14701 | _view_name = Unicode('DropdownView', sync=True) | ||
|
r14592 | |||
|
r17729 | button_style = CaselessStrEnum( | ||
values=['primary', 'success', 'info', 'warning', 'danger', ''], | ||||
default_value='', allow_none=True, sync=True, help="""Use a | ||||
predefined styling for the buttons.""") | ||||
|
r18533 | @register('IPython.RadioButtons') | ||
|
r17598 | class RadioButtons(_Selection): | ||
|
r17602 | """Group of radio buttons that represent an enumeration. Only one radio | ||
button can be toggled at any point in time.""" | ||||
|
r14701 | _view_name = Unicode('RadioButtonsView', sync=True) | ||
|
r14592 | |||
|
r18531 | |||
|
r18533 | @register('IPython.Select') | ||
|
r17598 | class Select(_Selection): | ||
|
r17602 | """Listbox that only allows one item to be selected at any given time.""" | ||
|
r14834 | _view_name = Unicode('SelectView', sync=True) | ||
|
r17598 | |||
|
r17602 | |||
# Remove in IPython 4.0 | ||||
|
r17598 | ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget') | ||
DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget') | ||||
RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget') | ||||
SelectWidget = DeprecatedClass(Select, 'SelectWidget') | ||||