Inkycal/inkycal/display/display.py

205 lines
7.0 KiB
Python
Raw Normal View History

"""
2022-10-02 00:49:27 +02:00
Inkycal ePaper driving functions
2024-06-26 01:47:58 +02:00
Copyright by aceinnolab
"""
from importlib import import_module
import PIL
2023-05-20 03:09:15 +02:00
from PIL import Image
from inkycal.display.supported_models import supported_models
2023-06-07 12:32:06 +02:00
def import_driver(model):
return import_module(f'inkycal.display.drivers.{model}')
class Display:
"""Display class for inkycal
2020-11-24 00:40:49 +01:00
Creates an instance of the driver for the selected E-Paper model and allows
rendering images and calibrating the E-Paper display
2020-12-05 00:18:14 +01:00
2023-05-20 03:09:15 +02:00
Args:
- epaper_model: The name of your E-Paper model.
2020-11-24 00:40:49 +01:00
"""
2020-12-05 00:18:14 +01:00
def __init__(self, epaper_model):
"""Load the drivers for this epaper model"""
2020-11-24 00:40:49 +01:00
2023-06-07 12:32:06 +02:00
if 'colour' in epaper_model:
self.supports_colour = True
else:
self.supports_colour = False
try:
driver = import_driver(epaper_model)
self._epaper = driver.EPD()
self.model_name = epaper_model
2020-11-24 00:40:49 +01:00
except ImportError:
raise Exception('This module is not supported. Check your spellings?')
except FileNotFoundError:
raise Exception('SPI could not be found. Please check if SPI is enabled')
def test(self) -> None:
"""Test the display by showing a test image"""
# TODO implement test image
raise NotImplementedError("Devs were too lazy again, sorry, please try again later")
def render(self, im_black: PIL.Image, im_colour: PIL.Image or None = None) -> None:
"""Renders an image on the selected E-Paper display.
Initlializes the E-Paper display, sends image data and executes command
to update the display.
Args:
- im_black: The image for the black-pixels. Anything in this image that is
black is rendered as black on the display. This is required and ideally
should be a black-white image.
- im_colour: For E-Paper displays supporting colour, a separate image,
ideally black-white is required for the coloured pixels. Anything that is
black in this image will show up as either red/yellow.
Rendering an image for black-white E-Paper displays:
2023-05-20 03:09:15 +02:00
>>> sample_image = Image.open('path/to/file.png')
>>> display = Display('my_black_white_display')
>>> display.render(sample_image)
Rendering black-white on coloured E-Paper displays:
2023-05-20 03:09:15 +02:00
>>> sample_image = Image.open('path/to/file.png')
>>> display = Display('my_coloured_display')
>>> display.render(sample_image, sample_image)
2020-11-24 00:40:49 +01:00
Rendering coloured image where 2 images are available:
2020-11-24 00:40:49 +01:00
2023-05-20 03:09:15 +02:00
>>> black_image = Image.open('path/to/file.png') # black pixels
>>> colour_image = Image.open('path/to/file.png') # coloured pixels
>>> display = Display('my_coloured_display')
>>> display.render(black_image, colour_image)
"""
epaper = self._epaper
2023-11-22 12:45:07 +01:00
if self.supports_colour:
if not im_colour:
raise Exception('im_colour is required for coloured epaper displays')
2023-06-07 12:32:06 +02:00
print('Initialising..', end='')
epaper.init()
print('Updating display......', end='')
2023-11-22 12:45:07 +01:00
epaper.display(epaper.getbuffer(im_black), epaper.getbuffer(im_colour))
2023-06-07 12:32:06 +02:00
print('Done')
2023-11-22 12:45:07 +01:00
else:
2023-06-07 12:32:06 +02:00
print('Initialising..', end='')
epaper.init()
print('Updating display......', end='')
2023-11-22 12:45:07 +01:00
epaper.display(epaper.getbuffer(im_black))
2023-06-07 12:32:06 +02:00
print('Done')
print('Sending E-Paper to deep sleep...', end='')
epaper.sleep()
print('Done')
def calibrate(self, cycles=3):
"""Calibrates the display to retain crisp colours
Flushes the selected display several times with it's supported colours,
removing any previous effects of ghosting.
Args:
- cycles: -> int. The number of times to flush the display with it's
supported colours.
It's recommended to calibrate the display after every 6 display updates
for best results. For black-white only displays, calibration is less
critical, but not calibrating regularly results in grey-ish text.
Please note that calibration takes a while to complete. 3 cycles may
take 10 minutes on black-white E-Papers while it takes 20 minutes on coloured
E-Paper displays.
"""
epaper = self._epaper
epaper.init()
2023-06-07 12:32:06 +02:00
display_size = self.get_display_size(self.model_name)
white = Image.new('1', display_size, 'white')
black = Image.new('1', display_size, 'black')
print('----------Started calibration of ePaper display----------')
2023-06-07 12:32:06 +02:00
if self.supports_colour:
for _ in range(cycles):
print('Calibrating...', end=' ')
print('black...', end=' ')
epaper.display(epaper.getbuffer(black), epaper.getbuffer(white))
print('colour...', end=' ')
epaper.display(epaper.getbuffer(white), epaper.getbuffer(black))
print('white...')
epaper.display(epaper.getbuffer(white), epaper.getbuffer(white))
print(f'Cycle {_ + 1} of {cycles} complete')
2023-06-07 12:32:06 +02:00
if not self.supports_colour:
for _ in range(cycles):
print('Calibrating...', end=' ')
print('black...', end=' ')
epaper.display(epaper.getbuffer(black))
print('white...')
epaper.display(epaper.getbuffer(white)),
print(f'Cycle {_ + 1} of {cycles} complete')
print('-----------Calibration complete----------')
epaper.sleep()
2023-06-07 12:32:06 +02:00
@classmethod
def get_display_size(cls, model_name) -> (int, int):
"""Returns the size of the display as a tuple -> (width, height)
Looks inside supported_models file for the given model name, then returns it's
2023-06-07 12:32:06 +02:00
size.
Args:
model_name: str -> The name of the E-Paper display to get it's size.
Returns:
(width, height) representing the size of the display
Raises:
AssertionError: If the display name was not found in the supported models.
You can use this function directly without creating the Display class:
>>> Display.get_display_size('model_name')
"""
if model_name in supported_models:
return supported_models[model_name]
raise AssertionError(f'{model_name} not found in supported models')
@classmethod
2023-05-20 03:09:15 +02:00
def get_display_names(cls) -> list:
"""Prints all supported E-Paper models.
Fetches all filenames in driver folder and prints them on the console.
Returns:
Printed version of all supported Displays.
Use one of the models to intilialize the Display class in order to gain
access to the E-Paper.
You can use this function directly without creating the Display class:
>>> Display.get_display_names()
"""
2024-06-23 22:25:11 +02:00
return list(supported_models.keys())
2020-11-24 00:40:49 +01:00
2020-12-05 00:18:14 +01:00
if __name__ == '__main__':
print("Running Display class in standalone mode")