File: selection.py

package info (click to toggle)
python-echo 0.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 368 kB
  • sloc: python: 2,421; makefile: 148
file content (113 lines) | stat: -rw-r--r-- 4,511 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import random
from weakref import WeakKeyDictionary

import numpy as np

from .core import CallbackProperty

__all__ = ['ChoiceSeparator', 'SelectionCallbackProperty']


class ChoiceSeparator(str):
    pass


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)

    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)

    def get_display_func(self, instance):
        return self._display.get(instance, self._default_display_func)

    def set_display_func(self, instance, display):
        self._display[instance] = display
        # selection = self.__get__(instance)
        # self.notify(instance, selection, selection)

    def get_choices(self, instance):
        return self._choices.get(instance, self.default_choices)

    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

    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)