Implementation of interface (template) for all modules

- Correct setup of logging
- all inkycal-modules inherit from the given template
- Added basic, optional validation
- more code cleanups
- fixed a few minor bugs
This commit is contained in:
Ace 2020-05-23 01:45:40 +02:00
parent f631733bf5
commit c3fbd79eda
14 changed files with 481 additions and 352 deletions

View File

@ -1,47 +1,40 @@
from importlib import import_module from inkycal.config import settings
from inkycal.config import settings, layout
##modules = settings.which_modules() f = '/home/pi/Desktop/settings.json'
##for module in modules:
## if module == 'inkycal_rss':
## module = import_module('inkycal.modules.'+module)
## #import_module('modules.'+module)
##print(module)
settings_file = "/home/pi/Desktop/Inkycal/inkycal/config/settings.json" settings = settings(f)
class inkycal: specified_modules = settings.active_modules()
"""Main class for inkycal for module in specified_modules:
""" try:
def __init__(self, settings_file_path):
"""Load settings file from path"""
# Load settings file
self.settings = settings(settings_file_path)
self.model = self.settings.model
def create_canvas(self):
"""Create a canvas with same size as the specified model"""
self.layout = layout(model=self.model)
def create_custom_canvas(self, width=None, height=None,
supports_colour=False):
"""Create a custom canvas by specifying height and width"""
self.layout = layout(model=model, width=width, height=height,
supports_colour=supports_colour)
def create_sections(self):
"""Create sections with default sizes"""
self.layout.create_sections()
def create_custom_sections(self, top_section=0.10, middle_section=0.65,
bottom_section=0.25):
"""Create custom-sized sections in the canvas"""
self.layout.create_sections(top_section=top_section,
middle_section=middle_section,
bottom_section=bottom_section)
if module == 'inkycal_weather':
from inkycal import weather
conf = settings.get_config(module)
weather = weather(conf['size'], conf['config'])
if module == 'inkycal_rss':
from inkycal import rss
conf = settings.get_config(module)
rss = rss(conf['size'], conf['config'])
if module == 'inkycal_image':
from inkycal import image
conf = settings.get_config(module)
image = image(conf['size'], conf['config'])
if module == 'inkycal_calendar':
from inkycal import calendar
conf = settings.get_config(module)
calendar = calendar(conf['size'], conf['config'])
if module == 'inkycal_agenda':
from inkycal import agenda
conf = settings.get_config(module)
agenda = agenda(conf['size'], conf['config'])
except ImportError:
print(
'Could not find module: "{}". Please try to import manually.'.format(
module))

View File

@ -1 +1,5 @@
from .Inkycal import inkycal from inkycal.modules.inkycal_agenda import agenda
from inkycal.modules.inkycal_calendar import calendar
from inkycal.modules.inkycal_weather import weather
from inkycal.modules.inkycal_rss import rss

View File

@ -1,4 +1,2 @@
from .settings_parser import settings from .settings_parser import settings
print('loaded settings')
from .layout import layout from .layout import layout
print('loaded layout')

View File

@ -6,22 +6,21 @@ Copyright by aceisace
""" """
import logging import logging
import os
filename = os.path.basename(__file__).split('.py')[0]
logger = logging.getLogger(filename)
logger.setLevel(level=logging.INFO)
class layout: class layout:
"""Page layout handling""" """Page layout handling"""
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
def __init__(self, model=None, width=None, height=None, def __init__(self, model=None, width=None, height=None,
supports_colour=False): supports_colour=False):
"""Initialize parameters for specified epaper model """Initialize parameters for specified epaper model
Use model parameter to specify display OR Use model parameter to specify display OR
Crate a custom display with given width and height""" Crate a custom display with given width and height"""
self.background_colour = 'white'
self.text_colour = 'black'
if (model != None) and (width == None) and (height == None): if (model != None) and (width == None) and (height == None):
display_dimensions = { display_dimensions = {
'epd_7_in_5_v2_colour': (800, 400), 'epd_7_in_5_v2_colour': (800, 400),
@ -78,15 +77,15 @@ class layout:
self.bottom_section_height = (self.display_height - self.bottom_section_height = (self.display_height -
self.top_section_height - self.middle_section_height) self.top_section_height - self.middle_section_height)
logging.debug('top-section size: {} x {} px'.format( logger.debug('top-section size: {} x {} px'.format(
self.top_section_width, self.top_section_height)) self.top_section_width, self.top_section_height))
logging.debug('middle-section size: {} x {} px'.format( logger.debug('middle-section size: {} x {} px'.format(
self.middle_section_width, self.middle_section_height)) self.middle_section_width, self.middle_section_height))
logging.debug('bottom-section size: {} x {} px'.format( logger.debug('bottom-section size: {} x {} px'.format(
self.bottom_section_width, self.bottom_section_height)) self.bottom_section_width, self.bottom_section_height))
def get_section_size(self, section): def get_size(self, section):
"""Enter top/middle/bottom to get the size of the section as a tuple: """Enter top/middle/bottom to get the size of the section as a tuple:
(width, height)""" (width, height)"""

View File

@ -6,6 +6,7 @@ Copyright by aceisace
""" """
import json import json
import os import os
from inkycal.config.layout import layout
class settings: class settings:
"""Load and validate settings from the settings file""" """Load and validate settings from the settings file"""
@ -25,7 +26,6 @@ class settings:
def __init__(self, settings_file_path): def __init__(self, settings_file_path):
"""Load settings from path (folder or settings.json file)""" """Load settings from path (folder or settings.json file)"""
try: try:
# If
if settings_file_path.endswith('settings.json'): if settings_file_path.endswith('settings.json'):
folder = settings_file_path.split('/settings.json')[0] folder = settings_file_path.split('/settings.json')[0]
else: else:
@ -39,14 +39,23 @@ class settings:
except FileNotFoundError: except FileNotFoundError:
print('No settings file found in specified location') print('No settings file found in specified location')
# Validate the settings
self._validate() self._validate()
# Get the height-percentages of the modules
heights = [_['height']/100 for _ in self._settings['panels']]
self.layout = layout(model=self.model)
self.layout.create_sections(top_section= heights[0],
middle_section=heights[1],
bottom_section=heights[2])
def _validate(self): def _validate(self):
"""Validate the basic config""" """Validate the basic config"""
settings = self._settings settings = self._settings
required = ['language', 'units', 'hours', 'model', 'calibration_hours', required = ['language', 'units', 'hours', 'model', 'calibration_hours']
'display_orientation'] #'display_orientation']
# Check if all required settings exist # Check if all required settings exist
for param in required: for param in required:
@ -60,7 +69,7 @@ class settings:
self.hours = settings['hours'] self.hours = settings['hours']
self.model = settings['model'] self.model = settings['model']
self.calibration_hours = settings['calibration_hours'] self.calibration_hours = settings['calibration_hours']
self.display_orientation = settings['display_orientation'] #self.display_orientation = settings['display_orientation']
# Validate the parameters # Validate the parameters
if (not isinstance(self.language, str) or self.language not in if (not isinstance(self.language, str) or self.language not in
@ -87,30 +96,31 @@ class settings:
print('calibration_hours not supported, switching to fallback, [0,12,18]') print('calibration_hours not supported, switching to fallback, [0,12,18]')
self.calibration_hours = [0,12,18] self.calibration_hours = [0,12,18]
if (not isinstance(self.display_orientation, str) or self.display_orientation not in ## if (not isinstance(self.display_orientation, str) or self.display_orientation not in
self._supported_display_orientation): ## self._supported_display_orientation):
print('display orientation not supported, switching to fallback, normal') ## print('display orientation not supported, switching to fallback, normal')
self.display_orientation = 'normal' ## self.display_orientation = 'normal'
print('Settings file loaded') print('Settings file OK!')
def _active_modules(self): def active_modules(self):
modules = [section['type'] for section in self._settings['panels']] modules = [section['type'] for section in self._settings['panels']]
return modules return modules
def get_config(self, module_name): def get_config(self, module_name):
"""Ge the config of this module""" """Ge the config of this module (size, config)"""
if module_name not in self._active_modules(): if module_name not in self.active_modules():
print('No config is available for this module') print('No config is available for this module')
else: else:
for section in self._settings['panels']: for section in self._settings['panels']:
if section['type'] == module_name: if section['type'] == module_name:
config = section['config'] config = section['config']
return config size = self.layout.get_size(self.get_position(module_name))
return {'size':size, 'config':config}
def get_position(self, module_name): def get_position(self, module_name):
"""Get the position of this module's image on the display""" """Get the position of this module's image on the display"""
if module_name not in self._active_modules(): if module_name not in self.active_modules():
print('No position is available for this module') print('No position is available for this module')
else: else:
for section in self._settings['panels']: for section in self._settings['panels']:

View File

@ -107,11 +107,11 @@ def write(image, xy, box_size, text, font=None, **kwargs):
# Truncate text if text is too long so it can fit inside the box # Truncate text if text is too long so it can fit inside the box
if (text_width, text_height) > (box_width, box_height): if (text_width, text_height) > (box_width, box_height):
logging.debug('text too big for space, truncating now...') logging.debug(('truncating {}'.format(text)))
while (text_width, text_height) > (box_width, box_height): while (text_width, text_height) > (box_width, box_height):
text=text[0:-1] text=text[0:-1]
text_width, text_height = font.getsize(text)[0], font.getsize('hg')[1] text_width, text_height = font.getsize(text)[0], font.getsize('hg')[1]
logging.debug(('truncated text:', text)) logging.debug((text))
# Align text to desired position # Align text to desired position
if alignment == "center" or None: if alignment == "center" or None:
@ -178,35 +178,41 @@ def draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1,0.1)):
""" """
colour='black' colour='black'
# size from function paramter # size from function paramter
width, height = size[0]*(1-shrinkage[0]), size[1]*(1-shrinkage[1]) width, height = int(size[0]*(1-shrinkage[0])), int(size[1]*(1-shrinkage[1]))
# shift cursor to move rectangle to center
offset_x, offset_y = int((size[0] - width)/2), int((size[1]- height)/2) offset_x, offset_y = int((size[0] - width)/2), int((size[1]- height)/2)
x, y, diameter = xy[0]+offset_x, xy[1]+offset_y, radius*2 x, y, diameter = xy[0]+offset_x, xy[1]+offset_y, radius*2
# lenght of rectangle size # lenght of rectangle size
a,b = (width - diameter), (height-diameter) a,b = (width - diameter), (height-diameter)
# Set coordinates for round square # Set coordinates for staright lines
p1, p2 = (x+radius, y), (x+radius+a, y) p1, p2 = (x+radius, y), (x+radius+a, y)
p3, p4 = (x+width, y+radius), (x+width, y+radius+b) p3, p4 = (x+width, y+radius), (x+width, y+radius+b)
p5, p6 = (p2[0], y+height), (p1[0], y+height) p5, p6 = (p2[0], y+height), (p1[0], y+height)
p7, p8 = (x, p4[1]), (x,p3[1]) p7, p8 = (x, p4[1]), (x,p3[1])
c1, c2 = (x,y), (x+diameter, y+diameter) if radius != 0:
c3, c4 = ((x+width)-diameter, y), (x+width, y+diameter) # Set coordinates for arcs
c5, c6 = ((x+width)-diameter, (y+height)-diameter), (x+width, y+height) c1, c2 = (x,y), (x+diameter, y+diameter)
c7, c8 = (x, (y+height)-diameter), (x+diameter, y+height) c3, c4 = ((x+width)-diameter, y), (x+width, y+diameter)
c5, c6 = ((x+width)-diameter, (y+height)-diameter), (x+width, y+height)
c7, c8 = (x, (y+height)-diameter), (x+diameter, y+height)
# Draw lines and arcs, creating a square with round corners # Draw lines and arcs, creating a square with round corners
draw = ImageDraw.Draw(image) draw = ImageDraw.Draw(image)
draw.line( (p1, p2) , fill=colour, width = thickness) draw.line( (p1, p2) , fill=colour, width = thickness)
draw.line( (p3, p4) , fill=colour, width = thickness) draw.line( (p3, p4) , fill=colour, width = thickness)
draw.line( (p5, p6) , fill=colour, width = thickness) draw.line( (p5, p6) , fill=colour, width = thickness)
draw.line( (p7, p8) , fill=colour, width = thickness) draw.line( (p7, p8) , fill=colour, width = thickness)
draw.arc( (c1, c2) , 180, 270, fill=colour, width=thickness)
draw.arc( (c3, c4) , 270, 360, fill=colour, width=thickness) if radius != 0:
draw.arc( (c5, c6) , 0, 90, fill=colour, width=thickness) draw.arc( (c1, c2) , 180, 270, fill=colour, width=thickness)
draw.arc( (c7, c8) , 90, 180, fill=colour, width=thickness) draw.arc( (c3, c4) , 270, 360, fill=colour, width=thickness)
draw.arc( (c5, c6) , 0, 90, fill=colour, width=thickness)
draw.arc( (c7, c8) , 90, 180, fill=colour, width=thickness)
"""Not required anymore?""" """Not required anymore?"""
@ -230,18 +236,6 @@ def draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1,0.1)):
## return image ## return image
"""Not required anymore?"""
##def fix_ical(ical_url):
## """Use iCalendars in compatability mode (without alarms)"""
## ical = str(urlopen(ical_url).read().decode())
## beginAlarmIndex = 0
## while beginAlarmIndex >= 0:
## beginAlarmIndex = ical.find('BEGIN:VALARM')
## if beginAlarmIndex >= 0:
## endAlarmIndex = ical.find('END:VALARM')
## ical = ical[:beginAlarmIndex] + ical[endAlarmIndex+12:]
## return ical
"""Not required anymore?""" """Not required anymore?"""
##def image_cleanup(): ##def image_cleanup():
@ -296,7 +290,7 @@ def draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1,0.1)):
## with open(path+'release.txt','r') as file: ## with open(path+'release.txt','r') as file:
## lines = file.readlines() ## lines = file.readlines()
## installed_release = lines[0].rstrip() ## installed_release = lines[0].rstrip()
##
## temp = subp.check_output(['curl','-s','https://github.com/aceisace/Inky-Calendar/releases/latest']) ## temp = subp.check_output(['curl','-s','https://github.com/aceisace/Inky-Calendar/releases/latest'])
## latest_release_url = str(temp).split('"')[1] ## latest_release_url = str(temp).split('"')[1]
## latest_release = latest_release_url.split('/tag/')[1] ## latest_release = latest_release_url.split('/tag/')[1]

View File

@ -0,0 +1,4 @@
##from .inkycal_agenda import agenda
##from .inkycal_calendar import calendar
##from .inkycal_weather import weather
##from .inkycal_rss import rss

View File

@ -5,7 +5,7 @@ iCalendar (parsing) module for Inky-Calendar Project
Copyright by aceisace Copyright by aceisace
""" """
""" ---info about iCalendars--- """ ---info about iCalendars---
all day events start at midnight, ending at midnight of the next day all day events start at midnight, ending at midnight of the next day
iCalendar saves all event timings in UTC -> need to be converted into local iCalendar saves all event timings in UTC -> need to be converted into local
time time
@ -17,6 +17,7 @@ import arrow
from urllib.request import urlopen from urllib.request import urlopen
import logging import logging
import time # timezone, timing speed of execution import time # timezone, timing speed of execution
import os
try: try:
import recurring_ical_events import recurring_ical_events
@ -30,20 +31,15 @@ except ModuleNotFoundError:
print('icalendar library could not be found. Please install this with:') print('icalendar library could not be found. Please install this with:')
print('pip3 install icalendar') print('pip3 install icalendar')
urls = [
# Default calendar filename = os.path.basename(__file__).split('.py')[0]
'https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics', logger = logging.getLogger(filename)
# inkycal debug calendar logger.setLevel(level=logging.INFO)
'https://calendar.google.com/calendar/ical/6nqv871neid5l0t7hgk6jgr24c%40group.calendar.google.com/private-c9ab692c99fb55360cbbc28bf8dedb3a/basic.ics'
]
class icalendar: class icalendar:
"""iCalendar parsing moudule for inkycal. """iCalendar parsing moudule for inkycal.
Parses events from given iCalendar URLs / paths""" Parses events from given iCalendar URLs / paths"""
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
def __init__(self): def __init__(self):
self.icalendars = [] self.icalendars = []
self.parsed_events = [] self.parsed_events = []
@ -82,7 +78,7 @@ class icalendar:
# Add the parsed icalendar/s to the self.icalendars list # Add the parsed icalendar/s to the self.icalendars list
if ical: self.icalendars += ical if ical: self.icalendars += ical
logging.info('loaded iCalendars from URLs') logger.info('loaded iCalendars from URLs')
def load_from_file(self, filepath): def load_from_file(self, filepath):
"""Input a string or list of strings containing valid iCalendar filepaths """Input a string or list of strings containing valid iCalendar filepaths
@ -97,7 +93,7 @@ class icalendar:
raise Exception ("Input: '{}' is not a string or list!".format(url)) raise Exception ("Input: '{}' is not a string or list!".format(url))
self.icalendars += icals self.icalendars += icals
logging.info('loaded iCalendars from filepaths') logger.info('loaded iCalendars from filepaths')
def get_events(self, timeline_start, timeline_end, timezone=None): def get_events(self, timeline_start, timeline_end, timezone=None):
"""Input an arrow (time) object for: """Input an arrow (time) object for:
@ -148,8 +144,6 @@ class icalendar:
if arrow.get(events.get('dtstart').dt).format('HH:mm') != '00:00' else 'UTC') if arrow.get(events.get('dtstart').dt).format('HH:mm') != '00:00' else 'UTC')
} for ical in recurring_events for events in ical] } for ical in recurring_events for events in ical]
# if any recurring events were found, add them to parsed_events # if any recurring events were found, add them to parsed_events
if re_events: self.parsed_events += re_events if re_events: self.parsed_events += re_events
@ -159,9 +153,9 @@ class icalendar:
return self.parsed_events return self.parsed_events
def sort(self): def sort(self):
"""Sort all parsed events""" """Sort all parsed events in order of beginning time"""
if not self.parsed_events: if not self.parsed_events:
logging.debug('no events found to be sorted') logger.debug('no events found to be sorted')
else: else:
by_date = lambda event: event['begin'] by_date = lambda event: event['begin']
self.parsed_events.sort(key=by_date) self.parsed_events.sort(key=by_date)
@ -208,7 +202,7 @@ class icalendar:
""" """
if not self.parsed_events: if not self.parsed_events:
logging.debug('no events found to be shown') logger.debug('no events found to be shown')
else: else:
line_width = max(len(_['title']) for _ in self.parsed_events) line_width = max(len(_['title']) for _ in self.parsed_events)
for events in self.parsed_events: for events in self.parsed_events:
@ -217,6 +211,18 @@ class icalendar:
print('{0} {1} | {2} | {3}'.format( print('{0} {1} | {2} | {3}'.format(
title, ' ' * (line_width - len(title)), begin, end)) title, ' ' * (line_width - len(title)), begin, end))
if __name__ == '__main__':
print('running {0} in standalone mode'.format(filename))
urls = [
# Default calendar
'https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics',
# inkycal debug calendar
'https://calendar.google.com/calendar/ical/6nqv871neid5l0t7hgk6jgr24c%40group.calendar.google.com/private-c9ab692c99fb55360cbbc28bf8dedb3a/basic.ics'
]
##a = icalendar() ##a = icalendar()
##a.load_url(urls) ##a.load_url(urls)
##a.get_events(arrow.now(), arrow.now().shift(weeks=4), timezone = a.get_system_tz()) ##a.get_events(arrow.now(), arrow.now().shift(weeks=4), timezone = a.get_system_tz())

View File

@ -5,85 +5,74 @@ Agenda module for Inky-Calendar Project
Copyright by aceisace Copyright by aceisace
""" """
from inkycal.modules.template import inkycal_module
from inkycal.custom import * from inkycal.custom import *
import calendar as cal
import arrow
from inkycal.modules.ical_parser import icalendar from inkycal.modules.ical_parser import icalendar
size = (400, 520) import calendar as cal
config = {'week_starts_on': 'Monday', 'ical_urls': ['https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics']} import arrow
filename = os.path.basename(__file__).split('.py')[0]
logger = logging.getLogger(filename)
logger.setLevel(level=logging.INFO)
class agenda: class agenda(inkycal_module):
"""Agenda class """Agenda class
Create agenda and show events from given icalendars Create agenda and show events from given icalendars
""" """
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
def __init__(self, section_size, section_config): def __init__(self, section_size, section_config):
"""Initialize inkycal_agenda module""" """Initialize inkycal_agenda module"""
self.name = os.path.basename(__file__).split('.py')[0]
self.config = section_config
self.width, self.height = section_size
self.background_colour = 'white'
self.font_colour = 'black'
self.fontsize = 12
self.font = ImageFont.truetype(
fonts['NotoSans-SemiCondensed'], size = self.fontsize)
self.padding_x = 0.02 #rename to margin?
self.padding_y = 0.05
# Section specific config super().__init__(section_size, section_config)
# Format for formatting dates # Module specific parameters
required = ['week_starts_on', 'ical_urls']
for param in required:
if not param in section_config:
raise Exception('config is missing {}'.format(param))
# module name
self.name = filename
# module specific parameters
self.date_format = 'ddd D MMM' self.date_format = 'ddd D MMM'
# Fromat for formatting event timings self.time_format = "HH:mm"
self.time_format = "HH:mm" #use auto for 24/12 hour format? self.language = 'en'
self.language = 'en' # Grab from settings file?
self.timezone = get_system_tz() self.timezone = get_system_tz()
# urls of icalendars
self.ical_urls = config['ical_urls'] self.ical_urls = config['ical_urls']
# filepaths of icalendar files
self.ical_files = [] self.ical_files = []
# give an OK message
print('{0} loaded'.format(self.name)) print('{0} loaded'.format(self.name))
def set(self, **kwargs): def _validate(self):
"""Manually set some parameters of this module""" """Validate module-specific parameters"""
if not isinstance(self.date_format, str):
for key, value in kwargs.items(): print('date_format has to be an arrow-compatible token')
if key in self.__dict__: if not isinstance(self.time_format, str):
setattr(self, key, value) print('time_format has to be an arrow-compatible token')
else: if not isinstance(self.language, str):
print('{0} does not exist'.format(key)) print('language has to be a string: "en" ')
pass if not isinstance(self.timezone, str):
print('The timezone has bo be a string.')
def get(self, **kwargs): if not isinstance(self.ical_urls, list):
"""Manually get some parameters of this module""" print('ical_urls has to be a list ["url1", "url2"] ')
if not isinstance(self.ical_files, list):
for key, value in kwargs.items(): print('ical_files has to be a list ["path1", "path2"] ')
if key in self.__dict__:
getattr(self, key, value)
else:
print('{0} does not exist'.format(key))
pass
def get_options(self):
"""Get all options which can be changed"""
return self.__dict__
def generate_image(self): def generate_image(self):
"""Generate image for this module""" """Generate image for this module"""
# Define new image size with respect to padding # Define new image size with respect to padding
im_width = int(self.width - (self.width * 2 * self.padding_x)) im_width = int(self.width - (self.width * 2 * self.margin_x))
im_height = int(self.height - (self.height * 2 * self.padding_y)) im_height = int(self.height - (self.height * 2 * self.margin_y))
im_size = im_width, im_height im_size = im_width, im_height
logging.info('Image size: {0}'.format(im_size)) logger.info('Image size: {0}'.format(im_size))
# Create an image for black pixels and one for coloured pixels # Create an image for black pixels and one for coloured pixels
im_black = Image.new('RGB', size = im_size, color = self.background_colour) im_black = Image.new('RGB', size = im_size, color = 'white')
im_colour = Image.new('RGB', size = im_size, color = 'white') im_colour = Image.new('RGB', size = im_size, color = 'white')
# Calculate the max number of lines that can fit on the image # Calculate the max number of lines that can fit on the image
@ -91,7 +80,7 @@ class agenda:
line_height = int(self.font.getsize('hg')[1]) + line_spacing line_height = int(self.font.getsize('hg')[1]) + line_spacing
line_width = im_width line_width = im_width
max_lines = im_height // line_height max_lines = im_height // line_height
logging.debug(('max lines:',max_lines)) logger.debug(('max lines:',max_lines))
# Create timeline for agenda # Create timeline for agenda
now = arrow.now() now = arrow.now()
@ -117,37 +106,37 @@ class agenda:
# Sort events by beginning time # Sort events by beginning time
parser.sort() parser.sort()
# parser.show_events() # parser.show_events()
# Set the width for date, time and event titles # Set the width for date, time and event titles
date_width = int(max([self.font.getsize( date_width = int(max([self.font.getsize(
dates['begin'].format(self.date_format, locale=self.language))[0] dates['begin'].format(self.date_format, locale=self.language))[0]
for dates in agenda_events]) * 1.2) for dates in agenda_events]) * 1.2)
logging.debug(('date_width:', date_width)) logger.debug(('date_width:', date_width))
# Check if any events were filtered # Check if any events were filtered
if upcoming_events: if upcoming_events:
# Find out how much space the event times take # Find out how much space the event times take
time_width = int(max([self.font.getsize( time_width = int(max([self.font.getsize(
events['begin'].format(self.time_format, locale=self.language))[0] events['begin'].format(self.time_format, locale=self.language))[0]
for events in upcoming_events]) * 1.2) for events in upcoming_events]) * 1.2)
logging.debug(('time_width:', time_width)) logger.debug(('time_width:', time_width))
# Calculate x-pos for time # Calculate x-pos for time
x_time = date_width x_time = date_width
logging.debug(('x-time:', x_time)) logger.debug(('x-time:', x_time))
# Find out how much space is left for event titles # Find out how much space is left for event titles
event_width = im_width - time_width - date_width event_width = im_width - time_width - date_width
logging.debug(('width for events:', event_width)) logger.debug(('width for events:', event_width))
# Calculate x-pos for event titles # Calculate x-pos for event titles
x_event = date_width + time_width x_event = date_width + time_width
logging.debug(('x-event:', x_event)) logger.debug(('x-event:', x_event))
# Calculate positions for each line # Calculate positions for each line
line_pos = [(0, int(line * line_height)) for line in range(max_lines)] line_pos = [(0, int(line * line_height)) for line in range(max_lines)]
logging.debug(('line_pos:', line_pos)) logger.debug(('line_pos:', line_pos))
# Merge list of dates and list of events # Merge list of dates and list of events
agenda_events += upcoming_events agenda_events += upcoming_events
@ -159,7 +148,7 @@ class agenda:
# Delete more entries than can be displayed (max lines) # Delete more entries than can be displayed (max lines)
del agenda_events[max_lines:] del agenda_events[max_lines:]
#print(agenda_events) self._agenda_events = agenda_events
cursor = 0 cursor = 0
for _ in agenda_events: for _ in agenda_events:
@ -170,7 +159,7 @@ class agenda:
ImageDraw.Draw(im_colour).line( ImageDraw.Draw(im_colour).line(
(0, line_pos[cursor][1], im_width, line_pos[cursor][1]), (0, line_pos[cursor][1], im_width, line_pos[cursor][1]),
fill = 'black') fill = 'black')
write(im_black, line_pos[cursor], (date_width, line_height), write(im_black, line_pos[cursor], (date_width, line_height),
title, font = self.font, alignment='left') title, font = self.font, alignment='left')
@ -185,12 +174,12 @@ class agenda:
write(im_black, (x_time, line_pos[cursor][1]), write(im_black, (x_time, line_pos[cursor][1]),
(time_width, line_height), time, (time_width, line_height), time,
font = self.font, alignment='left') font = self.font, alignment='left')
write(im_black, (x_event, line_pos[cursor][1]), write(im_black, (x_event, line_pos[cursor][1]),
(event_width, line_height), (event_width, line_height),
''+title, font = self.font, alignment='left') ''+title, font = self.font, alignment='left')
cursor += 1 cursor += 1
# If no events were found, write only dates and lines # If no events were found, write only dates and lines
else: else:
cursor = 0 cursor = 0
@ -199,25 +188,21 @@ class agenda:
ImageDraw.Draw(im_colour).line( ImageDraw.Draw(im_colour).line(
(0, line_pos[cursor][1], im_width, line_pos[cursor][1]), (0, line_pos[cursor][1], im_width, line_pos[cursor][1]),
fill = 'black') fill = 'black')
write(im_black, line_pos[cursor], (date_width, line_height), write(im_black, line_pos[cursor], (date_width, line_height),
title, font = self.font, alignment='left') title, font = self.font, alignment='left')
cursor += 1 cursor += 1
logging.info('no events found') logger.info('no events found')
############################################################################
# Exception handling
############################################################################
# Save image of black and colour channel in image-folder # Save image of black and colour channel in image-folder
im_black.save(images+self.name+'.png') im_black.save(images+self.name+'.png')
im_colour.save(images+self.name+'_colour.png') im_colour.save(images+self.name+'_colour.png')
if __name__ == '__main__': if __name__ == '__main__':
print('running {0} in standalone mode'.format( print('running {0} in standalone mode'.format(filename))
os.path.basename(__file__).split('.py')[0]))
# remove below line later! ##size = (400, 520)
a = agenda(size, config).generate_image() ##config = {'week_starts_on': 'Monday', 'ical_urls': ['https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics']}
##a = agenda(size, config).generate_image()

View File

@ -4,81 +4,62 @@
Calendar module for Inky-Calendar Project Calendar module for Inky-Calendar Project
Copyright by aceisace Copyright by aceisace
""" """
from inkycal.modules.template import inkycal_module
from inkycal.custom import * from inkycal.custom import *
import calendar as cal import calendar as cal
import arrow import arrow
size = (400, 520) filename = os.path.basename(__file__).split('.py')[0]
config = {'week_starts_on': 'Monday', 'ical_urls': ['https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics']} logger = logging.getLogger(filename)
logger.setLevel(level=logging.INFO)
class calendar(inkycal_module):
class calendar:
"""Calendar class """Calendar class
Create monthly calendar and show events from given icalendars Create monthly calendar and show events from given icalendars
""" """
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
def __init__(self, section_size, section_config): def __init__(self, section_size, section_config):
"""Initialize inkycal_calendar module""" """Initialize inkycal_calendar module"""
self.name = os.path.basename(__file__).split('.py')[0] super().__init__(section_size, section_config)
self.config = section_config
self.width, self.height = section_size # Module specific parameters
self.fontsize = 12 required = ['week_starts_on']
self.font = ImageFont.truetype( for param in required:
fonts['NotoSans-SemiCondensed'], size = self.fontsize) if not param in section_config:
self.padding_x = 0.02 raise Exception('config is missing {}'.format(param))
self.padding_y = 0.05
# module name
self.name = filename
# module specific parameters
self.shuffle_feeds = True
self.num_font = ImageFont.truetype( self.num_font = ImageFont.truetype(
fonts['NotoSans-SemiCondensed'], size = self.fontsize) fonts['NotoSans-SemiCondensed'], size = self.fontsize)
self.weekstart = 'Monday' self.weekstart = self.config['week_starts_on']
self.show_events = True self.show_events = True
self.date_format = 'D MMM' # used for dates self.date_format = 'D MMM' # used for dates
self.time_format = "HH:mm" # used for timings self.time_format = "HH:mm" # used for timings
self.language = 'en' # Grab from settings file? self.language = 'en' # Grab from settings file?
self.timezone = get_system_tz() self.timezone = get_system_tz()
self.ical_urls = config['ical_urls'] self.ical_urls = self.config['ical_urls']
self.ical_files = [] self.ical_files = []
# give an OK message
print('{0} loaded'.format(self.name)) print('{0} loaded'.format(self.name))
def set(self, **kwargs):
"""Manually set some parameters of this module"""
for key, value in kwargs.items():
if key in self.__dict__:
setattr(self, key, value)
else:
print('{0} does not exist'.format(key))
pass
def get(self, **kwargs):
"""Manually get some parameters of this module"""
for key, value in kwargs.items():
if key in self.__dict__:
getattr(self, key, value)
else:
print('{0} does not exist'.format(key))
pass
def get_options(self):
"""Get all options which can be changed"""
return self.__dict__
def generate_image(self): def generate_image(self):
"""Generate image for this module""" """Generate image for this module"""
# Define new image size with respect to padding # Define new image size with respect to padding
im_width = int(self.width - (self.width * 2 * self.padding_x)) im_width = int(self.width - (self.width * 2 * self.margin_x))
im_height = int(self.height - (self.height * 2 * self.padding_y)) im_height = int(self.height - (self.height * 2 * self.margin_y))
im_size = im_width, im_height im_size = im_width, im_height
logging.info('Image size: {0}'.format(im_size)) logger.info('Image size: {0}'.format(im_size))
# Create an image for black pixels and one for coloured pixels # Create an image for black pixels and one for coloured pixels
im_black = Image.new('RGB', size = im_size, color = 'white') im_black = Image.new('RGB', size = im_size, color = 'white')
@ -91,13 +72,13 @@ class calendar:
if self.show_events == True: if self.show_events == True:
calendar_height = int(self.height*0.6) calendar_height = int(self.height*0.6)
events_height = int(self.height*0.25) events_height = int(self.height*0.25)
logging.debug('calendar-section size: {0} x {1} px'.format( logger.debug('calendar-section size: {0} x {1} px'.format(
im_width, calendar_height)) im_width, calendar_height))
logging.debug('events-section size: {0} x {1} px'.format( logger.debug('events-section size: {0} x {1} px'.format(
im_width, events_height)) im_width, events_height))
else: else:
calendar_height = self.height - month_name_height - weekday_height calendar_height = self.height - month_name_height - weekday_height
logging.debug('calendar-section size: {0} x {1} px'.format( logger.debug('calendar-section size: {0} x {1} px'.format(
im_width, calendar_height)) im_width, calendar_height))
# Create grid and calculate icon sizes # Create grid and calculate icon sizes
@ -141,7 +122,7 @@ class calendar:
# Set up weeknames in local language and add to main section # Set up weeknames in local language and add to main section
weekday_names = [weekstart.shift(days=+_).format('ddd',locale=self.language) weekday_names = [weekstart.shift(days=+_).format('ddd',locale=self.language)
for _ in range(7)] for _ in range(7)]
logging.debug('weekday names: {}'.format(weekday_names)) logger.debug('weekday names: {}'.format(weekday_names))
for _ in range(len(weekday_pos)): for _ in range(len(weekday_pos)):
write( write(
@ -300,8 +281,10 @@ class calendar:
im_colour.save(images+self.name+'_colour.png') im_colour.save(images+self.name+'_colour.png')
if __name__ == '__main__': if __name__ == '__main__':
print('running {0} in standalone mode'.format( print('running {0} in standalone mode'.format(filename))
os.path.basename(__file__).split('.py')[0]))
##size = (400, 520)
##config = {'week_starts_on': 'Monday', 'ical_urls': ['https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics']}
##a = calendar(size, config) ##a = calendar(size, config)
##a.generate_image() ##a.generate_image()

View File

@ -1,81 +1,64 @@
#!/usr/bin/python3 #!/usr/bin/python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
RSS module for Inky-Calendar Project RSS module for Inky-Calendar Project
Copyright by aceisace Copyright by aceisace
""" """
from inkycal.modules.template import inkycal_module
from inkycal.custom import * from inkycal.custom import *
from random import shuffle
from random import shuffle
try: try:
import feedparser import feedparser
except ImportError: except ImportError:
print('feedparser is not installed! Please install with:') print('feedparser is not installed! Please install with:')
print('pip3 install feedparser') print('pip3 install feedparser')
filename = os.path.basename(__file__).split('.py')[0]
logger = logging.getLogger(filename)
logger.setLevel(level=logging.INFO)
# Debug Data (not for production use!) class rss(inkycal_module):
size = (384, 160)
config = {'rss_urls': ['http://feeds.bbci.co.uk/news/world/rss.xml#']}
#config = {'rss_urls': ['http://www.tagesschau.de/xml/atom/']}
#https://www.tagesschau.de/xml/rss2
class rss:
"""RSS class """RSS class
parses rss feeds from given urls parses rss feeds from given urls
""" """
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
def __init__(self, section_size, section_config): def __init__(self, section_size, section_config):
"""Initialize inkycal_rss module""" """Initialize inkycal_rss module"""
super().__init__(section_size, section_config)
self.name = os.path.basename(__file__).split('.py')[0] # Module specific parameters
self.config = section_config required = ['rss_urls']
self.width, self.height = section_size for param in required:
self.fontsize = 12 if not param in section_config:
self.padding_x = 0.02 raise Exception('config is missing {}'.format(param))
self.padding_y = 0.05
self.font = ImageFont.truetype(fonts['NotoSans-SemiCondensed'],
size = self.fontsize)
# module specifc config # module name
self.name = filename
# module specific parameters
self.shuffle_feeds = True self.shuffle_feeds = True
# give an OK message
print('{0} loaded'.format(self.name)) print('{0} loaded'.format(self.name))
def set(self, **kwargs): def _validate(self):
"""Manually set some parameters of this module""" """Validate module-specific parameters"""
for key, value in kwargs.items(): if not isinstance(self.shuffle_feeds, bool):
if key in self.__dict__: print('shuffle_feeds has to be a boolean: True/False')
setattr(self, key, value)
else:
print('{0} does not exist'.format(key))
pass
def get(self, **kwargs):
"""Manually get some parameters of this module"""
for key, value in kwargs.items():
if key in self.__dict__:
getattr(self, key, value)
else:
print('{0} does not exist'.format(key))
pass
def get_options(self):
"""Get all options which can be changed"""
return self.__dict__
def generate_image(self): def generate_image(self):
"""Generate image for this module""" """Generate image for this module"""
# Define new image size with respect to padding # Define new image size with respect to padding
im_width = int(self.width - (self.width * 2 * self.padding_x)) im_width = int(self.width - (self.width * 2 * self.margin_x))
im_height = int(self.height - (self.height * 2 * self.padding_y)) im_height = int(self.height - (self.height * 2 * self.margin_y))
im_size = im_width, im_height im_size = im_width, im_height
logging.info('image size: {} x {} px'.format(im_width, im_height)) logger.info('image size: {} x {} px'.format(im_width, im_height))
# Create an image for black pixels and one for coloured pixels # Create an image for black pixels and one for coloured pixels
im_black = Image.new('RGB', size = im_size, color = 'white') im_black = Image.new('RGB', size = im_size, color = 'white')
@ -83,7 +66,7 @@ class rss:
# Check if internet is available # Check if internet is available
if internet_available() == True: if internet_available() == True:
logging.info('Connection test passed') logger.info('Connection test passed')
else: else:
raise Exception('Network could not be reached :/') raise Exception('Network could not be reached :/')
@ -101,8 +84,6 @@ class rss:
line_positions = [ line_positions = [
(0, spacing_top + _ * line_height ) for _ in range(max_lines)] (0, spacing_top + _ * line_height ) for _ in range(max_lines)]
# Create list containing all rss-feeds from all rss-feed urls # Create list containing all rss-feeds from all rss-feed urls
parsed_feeds = [] parsed_feeds = []
for feeds in self.config['rss_urls']: for feeds in self.config['rss_urls']:
@ -149,9 +130,20 @@ class rss:
im_black.save(images+self.name+'.png', 'PNG') im_black.save(images+self.name+'.png', 'PNG')
im_colour.save(images+self.name+'_colour.png', 'PNG') im_colour.save(images+self.name+'_colour.png', 'PNG')
if __name__ == '__main__':
print('running {0} in standalone mode'.format(
os.path.basename(__file__).split('.py')[0]))
##def main():
## print('Main got executed just now~~~~')
if __name__ == '__main__':
print('running {0} in standalone/debug mode'.format(filename))
##else:
## print(filename, 'imported')
## main()
##a = rss(size, config) ##a = rss(size, config)
##a.generate_image() ##a.generate_image()
##size = (384, 160)
##config = {'rss_urls': ['http://feeds.bbci.co.uk/news/world/rss.xml#']}
#config = {'rss_urls': ['http://www.tagesschau.de/xml/atom/']}
#https://www.tagesschau.de/xml/rss2 -> problematic feed

View File

@ -4,46 +4,43 @@
Weather module for Inky-Calendar software. Weather module for Inky-Calendar software.
Copyright by aceisace Copyright by aceisace
""" """
from inkycal.modules.template import inkycal_module
from inkycal.custom import * from inkycal.custom import *
import math, decimal import math, decimal
import arrow import arrow
from locale import getdefaultlocale as sys_locale from locale import getdefaultlocale as sys_locale
try: try:
import pyowm import pyowm
except ImportError: except ImportError:
print('pyowm is not installed! Please install with:') print('pyowm is not installed! Please install with:')
print('pip3 install pyowm') print('pip3 install pyowm')
filename = os.path.basename(__file__).split('.py')[0]
logger = logging.getLogger(filename)
logger.setLevel(level=logging.INFO)
# Debug Data (not for production use!) class weather(inkycal_module):
config = {'api_key': 'secret', 'location': 'Stuttgart, DE'}
size = (384,80)
class weather:
"""weather class """weather class
parses weather details from openweathermap parses weather details from openweathermap
""" """
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
def __init__(self, section_size, section_config): def __init__(self, section_size, section_config):
"""Initialize inkycal_weather module""" """Initialize inkycal_weather module"""
self.name = os.path.basename(__file__).split('.py')[0]
self.config = section_config super().__init__(section_size, section_config)
self.width, self.height = section_size
self.background_colour = 'white'
self.font_colour = 'black'
self.fontsize = 12
self.font = ImageFont.truetype(fonts['NotoSans-SemiCondensed'],
size = self.fontsize)
self.padding_x = 0.02
self.padding_y = 0.05
# Weather-specfic options # Module specific parameters
self.owm = pyowm.OWM(config['api_key']) required = ['api_key','location']
for param in required:
if not param in section_config:
raise Exception('config is missing {}'.format(param))
# module name
self.name = filename
# module specific parameters
self.owm = pyowm.OWM(self.config['api_key'])
self.units = 'metric' # metric # imperial self.units = 'metric' # metric # imperial
self.hour_format = '24' # 12 #24 self.hour_format = '24' # 12 #24
self.timezone = get_system_tz() self.timezone = get_system_tz()
@ -54,52 +51,25 @@ class weather:
self.locale = sys_locale()[0] self.locale = sys_locale()[0]
self.weatherfont = ImageFont.truetype(fonts['weathericons-regular-webfont'], self.weatherfont = ImageFont.truetype(fonts['weathericons-regular-webfont'],
size = self.fontsize) size = self.fontsize)
# give an OK message
print('{0} loaded'.format(self.name)) print('{0} loaded'.format(self.name))
def set(self, **kwargs):
"""Manually set some parameters of this module"""
for key, value in kwargs.items():
if key in self.__dict__:
setattr(self, key, value)
else:
print('{0} does not exist'.format(key))
pass
def get(self, **kwargs):
"""Manually get some parameters of this module"""
for key, value in kwargs.items():
if key in self.__dict__:
getattr(self, key, value)
else:
print('{0} does not exist'.format(key))
pass
def get_options(self):
"""Get all options which can be changed"""
return self.__dict__
def generate_image(self): def generate_image(self):
"""Generate image for this module""" """Generate image for this module"""
# Define new image size with respect to padding # Define new image size with respect to padding
im_width = int(self.width - (self.width * 2 * self.padding_x)) im_width = int(self.width - (self.width * 2 * self.margin_x))
im_height = int(self.height - (self.height * 2 * self.padding_y)) im_height = int(self.height - (self.height * 2 * self.margin_y))
im_size = im_width, im_height im_size = im_width, im_height
logging.info('image size: {} x {} px'.format(im_width, im_height)) logger.info('image size: {} x {} px'.format(im_width, im_height))
# Create an image for black pixels and one for coloured pixels # Create an image for black pixels and one for coloured pixels
im_black = Image.new('RGB', size = im_size, color = self.background_colour) im_black = Image.new('RGB', size = im_size, color = 'white')
im_colour = Image.new('RGB', size = im_size, color = 'white') im_colour = Image.new('RGB', size = im_size, color = 'white')
# Check if internet is available # Check if internet is available
if internet_available() == True: if internet_available() == True:
logging.info('Connection test passed') logger.info('Connection test passed')
else: else:
raise Exception('Network could not be reached :(') raise Exception('Network could not be reached :(')
@ -353,7 +323,7 @@ class weather:
} }
for key,val in fc_data.items(): for key,val in fc_data.items():
logging.info((key,val)) logger.info((key,val))
# Get some current weather details # Get some current weather details
temperature = '{}°'.format(weather.get_temperature(unit=temp_unit)['temp']) temperature = '{}°'.format(weather.get_temperature(unit=temp_unit)['temp'])
@ -450,16 +420,16 @@ class weather:
draw_border(im_black, (col6, row1), (col_width, im_height)) draw_border(im_black, (col6, row1), (col_width, im_height))
draw_border(im_black, (col7, row1), (col_width, im_height)) draw_border(im_black, (col7, row1), (col_width, im_height))
############################################################################## # Save image of black and colour channel in image-folder
# Error Handling
##############################################################################
# Save image of black and colour channel in image-folder
im_black.save(images+self.name+'.png', "PNG") im_black.save(images+self.name+'.png', "PNG")
im_colour.save(images+self.name+'_colour.png', "PNG") im_colour.save(images+self.name+'_colour.png', "PNG")
if __name__ == '__main__': if __name__ == '__main__':
print('running {0} in standalone mode'.format( print('running {0} in standalone mode'.format(filename))
os.path.basename(__file__).split('.py')[0]))
a = weather(size, config)
a.generate_image() ##config = {'api_key': 'secret', 'location': 'Stuttgart, DE'}
##size = (384,80)
##a = weather(size, config)
##a.generate_image()
# Debug Data (not for production use!)

View File

@ -0,0 +1,54 @@
import abc
from inkycal.custom import *
class inkycal_module(metaclass=abc.ABCMeta):
"""Generic base class for inykcal modules"""
@classmethod
def __subclasshook__(cls, subclass):
return (hasattr(subclass, 'generate_image') and
callable(subclass.generate_image) or
NotImplemented)
def __init__(self, section_size, section_config):
# Initializes base module
# sets properties shared amongst all sections
self.config = section_config
self.width, self.height = section_size
self.fontsize = 12
self.margin_x = 0.02
self.margin_y = 0.05
self.font = ImageFont.truetype(
fonts['NotoSans-SemiCondensed'], size = self.fontsize)
def set(self, help=False, **kwargs):
"""Set attributes of class, e.g. class.set(key=value)
see that can be changed by setting help to True
"""
lst = dir(self).copy()
options = [_ for _ in lst if not _.startswith('_')]
if 'logger' in options: options.remove('logger')
if help == True:
print('The following can be configured:')
print(options)
for key, value in kwargs.items():
if key in options:
setattr(self, key, value)
print("set '{}' to '{}'".format(key,value))
else:
print('{0} does not exist'.format(key))
pass
# Check if validation has been implemented
try:
self._validate()
except AttributeError:
print('no validation implemented')
@abc.abstractmethod
def generate_image(self):
# Generate image for this module with specified parameters
raise NotImplementedError(
'The developers were too lazy to implement this function')

137
inkycal/modules/test.py Normal file
View File

@ -0,0 +1,137 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Module template for Inky-Calendar Project
Create your own module with this template
Copyright by aceisace
"""
#############################################################################
# Required imports (do not remove)
#############################################################################
# Required for setting up this module
from inkycal.modules.template import inkycal_module
from inkycal.custom import *
#############################################################################
# Built-in library imports
#############################################################################
# Built-in libraries go here
from random import shuffle
#############################################################################
# External library imports
#############################################################################
# For external libraries, which require installing,
# use try...except ImportError to check if it has been installed
# If it is not found, print a short message on how to install this dependency
try:
import feedparser
except ImportError:
print('feedparser is not installed! Please install with:')
print('pip3 install feedparser')
#############################################################################
# Filename + logging (do not remove)
#############################################################################
# Get the name of this file, set up logging for this filename
filename = os.path.basename(__file__).split('.py')[0]
logger = logging.getLogger(filename)
logger.setLevel(level=logging.INFO)
#############################################################################
# Class setup
#############################################################################
class simple(inkycal_module):
""" Simple Class
Explain what this module does...
"""
# Initialise the class (do not remove)
def __init__(self, section_size, section_config):
"""Initialize inkycal_rss module"""
# Initialise this module via the inkycal_module template (required)
super().__init__(section_size, section_config)
# module name (required)
self.name = filename
# module specific parameters (optional)
self.do_something = True
# give an OK message (optional)
print('{0} loaded'.format(self.name))
#############################################################################
# Validation of module specific parameters #
#############################################################################
def _validate(self):
"""Validate module-specific parameters"""
# Check the type of module-specific parameters
# This function is optional, but very useful for debugging.
# Here, we are checking if do_something (from init) is True/False
if not isinstance(self.do_something, bool):
print('do_something has to be a boolean: True/False')
#############################################################################
# Generating the image #
#############################################################################
def generate_image(self):
"""Generate image for this module"""
# Define new image size with respect to padding (required)
im_width = int(self.width - (self.width * 2 * self.margin_x))
im_height = int(self.height - (self.height * 2 * self.margin_y))
im_size = im_width, im_height
# Use logger.info(), logger.debug(), logger.warning() to display
# useful information for the developer
logger.info('image size: {} x {} px'.format(im_width, im_height))
# Create an image for black pixels and one for coloured pixels (required)
im_black = Image.new('RGB', size = im_size, color = 'white')
im_colour = Image.new('RGB', size = im_size, color = 'white')
#################################################################
# Your code goes here #
# Write/Draw something on the image
# You can use these custom functions to help you create the image:
# - write() -> write text on the image
# - get_fonts() -> see which fonts are available
# - get_system_tz() -> Get the system's current timezone
# - auto_fontsize() -> Scale the fontsize to the provided height
# - textwrap() -> Split a paragraph into smaller lines
# - internet_available() -> Check if internet is available
# - draw_border() -> Draw a border around the specified area
# If these aren't enough, take a look at python Pillow (imaging library)'s
# documentation.
#################################################################
# Save image of black and colour channel in image-folder
im_black.save(images+self.name+'.png', 'PNG')
im_colour.save(images+self.name+'_colour.png', 'PNG')
# Check if the module is being run by itself
if __name__ == '__main__':
print('running {0} in standalone mode'.format(filename))