- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pycqa/pylint
- rev: pylint-2.7.1
+ rev: v2.11.1
hooks:
- id: pylint
name: pylint (library code)
--- /dev/null
+# SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams
+#
+# SPDX-License-Identifier: MIT
+
+"""
+`_typing`
+================================================================================
+
+Type aliases for Blinka
+
+**Software and Dependencies:**
+
+* Adafruit Blinka:
+ https://github.com/adafruit/Adafruit_Blinka/releases
+
+* Author(s): Melissa LeBlanc-Williams
+
+"""
+
+from typing import Union
+
+__version__ = "0.0.0-auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_Blinka_Displayio.git"
+
+
+WriteableBuffer = Union[
+ bytearray,
+ memoryview,
+ # array.array,
+ # ulab.numpy.ndarray,
+ # rgbmatrix.RGBMatrix
+]
+
+ReadableBuffer = Union[
+ bytes,
+ WriteableBuffer,
+]
"""
-from displayio.bitmap import Bitmap
-from displayio.colorconverter import ColorConverter
-from displayio.display import Display
-from displayio.epaperdisplay import EPaperDisplay
-from displayio.fourwire import FourWire
-from displayio.group import Group
-from displayio.i2cdisplay import I2CDisplay
-from displayio.ondiskbitmap import OnDiskBitmap
-from displayio.palette import Palette
-from displayio.parallelbus import ParallelBus
-from displayio.shape import Shape
-from displayio.tilegrid import TileGrid
-from displayio.display import displays
+from typing import Union
+from ._fourwire import FourWire
+from ._i2cdisplay import I2CDisplay
+from ._bitmap import Bitmap
+from ._colorspace import Colorspace
+from ._colorconverter import ColorConverter
+from ._display import Display
+from ._epaperdisplay import EPaperDisplay
+from ._group import Group
+from ._ondiskbitmap import OnDiskBitmap
+from ._palette import Palette
+from ._shape import Shape
+from ._tilegrid import TileGrid
+from ._display import displays
+from ._displaybus import _DisplayBus
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
-def release_displays():
+def release_displays() -> None:
"""Releases any actively used displays so their busses and pins can be used again.
Use this once in your code.py if you initialize a display. Place it right before the
"""
+from __future__ import annotations
+from typing import Union, Tuple
from recordclass import recordclass
from PIL import Image
class Bitmap:
"""Stores values of a certain size in a 2D array"""
- def __init__(self, width, height, value_count):
+ def __init__(self, width: int, height: int, value_count: int):
"""Create a Bitmap object with the given fixed size. Each pixel stores a value that is
used to index into a corresponding palette. This enables differently colored sprites to
share the underlying Bitmap. value_count is used to minimize the memory used to store
self._image = Image.new("P", (width, height), 0)
self._dirty_area = Rectangle(0, 0, width, height)
- def __getitem__(self, index):
+ def __getitem__(self, index: Union[Tuple[int, int], int]) -> int:
"""
Returns the value at the given index. The index can either be
an x,y tuple or an int equal to `y * width + x`.
raise TypeError("Index is not an int, list, or tuple")
if x > self._image.width or y > self._image.height:
- raise ValueError("Index {} is out of range".format(index))
+ raise ValueError(f"Index {index} is out of range")
return self._image.getpixel((x, y))
- def __setitem__(self, index, value):
+ def __setitem__(self, index: Union[Tuple[int, int], int], value: int) -> None:
"""
Sets the value at the given index. The index can either be
an x,y tuple or an int equal to `y * width + x`.
self._dirty_area.x1 = 0
self._dirty_area.x2 = 0
- def fill(self, value):
+ def fill(self, value: int) -> None:
"""Fills the bitmap with the supplied palette index value."""
self._image = Image.new("P", (self._bmp_width, self._bmp_height), value)
self._dirty_area = Rectangle(0, 0, self._bmp_width, self._bmp_height)
+ def blit(
+ self,
+ x: int,
+ y: int,
+ source_bitmap: Bitmap,
+ *,
+ x1: int,
+ y1: int,
+ x2: int,
+ y2: int,
+ skip_index: int,
+ ) -> None:
+ # pylint: disable=unnecessary-pass, invalid-name
+ """Inserts the source_bitmap region defined by rectangular boundaries"""
+ pass
+
+ def dirty(self, x1: int = 0, y1: int = 0, x2: int = -1, y2: int = -1) -> None:
+ # pylint: disable=unnecessary-pass, invalid-name
+ """Inform displayio of bitmap updates done via the buffer protocol."""
+ pass
+
@property
- def width(self):
+ def width(self) -> int:
"""Width of the bitmap. (read only)"""
return self._bmp_width
@property
- def height(self):
+ def height(self) -> int:
"""Height of the bitmap. (read only)"""
return self._bmp_height
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
+from ._colorspace import Colorspace
+
class ColorConverter:
"""Converts one color format to another. Color converter based on original displayio
code for consistency.
"""
- def __init__(self, *, dither=False):
+ def __init__(
+ self, *, input_colorspace: Colorspace = Colorspace.RGB888, dither: bool = False
+ ):
"""Create a ColorConverter object to convert color formats.
Only supports rgb888 to RGB565 currently.
:param bool dither: Adds random noise to dither the output image
"""
self._dither = dither
self._depth = 16
+ self._transparent_color = None
self._rgba = False
+ self._input_colorspace = input_colorspace
- # pylint: disable=no-self-use
- def _compute_rgb565(self, color):
+ def _compute_rgb565(self, color: int):
self._depth = 16
return (color[0] & 0xF8) << 8 | (color[1] & 0xFC) << 3 | color[2] >> 3
- def _compute_luma(self, color):
+ @staticmethod
+ def _compute_luma(color: int):
red = color >> 16
green = (color >> 8) & 0xFF
blue = color & 0xFF
return (red * 19) / 255 + (green * 182) / 255 + (blue + 54) / 255
- def _compute_chroma(self, color):
+ @staticmethod
+ def _compute_chroma(color: int):
red = color >> 16
green = (color >> 8) & 0xFF
blue = color & 0xFF
return max(red, green, blue) - min(red, green, blue)
- def _compute_hue(self, color):
+ def _compute_hue(self, color: int):
red = color >> 16
green = (color >> 8) & 0xFF
blue = color & 0xFF
return hue
- def _dither_noise_1(self, noise):
+ @staticmethod
+ def _dither_noise_1(noise):
noise = (noise >> 13) ^ noise
more_noise = (
noise * (noise * noise * 60493 + 19990303) + 1376312589
def _compute_tricolor(self):
pass
- def convert(self, color):
+ def convert(self, color: int) -> int:
"Converts the given rgb888 color to RGB565"
if isinstance(color, int):
color = ((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, 255)
return color
return self._compute_rgb565(color)
- # pylint: enable=no-self-use
+ def make_transparent(self, color: int) -> None:
+ """Set the transparent color or index for the ColorConverter. This will
+ raise an Exception if there is already a selected transparent index.
+ """
+ self._transparent_color = color
+
+ def make_opaque(self, color: int) -> None:
+ # pylint: disable=unused-argument
+ """Make the ColorConverter be opaque and have no transparent pixels."""
+ self._transparent_color = None
@property
- def dither(self):
+ def dither(self) -> bool:
"""When true the color converter dithers the output by adding
random noise when truncating to display bitdepth
"""
return self._dither
@dither.setter
- def dither(self, value):
+ def dither(self, value: bool):
if not isinstance(value, bool):
raise ValueError("Value should be boolean")
self._dither = value
--- /dev/null
+# SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+
+"""
+`displayio.colorspace`
+================================================================================
+
+displayio for Blinka
+
+**Software and Dependencies:**
+
+* Adafruit Blinka:
+ https://github.com/adafruit/Adafruit_Blinka/releases
+
+* Author(s): Melissa LeBlanc-Williams
+
+"""
+
+__version__ = "0.0.0-auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
+
+
+class Colorspace:
+ """The colorspace for a ColorConverter to operate in."""
+
+ # pylint: disable=too-few-public-methods
+ def __init__(self, colorspace):
+ self._type = colorspace
+
+
+Colorspace.RGB888 = Colorspace("RGB888")
+Colorspace.RGB565 = Colorspace("RGB565")
+Colorspace.RGB565_SWAPPED = Colorspace("RGB565_SWAPPED")
+Colorspace.RGB555 = Colorspace("RGB555")
+Colorspace.RGB555_SWAPPED = Colorspace("RGB555_SWAPPED")
+Colorspace.BGR565 = Colorspace("BGR565")
+Colorspace.BGR565_SWAPPED = Colorspace("BGR565_SWAPPED")
+Colorspace.BGR555 = Colorspace("BGR555")
+Colorspace.BGR555_SWAPPED = Colorspace("BGR555_SWAPPED")
+Colorspace.L8 = Colorspace("L8")
--- /dev/null
+# SPDX-FileCopyrightText: 2021 James Carr
+#
+# SPDX-License-Identifier: MIT
+
+
+"""Constants"""
+
+
+__version__ = "0.0.0-auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_Blinka_Displayio.git"
+
+
+DISPLAY_COMMAND = 0
+DISPLAY_DATA = 1
+
+CHIP_SELECT_UNTOUCHED = 0
+CHIP_SELECT_TOGGLE_EVERY_BYTE = 1
import time
import struct
import threading
+from typing import Optional
import digitalio
from PIL import Image
import numpy
+import microcontroller
from recordclass import recordclass
-from displayio.colorconverter import ColorConverter
+import _typing
+from ._displaybus import _DisplayBus
+from ._colorconverter import ColorConverter
+from ._group import Group
+from ._constants import (
+ CHIP_SELECT_TOGGLE_EVERY_BYTE,
+ CHIP_SELECT_UNTOUCHED,
+ DISPLAY_COMMAND,
+ DISPLAY_DATA,
+)
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
def __init__(
self,
- display_bus,
- init_sequence,
+ display_bus: _DisplayBus,
+ init_sequence: _typing.ReadableBuffer,
*,
- width,
- height,
- colstart=0,
- rowstart=0,
- rotation=0,
- color_depth=16,
- grayscale=False,
- pixels_in_byte_share_row=True,
- bytes_per_cell=1,
- reverse_pixels_in_byte=False,
- set_column_command=0x2A,
- set_row_command=0x2B,
- write_ram_command=0x2C,
- set_vertical_scroll=0,
- backlight_pin=None,
- brightness_command=None,
- brightness=1.0,
- auto_brightness=False,
- single_byte_bounds=False,
- data_as_commands=False,
- auto_refresh=True,
- native_frames_per_second=60
+ width: int,
+ height: int,
+ colstart: int = 0,
+ rowstart: int = 0,
+ rotation: int = 0,
+ color_depth: int = 16,
+ grayscale: bool = False,
+ pixels_in_byte_share_row: bool = True,
+ bytes_per_cell: int = 1,
+ reverse_pixels_in_byte: bool = False,
+ set_column_command: int = 0x2A,
+ set_row_command: int = 0x2B,
+ write_ram_command: int = 0x2C,
+ backlight_pin: Optional[microcontroller.Pin] = None,
+ brightness_command: Optional[int] = None,
+ brightness: float = 1.0,
+ auto_brightness: bool = False,
+ single_byte_bounds: bool = False,
+ data_as_commands: bool = False,
+ auto_refresh: bool = True,
+ native_frames_per_second: int = 60,
+ backlight_on_high: bool = True,
+ SH1107_addressing: bool = False,
+ set_vertical_scroll: int = 0,
):
- # pylint: disable=unused-argument,too-many-locals
+ # pylint: disable=unused-argument,too-many-locals,invalid-name
"""Create a Display object on the given display bus (`displayio.FourWire` or
- `displayio.ParallelBus`).
+ `paralleldisplay.ParallelBus`).
The ``init_sequence`` is bitpacked to minimize the ram impact. Every command begins
with a command byte followed by a byte to determine the parameter count and if a
self._backlight_type = None
if backlight_pin is not None:
try:
- from pulseio import PWMOut # pylint: disable=import-outside-toplevel
+ from pwmio import PWMOut # pylint: disable=import-outside-toplevel
# 100Hz looks decent and doesn't keep the CPU too busy
self._backlight = PWMOut(backlight_pin, frequency=100, duty_cycle=0)
delay = (data_size & 0x80) > 0
data_size &= ~0x80
- self._write(command, init_sequence[i + 2 : i + 2 + data_size])
+ self._send(command, init_sequence[i + 2 : i + 2 + data_size])
delay_time_ms = 10
if delay:
data_size += 1
time.sleep(delay_time_ms / 1000)
i += 2 + data_size
- def _write(self, command, data):
- self._bus.begin_transaction()
+ def _send(self, command, data):
+ # pylint: disable=protected-access
+ self._bus._begin_transaction()
if self._data_as_commands:
- if command is not None:
- self._bus.send(True, bytes([command]), toggle_every_byte=True)
- self._bus.send(command is not None, data)
+ self._bus._send(
+ DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, bytes([command] + data)
+ )
else:
- self._bus.send(True, bytes([command]), toggle_every_byte=True)
- self._bus.send(False, data)
- self._bus.end_transaction()
+ self._bus._send(
+ DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, bytes([command])
+ )
+ self._bus._send(DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, data)
+ self._bus._end_transaction()
+
+ def _send_pixels(self, data):
+ # pylint: disable=protected-access
+ if not self._data_as_commands:
+ self._bus._send(
+ DISPLAY_COMMAND,
+ CHIP_SELECT_TOGGLE_EVERY_BYTE,
+ bytes([self._write_ram_command]),
+ )
+ self._bus._send(DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, data)
def _release(self):
self._bus._release() # pylint: disable=protected-access
self._bus = None
- def show(self, group):
+ def show(self, group: Group) -> None:
"""Switches to displaying the given group of layers. When group is None, the
default CircuitPython terminal will be shown.
"""
self._current_group = group
- def refresh(self, *, target_frames_per_second=60, minimum_frames_per_second=1):
+ def refresh(
+ self,
+ *,
+ target_frames_per_second: Optional[int] = None,
+ minimum_frames_per_second: int = 0,
+ ) -> bool:
# pylint: disable=unused-argument
"""When auto refresh is off, waits for the target frame rate and then refreshes the
display, returning True. If the call has taken too long since the last refresh call
for area in self._subrectangles:
self._refresh_display_area(area)
+ return True
+
def _refresh_loop(self):
while self._auto_refresh:
self.refresh()
numpy.dstack(((color >> 8) & 0xFF, color & 0xFF)).flatten().tolist()
)
- self._write(
+ self._send(
self._set_column_command,
self._encode_pos(
display_rectangle.x1 + self._colstart,
display_rectangle.x2 + self._colstart - 1,
),
)
- self._write(
+ self._send(
self._set_row_command,
self._encode_pos(
display_rectangle.y1 + self._rowstart,
),
)
- if self._data_as_commands:
- self._write(None, pixels)
- else:
- self._write(self._write_ram_command, pixels)
+ self._bus._begin_transaction() # pylint: disable=protected-access
+ self._send_pixels(pixels)
+ self._bus._end_transaction() # pylint: disable=protected-access
def _clip(self, rectangle):
if self._rotation in (90, 270):
else:
width, height = self._width, self._height
- if rectangle.x1 < 0:
- rectangle.x1 = 0
- if rectangle.y1 < 0:
- rectangle.y1 = 0
- if rectangle.x2 > width:
- rectangle.x2 = width
- if rectangle.y2 > height:
- rectangle.y2 = height
+ rectangle.x1 = max(rectangle.x1, 0)
+ rectangle.y1 = max(rectangle.y1, 0)
+ rectangle.x2 = min(rectangle.x2, width)
+ rectangle.y2 = min(rectangle.y2, height)
return rectangle
def _encode_pos(self, x, y):
"""Encode a postion into bytes."""
- return struct.pack(self._bounds_encoding, x, y)
+ return struct.pack(self._bounds_encoding, x, y) # pylint: disable=no-member
- def fill_row(self, y, buffer):
+ def fill_row(
+ self, y: int, buffer: _typing.WriteableBuffer
+ ) -> _typing.WriteableBuffer:
"""Extract the pixels from a single row"""
for x in range(0, self._width):
_rgb_565 = self._colorconverter.convert(self._buffer.getpixel((x, y)))
return buffer
@property
- def auto_refresh(self):
+ def auto_refresh(self) -> bool:
"""True when the display is refreshed automatically."""
return self._auto_refresh
@auto_refresh.setter
- def auto_refresh(self, value):
+ def auto_refresh(self, value: bool):
self._auto_refresh = value
if self._refresh_thread is None:
self._refresh_thread = threading.Thread(
self._refresh_thread.join()
@property
- def brightness(self):
+ def brightness(self) -> float:
"""The brightness of the display as a float. 0.0 is off and 1.0 is full `brightness`.
When `auto_brightness` is True, the value of `brightness` will change automatically.
If `brightness` is set, `auto_brightness` will be disabled and will be set to False.
return self._brightness
@brightness.setter
- def brightness(self, value):
+ def brightness(self, value: float):
if 0 <= float(value) <= 1.0:
self._brightness = value
if self._backlight_type == BACKLIGHT_IN_OUT:
elif self._backlight_type == BACKLIGHT_PWM:
self._backlight.duty_cycle = self._brightness * 65535
elif self._brightness_command is not None:
- self._write(self._brightness_command, round(value * 255))
+ self._send(self._brightness_command, round(value * 255))
else:
raise ValueError("Brightness must be between 0.0 and 1.0")
@property
- def auto_brightness(self):
+ def auto_brightness(self) -> bool:
"""True when the display brightness is adjusted automatically, based on an ambient
light sensor or other method. Note that some displays may have this set to True by
default, but not actually implement automatic brightness adjustment.
return self._auto_brightness
@auto_brightness.setter
- def auto_brightness(self, value):
+ def auto_brightness(self, value: bool):
self._auto_brightness = value
@property
- def width(self):
+ def width(self) -> int:
"""Display Width"""
return self._width
@property
- def height(self):
+ def height(self) -> int:
"""Display Height"""
return self._height
@property
- def rotation(self):
+ def rotation(self) -> int:
"""The rotation of the display as an int in degrees."""
return self._rotation
@rotation.setter
- def rotation(self, value):
+ def rotation(self, value: int):
if value not in (0, 90, 180, 270):
raise ValueError("Rotation must be 0/90/180/270")
self._rotation = value
@property
- def bus(self):
+ def bus(self) -> _DisplayBus:
"""Current Display Bus"""
return self._bus
--- /dev/null
+# SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams
+#
+# SPDX-License-Identifier: MIT
+
+"""
+`displayio._displaybus`
+================================================================================
+
+Type aliases for Blinka
+
+**Software and Dependencies:**
+
+* Adafruit Blinka:
+ https://github.com/adafruit/Adafruit_Blinka/releases
+
+* Author(s): Melissa LeBlanc-Williams
+
+"""
+
+from typing import Union
+import paralleldisplay
+from ._fourwire import FourWire
+from ._i2cdisplay import I2CDisplay
+
+__version__ = "0.0.0-auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_Blinka_Displayio.git"
+
+_DisplayBus = Union[FourWire, I2CDisplay, paralleldisplay.ParallelBus]
"""
+from typing import Optional
+import microcontroller
+import _typing
+from ._group import Group
+from ._displaybus import _DisplayBus
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
def __init__(
self,
- display_bus,
- start_sequence,
- stop_sequence,
+ display_bus: _DisplayBus,
+ start_sequence: _typing.ReadableBuffer,
+ stop_sequence: _typing.ReadableBuffer,
*,
- width,
- height,
- ram_width,
- ram_height,
- colstart=0,
- rowstart=0,
- rotation=0,
- set_column_window_command=None,
- set_row_window_command=None,
- set_current_column_command=None,
- set_current_row_command=None,
- write_black_ram_command,
- black_bits_inverted=False,
- write_color_ram_command=None,
- color_bits_inverted=False,
- highlight_color=0x000000,
- refresh_display_command,
- refresh_time=40,
- busy_pin=None,
- busy_state=True,
- seconds_per_frame=180,
- always_toggle_chip_select=False,
- grayscale=False,
+ width: int,
+ height: int,
+ ram_width: int,
+ ram_height: int,
+ colstart: int = 0,
+ rowstart: int = 0,
+ rotation: int = 0,
+ set_column_window_command: Optional[int] = None,
+ set_row_window_command: Optional[int] = None,
+ set_current_column_command: Optional[int] = None,
+ set_current_row_command: Optional[int] = None,
+ write_black_ram_command: int,
+ black_bits_inverted: bool = False,
+ write_color_ram_command: Optional[int] = None,
+ color_bits_inverted: bool = False,
+ highlight_color: int = 0x000000,
+ refresh_display_command: int,
+ refresh_time: float = 40.0,
+ busy_pin: Optional[microcontroller.Pin] = None,
+ busy_state: bool = True,
+ seconds_per_frame: float = 180.0,
+ always_toggle_chip_select: bool = False,
+ grayscale: bool = False,
):
- # pylint: disable=too-many-locals,unnecessary-pass
+ # pylint: disable=too-many-locals, unused-argument
"""
Create a EPaperDisplay object on the given display bus (displayio.FourWire or
- displayio.ParallelBus).
+ paralleldisplay.ParallelBus).
The start_sequence and stop_sequence are bitpacked to minimize the ram impact. Every
command begins with a command byte followed by a byte to determine the parameter
:param bool always_toggle_chip_select: When True, chip select is toggled every byte
:param bool grayscale: When true, the color ram is the low bit of 2-bit grayscale
"""
- pass
+ self._bus = display_bus
+ self._width = width
+ self._height = height
- def show(self, group):
+ def show(self, group: Group) -> None:
# pylint: disable=unnecessary-pass
"""Switches to displaying the given group of layers. When group is None, the default
CircuitPython terminal will be shown (eventually).
"""
pass
- def refresh(self):
+ def refresh(self) -> None:
# pylint: disable=unnecessary-pass
"""Refreshes the display immediately or raises an exception if too soon. Use
``time.sleep(display.time_to_refresh)`` to sleep until a refresh can occur.
pass
@property
- def time_to_refresh(self):
+ def time_to_refresh(self) -> float:
"""Time, in fractional seconds, until the ePaper display can be refreshed."""
- return 0
+ return 0.0
@property
- def width(self):
- # pylint: disable=unnecessary-pass
+ def width(self) -> int:
"""Display Width"""
- pass
+ return self._width
@property
- def height(self):
- # pylint: disable=unnecessary-pass
+ def height(self) -> int:
"""Display Height"""
- pass
+ return self._height
@property
- def bus(self):
- # pylint: disable=unnecessary-pass
+ def bus(self) -> _DisplayBus:
"""Current Display Bus"""
- pass
+ return self._bus
"""
import time
+from typing import Optional
import digitalio
+import busio
+import microcontroller
+import _typing
+from ._constants import (
+ CHIP_SELECT_TOGGLE_EVERY_BYTE,
+ CHIP_SELECT_UNTOUCHED,
+ DISPLAY_COMMAND,
+ DISPLAY_DATA,
+)
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
def __init__(
self,
- spi_bus,
+ spi_bus: busio.SPI,
*,
- command,
- chip_select,
- reset=None,
- baudrate=24000000,
- polarity=0,
- phase=0
+ command: microcontroller.Pin,
+ chip_select: microcontroller.Pin,
+ reset: Optional[microcontroller.Pin] = None,
+ baudrate: int = 24000000,
+ polarity: int = 0,
+ phase: int = 0,
):
"""Create a FourWire object associated with the given pins.
if self._reset is not None:
self._reset.deinit()
- def reset(self):
+ def reset(self) -> None:
"""Performs a hardware reset via the reset pin.
Raises an exception if called when no reset pin is available.
"""
self._reset.value = True
time.sleep(0.001)
- def send(self, is_command, data, *, toggle_every_byte=False):
- """Sends the given command value followed by the full set of data. Display state,
+ def send(
+ self, command, data: _typing.ReadableBuffer, *, toggle_every_byte: bool = False
+ ) -> None:
+ """
+ Sends the given command value followed by the full set of data. Display state,
such as vertical scroll, set via ``send`` may or may not be reset once the code is
done.
"""
- self._dc.value = not is_command
- if toggle_every_byte:
+ if not 0 <= command <= 255:
+ raise ValueError("Command must be an int between 0 and 255")
+ chip_select = (
+ CHIP_SELECT_TOGGLE_EVERY_BYTE
+ if toggle_every_byte
+ else CHIP_SELECT_UNTOUCHED
+ )
+ self._begin_transaction()
+ self._send(DISPLAY_COMMAND, chip_select, command)
+ self._send(DISPLAY_DATA, chip_select, data)
+ self._end_transaction()
+
+ def _send(self, data_type: int, chip_select: int, data: _typing.ReadableBuffer):
+ self._dc.value = data_type == DISPLAY_DATA
+ if chip_select == CHIP_SELECT_TOGGLE_EVERY_BYTE:
for byte in data:
self._spi.write(bytes([byte]))
self._chip_select.value = True
else:
self._spi.write(data)
- def begin_transaction(self):
+ def _begin_transaction(self):
"""Begin the SPI transaction by locking, configuring, and setting Chip Select"""
while not self._spi.try_lock():
pass
)
self._chip_select.value = False
- def end_transaction(self):
+ def _end_transaction(self):
"""End the SPI transaction by unlocking and setting Chip Select"""
self._chip_select.value = True
self._spi.unlock()
"""
+from __future__ import annotations
+from typing import Union, Callable
from recordclass import recordclass
-from displayio.tilegrid import TileGrid
+from ._tilegrid import TileGrid
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
leads to a layer's pixel being 2x2 pixels when in the group.
"""
- def __init__(self, *, max_size=None, scale=1, x=0, y=0):
+ def __init__(self, *, scale: int = 1, x: int = 0, y: int = 0):
"""
- :param Optional(int) max_size: *DEPRECATED* This has been removed in CircuitPython 7 and
- will be removed in a future version of ``Adafruit_Blinka_Displayio``
:param int scale: Scale of layer pixels in one dimension.
:param int x: Initial x position within the parent.
:param int y: Initial y position within the parent.
"""
- if max_size is not None:
- print(
- "The max_size parameter displayio.Group() has been deprecated. "
- "Please remove max_size from your code."
- )
-
if not isinstance(scale, int) or scale < 1:
raise ValueError("Scale must be >= 1")
self._scale = 1 # Use the setter below to actually set the scale
self._absolute_transform = Transform(0, 0, 1, 1, 1, False, False, False)
self._set_scale(scale) # Set the scale via the setter
- def update_transform(self, parent_transform):
+ def _update_transform(self, parent_transform):
"""Update the parent transform and child transforms"""
self.in_group = parent_transform is not None
if self.in_group:
self._update_child_transforms()
def _update_child_transforms(self):
+ # pylint: disable=protected-access
if self.in_group:
for layer in self._layers:
- layer.update_transform(self._absolute_transform)
+ layer._update_transform(self._absolute_transform)
def _removal_cleanup(self, index):
+ # pylint: disable=protected-access
layer = self._layers[index]
- layer.update_transform(None)
+ layer._update_transform(None)
def _layer_update(self, index):
+ # pylint: disable=protected-access
layer = self._layers[index]
- layer.update_transform(self._absolute_transform)
+ layer._update_transform(self._absolute_transform)
- def append(self, layer):
+ def append(self, layer: Union[Group, TileGrid]) -> None:
"""Append a layer to the group. It will be drawn
above other layers.
"""
self.insert(len(self._layers), layer)
- def insert(self, index, layer):
+ def insert(self, index: int, layer: Union[Group, TileGrid]) -> None:
"""Insert a layer into the group."""
if not isinstance(layer, self._supported_types):
raise ValueError("Invalid Group Member")
self._layers.insert(index, layer)
self._layer_update(index)
- def index(self, layer):
+ def index(self, layer: Union[Group, TileGrid]) -> int:
"""Returns the index of the first copy of layer.
Raises ValueError if not found.
"""
return self._layers.index(layer)
- def pop(self, index=-1):
+ def pop(self, index: int = -1) -> Union[Group, TileGrid]:
"""Remove the ith item and return it."""
self._removal_cleanup(index)
return self._layers.pop(index)
- def remove(self, layer):
+ def remove(self, layer: Union[Group, TileGrid]) -> None:
"""Remove the first copy of layer. Raises ValueError
if it is not present."""
index = self.index(layer)
self._layers.pop(index)
- def __len__(self):
+ def __bool__(self) -> bool:
+ """Returns if there are any layers"""
+ return len(self._layers) > 0
+
+ def __len__(self) -> int:
"""Returns the number of layers in a Group"""
return len(self._layers)
- def __getitem__(self, index):
+ def __getitem__(self, index: int) -> Union[Group, TileGrid]:
"""Returns the value at the given index."""
return self._layers[index]
- def __setitem__(self, index, value):
+ def __setitem__(self, index: int, value: Union[Group, TileGrid]) -> None:
"""Sets the value at the given index."""
self._removal_cleanup(index)
self._layers[index] = value
self._layer_update(index)
- def __delitem__(self, index):
+ def __delitem__(self, index: int) -> None:
"""Deletes the value at the given index."""
del self._layers[index]
if isinstance(layer, (Group, TileGrid)):
layer._fill_area(buffer) # pylint: disable=protected-access
+ def sort(self, key: Callable, reverse: bool) -> None:
+ """Sort the members of the group."""
+ self._layers.sort(key=key, reverse=reverse)
+
@property
- def hidden(self):
- """True when the Group and all of it’s layers are not visible. When False, the
- Group’s layers are visible if they haven’t been hidden.
+ def hidden(self) -> bool:
+ """True when the Group and all of it's layers are not visible. When False, the
+ Group’s layers are visible if they haven't been hidden.
"""
return self._hidden_group
@hidden.setter
- def hidden(self, value):
+ def hidden(self, value: bool):
if not isinstance(value, (bool, int)):
raise ValueError("Expecting a boolean or integer value")
self._hidden_group = bool(value)
@property
- def scale(self):
+ def scale(self) -> int:
"""Scales each pixel within the Group in both directions. For example, when
scale=2 each pixel will be represented by 2x2 pixels.
"""
return self._scale
@scale.setter
- def scale(self, value):
+ def scale(self, value: int):
self._set_scale(value)
- def _set_scale(self, value):
+ def _set_scale(self, value: int):
# This is method allows the scale to be set by this class even when
# the scale property is over-ridden by a subclass.
if not isinstance(value, int) or value < 1:
self._update_child_transforms()
@property
- def x(self):
+ def x(self) -> int:
"""X position of the Group in the parent."""
return self._group_x
@x.setter
- def x(self, value):
+ def x(self, value: int):
if not isinstance(value, int):
raise ValueError("x must be an integer")
if self._group_x != value:
self._update_child_transforms()
@property
- def y(self):
+ def y(self) -> int:
"""Y position of the Group in the parent."""
return self._group_y
@y.setter
- def y(self, value):
+ def y(self, value: int):
if not isinstance(value, int):
raise ValueError("y must be an integer")
if self._group_y != value:
"""
-__version__ = "0.0.0-auto.0"
-__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
-
import time
import busio
import digitalio
+import _typing
+from ._constants import CHIP_SELECT_UNTOUCHED, DISPLAY_COMMAND
+
+__version__ = "0.0.0-auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
class I2CDisplay:
time.sleep(0.0001)
self._reset.value = True
- def begin_transaction(self) -> None:
- """
- Lock the bus before sending data.
- """
+ def _begin_transaction(self) -> None:
+ """Lock the bus before sending data."""
while not self._i2c.try_lock():
pass
- def send(self, command: bool, data, *, toggle_every_byte=False) -> None:
- # pylint: disable=unused-argument
+ def send(self, command: int, data: _typing.ReadableBuffer) -> None:
"""
Sends the given command value followed by the full set of data. Display state,
such as vertical scroll, set via ``send`` may or may not be reset once the code is
done.
"""
- # NOTE: we have to have a toggle_every_byte parameter, which we ignore,
- # because Display._write() sets it regardless of bus type.
+ self._begin_transaction()
+ self._send(DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, bytes([command] + data))
+ self._end_transaction()
- if command:
+ def _send(self, data_type: int, chip_select: int, data: _typing.ReadableBuffer):
+ # pylint: disable=unused-argument
+ if data_type == DISPLAY_COMMAND:
n = len(data)
if n > 0:
command_bytes = bytearray(n * 2)
command_bytes[2 * i + 1] = data[i]
self._i2c.writeto(self._dev_addr, buffer=command_bytes, stop=True)
-
else:
data_bytes = bytearray(len(data) + 1)
data_bytes[0] = 0x40
data_bytes[1:] = data
self._i2c.writeto(self._dev_addr, buffer=data_bytes, stop=True)
- def end_transaction(self) -> None:
- """
- Release the bus after sending data.
- """
+ def _end_transaction(self) -> None:
+ """Release the bus after sending data."""
self._i2c.unlock()
"""
+from typing import Union, BinaryIO
from PIL import Image
+from ._colorconverter import ColorConverter
+from ._palette import Palette
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
pixel load times. These load times may result in frame tearing where only part of the
image is visible."""
- def __init__(self, file):
+ def __init__(self, file: Union[str, BinaryIO]):
self._image = Image.open(file).convert("RGBA")
@property
- def width(self):
+ def width(self) -> int:
"""Width of the bitmap. (read only)"""
return self._image.width
@property
- def height(self):
+ def height(self) -> int:
"""Height of the bitmap. (read only)"""
return self._image.height
- def __getitem__(self, index):
+ @property
+ def pixel_shader(self) -> Union[ColorConverter, Palette]:
+ """Height of the bitmap. (read only)"""
+ return self._image.height
+
+ def __getitem__(self, index: Union[tuple, list, int]) -> int:
"""
Returns the value at the given index. The index can either be
an x,y tuple or an int equal to `y * width + x`.
"""
+from typing import Optional, Union, Tuple
+import _typing
+
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
format internally to save memory.
"""
- def __init__(self, color_count):
+ def __init__(self, color_count: int):
"""Create a Palette object to store a set number of colors."""
self._needs_refresh = False
return color
- def __len__(self):
+ def __len__(self) -> int:
"""Returns the number of colors in a Palette"""
return len(self._colors)
- def __setitem__(self, index, value):
+ def __setitem__(
+ self,
+ index: int,
+ value: Union[int, _typing.ReadableBuffer, Tuple[int, int, int]],
+ ) -> None:
"""Sets the pixel color at the given index. The index should be
an integer in the range 0 to color_count-1.
self._colors[index] = self._make_color(value)
self._update_rgba(index)
- def __getitem__(self, index):
+ def __getitem__(self, index: int) -> Optional[int]:
if not 0 <= index < len(self._colors):
raise ValueError("Palette index out of range")
return self._colors[index]["rgb888"]
- def make_transparent(self, palette_index):
+ def make_transparent(self, palette_index: int) -> None:
"""Set the palette index to be a transparent color"""
self._colors[palette_index]["transparent"] = True
self._update_rgba(palette_index)
- def make_opaque(self, palette_index):
+ def make_opaque(self, palette_index: int) -> None:
"""Set the palette index to be an opaque color"""
self._colors[palette_index]["transparent"] = False
self._update_rgba(palette_index)
for _ in range(3):
palette += [0 if color["transparent"] else 255]
return palette
+
+ def is_transparent(self, palette_index: int) -> bool:
+ """Returns True if the palette index is transparent. Returns False if opaque."""
+ return self._colors[palette_index]["transparent"]
"""
-from displayio.bitmap import Bitmap
+from ._bitmap import Bitmap
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
full row.
"""
- def __init__(self, width, height, *, mirror_x=False, mirror_y=False):
+ def __init__(
+ self, width: int, height: int, *, mirror_x: bool = False, mirror_y: bool = False
+ ):
# pylint: disable=unused-argument
"""Create a Shape object with the given fixed size. Each pixel is one bit and is
stored by the column boundaries of the shape on each row. Each row’s boundary
"""
super().__init__(width, height, 2)
- def set_boundary(self, y, start_x, end_x):
+ def set_boundary(self, y: int, start_x: int, end_x: int) -> None:
# pylint: disable=unnecessary-pass
"""Loads pre-packed data into the given row."""
pass
"""
+from typing import Union, Optional, Tuple
from recordclass import recordclass
from PIL import Image
-from displayio.bitmap import Bitmap
-from displayio.colorconverter import ColorConverter
-from displayio.ondiskbitmap import OnDiskBitmap
-from displayio.shape import Shape
-from displayio.palette import Palette
+from ._bitmap import Bitmap
+from ._colorconverter import ColorConverter
+from ._ondiskbitmap import OnDiskBitmap
+from ._shape import Shape
+from ._palette import Palette
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
def __init__(
self,
- bitmap,
+ bitmap: Union[Bitmap, OnDiskBitmap, Shape],
*,
- pixel_shader,
- width=1,
- height=1,
- tile_width=None,
- tile_height=None,
- default_tile=0,
- x=0,
- y=0
+ pixel_shader: Union[ColorConverter, Palette],
+ width: int = 1,
+ height: int = 1,
+ tile_width: Optional[int] = None,
+ tile_height: Optional[int] = None,
+ default_tile: int = 0,
+ x: int = 0,
+ y: int = 0,
):
"""Create a TileGrid object. The bitmap is source for 2d pixels. The pixel_shader is
used to convert the value and its location to a display native pixel color. This may
tile_width = bitmap_width
if tile_height is None or tile_width == 0:
tile_height = bitmap_height
- if tile_width < 1:
- tile_width = 1
- if tile_height < 1:
- tile_height = 1
+ if tile_width < 1 or tile_height < 1:
+ raise ValueError("Tile width and height must be greater than 0")
if bitmap_width % tile_width != 0:
raise ValueError("Tile width must exactly divide bitmap width")
self._tile_width = tile_width
self._current_area = Rectangle(0, 0, self._pixel_width, self._pixel_height)
self._moved = False
- def update_transform(self, absolute_transform):
+ def _update_transform(self, absolute_transform):
"""Update the parent transform and child transforms"""
self._absolute_transform = absolute_transform
if self._absolute_transform is not None:
buffer.alpha_composite(image, (x, y), source=(source_x, source_y))
@property
- def hidden(self):
+ def hidden(self) -> bool:
"""True when the TileGrid is hidden. This may be False even
when a part of a hidden Group."""
return self._hidden_tilegrid
@hidden.setter
- def hidden(self, value):
+ def hidden(self, value: bool):
if not isinstance(value, (bool, int)):
raise ValueError("Expecting a boolean or integer value")
self._hidden_tilegrid = bool(value)
@property
- def x(self):
+ def x(self) -> int:
"""X position of the left edge in the parent."""
return self._x
@x.setter
- def x(self, value):
+ def x(self, value: int):
if not isinstance(value, int):
raise TypeError("X should be a integer type")
if self._x != value:
self._update_current_x()
@property
- def y(self):
+ def y(self) -> int:
"""Y position of the top edge in the parent."""
return self._y
@y.setter
- def y(self, value):
+ def y(self, value: int):
if not isinstance(value, int):
raise TypeError("Y should be a integer type")
if self._y != value:
self._update_current_y()
@property
- def flip_x(self):
+ def flip_x(self) -> bool:
"""If true, the left edge rendered will be the right edge of the right-most tile."""
return self._flip_x
@flip_x.setter
- def flip_x(self, value):
+ def flip_x(self, value: bool):
if not isinstance(value, bool):
raise TypeError("Flip X should be a boolean type")
if self._flip_x != value:
self._flip_x = value
@property
- def flip_y(self):
+ def flip_y(self) -> bool:
"""If true, the top edge rendered will be the bottom edge of the bottom-most tile."""
return self._flip_y
@flip_y.setter
- def flip_y(self, value):
+ def flip_y(self, value: bool):
if not isinstance(value, bool):
raise TypeError("Flip Y should be a boolean type")
if self._flip_y != value:
self._flip_y = value
@property
- def transpose_xy(self):
- """If true, the TileGrid’s axis will be swapped. When combined with mirroring, any 90
+ def transpose_xy(self) -> bool:
+ """If true, the TileGrid's axis will be swapped. When combined with mirroring, any 90
degree rotation can be achieved along with the corresponding mirrored version.
"""
return self._transpose_xy
@transpose_xy.setter
- def transpose_xy(self, value):
+ def transpose_xy(self, value: bool):
if not isinstance(value, bool):
raise TypeError("Transpose XY should be a boolean type")
if self._transpose_xy != value:
self._update_current_y()
@property
- def pixel_shader(self):
+ def pixel_shader(self) -> Union[ColorConverter, Palette]:
"""The pixel shader of the tilegrid."""
return self._pixel_shader
raise ValueError("Tile index out of bounds")
return index
- def __getitem__(self, index):
+ def __getitem__(self, index: Union[Tuple[int, int], int]) -> int:
"""Returns the tile index at the given index. The index can either be
an x,y tuple or an int equal to ``y * width + x``'.
"""
index = self._extract_and_check_index(index)
return self._tiles[index]
- def __setitem__(self, index, value):
+ def __setitem__(self, index: Union[Tuple[int, int], int], value: int) -> None:
"""Sets the tile index at the given index. The index can either be
an x,y tuple or an int equal to ``y * width + x``.
"""
.. automodule:: displayio
:members: Bitmap, ColorConverter, Display, EPaperDisplay, FourWire, Group, I2CDisplay,
- OnDiskBitmap, Palette, ParallelBus, Shape, TileGrid
+ OnDiskBitmap, Palette, Shape, TileGrid
.. automodule:: fontio
:members:
.. automodule:: terminalio
:members:
+
+.. automodule:: paralleldisplay
+ :members:
# SPDX-License-Identifier: MIT
"""
-`displayio.parallelbus`
+`paralleldisplay`
================================================================================
-displayio for Blinka
+paralleldisplay for Blinka
**Software and Dependencies:**
"""
+import microcontroller
+import _typing
+
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
class ParallelBus:
"""Manage updating a display over 8-bit parallel bus in the background while Python code
runs. This protocol may be refered to as 8080-I Series Parallel Interface in datasheets.
- It doesn’t handle display initialization.
+ It doesn't handle display initialization.
"""
- def __init__(self, i2c_bus, *, device_address, reset=None):
+ def __init__(
+ self,
+ *,
+ data0: microcontroller.Pin,
+ command: microcontroller.Pin,
+ chip_select: microcontroller.Pin,
+ write: microcontroller.Pin,
+ read: microcontroller.Pin,
+ reset: microcontroller.Pin,
+ frequency: int = 30000000,
+ ):
# pylint: disable=unnecessary-pass
"""Create a ParallelBus object associated with the given pins. The
bus is inferred from data0 by implying the next 7 additional pins on a given GPIO
"""
pass
- def reset(self):
+ def reset(self) -> None:
"""Performs a hardware reset via the reset pin. Raises an exception if called when
no reset pin is available.
"""
raise NotImplementedError("ParallelBus reset has not been implemented yet")
- def send(self, command, data):
+ def send(self, command: int, data: _typing.ReadableBuffer) -> None:
"""Sends the given command value followed by the full set of data. Display state,
such as vertical scroll, set via ``send`` may or may not be reset once the code is
done.
keywords="adafruit blinka circuitpython micropython displayio lcd tft display pitft",
# You can just specify the packages manually here if your project is
# simple. Or you can use find_packages().
- py_modules=["fontio", "terminalio"],
+ py_modules=["fontio", "terminalio", "paralleldisplay", "_typing"],
packages=["displayio"],
)