Source code for orangecanvas.registry.base

"""
===============
Widget Registry
===============

"""

import logging
import bisect

from operator import attrgetter

import typing
from typing import Optional, List, Tuple, Dict, Union

from . description import CategoryDescription, WidgetDescription
from . import description

if typing.TYPE_CHECKING:
    CategoryWidgetsPair = Tuple[CategoryDescription, List[WidgetDescription]]

log = logging.getLogger(__name__)

# Registry hex version
VERSION_HEX = 0x000106


[docs]class WidgetRegistry(object): """ A container for widget and category descriptions. Parameters ---------- other : :class:`WidgetRegistry`, optional If supplied the registry is initialized with the contents of `other`. See also -------- WidgetDiscovery """ def __init__(self, other=None): # type: (Optional[WidgetRegistry]) -> None # A list of (category, widgets_list) tuples ordered by priority. self.registry = [] # type: List[CategoryWidgetsPair] # tuples from 'registry' indexed by name self._categories_dict = {} # type: Dict[str, CategoryWidgetsPair] # WidgetDescriptions by qualified name self._widgets_dict = {} # type: Dict[str, WidgetDescription] if other is not None: if not isinstance(other, WidgetRegistry): raise TypeError("Expected a 'WidgetRegistry' got %r." \ % type(other).__name__) self.registry = list(other.registry) self._categories_dict = dict(other._categories_dict) self._widgets_dict = dict(other._widgets_dict)
[docs] def categories(self): # type: () -> List[CategoryDescription] """ Return a list all top level :class:`CategoryDescription` instances ordered by `priority`. """ return [c for c, _ in self.registry]
[docs] def category(self, name): # type: (str) -> CategoryDescription """ Find and return a :class:`CategoryDescription` by its `name`. .. note:: Categories are identified by `name` attribute in contrast with widgets which are identified by `qualified_name`. Parameters ---------- name : str Category name """ return self._categories_dict[name][0]
[docs] def has_category(self, name): # type: (str) -> bool """ Return ``True`` if a category with `name` exist in this registry. Parameters ---------- name : str Category name """ return name in self._categories_dict
[docs] def widgets(self, category=None): # type: (Union[CategoryDescription, str, None]) -> List[WidgetDescription] """ Return a list of all widgets in the registry. If `category` is specified return only widgets which belong to the category. Parameters ---------- category : :class:`CategoryDescription` or str, optional Return only descriptions of widgets belonging to the category. """ if category is None: categories = self.categories() elif isinstance(category, str): categories = [self.category(category)] else: categories = [category] widgets = [] for cat in categories: if isinstance(cat, str): cat = self.category(cat) cat_widgets = self._categories_dict[cat.name][1] widgets.extend(sorted(cat_widgets, key=attrgetter("priority"))) return widgets
[docs] def widget(self, qualified_name): # type: (str) -> WidgetDescription """ Return a :class:`WidgetDescription` identified by `qualified_name`. Raise :class:`KeyError` if the description does not exist. Parameters ---------- qualified_name : str Widget description qualified name """ return self._widgets_dict[qualified_name]
[docs] def has_widget(self, qualified_name): # type: (str) -> bool """ Return ``True`` if the widget with `qualified_name` exists in this registry. """ return qualified_name in self._widgets_dict
[docs] def register_widget(self, desc): # type: (WidgetDescription) -> None """ Register a :class:`WidgetDescription` instance. """ if not isinstance(desc, description.WidgetDescription): raise TypeError("Expected a 'WidgetDescription' got %r." \ % type(desc).__name__) if self.has_widget(desc.qualified_name): raise ValueError("%r already exists in the registry." \ % desc.qualified_name) category = desc.category if category is None: category = "Unspecified" if self.has_category(category): cat_desc = self.category(category) else: log.warning("Creating a default category %r.", category) cat_desc = description.CategoryDescription(name=category) self.register_category(cat_desc) self._insert_widget(cat_desc, desc)
[docs] def register_category(self, desc): # type: (CategoryDescription) -> None """ Register a :class:`CategoryDescription` instance. .. note:: It is always best to register the category before the widgets belonging to it. """ if not isinstance(desc, description.CategoryDescription): raise TypeError("Expected a 'CategoryDescription' got %r." \ % type(desc).__name__) name = desc.name if not name: log.info("Creating a default category name.") name = "default" if any(name == c.name for c in self.categories()): log.info("A category with %r name already exists" % name) return self._insert_category(desc)
def _insert_category(self, desc): # type: (CategoryDescription) -> None """ Insert category description into 'registry' list """ priority = desc.priority priorities = [c.priority for c, _ in self.registry] insertion_i = bisect.bisect_right(priorities, priority) item = (desc, []) # type: CategoryWidgetsPair self.registry.insert(insertion_i, item) self._categories_dict[desc.name] = item def _insert_widget(self, category, desc): # type: (CategoryDescription, WidgetDescription) -> None """ Insert widget description `desc` into `category`. """ assert isinstance(category, description.CategoryDescription) _, widgets = self._categories_dict[category.name] priority = desc.priority priorities = [w.priority for w in widgets] insertion_i = bisect.bisect_right(priorities, priority) widgets.insert(insertion_i, desc) self._widgets_dict[desc.qualified_name] = desc