Source code for echo.selection

import random
from weakref import WeakKeyDictionary

import numpy as np

from .core import CallbackProperty

__all__ = ['ChoiceSeparator', 'SelectionCallbackProperty']


[docs]class ChoiceSeparator(str): pass
[docs]class SelectionCallbackProperty(CallbackProperty): def __init__(self, default_index=0, choices=None, display_func=None, comparison_type=None, **kwargs): if choices is not None and 'default' not in kwargs: kwargs['default'] = choices[default_index] super(SelectionCallbackProperty, self).__init__(**kwargs) self.default_index = default_index self.default_choices = choices or [] self.comparison_type = comparison_type self._default_display_func = display_func self._choices = WeakKeyDictionary() self._display = WeakKeyDictionary() self._force_next_sync = WeakKeyDictionary() def __set__(self, instance, value): if value is not None: choices = self.get_choices(instance) # For built-in scalar types we use ==, and for other types we use # is, otherwise e.g. ComponentID returns something that evaluates # to true when using ==. if self.comparison_type == 'equality' or (self.comparison_type is None and np.isscalar(value)): if not any(value == x for x in choices): raise ValueError('value {0} is not in valid choices: {1}'.format(value, choices)) if self.comparison_type == 'identity' or (self.comparison_type is None and not np.isscalar(value)): if not any(value is x for x in choices): raise ValueError('value {0} is not in valid choices: {1}'.format(value, choices)) super(SelectionCallbackProperty, self).__set__(instance, value)
[docs] def force_next_sync(self, instance): self._force_next_sync[instance] = True
def _get_full_info(self, instance): if self._force_next_sync.get(instance, False): try: return self.__get__(instance), random.random() finally: self._force_next_sync[instance] = False else: return self.__get__(instance), self.get_choices(instance), self.get_choice_labels(instance)
[docs] def get_display_func(self, instance): return self._display.get(instance, self._default_display_func)
[docs] def set_display_func(self, instance, display): self._display[instance] = display
# selection = self.__get__(instance) # self.notify(instance, selection, selection)
[docs] def get_choices(self, instance): return self._choices.get(instance, self.default_choices)
[docs] def get_choice_labels(self, instance): display = self._display.get(instance, str) labels = [] for choice in self.get_choices(instance): if isinstance(choice, ChoiceSeparator): labels.append(str(choice)) else: labels.append(display(choice)) return labels
[docs] def set_choices(self, instance, choices): self._choices[instance] = choices self._choices_updated(instance, choices) selection = self.__get__(instance) self.notify(instance, selection, selection)
def _choices_updated(self, instance, choices): if not choices: self.__set__(instance, None) return selection = self.__get__(instance) # We do the following because 'selection in choice' actually compares # equality not identity (and we really just care about identity here) # However, for simple Python types, we also need to check ==. for choice in choices: if selection is choice or (np.isscalar(choice) and (np.isreal(choice) or isinstance(choice, str)) and selection == choice): return choices_without_separators = [choice for choice in choices if not isinstance(choice, ChoiceSeparator)] if choices_without_separators: try: selection = choices_without_separators[self.default_index] except IndexError: if self.default_index > 0: selection = choices_without_separators[-1] else: selection = choices_without_separators[0] else: selection = None self.__set__(instance, selection)