Added rendering capabilites

Removed dummy settings.json file
This commit is contained in:
Ace 2020-05-29 04:00:39 +02:00
parent e7cb176530
commit 6efc1ffc71

View File

@ -1,7 +1,7 @@
from config import Settings, Layout from inkycal import Settings, Layout
from inkycal.custom import * from inkycal.custom import *
import os.path.exists from os.path import exists
import traceback import traceback
import logging import logging
import arrow import arrow
@ -20,27 +20,50 @@ except ImportError:
print('pip3 install numpy') print('pip3 install numpy')
logger = logging.getLogger('inkycal') logger = logging.getLogger('inkycal')
logger.setLevel(level=logging.DEBUG) logger.setLevel(level=logging.ERROR)
class inkycal: class Inkycal:
"""Main class""" """Main class"""
def __init__(self, settings_path, render=False): def __init__(self, settings_path, render=True):
"""initialise class """initialise class
settings_path = str -> location/folder of settings file settings_path = str -> location/folder of settings file
render = bool -> show something on the ePaper? render = bool -> show something on the ePaper?
""" """
self._release = '2.0.0beta'
# Check if render is boolean # Check if render is boolean
if not isinstance(render, bool): if not isinstance(render, bool):
raise Exception('render must be True or False, not "{}"'.format(render)) raise Exception('render must be True or False, not "{}"'.format(render))
self.render = render self.render = render
# load+validate settings file. Import and setup specified modules # Init settings class
self.Settings = Settings(settings_path) self.Settings = Settings(settings_path)
# Check if display support colour
self.supports_colour = self.Settings.Layout.supports_colour
# Option to flip image upside down
self.upside_down = False
# Option to use epaper image optimisation
self.optimize = True
# Load drivers if image should be rendered
if self.render == True:
# Get model and check if colour can be rendered
model= self.Settings.model
# Init Display class
from inkycal.display import Display
self.Display = Display(model)
# load+validate settings file. Import and setup specified modules
self.active_modules = self.Settings.active_modules() self.active_modules = self.Settings.active_modules()
for module in self.active_modules: for module in self.active_modules:
try: try:
loader = 'from modules import {0}'.format(module) loader = 'from inkycal.modules import {0}'.format(module)
module_data = self.Settings.get_config(module) module_data = self.Settings.get_config(module)
size, conf = module_data['size'], module_data['config'] size, conf = module_data['size'], module_data['config']
setup = 'self.{} = {}(size, conf)'.format(module, module) setup = 'self.{} = {}(size, conf)'.format(module, module)
@ -95,7 +118,12 @@ class inkycal:
return remaining_time return remaining_time
def test(self): def test(self):
"""Test if inkycal can be run correctly""" """Inkycal test run"""
print('You are running inkycal v{}'.format(self._release))
print('Running inkyal test-run for {} ePaper'.format(
self.Settings.model))
for module in self.active_modules: for module in self.active_modules:
generate_im = 'self.{0}.generate_image()'.format(module) generate_im = 'self.{0}.generate_image()'.format(module)
@ -107,17 +135,20 @@ class inkycal:
print('Error!') print('Error!')
print(traceback.format_exc()) print(traceback.format_exc())
def run(self, render = True): def run(self):
"""Runs the main inykcal program nonstop (cannot be stopped anymore!) """Runs the main inykcal program nonstop (cannot be stopped anymore!)
Set render to True to show something on the display""" Will show something on the display if render was set to True"""
# TODO: rendering
# TODO: printing traceback on display (or at least a smaller message?)
# Upside down
# Calibration
# Stitch images together ,merge black&colour if required
# Count the number of times without any crashs # TODO: printing traceback on display (or at least a smaller message?)
# Calibration
# Get the time of initial run
runtime = arrow.now()
# Function to flip images upside down
upside_down = lambda image: image.rotate(180, expand=True)
# Count the number of times without any errors
counter = 1 counter = 1
while True: while True:
@ -133,57 +164,169 @@ class inkycal:
counter = 0 counter = 0
print('OK') print('OK')
if render == True: # Assemble image from each module
print('rendering....') self._assemble()
## if upside_down == True:
## image = image.rotate(180, expand=True) # Check if image should be rendered
## if three_colour_support == True: if self.render == True:
## image_col = image_col.rotate(180, expand=True) Display = self.Display
if self.supports_colour == True:
im_black = Image.open(images+'canvas.png')
im_colour = Image.open(images+'canvas_colour.png')
# Flip the image by 180° if required
if self.upside_down == True:
upside_down(im_black)
upside_down(im_colour)
# render the image on the display
Display.render(im_black, im_colour)
# Part for black-white ePapers
elif self.supports_colour == False:
im_black = self._merge_bands()
# Flip the image by 180° if required
if self.upside_down == True:
upside_down(im_black)
Display.render(im_black)
print('\ninkycal has been running without any errors for', end = ' ') print('\ninkycal has been running without any errors for', end = ' ')
print('{} display_updates'.format(counter)) print('{} display updates'.format(counter))
print('That was {}'.format(runtime.humanize()))
counter += 1 counter += 1
sleep_time = self.countdown(10) ##### sleep_time = self.countdown()
time.sleep(sleep_time) time.sleep(sleep_time)
def _merge_bands():
"""Merges black and coloured bands for black-white ePapers
returns the merged image
"""
def _merge() im_path = images
"""Stitches images from each module a single one (for each colour)
Merges black and colour band for black-white epaper
"""
image = Image.new('RGB', im1_path, im2_path = images+'canvas.png', images+'canvas_colour.png'
im_location = images
# Check if both files exist # If there is an image for black and colour, merge them
# Center sub images if exists(im1_path) and exists(im2_path):
im1 = Image.open(im1_name).convert('RGBA')
im2 = Image.open(im2_name).convert('RGBA')
def clear_white(img):
"""Replace all white pixels from image with transparent pixels
"""
x = numpy.asarray(img.convert('RGBA')).copy()
x[:, :, 3] = (255 * (x[:, :, :3] != 255).any(axis=2)).astype(numpy.uint8)
return Image.fromarray(x)
im2 = clear_white(im2)
im1.paste(im2, (0,0), im2)
# If there is no image for the coloured-band, return the bw-image
elif exists(im1_path) and not exists(im2_path):
im1 = Image.open(im1_name).convert('RGBA')
return im1
for module in self.active_modules: def _assemble(self):
"""Assmebles all sub-images to a single image"""
im1_name, im2_name = module+'.png', module+'_colour.png'
# Check if display can only show black-white # Create an empty canvas with the size of the display
if self.Settings.supports_colour == False: width, height = self.Settings.Layout.display_size
if exists(im1_name) and exists(im2_name): height, width = width, height
im1 = Image.open(images+im1_name).convert('RGBA')
im2 = Image.open(images+im2_name).convert('RGBA')
# White to transparent pixels im_black = Image.new('RGB', (width, height), color = 'white')
def clear_white(img): im_colour = Image.new('RGB', (width ,height), color = 'white')
"""Replace all white pixels from image with transparent pixels
"""
x = numpy.asarray(img.convert('RGBA')).copy()
x[:, :, 3] = (255 * (x[:, :, :3] != 255).any(axis=2)).astype(numpy.uint8)
return Image.fromarray(x)
# Paste black pixels of im2 on im1 # Set cursor for y-axis
im2 = clear_white(im2) im1_cursor = 0
im1.paste(im2, (0,0), im2) im2_cursor = 0
im1.save(module+'_comb.png', 'PNG')
# Check if display can support colour for module in self.active_modules:
elif self.Settings.supports_colour == True:
im1_path = images+module+'.png'
im2_path = images+module+'_colour.png'
# Check if there is an image for the black band
if exists(im1_path):
# Get actual size of image
im1 = Image.open(im1_path).convert('RGBA')
im1_size = im1.size
# Get the size of the section
section_size = self.Settings.get_config(module)['size']
# Calculate coordinates to center the image
x = int( (section_size[0]-im1_size[0]) /2)
# If this is the first module, use the y-offset
if im1_cursor == 0:
y = int( (section_size[1]-im1_size[1]) /2)
else:
y = im1_cursor
# center the image in the section space
im_black.paste(im1, (x,y), im1)
# Shift the y-axis cursor at the beginning of next section
im1_cursor += section_size[1] - y
# Check if there is an image for the coloured band
if exists(im2_path):
# Get actual size of image
im2 = Image.open(im2_path).convert('RGBA')
im2_size = im2.size
# Get the size of the section
section_size = self.Settings.get_config(module)['size']
# Calculate coordinates to center the image
x = int( (section_size[0]-im2_size[0]) /2)
# If this is the first module, use the y-offset
if im2_cursor == 0:
y = int( (section_size[1]-im2_size[1]) /2)
else:
y = im2_cursor
# center the image in the section space
im_colour.paste(im2, (x,y), im2)
# Shift the y-axis cursor at the beginning of next section
im2_cursor += section_size[1] - y
if self.optimize == True:
self._optimize_im(im_black).save(images+'canvas.png', 'PNG')
self._optimize_im(im_colour).save(images+'canvas_colour.png', 'PNG')
else:
im_black.save(images+'canvas.png', 'PNG')
im_colour.save(images+'canvas_colour.png', 'PNG')
def _optimize_im(self, image, threshold=220):
"""Optimize the image for rendering on ePaper displays"""
buffer = numpy.array(image.convert('RGB'))
red, green = buffer[:, :, 0], buffer[:, :, 1]
buffer[numpy.logical_and(red <= threshold, green <= threshold)] = [0,0,0] #grey->black
image = Image.fromarray(buffer)
return image
def calibrate(self):
"""Calibrate the ePaper display to prevent burn-ins (ghosting)
Currently has to be run manually"""
self.Display.calibrate()
def _check_for_updates(self):
"""Check if a new update is available for inkycal"""
raise NotImplementedError('Tha developer were too lazy to implement this..')