widget_selection.py
149 lines
| 6.0 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 | ||
|
r14540 | from .widget import DOMWidget | ||
|
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") | ||||
values = Dict(help="""Dictionary of {name: value} the user can select. | ||||
The keys of this dictionary are the strings that will be displayed in the UI, | ||||
representing the actual Python choices. | ||||
The keys of this dictionary are also available as value_names. | ||||
""") | ||||
value_name = Unicode(help="The name of the selected value", sync=True) | ||||
|
r15025 | value_names = List(Unicode, help="""Read-only list of names for each value. | ||
|
r15024 | |||
If values is specified as a list, this is the string representation of each element. | ||||
Otherwise, it is the keys of the values dictionary. | ||||
These strings are used to display the choices in the front-end.""", sync=True) | ||||
|
r14588 | disabled = Bool(False, help="Enable or disable user changes", sync=True) | ||
description = Unicode(help="Description of the value this widget represents", sync=True) | ||||
|
r15024 | |||
|
r14592 | |||
|
r15024 | def __init__(self, *args, **kwargs): | ||
|
r14698 | self.value_lock = Lock() | ||
|
r15025 | self._in_values_changed = False | ||
|
r15024 | if 'values' in kwargs: | ||
values = kwargs['values'] | ||||
# convert list values to an dict of {str(v):v} | ||||
if isinstance(values, list): | ||||
# preserve list order with an OrderedDict | ||||
kwargs['values'] = OrderedDict((unicode_type(v), v) for v in values) | ||||
|
r16192 | # python3.3 turned on hash randomization by default - this means that sometimes, randomly | ||
# we try to set value before setting values, due to dictionary ordering. To fix this, force | ||||
# the setting of self.values right now, before anything else runs | ||||
|
r16196 | self.values = kwargs.pop('values') | ||
|
r15024 | DOMWidget.__init__(self, *args, **kwargs) | ||
def _values_changed(self, name, old, new): | ||||
"""Handles when the values dict has been changed. | ||||
|
r14698 | |||
|
r15024 | Setting values implies setting value names from the keys of the dict. | ||
""" | ||||
|
r15025 | self._in_values_changed = True | ||
try: | ||||
self.value_names = list(new.keys()) | ||||
finally: | ||||
self._in_values_changed = False | ||||
|
r15046 | |||
# ensure that the chosen value is one of the choices | ||||
if self.value not in new.values(): | ||||
self.value = next(iter(new.values())) | ||||
|
r15024 | |||
def _value_names_changed(self, name, old, new): | ||||
|
r15025 | if not self._in_values_changed: | ||
raise TraitError("value_names is a read-only proxy to values.keys(). Use the values dict instead.") | ||||
|
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 | ||
|
r15024 | for k,v in self.values.items(): | ||
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: | ||||
|
r15046 | self.value = self.values[new] | ||
|
r14698 | finally: | ||
self.value_lock.release() | ||||
|
r14592 | |||
|
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.""") | ||||
|
r14670 | |||
|
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.""") | ||||
|
r14592 | |||
|
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 | |||
|
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') | ||||