Source code for philander.display

"""Basic display module to support different kinds of a visible user interface.

This is a convergence layer to provide unified access to displays,
screens and other visible user interfaces beyond LEDs.
"""
__author__ = "Oliver Maye"
__version__ = "0.1"
__all__ = ["Display", ]

from abc import ABC, abstractmethod

from .module import Module
from .pwm import PWM
from .systypes import ErrorCode, RunLevel

 
[docs] class Display(ABC, Module): """Generic interface for a visual user interface (display). This is an abstract base class (ABC), that cannot be instantiated directly, but must be derived before making use of it. The design of this class aims at being agnostic to the display technology. Derivatives of this class may be more specific to e.g. just symbols displays, text-only displays or graphic displays. However, a display may tell about its nature, shape and capabilities. Moreover, it supports the concept of orientation and may adjust back light, contrast and inverse presentation. """ MODULE_PARAM_PREFIX = "display" # Mnemonics to describe the capabilities of the display. CAPABILITIES_UNKNOWN = 0 # Capabilities unknown or not set. CAPABILITIES_TEXT_ONLY = 1 # Text only, character size is likely fixed CAPABILITIES_GRAPHIC = 2 # Full graphic display # Mnemonics to describe intensities as percentages. # This is meant for brightness and contrast settings. INTENSITY_OFF = 0 INTENSITY_LOW = 25 INTENSITY_MEDIUM = 50 INTENSITY_HIGH = 75 INTENSITY_FULL = 100 # Display orientation. This is meant to compensate e.g. restrictions # enforced by the physical assembly. # Note that rotation is applied before flipping! # Normal, straight, default; Top-left advancing right ORIENTATION_NATURAL = 0x00 # Vertical flip; Top-right advancing left ORIENTATION_FLIP_V = 0x01 # Horizontal flip; Bottom-left advancing right ORIENTATION_FLIP_H = 0x02 # Twofold flip; Bottom-right advancing left ORIENTATION_FLIP_HV = (ORIENTATION_FLIP_H | ORIENTATION_FLIP_V) # Clock-wise rotation by 90; Top-right advancing down ORIENTATION_ROTATE_CW = 0x04 # Clock-wise rotation, then V-flip; Top-left advancing down ORIENTATION_ROTATE_CW_FLIP_V = (ORIENTATION_ROTATE_CW | ORIENTATION_FLIP_V) # Clock-wise rotation, then H-flip; Bottom-right advancing up ORIENTATION_ROTATE_CW_FLIP_H = (ORIENTATION_ROTATE_CW | ORIENTATION_FLIP_H) # Clock-wise rotation plus twofold flip; Bottom-left advancing up ORIENTATION_ROTATE_CW_FLIP_HV= (ORIENTATION_ROTATE_CW | ORIENTATION_FLIP_H | ORIENTATION_FLIP_V) # Synonyms # Rotation by 180°; Bottom-right advancing left ORIENTATION_ROTATE_180 = ORIENTATION_FLIP_HV # Counterclock-wise rotation; Bottom-left advancing up ORIENTATION_ROTATE_CCW = ORIENTATION_ROTATE_CW_FLIP_HV # Mnemonics to describe the shape of the display. SHAPE_RECTANGLE = 0 # Rectangle SHAPE_CIRCLE = 1 # Circle def __init__(self): # important states to remember self._runLevel = RunLevel.shutdown # read-only attributes to describe the physical properties and # capabilities of this display self._capabilities = Display.CAPABILITIES_UNKNOWN # Type of display self._orientation = Display.ORIENTATION_NATURAL self._shape = Display.SHAPE_RECTANGLE # Geometry of the display self._brightness = Display.INTENSITY_HIGH self._contrast = Display.INTENSITY_HIGH ############################# # Module API ############################# # # Non-public, internal driver interface # Wherever possible, method signatures are oriented on their # public counterparts. # @abstractmethod def _drvOpen(self, paramDict): """Open the low-level driver and prepare it for use. :param dict(str, object) paramDict: Configuration parameters. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ pass @abstractmethod def _drvClose(self): """Close this instance and release associated hardware resources. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ pass def _drvSetRunLevel(self, level): """Select the power-saving operation mode. :param RunLevel level: The level to switch to. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ del level return ErrorCode.errNotSupported # # Public API #
[docs] @classmethod def Params_init( cls, paramDict ): """Initialize parameters with default values. Supported key names and their meanings are: =========================== =============================================================================================== Key Meaning, Range, Default =========================== =============================================================================================== display.brightness Brightness of screen or backlight as a percentage; 0...100; :attr:`Display.INTENSITY_HIGH` display.contrast Screen contrast setting as a percentage; 0...100; :attr:`Display.INTENSITY_HIGH` display.light.pwm.* PWM configuration for the display light display.orientation Orientation of the screen; :attr:`Display.ORIENTATION_NATURAL` =========================== =============================================================================================== :param dict(str, object) paramDict: Dictionary of configuration settings. :return: none :rtype: None """ defaults = { "brightness": Display.INTENSITY_HIGH, "contrast": Display.INTENSITY_HIGH, "orientation": Display.ORIENTATION_NATURAL, } cls._aggregateParams( paramDict, defaults, cls.MODULE_PARAM_PREFIX+"." ) prefix = cls.MODULE_PARAM_PREFIX + ".light." tempDict = cls._extractParams( paramDict, prefix) PWM.Params_init(tempDict) cls._aggregateParams( paramDict, tempDict, prefix ) return None
[docs] def open(self, paramDict): self.Params_init(paramDict) ret = self._drvOpen(paramDict) if ret.isOk(): ret = self.setRunLevel( RunLevel.active ) if ret.isLight(): key = self.MODULE_PARAM_PREFIX + ".orientation" ret = self.setOrientation( paramDict[key] ) if ret.isLight(): key = self.MODULE_PARAM_PREFIX + ".brightness" ret = self.setBrightness( paramDict[key] ) if ret.isLight(): key = self.MODULE_PARAM_PREFIX + ".contrast" ret = self.setContrast( paramDict[key] ) if ret.isLight(): ret = ErrorCode.errOk return ret
[docs] def close(self): ret = self.setRunLevel( RunLevel.sleep ) err = self._drvClose() if not err.isOk(): ret = err return ret
[docs] def setRunLevel(self, level): SetDormant = [RunLevel.sleep, RunLevel.deepSleep, RunLevel.shutdown] ret = ErrorCode.errOk if level == RunLevel.active: intensity = Display.INTENSITY_HIGH elif level == RunLevel.idle: intensity = Display.INTENSITY_MEDIUM elif not level in SetDormant: # relax, snooze, nap intensity = Display.INTENSITY_LOW else: intensity = Display.INTENSITY_OFF if not level in SetDormant: # active ... nap ret = self._drvSetRunLevel(level) if ret.isLight(): ret = self.setBrightness( intensity ) if ret.isLight(): ret = self.setContrast( intensity ) else: ret = self.setBrightness( intensity ) if ret.isLight(): ret = self.setContrast( intensity ) if ret.isLight(): ret = self._drvSetRunLevel(level) if ret.isLight(): self._runLevel = level ret = ErrorCode.errOk return ret
############################# # Specific API ############################# # # Non-public, internal driver interface # Wherever possible, method signatures are oriented on their # public counterparts. # def _drvSetOrientation(self, orientation): """Switch to a new display orientation. :param int orientation: The new orientation to set. One of the ``Display.ORIENTATION_*`` values. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ del orientation return ErrorCode.errNotSupported def _drvSetBrightness(self, value): """Configure the brightness intensity. :param int value: The new value to set the brightness to. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ del value return ErrorCode.errNotSupported def _drvSetContrast(self, value): """ Adjusts the contrast intensity. :param int value: The new value to set the contrast to. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ del value return ErrorCode.errNotSupported def _drvSetInverse(self, inverseOn=True): """ Invert the display. :param bool inverseOn: `True` for inverse mode, `False` for normal mode. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ del inverseOn return ErrorCode.errNotSupported # # Public API # @property def capabilities(self): """Retrieve the capabilities of the underlying display hardware.""" return self._capabilities @property def orientation(self): """Retrieve the current display orientation.""" return self._orientation
[docs] def setOrientation(self, orientation): """Set the new display orientation. This is to maximize mechanical flexibility and to easily allow for flipping/rotating the content. :param int orientation: The new orientation to set. One of the ``Display.ORIENTATION_*`` values. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ ret = self._drvSetOrientation(orientation) if ret.isOk(): self._orientation = orientation return ret
@property def shape(self): """Retrieve the shape of the display.""" return self._shape
[docs] def setBrightness(self, value): """Configure the brightness or back light intensity. A bright light significantly improves the readability of the display content, but, at the same time, drastically increases electrical power consumption of the system. The situation-aware adjustment of the back light level could provide a chance to balance between these two contradicting requirements. The new brightness value is given as an integer percentage in the range of zero (0) to hundred (100), inclusively. Setting it to `0[%]` will effectively switch off the light. Giving a value of `100[%]` will put the light always on resulting in the brightest-possible illumination. Dimming the light corresponding to values in between might be implemented e.g. through pulse-width modulation (PWM). :param int value: The new value to set the brightness to. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ ret = ErrorCode.errOk if not isinstance( value, int) or \ (value < self.INTENSITY_OFF) or (value > self.INTENSITY_FULL): ret = ErrorCode.errInvalidParameter else: ret = self._drvSetBrightness(value) return ret
[docs] def setContrast(self, value): """ Adjusts the contrast intensity. Contrast is the difference between the brightness levels of bright white and full black pixels. For LCD due to technical reasons, the brightness of white pixels is not affected by this function. Instead, the saturation of black pixels can be faded from full black into faint grey. Note that low contrast will nominally reduce drawn current and thus, marginally save some power. At the same time, it will severely degrade reading comfort. The caller should deploy this function in conjunction with back light adjustment and power saving modes to balance readability with power consumption. The new contrast level is given as an integer percentage in the range of zero (0) to hundred (100), inclusively. A value of zero will obliterate any contrast and make black and white pixels appear the same resulting in nothing can be seen on an LCD. The value of `100` will impose the maximum contrast making black pixels clearly differ from white pixels. :param int value: The new value to set the contrast to. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ ret = ErrorCode.errOk if not isinstance( value, int) or \ (value < self.INTENSITY_OFF) or (value > self.INTENSITY_FULL): ret = ErrorCode.errInvalidParameter else: ret = self._drvSetContrast(value) return ret
[docs] def setInverse(self, inverseOn=True): """ Invert the display. Make white and light pixels appear black and gray, respectivley and vice-versa. :param bool inverseOn: `True` for inverse mode, `False` for normal mode. :return: An error code indicating either success or the reason of failure. :rtype: ErrorCode """ ret = self._drvSetInverse(inverseOn) return ret