Source code for scistag.common.component

"""
Implements the :class:`Component` class - the base class for all intelligent
components such as Widgets and Slides.
"""
import os
from typing import Any

from scistag.common.cache import Cache


[docs]class Component(Cache): """ The Component class integrates the functionality for intelligent property handling and dynamic data loading and unloading. It lays the foundation for the :class:`scistag.slidestag.widget.Widget` class which needs to automatically react to the change to single properties to minimize the need of using methods to do so while keeping the memory footprint of applications such as SlideStag as low as possible even when many users are using a service at the same time. * See :attr:`properties` for details about the property handling. """ def __init__(self, cache_dir: str = None): super().__init__() self.properties = {} """ Stores this classes' properties and their behavior. They define which properties automatically call a setter function when being changed, which properties are read-only, their type etc. To for example define a property ``x`` which shall automatically call it's change handler ``set_x`` when being changed just declare it as common member variable, add it to this variable as key and declare the ``set_x`` method. When ever you want to change it's value directly via ``myComponent.x = newValue`` the ``set_x`` function will be called. To prevent recursion this function then has to store the value directly to the object's dictionary via `self.__dict__['x'] = new_value`. To define read-only properties set theirs "readOnly" flag such as ``self.properties['x'] = {"readOnly": True}`` For further details see :const:`Component.PROPERTIES`. :param cache_dir: The directory in which temporary computing results can be stored on disk. """
[docs] def __setattr__(self, key, value) -> None: """ Customizes the setter behavior of the attributes of Component class objects. If a property is defined in :attr:`properties` and a setter function with the name ``set_{key}`` exists it is automatically called instead of setting the value directly. If a property is flagged as "readOnly" and the user tries to modify this property a ValueException is raised. :param key: The key of the (potential) property :param value: The new value """ properties = self.__dict__.get("properties", None) if properties is not None and key in properties: setter = getattr(self, f"set_{key}", None) if setter is not None: setter(value) return if self.properties[key].get("readOnly", False): raise ValueError("Tried to modify a read-only property") super().__setattr__(key, value)
PROPERTIES = {} """ The Component class' base properties as dictionary Every dictionary entry should have the following structure: * **info**: A short information describing the property's data * (optional) **type**: The property's type, either a string or a Python type * (optional) **readOnly**: Defines if the property may only be read. False by default A class derived from :class:`Component` should always extend it's ancestor class' PROPERTIES class attribute: ``PROPERTIES = {"myNewAttribute": {"info": "Example for a new attribute", "type": int}} | Component.PROPERTIES`` The PROPERTY class attribute should be assigned to every object's `property` object attribute at the end of it's constructor: ``self.properties = self.PROPERTIES`` See :class:`scistag.slidestag.widget.Widget` for an extended example of it's usage. """