From ef743378f54cd3ce3626983a97adf9a1d706bbb6 Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 26 May 2020 19:20:18 +0200 Subject: [PATCH] Added main file (work in progress!) Work in progress! Serves as the main file for inkycal. --- inkycal/main.py | 189 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 inkycal/main.py diff --git a/inkycal/main.py b/inkycal/main.py new file mode 100644 index 0000000..116e2ab --- /dev/null +++ b/inkycal/main.py @@ -0,0 +1,189 @@ +from config import Settings, Layout +from inkycal.custom import * + +import os.path.exists +import traceback +import logging +import arrow +import time + +try: + from PIL import Image +except ImportError: + print('Pillow is not installed! Please install with:') + print('pip3 install Pillow') + +try: + import numpy +except ImportError: + print('numpy is not installed! Please install with:') + print('pip3 install numpy') + +logger = logging.getLogger('inkycal') +logger.setLevel(level=logging.DEBUG) + +class inkycal: + """Main class""" + + def __init__(self, settings_path, render=False): + """initialise class + settings_path = str -> location/folder of settings file + render = bool -> show something on the ePaper? + """ + # Check if render is boolean + if not isinstance(render, bool): + raise Exception('render must be True or False, not "{}"'.format(render)) + self.render = render + + # load+validate settings file. Import and setup specified modules + self.Settings = Settings(settings_path) + self.active_modules = self.Settings.active_modules() + for module in self.active_modules: + try: + loader = 'from modules import {0}'.format(module) + module_data = self.Settings.get_config(module) + size, conf = module_data['size'], module_data['config'] + setup = 'self.{} = {}(size, conf)'.format(module, module) + exec(loader) + exec(setup) + logger.debug(('{}: size: {}, config: {}'.format(module, size, conf))) + + # If a module was not found, print an error message + except ImportError: + print( + 'Could not find module: "{}". Please try to import manually.'.format( + module)) + + # Give an OK message + print('loaded inkycal') + + + def countdown(self, interval_mins=None ): + """Returns the remaining time in seconds until next display update""" + + # Validate update interval + allowed_intervals = [10, 15, 20, 30, 60] + + # Check if empty, if empty, use value from settings file + if interval_mins == None: + interval_mins = self.Settings.update_interval + + # Check if integer + if not isinstance(interval_mins, int): + raise Exception('Update interval must be an integer -> 60') + + # Check if value is supported + if interval_mins not in allowed_intervals: + raise Exception('Update interval is {}, but should be one of: {}'.format( + interval_mins, allowed_intervals)) + + # Find out at which minutes the update should happen + now = arrow.now() + update_timings = [(60 - int(interval_mins)*updates) for updates in + range(60//int(interval_mins))][::-1] + + # Calculate time in mins until next update + minutes = [_ for _ in update_timings if _>= now.minute][0] - now.minute + + # Print the remaining time in mins until next update + print('{0} Minutes left until next refresh'.format(minutes)) + + # Calculate time in seconds until next update + remaining_time = minutes*60 + (60 - now.second) + + # Return seconds until next update + return remaining_time + + def test(self): + """Test if inkycal can be run correctly""" + + for module in self.active_modules: + generate_im = 'self.{0}.generate_image()'.format(module) + print('generating image for {} module...'.format(module), end = '') + try: + exec(generate_im) + print('OK!') + except Exception as Error: + print('Error!') + print(traceback.format_exc()) + + def run(self, render = True): + """Runs the main inykcal program nonstop (cannot be stopped anymore!) + Set render to True to show something on the display""" + + # 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 + counter = 1 + + while True: + print('Generating images for all modules...') + for module in self.active_modules: + generate_im = 'self.{0}.generate_image()'.format(module) + try: + exec(generate_im) + except Exception as Error: + print('Error!') + message = traceback.format_exc() + print(message) + counter = 0 + print('OK') + + if render == True: + print('rendering....') +## if upside_down == True: +## image = image.rotate(180, expand=True) +## if three_colour_support == True: +## image_col = image_col.rotate(180, expand=True) + + print('\ninkycal has been running without any errors for', end = ' ') + print('{} display_updates'.format(counter)) + counter += 1 + + sleep_time = self.countdown(10) ##### + time.sleep(sleep_time) + + + def _merge() + """Stitches images from each module a single one (for each colour) + Merges black and colour band for black-white epaper + """ + + image = Image.new('RGB', + im_location = images + # Check if both files exist + # Center sub images + + + for module in self.active_modules: + + im1_name, im2_name = module+'.png', module+'_colour.png' + + # Check if display can only show black-white + if self.Settings.supports_colour == False: + if exists(im1_name) and exists(im2_name): + im1 = Image.open(images+im1_name).convert('RGBA') + im2 = Image.open(images+im2_name).convert('RGBA') + + # White to transparent pixels + 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) + + # Paste black pixels of im2 on im1 + im2 = clear_white(im2) + im1.paste(im2, (0,0), im2) + im1.save(module+'_comb.png', 'PNG') + + # Check if display can support colour + elif self.Settings.supports_colour == True: + + +