source: orange/Orange/OrangeCanvas/registry/base.py @ 11418:6abbcdf4ff7c

Revision 11418:6abbcdf4ff7c, 6.3 KB checked in by Ales Erjavec <ales.erjavec@…>, 13 months ago (diff)

Widget registry documentation fixup.

RevLine 
[11098]1"""
[11418]2===============
3Widget Registry
4===============
[11098]5
6"""
7
8import logging
9import bisect
10
11from operator import attrgetter
12
13from . import description
14
15log = logging.getLogger(__name__)
16
17# Registry hex version
[11263]18VERSION_HEX = 0x000102
[11098]19
20
21class WidgetRegistry(object):
[11368]22    """
23    A container for widget and category descriptions.
[11098]24
25    >>> reg = WidgetRegistry()
[11368]26    >>> file_desc = WidgetDescription.from_module(
[11098]27    ...     "Orange.OrangeWidgets.Data.OWFile"
28    ... )
29    ...
[11368]30    >>> reg.register_widget(file_desc)  # register the description
31    >>> print reg.widgets()
[11098]32    [...
33
[11368]34    Parameters
35    ----------
36    other : :class:`WidgetRegistry`, optional
37        If supplied the registry is initialized with the contents of `other`.
38
[11418]39    See also
40    --------
41    WidgetDiscovery
42
[11098]43    """
44
45    def __init__(self, other=None):
46        # A list of (category, widgets_list) tuples ordered by priority.
47        self.registry = []
[11418]48
[11098]49        # tuples from 'registry' indexed by name
50        self._categories_dict = {}
[11418]51
52        # WidgetDecriptions by qualified name
[11098]53        self._widgets_dict = {}
54
55        if other is not None:
56            if not isinstance(other, WidgetRegistry):
57                raise TypeError("Expected a 'WidgetRegistry' got %r." \
58                                % type(other).__name__)
59
60            self.registry = list(other.registry)
61            self._categories_dict = dict(other._categories_dict)
62            self._widgets_dict = dict(other._widgets_dict)
63
64    def categories(self):
[11368]65        """
66        Return a list all top level :class:`CategoryDescription` instances
[11418]67        ordered by `priority`.
[11368]68
[11098]69        """
70        return [c for c, _ in self.registry]
71
72    def category(self, name):
[11368]73        """
[11418]74        Find and return a :class:`CategoryDescription` by its `name`.
[11098]75
76        .. note:: Categories are identified by `name` attribute in contrast
[11368]77                  with widgets which are identified by `qualified_name`.
78
79        Parameters
80        ----------
81        name : str
82            Category name
[11098]83
84        """
85        return self._categories_dict[name][0]
86
87    def has_category(self, name):
[11368]88        """
[11418]89        Return ``True`` if a category with `name` exist in this registry.
[11368]90
91        Parameters
92        ----------
93        name : str
94            Category name
95
[11098]96        """
97        return name in self._categories_dict
98
99    def widgets(self, category=None):
[11368]100        """
101        Return a list of all widgets in the registry. If `category` is
102        specified return only widgets which belong to the category.
103
104        Parameters
105        ----------
106        category : :class:`CategoryDescription` or str, optional
107            Return only descriptions of widgets belonging to the category.
[11098]108
109        """
110        if category is None:
111            categories = self.categories()
112        elif isinstance(category, basestring):
113            categories = [self.category(category)]
114        else:
115            categories = [category]
116
117        widgets = []
118        for cat in categories:
119            if isinstance(cat, basestring):
120                cat = self.category(cat)
121            cat_widgets = self._categories_dict[cat.name][1]
122            widgets.extend(sorted(cat_widgets,
123                                  key=attrgetter("priority")))
124        return widgets
125
126    def widget(self, qualified_name):
[11368]127        """
128        Return a :class:`WidgetDescription` identified by `qualified_name`.
129
130        Raise :class:`KeyError` if the description does not exist.
131
132        Parameters
133        ----------
134        qualified_name : str
135            Widget description qualified name
136
[11098]137        """
138        return self._widgets_dict[qualified_name]
139
140    def has_widget(self, qualified_name):
[11368]141        """
[11418]142        Return ``True`` if the widget with `qualified_name` exists in
143        this registry.
144
[11098]145        """
146        return qualified_name in self._widgets_dict
147
148    def register_widget(self, desc):
[11368]149        """
150        Register a :class:`WidgetDescription` instance.
[11098]151        """
152        if not isinstance(desc, description.WidgetDescription):
153            raise TypeError("Expected a 'WidgetDescription' got %r." \
154                            % type(desc).__name__)
155
156        if self.has_widget(desc.qualified_name):
157            raise ValueError("%r already exists in the registry." \
158                             % desc.qualified_name)
159
160        category = desc.category
161        if category is None:
162            category = "Unspecified"
163
164        if self.has_category(category):
165            cat_desc = self.category(category)
166        else:
167            log.warning("Creating a default category %r.", category)
168            cat_desc = description.CategoryDescription(name=category)
169            self.register_category(cat_desc)
170
171        self._insert_widget(cat_desc, desc)
172
173    def register_category(self, desc):
[11368]174        """
175        Register a :class:`CategoryDescription` instance.
[11098]176
177        .. note:: It is always best to register the category
[11368]178                  before the widgets belonging to it.
[11098]179
180        """
181        if not isinstance(desc, description.CategoryDescription):
182            raise TypeError("Expected a 'CategoryDescription' got %r." \
183                            % type(desc).__name__)
184
185        name = desc.name
186        if not name:
[11282]187            log.info("Creating a default category name.")
[11098]188            name = "default"
189
190        if any(name == c.name for c in self.categories()):
[11282]191            log.info("A category with %r name already exists" % name)
[11098]192            return
193
194        self._insert_category(desc)
195
196    def _insert_category(self, desc):
[11368]197        """
198        Insert category description into 'registry' list
[11098]199        """
200        priority = desc.priority
201        priorities = [c.priority for c, _ in self.registry]
202        insertion_i = bisect.bisect_right(priorities, priority)
203
204        item = (desc, [])
205        self.registry.insert(insertion_i, item)
206        self._categories_dict[desc.name] = item
207
208    def _insert_widget(self, category, desc):
[11368]209        """
210        Insert widget description `desc` into `category`.
[11098]211        """
212        assert(isinstance(category, description.CategoryDescription))
213        _, widgets = self._categories_dict[category.name]
214
215        priority = desc.priority
216        priorities = [w.priority for w in widgets]
217        insertion_i = bisect.bisect_right(priorities, priority)
218        widgets.insert(insertion_i, desc)
219        self._widgets_dict[desc.qualified_name] = desc
Note: See TracBrowser for help on using the repository browser.