diff --git a/.gitignore b/.gitignore index 5d8ee74..ac24778 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,4 @@ /Calendar/CalendarEvent.pyc /Calendar/design_exported_old.png /Calendar/settings_dev.py -/Calendar/settings_pers.py -/.vscode -/Calendar/images/ \ No newline at end of file +/.vscode \ No newline at end of file diff --git a/Calendar/AgendaListDesign.py b/Calendar/AgendaListDesign.py index f89f15c..096c66d 100644 --- a/Calendar/AgendaListDesign.py +++ b/Calendar/AgendaListDesign.py @@ -4,15 +4,12 @@ from datetime import datetime, date, timedelta from TableDesign import TableDesign from PIL import ImageDraw, ImageFont from TextFormatter import date_summary_str, event_prefix_str -from settings import line_thickness - -separator_width = line_thickness +separator_width = 1 class AgendaListDesign (DesignEntity): '''Lists upcoming events in chronological order and groups them by days''' - - def __init__(self, size, calendar, line_spacing=0, col_spacing=8, text_size=defaultfontsize, start_date=date.today(), always_add_start_row=True): + def __init__ (self, size, calendar, line_spacing = 0, col_spacing = 8, text_size = defaultfontsize, start_date = date.today(), always_add_start_row = True): super(AgendaListDesign, self).__init__(size) self.calendar = calendar self.line_spacing = line_spacing @@ -21,19 +18,19 @@ class AgendaListDesign (DesignEntity): self.start_dt = date(start_date.year, start_date.month, start_date.day) self.always_add_start_row = always_add_start_row - def __finish_image__(self): + def __finish_image__ (self): self.__calculate_parameter__() self.__create_infos_events__() self.__draw_infos__() self.__draw_lines__() - def __calculate_parameter__(self): + def __calculate_parameter__ (self): self.__line_height__ = self.line_spacing + self.__get_text_height__() self.__event_number__ = int(int(self.size[1]) // self.__line_height__) self.__date_fontsize__ = self.text_size self.__date_linespace__ = self.line_spacing - def __create_infos_events__(self): + def __create_infos_events__ (self): self.infos = [] self.cell_props = [] fetch_day = self.start_dt @@ -41,7 +38,7 @@ class AgendaListDesign (DesignEntity): day_events = self.calendar.get_day_events(fetch_day) fetch_day_added_once = False for event in day_events: - row = [""] + row = [ "" ] if fetch_day_added_once is False: row.append(date_summary_str(fetch_day)) fetch_day_added_once = True @@ -51,48 +48,46 @@ class AgendaListDesign (DesignEntity): row.append(event_prefix_str(event, fetch_day)) row.append(event.title) self.cell_props.append(self.__get_row_props__(event)) - + self.infos.append(row) fetch_day = fetch_day + timedelta(1) - + if self.infos[0][1] != date_summary_str(self.start_dt) and self.always_add_start_row: row = ["", date_summary_str(self.start_dt), "", ""] props = self.__get_row_props__() self.infos.insert(0, row) self.cell_props.insert(0, props) - - def __draw_infos__(self): - table = TableDesign(self.size, self.infos, fontsize=self.__date_fontsize__, - line_spacing=self.__date_linespace__, col_spacing=self.col_spacing, cell_properties=self.cell_props) + + def __draw_infos__ (self): + table = TableDesign(self.size, self.infos, fontsize = self.__date_fontsize__, line_spacing=self.__date_linespace__, col_spacing = self.col_spacing, cell_properties=self.cell_props) self.draw_design(table) - def __draw_lines__(self): + def __draw_lines__ (self): for i, (_, date, _, _) in enumerate(self.infos[1:]): if date is not "": self.__draw_line__(i + 1) - def __draw_line__(self, index): + def __draw_line__ (self, index): ypos = index * self.__line_height__ - self.line_spacing / 2 pos = (0, ypos) - positions = [pos, (self.size[0], ypos)] + positions = [ pos, (self.size[0], ypos) ] - ImageDraw.Draw(self.__image__).line( - positions, fill=colors["fg"], width=separator_width) + ImageDraw.Draw(self.__image__).line(positions, fill=colors["fg"], width=separator_width) - def __get_row_props__(self, event=None): + def __get_row_props__ (self, event = None): color = colors["fg"] bg_color = colors["bg"] default_cell = { - "color": color, - "background_color": bg_color + "color" : color, + "background_color" : bg_color } if event is not None and event.highlight: color = colors["hl"] cell = { - "color": color, - "background_color": bg_color + "color" : color, + "background_color" : bg_color } - return [default_cell, default_cell, cell, cell] + return [default_cell, default_cell, cell, cell ] def __get_text_height__(self): - return ImageFont.truetype(path + defaultfont, self.text_size).font.height + return ImageFont.truetype(path + defaultfont, self.text_size).font.height \ No newline at end of file diff --git a/Calendar/AgendaListPanel.py b/Calendar/AgendaListPanel.py index 6895ab0..9cf8b6c 100644 --- a/Calendar/AgendaListPanel.py +++ b/Calendar/AgendaListPanel.py @@ -1,37 +1,33 @@ from PanelDesign import PanelDesign from AgendaListDesign import AgendaListDesign from WeatherHeaderDesign import WeatherHeaderDesign -from settings import general_settings, line_thickness +from settings import general_settings from PIL import ImageDraw from Assets import colors from RssPostListDesign import RssPostListDesign -from CryptoListDesign import CryptoListDesign agenda_ypadding = 5 weatherheader_height = 0.113 -seperator_width = line_thickness +seperator_width = 3 infolist_size = (1, 0.24) -infolist_padding = 0 - +infolist_padding = 2 class AgendaListPanel (PanelDesign): '''Lists upcoming events in chronological order and groups them by days''' - def __init__(self, size): super(AgendaListPanel, self).__init__(size) self.weather_size = (0, 0) self.info_size = (0, 0) if general_settings["weather-info"]: - self.weather_size = ( - self.size[0], self.size[1] * weatherheader_height) + self.weather_size = (self.size[0], self.size[1] * weatherheader_height) - def add_weather(self, weather): + def add_weather (self, weather): self.weather = weather - def add_calendar(self, calendar): + def add_calendar (self, calendar): self.calendar = calendar - def add_rssfeed(self, rss): + def add_rssfeed (self, rss): if general_settings["info-area"] != "rss": return @@ -44,41 +40,24 @@ class AgendaListPanel (PanelDesign): self.__draw_seperator__(1-infolist_size[1], colors["fg"]) - def add_tasks(self, tasks): + def add_taks (self, tasks): pass - def add_crypto(self, crypto): - if general_settings["info-area"] != "crypto": - return - - self.info_size = self.__abs_pos__(infolist_size) - pos = (0, self.size[1] - self.info_size[1] + infolist_padding) - - list = CryptoListDesign(self.info_size, crypto) - height = list.get_estimated_height() - list.pos = (pos[0], pos[1] + (self.info_size[1] - height)) - self.draw_design(list) - - self.info_size = (self.size[0], height) - self.__draw_seperator__(list.pos[1] / self.size[1], colors["fg"]) - def __finish_panel__(self): self.__draw_calendar__() if general_settings["weather-info"]: self.__draw_weather__() - def __draw_seperator__(self, height, color): - ImageDraw.Draw(self.__image__).line([self.__abs_pos__( - (0, height)), self.__abs_pos__((1, height))], fill=color, width=seperator_width) + def __draw_seperator__ (self, height, color): + ImageDraw.Draw(self.__image__).line([ self.__abs_pos__((0, height)), self.__abs_pos__((1, height)) ], fill=color, width=seperator_width) - def __abs_pos__(self, pos, size=None): + def __abs_pos__ (self, pos, size = None): if size is None: size = self.size return (int(pos[0] * size[0]), int(pos[1] * size[1])) def __draw_calendar__(self): - size = (self.size[0], self.size[1] - self.weather_size[1] - - self.info_size[1] - agenda_ypadding) + size = (self.size[0], self.size[1] - self.weather_size[1] - self.info_size[1] - agenda_ypadding) agenda = AgendaListDesign(size, self.calendar) agenda.pos = (0, agenda_ypadding + self.weather_size[1]) @@ -87,4 +66,4 @@ class AgendaListPanel (PanelDesign): def __draw_weather__(self): header = WeatherHeaderDesign(self.weather_size, self.weather) self.draw_design(header) - self.__draw_seperator__(weatherheader_height, colors["hl"]) + self.__draw_seperator__(weatherheader_height, colors["hl"]) \ No newline at end of file diff --git a/Calendar/Assets.py b/Calendar/Assets.py index e5c750a..bb8ffff 100644 --- a/Calendar/Assets.py +++ b/Calendar/Assets.py @@ -9,87 +9,41 @@ path = os.path.dirname(os.path.abspath(__file__)).replace("\\", "/") if path != "" and path[-1] != "/": path += "/" -wpath = path+'weather-icons/' -opath = path+'other/' -fpath = 'fonts/' +wpath = path+'weather-icons/' +opath = path+'other/' +fpath = 'fonts/' -tempicon = im_open(opath+'temperature.jpeg') -humicon = im_open(opath+'humidity.jpeg') -no_response = im_open(opath+'cloud-no-response.jpeg') -sunriseicon = im_open(opath+'wi-sunrise.jpeg') -sunseticon = im_open(opath+'wi-sunset.jpeg') -windicon = im_open(opath+'wi-strong-wind.jpeg') +tempicon = im_open(opath+'temperature.jpeg') +humicon = im_open(opath+'humidity.jpeg') +no_response= im_open(opath+'cloud-no-response.jpeg') +sunriseicon = im_open(opath+'wi-sunrise.jpeg') +sunseticon = im_open(opath+'wi-sunset.jpeg') +windicon = im_open(opath+'wi-strong-wind.jpeg') fonts = { - "extralight": fpath + "Assistant-ExtraLight.otf", - "light": fpath + "Assistant-Light.otf", - "regular": fpath + "Assistant-Regular.otf", - "semibold": fpath + "Assistant-SemiBold.otf", - "bold": fpath + "Assistant-Bold.otf", - "extrabold": fpath + "Assistant-ExtraBold.otf" + "extralight" : fpath + "Assistant-ExtraLight.otf", + "light" : fpath + "Assistant-Light.otf", + "regular" : fpath + "Assistant-Regular.otf", + "semibold" : fpath + "Assistant-SemiBold.otf", + "bold" : fpath + "Assistant-Bold.otf", + "extrabold" : fpath + "Assistant-ExtraBold.otf" } defaultfont = fonts[font_boldness] defaultfontsize = int(font_size) weathericons = { - '01d': 'wi-day-sunny', '02d': 'wi-day-cloudy', '03d': 'wi-cloudy', - '04d': 'wi-cloudy-windy', '09d': 'wi-showers', '10d': 'wi-rain', - '11d': 'wi-thunderstorm', '13d': 'wi-snow', '50d': 'wi-fog', - '01n': 'wi-night-clear', '02n': 'wi-night-cloudy', - '03n': 'wi-night-cloudy', '04n': 'wi-night-cloudy', - '09n': 'wi-night-showers', '10n': 'wi-night-rain', - '11n': 'wi-night-thunderstorm', '13n': 'wi-night-snow', - '50n': 'wi-night-alt-cloudy-windy'} +'01d': 'wi-day-sunny', '02d':'wi-day-cloudy', '03d': 'wi-cloudy', +'04d': 'wi-cloudy-windy', '09d': 'wi-showers', '10d':'wi-rain', +'11d':'wi-thunderstorm', '13d':'wi-snow', '50d': 'wi-fog', +'01n': 'wi-night-clear', '02n':'wi-night-cloudy', +'03n': 'wi-night-cloudy', '04n': 'wi-night-cloudy', +'09n': 'wi-night-showers', '10n':'wi-night-rain', +'11n':'wi-night-thunderstorm', '13n':'wi-night-snow', +'50n': 'wi-night-alt-cloudy-windy'} colors = { - "hl": "red", - "fg": "black", - "bg": "white" -} - -supported_img_formats = [ - "BMP", - "DIB", - "EPS", - "GIF", - "ICNS", - "ICO", - "IM", - "JPG", - "JPEG", - "J2K", - "J2P", - "JPX", - "MSP", - "PCX", - "PNG", - "PPM", - "SGI", - "SPI", - "TGA", - "TIFF", - "WEBP", - "XBM", - "BLP", - "CUR", - "DCX", - "DDS", - "FLI", - "FLC", - "FPX", - "FTEX", - "GBR", - "GD", - "IMT", - "IPTC", - "NAA", - "MCIDAS", - "MIC", - "MPO", - "PCD", - "PIXAR", - "PSD", - "WAL", - "XPM", -] + "hl" : "red", + "fg" : "black", + "bg" : "white" +} \ No newline at end of file diff --git a/Calendar/BoxDesign.py b/Calendar/BoxDesign.py index 3fbf281..2138d84 100644 --- a/Calendar/BoxDesign.py +++ b/Calendar/BoxDesign.py @@ -1,10 +1,8 @@ from DesignEntity import DesignEntity from PIL import ImageDraw, ImageOps - class BoxDesign (DesignEntity): """Redefinition of ImageDraw.Draw.Rectangle""" - def __init__(self, size, fill=None, outline=None, width=0): super(BoxDesign, self).__init__((size[0]+1, size[1]+1), mask=True) self.size = size @@ -14,10 +12,9 @@ class BoxDesign (DesignEntity): self.width = width def __define_corners__(self): - topleft = (0, 0) + topleft = (0,0) bottomright = self.size self.corners = [topleft, bottomright] - def __finish_image__(self): - ImageDraw.Draw(self.__image__).rectangle( - self.corners, fill=self.fill, outline=self.outline, width=self.width) + def __finish_image__ (self): + ImageDraw.Draw(self.__image__).rectangle(self.corners, fill=self.fill, outline=self.outline, width=self.width) \ No newline at end of file diff --git a/Calendar/CalendarEvent.py b/Calendar/CalendarEvent.py index 0f97f2f..316d054 100644 --- a/Calendar/CalendarEvent.py +++ b/Calendar/CalendarEvent.py @@ -1,7 +1,6 @@ class CalendarEvent (object): """Defines a calendar event, independent of any implementation""" - - def __init__(self): + def __init__ (self): self.begin_datetime = None self.end_datetime = None self.duration = None @@ -15,10 +14,9 @@ class CalendarEvent (object): self.highlight = None self.calendar_name = None - self.calendar_url = None self.location = None self.fetch_datetime = None def __repr__(self): - return self.title + return self.title \ No newline at end of file diff --git a/Calendar/CalendarInterface.py b/Calendar/CalendarInterface.py index c17eab7..6402cb7 100644 --- a/Calendar/CalendarInterface.py +++ b/Calendar/CalendarInterface.py @@ -3,40 +3,32 @@ from datetime import datetime, timezone, timedelta, date from dateutil.rrule import rrulestr from dateutil.parser import parse import calendar -from CalendarEvent import CalendarEvent - class CalendarInterface (DataSourceInterface): """Interface for fetching and processing calendar event information.""" - - def __init__(self): + def __init__ (self): self.events = [] - self.excluded_urls = [] - def reload(self): + def reload (self): if self.is_available() == False: return self.events = self.__get_events__() self.events = self.__sort_events__(self.events) - def exclude_calendars(self, urls=[]): - self.excluded_urls = urls - - def __sort_events__(self, events): - events.sort(key=lambda x: x.begin_datetime) + def __sort_events__ (self, events): + events.sort(key=lambda x : x.begin_datetime) return events - def __sort_event_types__(self, events): + def __sort_event_types__ (self, events): multiday = [ev for ev in events if ev.multiday] allday = [ev for ev in events if ev.allday and ev.multiday == False] - timed = [ev for ev in events if ev.allday == - False and ev.multiday == False] + timed = [ev for ev in events if ev.allday == False and ev.multiday == False] return multiday + allday + timed - def __get_events__(self): + def __get_events__ (self): raise NotImplementedError("Functions needs to be implemented") - def get_upcoming_events(self, timespan=None, start_time=None): + def get_upcoming_events (self, timespan = None, start_time = None): if timespan is None: timespan = timedelta(31) if start_time == None: @@ -44,31 +36,28 @@ class CalendarInterface (DataSourceInterface): start_time = datetime.now(local_tzinfo) return self.__get_events_in_range__(start_time, timespan) - def get_today_events(self): + def get_today_events (self): return self.get_day_events(date.today()) - def get_day_events(self, day): + def get_day_events (self, day): if type(day) is not type(date.today()): - raise TypeError( - "get_day_events only takes date-objects as parameters, not \"%s\"" % str(type(day))) + raise TypeError("get_day_events only takes date-objects as parameters, not \"%s\"" % str(type(day))) local_tzinfo = datetime.now(timezone.utc).astimezone().tzinfo - day_start = datetime(day.year, day.month, day.day, - 0, 0, 0, 0, local_tzinfo) + day_start = datetime(day.year, day.month, day.day, 0, 0, 0, 0, local_tzinfo) return self.__get_events_in_range__(day_start, timedelta(1)) - def get_month_events(self, month=-1, year=-1): + def get_month_events (self, month = -1, year = -1): if month < 0: month = datetime.now().month if year < 0: year = datetime.now().year - + local_tzinfo = datetime.now(timezone.utc).astimezone().tzinfo month_start = datetime(year, month, 1, 0, 0, 0, 0, local_tzinfo) - month_days = calendar.monthrange( - month_start.year, month_start.month)[1] + month_days = calendar.monthrange(month_start.year, month_start.month)[1] return self.__get_events_in_range__(month_start, timedelta(month_days)) - def __get_events_in_range__(self, start, duration): + def __get_events_in_range__ (self, start, duration): if self.events is None: return [] @@ -77,19 +66,14 @@ class CalendarInterface (DataSourceInterface): events_in_range = [] for event in self.events: - # Is excluded? - if event.calendar_url in self.excluded_urls: - continue - - event_occurrence = self.__get_if_event_in_range__( - event, start, duration) + event_occurrence = self.__get_if_event_in_range__(event, start, duration) if event_occurrence: events_in_range.extend(event_occurrence) events_in_range = self.__sort_events__(events_in_range) return self.__sort_event_types__(events_in_range) - def __get_if_event_in_range__(self, event, start, duration): + def __get_if_event_in_range__ (self, event, start, duration): '''Returns list or None''' if event is None: return None @@ -99,7 +83,7 @@ class CalendarInterface (DataSourceInterface): else: return self.__is_repeating_in_range__(event, start, duration) - def __is_onetime_in_range__(self, event, start, duration): + def __is_onetime_in_range__ (self, event, start, duration): if event.begin_datetime > start: first_start = start first_duration = duration @@ -110,73 +94,45 @@ class CalendarInterface (DataSourceInterface): second_start = start if (second_start - first_start) < first_duration: - return [event] + return [ event ] else: return None - def __is_repeating_in_range__(self, event, start, duration): + def __is_repeating_in_range__ (self, event, start, duration): end = start + duration occurrences = [] - try: - r_string = "" - r_string = self.__add_timezoneawarness__(event.rrule) - rule = rrulestr(r_string, dtstart=event.begin_datetime) - for occurrence in rule: - if occurrence - end > timedelta(0): - return occurrences - merged_event = self.__merge_event_data__( - event, start=occurrence) - if self.__is_onetime_in_range__(merged_event, start, duration): - occurrences.append(merged_event) - return occurrences - except Exception as ex: - print("\"is_repeating_in_range\" failed while processing: dtstart="+str(event.begin_datetime) + - " dtstart.tzinfo="+str(event.begin_datetime.tzinfo)+" rrule="+r_string) - raise ex + r_string=self.__add_timezoneawarness__(event.rrule) + rule=rrulestr(r_string,dtstart=event.begin_datetime) + for occurrence in rule: + if occurrence - end > timedelta(0): + return occurrences + merged_event = self.__merge_event_data__(event, start=occurrence) + if self.__is_onetime_in_range__(merged_event, start, duration): + occurrences.append(merged_event) + return occurrences - def __merge_event_data__(self, event, start=None): - merged_event = CalendarEvent() - - merged_event.begin_datetime = event.begin_datetime - merged_event.end_datetime = event.end_datetime - merged_event.duration = event.duration - merged_event.allday = event.allday - merged_event.multiday = event.multiday - merged_event.rrule = event.rrule - - merged_event.title = event.title - merged_event.description = event.description - merged_event.attendees = event.attendees - merged_event.highlight = event.highlight - - merged_event.calendar_name = event.calendar_name - merged_event.calendar_url = event.calendar_url - - merged_event.location = event.location - merged_event.fetch_datetime = event.fetch_datetime - + def __merge_event_data__ (self, event, start = None): if start is not None: - merged_event.begin_datetime = start - merged_event.end_datetime = start + event.duration + event.begin_datetime = start + event.end_datetime = start + event.duration + + return event - return merged_event - - def __add_timezoneawarness__(self, rrule): - """UNTIL must be specified in UTC when DTSTART is timezone-aware (which it is)""" + def __add_timezoneawarness__ (self, rrule): if "UNTIL" not in rrule: return rrule timezone_str = "T000000Z" - until_template = "UNTIL=YYYYMMDD" + until_example = "UNTIL=YYYYMMDD" until_index = rrule.index("UNTIL") - tz_index = until_index + len(until_template) - if until_index < 0 or (tz_index < len(rrule) and rrule[tz_index] is "T"): + tz_index = until_index + len(until_example) + if tz_index < 0 or tz_index >= len(rrule): + return rrule + + if rrule[tz_index] is "T": return rrule - if tz_index == len(rrule): - return rrule + timezone_str - else: - return rrule[:tz_index] + timezone_str + rrule[tz_index:] + return rrule[:tz_index] + timezone_str + rrule[tz_index:] \ No newline at end of file diff --git a/Calendar/CryptoCoin.py b/Calendar/CryptoCoin.py deleted file mode 100644 index 8736791..0000000 --- a/Calendar/CryptoCoin.py +++ /dev/null @@ -1,10 +0,0 @@ -class CryptoCoin(object): - def __init__(self): - self.name = None - self.symbol = None - self.price = None - self.day_change = None - self.currency = None - self.datetime = None - - self.fetch_datetime = None diff --git a/Calendar/CryptoInterface.py b/Calendar/CryptoInterface.py deleted file mode 100644 index a6cc351..0000000 --- a/Calendar/CryptoInterface.py +++ /dev/null @@ -1,17 +0,0 @@ -from DataSourceInterface import DataSourceInterface - - -class CryptoInterface(DataSourceInterface): - def __init__(self): - self.crypto_coins = [] - - def reload(self): - if self.is_available() == False: - return - self.crypto_coins = self.__get_coins__() - - def __get_coins__(self): - raise NotImplementedError("Function needs to be implemented") - - def get_coins(self): - return self.crypto_coins diff --git a/Calendar/CryptoListDesign.py b/Calendar/CryptoListDesign.py deleted file mode 100644 index 18028d6..0000000 --- a/Calendar/CryptoListDesign.py +++ /dev/null @@ -1,39 +0,0 @@ -from DesignEntity import DesignEntity -from TableDesign import TableDesign -from Assets import defaultfontsize -from GeckoCrypto import GeckoCrypto -from settings import crypto_coins - -xpadding = 5 - - -class CryptoListDesign (DesignEntity): - def __init__(self, size, crypto, text_size=defaultfontsize): - super(CryptoListDesign, self).__init__(size) - self.crypto = crypto - self.text_size = text_size - self.matrix = self.__get_matrix__() - - def __finish_image__(self): - col_spacing = 10 - if len(self.matrix) > 0: - col_spacing = (self.size[0] / len(self.matrix[0])) * 0.5 - - table_design = TableDesign(self.size, matrix=self.matrix, col_spacing=col_spacing, - fontsize=self.text_size, mask=False, truncate_rows=True) - table_design.pos = (xpadding, 0) - self.draw_design(table_design) - - def __get_matrix__(self): - matrix = [] - coins = self.crypto.get_coins() - for coin in coins: - row = [coin.symbol.upper(), coin.name, coin.currency + " " + - str(coin.price), "% " + str(coin.day_change)] - matrix.append(row) - return matrix - - def get_estimated_height(self): - line_height = self.text_size * 1.25 - height = line_height * len(self.matrix) - return height diff --git a/Calendar/DataSourceInterface.py b/Calendar/DataSourceInterface.py index b6b541a..4c5004c 100644 --- a/Calendar/DataSourceInterface.py +++ b/Calendar/DataSourceInterface.py @@ -1,8 +1,7 @@ class DataSourceInterface (object): """Interface for child interfaces that fetch data.""" - - def is_available(self): + def is_available (self): raise NotImplementedError("Functions needs to be implemented") - def reload(self): - raise NotImplementedError("Functions needs to be implemented") + def reload (self): + raise NotImplementedError("Functions needs to be implemented") \ No newline at end of file diff --git a/Calendar/DayBoxDesign.py b/Calendar/DayBoxDesign.py index e2fea33..2128280 100644 --- a/Calendar/DayBoxDesign.py +++ b/Calendar/DayBoxDesign.py @@ -1,33 +1,13 @@ from DesignEntity import DesignEntity -from SingelDayEventListDesign import SingelDayEventListDesign -from TextDesign import TextDesign - -header_height = 0.2 - class DayBoxDesign (DesignEntity): """Represents a day with its events in a box.""" - def __init__(self, size, date): super(DayBoxDesign, self).__init__(size) self.date = date def add_calendar(self, calendar): - self.calendar = calendar - - def __finish_image__(self): - self.__draw_header__() - self.__draw_events__() - - def __draw_header__(self): pass - def __draw_events__(self): - events = self.calendar.get_day_events(self.date) - - pos = (0, self.size[1] * header_height) - size = (self.size[0], self.size[1] - pos[1]) - - event_list = SingelDayEventListDesign(size, events) - event_list.pos = pos - self.draw_design(event_list) + def __finish_image__(self): + pass \ No newline at end of file diff --git a/Calendar/DayFocusListPanel.py b/Calendar/DayFocusListPanel.py deleted file mode 100644 index 751e0d9..0000000 --- a/Calendar/DayFocusListPanel.py +++ /dev/null @@ -1,151 +0,0 @@ -from datetime import date, datetime, timedelta, timezone -from settings import line_thickness, general_settings -from DayHeaderDesign import DayHeaderDesign -from HourListDesign import HourListDesign -from DayRowDesign import DayRowDesign -from PanelDesign import PanelDesign -from Assets import colors -from PIL import ImageDraw - -HEADER_SIZE = (1, 0.2) -HOURLIST_HEIGHT = 0.3 -HOURLIST_SIZE = (1, HOURLIST_HEIGHT) -DAYLIST_YPOS = HEADER_SIZE[1] + HOURLIST_SIZE[1] -DAYLIST_HEIGHT = 1 - HEADER_SIZE[1] - HOURLIST_SIZE[1] -DAYLIST_SIZE = (1, DAYLIST_HEIGHT) -HOURS_COUNT = 6 -DAYROW_MIN_FORMAT = 40 / 384 -DAYROW_MAX_FORMAT = 60 / 384 -PANEL_LINE_THICKNESS = line_thickness - - -class DayFocusListPanel (PanelDesign): - """Shows Day-View for today and a short Day-List for - the upcoming days.""" - - def __init__(self, size): - super(DayFocusListPanel, self).__init__(size) - self.hours_count = HOURS_COUNT - self.__init_modules__() - - def __abs_co__(self, coordinates): - return (int(coordinates[0] * self.size[0]), int(coordinates[1] * self.size[1])) - - def __init_modules__(self): - self.__init_header__() - self.__init_hourlist__() - self.__init_daylist__() - - def __init_header__(self): - self.__header__ = DayHeaderDesign( - self.__abs_co__(HEADER_SIZE), date.today()) - self.__header__.pos = (0, 0) - - def __init_hourlist__(self): - start, end = self.__get_current_hour_range__() - size = self.__abs_co__(HOURLIST_SIZE) - - self.__hourlist__ = HourListDesign(size, start, end) - self.__hourlist__.pos = (0, self.__header__.size[1]) - - def __init_daylist__(self): - self.__daylist_rows__ = [] - self.__calc_dayrow_size__() - self.__create_day_rows__() - - def __calc_dayrow_size__(self): - max_area_height = DAYLIST_HEIGHT * self.size[1] - max_row_number = max_area_height / (DAYROW_MIN_FORMAT * self.size[0]) - min_row_number = max_area_height / (DAYROW_MAX_FORMAT * self.size[0]) - average_row_number = (max_row_number + min_row_number) / 2 - self.dayrow_count = round(average_row_number) - row_height = max_area_height / self.dayrow_count - self.dayrow_size = (1, row_height / self.size[1]) - - def __create_day_rows__(self): - following_days = self.__get_following_days__() - for i, date in enumerate(following_days): - row = DayRowDesign(self.__abs_co__(self.dayrow_size), date) - row.pos = self.__get_day_row_pos__(i) - self.__daylist_rows__.append(row) - - def __get_following_days__(self): - following_days = [] - for i in range(self.dayrow_count): - following_days.append(date.today() + timedelta(days=i + 1)) - return following_days - - def __get_day_row_pos__(self, i): - ypos = self.size[1] * DAYLIST_YPOS - down_shift = i * self.dayrow_size[1] * self.size[1] - return (0, int(ypos + down_shift)) - - def __finish_panel__(self): - self.draw_design(self.__header__) - self.draw_design(self.__hourlist__) - - for row in self.__daylist_rows__: - self.draw_design(row) - self.__draw_daylist_lines__() - - def __draw_daylist_lines__(self): - positions = [] - for i in range(len(self.__daylist_rows__)): - positions.append(self.__get_day_row_pos__(i)[1]) - for ypos in positions: - line_start = (0, ypos) - line_end = (self.size[0], ypos) - ImageDraw.Draw(self.__image__).line( - [line_start, line_end], fill=colors["fg"], width=PANEL_LINE_THICKNESS) - - def __get_current_hour_range__(self): - start_hour = datetime.now().hour - additional_hours = self.hours_count - 1 - - if start_hour + additional_hours > 23: - start_hour = 23 - additional_hours - - return start_hour, start_hour + additional_hours - - def add_weather(self, weather): - self.__header__.add_weather(weather) - - def add_calendar(self, calendar): - allday_ev, timed_ev = self.__split_events__( - calendar.get_today_events()) - self.__header__.add_events(allday_ev) - self.__hourlist__.add_events(timed_ev) - - self.__add_calendar_daylist__(calendar) - - def __split_events__(self, events): - allday_ev = [] - timed_ev = [] - - for event in events: - if event.allday: - allday_ev.append(event) - elif event.multiday: - if self.__is_today__(event.begin_datetime): - timed_ev.append(event) - elif self.__is_today__(event.end_datetime): - timed_ev.append(event) - else: - allday_ev.append(event) - else: - timed_ev.append(event) - return allday_ev, timed_ev - - def __is_today__(self, dt): - today = date.today() - return dt.day == today.day and \ - dt.month == today.month and \ - dt.year == today.year - - def __add_calendar_daylist__(self, calendar): - calendar.exclude_calendars(general_settings["extra-excluded-urls"]) - - for row in self.__daylist_rows__: - row.add_calendar(calendar) - - calendar.exclude_calendars() diff --git a/Calendar/DayHeaderDesign.py b/Calendar/DayHeaderDesign.py index 817163c..4e69792 100644 --- a/Calendar/DayHeaderDesign.py +++ b/Calendar/DayHeaderDesign.py @@ -27,71 +27,59 @@ numberbox_font_color = colors["bg"] numberbox_background_color = colors["hl"] weekday_font = fonts["bold"] - class DayHeaderDesign (DesignEntity): """Detailed and big view of a given date.""" - - def __init__(self, size, date): + def __init__ (self, size, date): super(DayHeaderDesign, self).__init__(size) self.weather_column_width = 0 self.date = date - def add_weather(self, weather): + def add_weather (self, weather): if general_settings["weather-info"] == False: return - forecast = weather.get_forecast_in_days( - self.date.day - date.today().day) + forecast = weather.get_forecast_in_days(self.date.day - date.today().day) self.weather_column_width = weathercolumn_y_size[0] * self.size[1] - size = (self.weather_column_width, - weathercolumn_y_size[1] * self.size[1]) + size = (self.weather_column_width, weathercolumn_y_size[1] * self.size[1]) pos = (self.size[0] - size[0], 0) design = WeatherColumnDesign(size, forecast) design.pos = pos self.draw_design(design) - def add_calendar(self, calendar): + def add_calendar (self, calendar): local_tzinfo = datetime.now(timezone.utc).astimezone().tzinfo now = datetime.now(local_tzinfo) - time_until_tomorrow = (datetime( - now.year, now.month, now.day, 0, 0, 0, 0, local_tzinfo) + timedelta(1)) - now - self.__draw_event_list__( - calendar.get_upcoming_events(time_until_tomorrow, now)) + time_until_tomorrow = (datetime(now.year, now.month, now.day, 0, 0, 0, 0, local_tzinfo) + timedelta(1)) - now + self.__draw_event_list__(calendar.get_upcoming_events(time_until_tomorrow, now)) - def add_events(self, events): + def add_events (self, events): self.__draw_event_list__(events) - def add_rssfeed(self, rss): + def add_rssfeed (self, rss): pass - def add_crypto(self, crypto): - pass - - def __finish_image__(self): + def __finish_image__ (self): self.__draw_number_square__() self.__draw_month__() - def __draw_event_list__(self, events): + def __draw_event_list__ (self, events): box_ypos = numberbox_ypos * self.size[1] box_xpos = numberbox_ypos * self.size[1] box_height = numberbox_height * self.size[1] xpadding = eventlist_xpadding * self.size[0] ypadding = eventlist_ypadding * self.size[1] monthbox_height = (monthbox_ypadding + month_height) * self.size[1] - pos = (box_xpos + box_height + xpadding, - box_ypos + monthbox_height + ypadding) - size = (self.size[0] - pos[0] - self.weather_column_width, - self.size[1] - pos[1] - box_ypos) + pos = (box_xpos + box_height + xpadding, box_ypos + monthbox_height + ypadding) + size = (self.size[0] - pos[0] - self.weather_column_width, self.size[1] - pos[1] - box_ypos) fontsize = eventlist_static_fontsize rel_dates = [self.date for _ in range(len(events))] - event_list = SingelDayEventListDesign( - size, events, fontsize, event_prefix_rel_dates=rel_dates) + event_list = SingelDayEventListDesign(size, events, fontsize, event_prefix_rel_dates = rel_dates) event_list.pos = pos self.draw_design(event_list) - def __draw_month__(self): + def __draw_month__ (self): font_size = int(month_height * self.size[1]) xpadding = int(monthbox_xpadding * self.size[0]) ypadding = int(monthbox_ypadding * self.size[1]) @@ -99,26 +87,26 @@ class DayHeaderDesign (DesignEntity): box_height = int(numberbox_height * self.size[1]) box_pos = (box_ypos + box_height + xpadding, box_ypos + ypadding) box_size = (int(monthbox_width * self.size[0]), box_height) - + month_name = self.date.strftime("%B") month = TextDesign(box_size, text=month_name, fontsize=font_size) month.pos = box_pos self.draw_design(month) - def __draw_number_square__(self): + def __draw_number_square__ (self): box_height = numberbox_height * self.size[1] box_ypos = numberbox_ypos * self.size[1] box_pos = (box_ypos, box_ypos) box_size = (box_height, box_height) - box = BoxDesign(box_size, fill=numberbox_background_color) + box = BoxDesign(box_size, fill = numberbox_background_color) box.pos = box_pos self.draw_design(box) self.__draw_today_number__() self.__draw_weekday__() - def __draw_today_number__(self): + def __draw_today_number__ (self): font_size = number_height * self.size[1] box_height = numberbox_height * self.size[1] box_ypos = numberbox_ypos * self.size[1] @@ -127,28 +115,26 @@ class DayHeaderDesign (DesignEntity): pos = (box_ypos, box_ypos + ypadding) day_text = self.__get_day_text__() - number = TextDesign(size, text=day_text, background_color=numberbox_background_color, - color=numberbox_font_color, fontsize=font_size, horizontalalignment="center", verticalalignment="center") + number = TextDesign(size, text=day_text, background_color=numberbox_background_color, color=numberbox_font_color, fontsize=font_size, horizontalalignment="center", verticalalignment="center") number.pos = pos number.mask = False self.draw_design(number) - def __draw_weekday__(self): + def __draw_weekday__ (self): font_size = weekday_height * self.size[1] box_height = numberbox_height * self.size[1] size = (box_height, weekdaybox_height * box_height) box_ypos = numberbox_ypos * self.size[1] pos = (box_ypos, box_ypos) - + week_day_name = self.date.strftime("%A") - week_day = TextDesign(size, text=week_day_name, background_color=numberbox_background_color, color=numberbox_font_color, - fontsize=font_size, horizontalalignment="center", verticalalignment="center", font=weekday_font) + week_day = TextDesign(size, text=week_day_name, background_color=numberbox_background_color, color=numberbox_font_color, fontsize=font_size, horizontalalignment="center", verticalalignment = "center", font=weekday_font) week_day.pos = pos week_day.mask = False self.draw_design(week_day) - def __abs_co__(self, coordinates): - return (int(coordinates[0] * self.size[0]), int(coordinates[1] * self.size[1])) + def __abs_co__ (self, coordinates): + return (int(coordinates[0] * self.size[0]),int(coordinates[1] * self.size[1])) - def __get_day_text__(self): - return str(self.date.day) + def __get_day_text__ (self): + return str(self.date.day) \ No newline at end of file diff --git a/Calendar/DayListPanel.py b/Calendar/DayListPanel.py index 849db8a..a9d3d9a 100644 --- a/Calendar/DayListPanel.py +++ b/Calendar/DayListPanel.py @@ -1,20 +1,17 @@ from PanelDesign import PanelDesign from Assets import colors from settings import general_settings -import calendar as callib +import calendar as callib from datetime import datetime, timedelta, date from PIL import ImageDraw from TextDesign import TextDesign from DayHeaderDesign import DayHeaderDesign from DayRowDesign import DayRowDesign from RssPostListDesign import RssPostListDesign -from CryptoListDesign import CryptoListDesign -from settings import line_thickness -from math import ceil -todayheader_pos = (0, 0) -todayheader_size = (1, 0.25) -lines_thickness = line_thickness +todayheader_pos = (0,0) +todayheader_size = (1,0.25) +lines_thickness = 1 infoarea_replacedrowscount = 3 dayrowsarea_ypos = todayheader_size[1] @@ -22,48 +19,36 @@ dayrowsarea_height = 1 - todayheader_size[1] dayrow_min_format = 50 / 384 dayrow_max_format = 70 / 384 rss_y_padding = 5 -crypto_y_padding = 5 - class DayListPanel (PanelDesign): """Overview that focuses on the current day and lists following days in a list below.""" - - def __init__(self, size): + def __init__ (self, size): super(DayListPanel, self).__init__(size) self.__day_rows__ = [] self.__calc_dayrow_size__() self.__first_render__() - def __first_render__(self): + def __first_render__ (self): self.__draw_today_header__() self.__draw_day_rows__() - def add_weather(self, weather): + def add_weather (self, weather): for row in self.__day_rows__: row.add_weather(weather) - def add_calendar(self, calendar): + def add_calendar (self, calendar): for row in self.__day_rows__: row.add_calendar(calendar) - def add_rssfeed(self, rss): + def add_rssfeed (self, rss): for row in self.__day_rows__: row.add_rssfeed(rss) if general_settings["info-area"] is "rss": - self.__day_rows__ = self.__day_rows__[:-infoarea_replacedrowscount] self.__draw_rss_infoarea__(rss) - def add_crypto(self, crypto): - if general_settings["info-area"] is "crypto": - self.__draw_crypto_infoarea__(crypto) - - def add_tasks(self, tasks): - pass - - def __draw_rss_infoarea__(self, rss): - height = infoarea_replacedrowscount * \ - self.dayrow_size[1] * self.size[1] - rss_y_padding + def __draw_rss_infoarea__ (self, rss): + height = infoarea_replacedrowscount * self.dayrow_size[1] * self.size[1] - rss_y_padding ypos = self.size[1] - height size = (self.size[0], height) pos = (0, ypos) @@ -72,35 +57,19 @@ class DayListPanel (PanelDesign): design.pos = pos self.draw_design(design) - def __draw_crypto_infoarea__(self, crypto): - height = infoarea_replacedrowscount * \ - self.dayrow_size[1] * self.size[1] - crypto_y_padding - ypos = self.size[1] - height - size = (self.size[0], height) - pos = (0, ypos) - - design = CryptoListDesign(size, crypto) - acutal_height = design.get_estimated_height() - design.pos = (pos[0], pos[1] + (height - acutal_height)) - self.draw_design(design) - - replaced_rows = ceil( - acutal_height / (self.dayrow_size[1] * self.size[1])) - self.__day_rows__ = self.__day_rows__[:-replaced_rows] - - def __draw_day_rows__(self): + def __draw_day_rows__ (self): following_days = self.__get_following_days__() for i, date in enumerate(following_days): row = DayRowDesign(self.__abs_co__(self.dayrow_size), date) row.pos = self.__get_day_row_pos__(i) self.__day_rows__.append(row) - def __get_day_row_pos__(self, i): + def __get_day_row_pos__ (self, i): ypos = self.size[1] * dayrowsarea_ypos down_shift = i * self.dayrow_size[1] * self.size[1] return (0, int(ypos + down_shift)) - def __calc_dayrow_size__(self): + def __calc_dayrow_size__ (self): max_area_height = dayrowsarea_height * self.size[1] max_row_number = max_area_height / (dayrow_min_format * self.size[0]) min_row_number = max_area_height / (dayrow_max_format * self.size[0]) @@ -109,27 +78,28 @@ class DayListPanel (PanelDesign): row_height = max_area_height / self.dayrow_count self.dayrow_size = (1, row_height / self.size[1]) + if general_settings["info-area"] in ["rss"]: + self.dayrow_count -= infoarea_replacedrowscount + def __get_following_days__(self): following_days = [] for i in range(self.dayrow_count): following_days.append(date.today() + timedelta(days=i + 1)) return following_days - def __draw_today_header__(self): - header = DayHeaderDesign(self.__abs_co__( - todayheader_size), date.today()) + def __draw_today_header__ (self): + header = DayHeaderDesign(self.__abs_co__(todayheader_size), date.today()) header.pos = self.__abs_co__(todayheader_pos) self.__day_rows__.append(header) def __draw_lines__(self): positions = [] - for i in range(len(self.__day_rows__)): + for i in range(self.dayrow_count + 1): positions.append(self.__get_day_row_pos__(i)[1]) for ypos in positions: line_start = (0, ypos) line_end = (self.size[0], ypos) - ImageDraw.Draw(self.__image__).line( - [line_start, line_end], fill=colors["fg"], width=lines_thickness) + ImageDraw.Draw(self.__image__).line([line_start, line_end], fill=colors["fg"], width=lines_thickness) def __finish_panel__(self): for design in self.__day_rows__: @@ -137,4 +107,4 @@ class DayListPanel (PanelDesign): self.__draw_lines__() def __abs_co__(self, coordinates): - return (int(coordinates[0] * self.size[0]), int(coordinates[1] * self.size[1])) + return (int(coordinates[0] * self.size[0]),int(coordinates[1] * self.size[1])) \ No newline at end of file diff --git a/Calendar/DayRowDesign.py b/Calendar/DayRowDesign.py index f51e413..84fc631 100644 --- a/Calendar/DayRowDesign.py +++ b/Calendar/DayRowDesign.py @@ -20,27 +20,25 @@ eventlist_y_fontsize = 0.2 font = fonts["light"] - class DayRowDesign (DesignEntity): """Detailed view of a given date.""" - - def __init__(self, size, date): + def __init__ (self, size, date): super(DayRowDesign, self).__init__(size) self.__init_image__() self.date = date - def add_weather(self, weather): + def add_weather (self, weather): if weather.is_available is False: return self.__draw_forecast__(weather) - def add_calendar(self, calendar): + def add_calendar (self, calendar): self.__draw_event_list__(calendar) - def add_rssfeed(self, rss): + def add_rssfeed (self, rss): pass - def __draw_event_list__(self, calendar): + def __draw_event_list__ (self, calendar): number_width = daynumber_y_size[0] * self.size[1] ypos = eventlist_ypos * self.size[1] weather_width = 0 @@ -49,18 +47,16 @@ class DayRowDesign (DesignEntity): pos = (number_width + eventlist_xpadding, ypos) size = (self.size[0] - pos[0] - weather_width, self.size[1] - pos[1]) fontsize = eventlist_y_fontsize * self.size[1] - + events = calendar.get_day_events(self.date) rel_dates = [self.date for _ in range(len(events))] - event_list = SingelDayEventListDesign( - size, events, fontsize, event_prefix_rel_dates=rel_dates) + event_list = SingelDayEventListDesign(size, events, fontsize, event_prefix_rel_dates = rel_dates) event_list.pos = pos self.draw_design(event_list) - def __draw_forecast__(self, weather): - forecast = weather.get_forecast_in_days( - self.date.day - datetime.today().day) - + def __draw_forecast__ (self, weather): + forecast = weather.get_forecast_in_days(self.date.day - datetime.today().day) + if forecast is None: return @@ -72,52 +68,48 @@ class DayRowDesign (DesignEntity): resized_icon = icon.resize(size, resample=Image.LANCZOS) self.draw(resized_icon, pos) - def __finish_image__(self): + def __finish_image__ (self): self.__draw_weekday__() self.__draw_day_number__() - def __draw_weekday__(self): + def __draw_weekday__ (self): font_size = int(weekday_fontsize * self.size[1]) - size = (weekday_y_size[0] * self.size[1], - weekday_y_size[1] * self.size[1]) + size = (weekday_y_size[0] * self.size[1], weekday_y_size[1] * self.size[1]) ypos = weekday_ypos * self.size[1] pos = (0, ypos) color = self.__get_day_color__() week_day_name = self.date.strftime("%a") - - week_day = TextDesign(size, text=week_day_name, font=font, color=color, - fontsize=font_size, horizontalalignment="center", verticalalignment="top") + + week_day = TextDesign(size, text=week_day_name, font=font, color=color, fontsize=font_size, horizontalalignment="center", verticalalignment="top") week_day.pos = pos week_day.mask = False self.draw_design(week_day) - def __draw_day_number__(self): + def __draw_day_number__ (self): font_size = int(daynumber_fontsize * self.size[1]) ypadding = daynumber_ypadding * self.size[1] - size = (daynumber_y_size[0] * self.size[1], - daynumber_y_size[1] * self.size[1]) + size = (daynumber_y_size[0] * self.size[1], daynumber_y_size[1] * self.size[1]) pos = (0, ypadding) day_text = self.__get_day_text__() color = self.__get_day_color__() - number = TextDesign(size, text=day_text, font=font, color=color, - fontsize=font_size, horizontalalignment="center", verticalalignment="bottom") + number = TextDesign(size, text=day_text, font=font, color=color, fontsize=font_size, horizontalalignment="center", verticalalignment="bottom") number.pos = pos self.draw_design(number) - def __abs_co__(self, coordinates): - return (int(coordinates[0] * self.size[0]), int(coordinates[1] * self.size[1])) + def __abs_co__ (self, coordinates): + return (int(coordinates[0] * self.size[0]),int(coordinates[1] * self.size[1])) - def __get_day_text__(self): + def __get_day_text__ (self): return str(self.date.day) - def __get_day_color__(self): + def __get_day_color__ (self): """Depending on week_starts_on""" if week_starts_on == "Monday" and self.date.strftime("%w") == "0": return colors["hl"] elif week_starts_on == "Sunday" and self.date.strftime("%w") == "6": return colors["hl"] else: - return colors["fg"] + return colors["fg"] \ No newline at end of file diff --git a/Calendar/DayViewPanel.py b/Calendar/DayViewPanel.py index 3277231..95bac18 100644 --- a/Calendar/DayViewPanel.py +++ b/Calendar/DayViewPanel.py @@ -7,8 +7,6 @@ from RssPostListDesign import RssPostListDesign from PIL import ImageDraw from Assets import colors from EventListDesign import EventListDesign -from CryptoListDesign import CryptoListDesign - header_size = (1, 0.2) hourlist_size = (1, 1 - header_size[1]) @@ -18,28 +16,25 @@ infoarea_replaced_hours = 4 infoarea_borderline_width = 1 infoarea_padding = 5 - class DayViewPanel (PanelDesign): """Overview that focuses on the current day and shows a timeline split into hours.""" - - def __init__(self, size): + def __init__ (self, size): super(DayViewPanel, self).__init__(size) self.shownhours_count = default_shownhours_count if general_settings["info-area"] not in ["", "empty"]: self.shownhours_count -= infoarea_replaced_hours self.__first_render__() - def __first_render__(self): + def __first_render__ (self): self.__init_header__() self.__init_hourlist__() - def add_weather(self, weather): + def add_weather (self, weather): self.__header__.add_weather(weather) - def add_calendar(self, calendar): - allday_ev, timed_ev = self.__split_events__( - calendar.get_today_events()) + def add_calendar (self, calendar): + allday_ev, timed_ev = self.__split_events__(calendar.get_today_events()) self.__header__.add_events(allday_ev) self.__hourlist__.add_events(timed_ev) @@ -47,28 +42,21 @@ class DayViewPanel (PanelDesign): self.__draw_event_list__(calendar) self.__draw_infoarea_line__() - def add_rssfeed(self, rss): + def add_rssfeed (self, rss): if general_settings["info-area"] == "rss": self.__draw_rss_feed__(rss) self.__draw_infoarea_line__() - def add_crypto(self, crypto): - if general_settings["info-area"] == "crypto": - self.__draw_crypto_feed__(crypto) - self.__draw_infoarea_line__() - def __draw_infoarea_line__(self): height = infoarea_replaced_hours * self.__hourlist__.row_size[1] ypos = self.size[1] - height line_start = (0, ypos) line_end = (self.size[0], ypos) - ImageDraw.Draw(self.__image__).line( - [line_start, line_end], fill=colors["fg"], width=infoarea_borderline_width) + ImageDraw.Draw(self.__image__).line([ line_start, line_end ], fill=colors["fg"], width=infoarea_borderline_width) def __draw_rss_feed__(self, rss): - height = infoarea_replaced_hours * \ - self.__hourlist__.row_size[1] - infoarea_padding + height = infoarea_replaced_hours * self.__hourlist__.row_size[1] - infoarea_padding size = (self.size[0], height) pos = (0, self.size[1] - size[1]) @@ -76,20 +64,8 @@ class DayViewPanel (PanelDesign): rss.pos = pos self.draw_design(rss) - def __draw_crypto_feed__(self, crypto): - height = infoarea_replaced_hours * \ - self.__hourlist__.row_size[1] - infoarea_padding - size = (self.size[0], height) - pos = (0, self.size[1] - size[1]) - - crypto = CryptoListDesign(size, crypto) - acutal_height = crypto.get_estimated_height() - crypto.pos = (pos[0], pos[1] + (height - acutal_height)) - self.draw_design(crypto) - def __draw_event_list__(self, calendar): - height = infoarea_replaced_hours * \ - self.__hourlist__.row_size[1] - infoarea_padding + height = infoarea_replaced_hours * self.__hourlist__.row_size[1] - infoarea_padding size = (self.size[0], height) pos = (0, self.size[1] - size[1]) @@ -97,40 +73,38 @@ class DayViewPanel (PanelDesign): events.pos = pos self.draw_design(events) - def add_tasks(self, tasks): + def add_taks (self, tasks): pass - def __finish_panel__(self): + def __finish_panel__ (self): self.draw_design(self.__header__) self.draw_design(self.__hourlist__) - def __init_header__(self): - self.__header__ = DayHeaderDesign( - self.__abs_co__(header_size), date.today()) + def __init_header__ (self): + self.__header__ = DayHeaderDesign(self.__abs_co__(header_size), date.today()) self.__header__.pos = (0, 0) - def __init_hourlist__(self): + def __init_hourlist__ (self): start, end = self.__get_current_hour_range__() size = self.__abs_co__(hourlist_size) - size = (size[0], size[1] * self.shownhours_count / - default_shownhours_count) - + size = (size[0], size[1] * self.shownhours_count / default_shownhours_count) + self.__hourlist__ = HourListDesign(size, start, end) self.__hourlist__.pos = (0, self.__header__.size[1]) def __get_current_hour_range__(self): start_hour = datetime.now().hour additional_hours = self.shownhours_count - 1 - + if start_hour + additional_hours > 23: start_hour = 23 - additional_hours return start_hour, start_hour + additional_hours - def __abs_co__(self, coordinates): - return (int(coordinates[0] * self.size[0]), int(coordinates[1] * self.size[1])) + def __abs_co__ (self, coordinates): + return (int(coordinates[0] * self.size[0]),int(coordinates[1] * self.size[1])) - def __split_events__(self, events): + def __split_events__ (self, events): allday_ev = [] timed_ev = [] @@ -148,8 +122,8 @@ class DayViewPanel (PanelDesign): timed_ev.append(event) return allday_ev, timed_ev - def __is_today__(self, dt): + def __is_today__ (self, dt): today = date.today() return dt.day == today.day and \ dt.month == today.month and \ - dt.year == today.year + dt.year == today.year \ No newline at end of file diff --git a/Calendar/DebugConsole.py b/Calendar/DebugConsole.py index 885be91..85d4271 100644 --- a/Calendar/DebugConsole.py +++ b/Calendar/DebugConsole.py @@ -3,11 +3,9 @@ from Assets import weathericons from datetime import datetime import traceback - class DebugConsole (DebugInterface): """Defines concrete console export of debug objects""" - - def print_event(self, event): + def print_event (self, event): print("\nCalendarEvent:") print("---------------------") print('Begin datetime: ' + str(event.begin_datetime)) @@ -24,7 +22,7 @@ class DebugConsole (DebugInterface): print('Location: ' + str(event.location)) print('Fetch datetime: ' + str(event.fetch_datetime)) - def print_forecast(self, forecast): + def print_forecast (self, forecast): print("\nWeatherForecast:") print("---------------------") print('Air temperature: ' + str(forecast.air_temperature)) @@ -47,12 +45,12 @@ class DebugConsole (DebugInterface): print('Location: ' + str(forecast.location)) print('Fetch datetime: ' + str(forecast.fetch_datetime)) - def print_line(self, content): + def print_line (self, content): if content is None: return print(str(content)) - def print_err(self, exception, msg=""): + def print_err (self, exception, msg=""): if exception is None: return @@ -62,4 +60,4 @@ class DebugConsole (DebugInterface): content += "\n" + str(msg) + "\n" traceback.print_exc() - self.print_line(str(content)) + self.print_line(str(content)) \ No newline at end of file diff --git a/Calendar/DebugInterface.py b/Calendar/DebugInterface.py index cd06cbd..a6dcec2 100644 --- a/Calendar/DebugInterface.py +++ b/Calendar/DebugInterface.py @@ -1,14 +1,13 @@ class DebugInterface (object): """Defines general interface for debugging operations""" - - def print_event(self, event): + def print_event (self, event): raise NotImplementedError("Functions needs to be implemented") - def print_forecast(self, forecast): + def print_forecast (self, forecast): raise NotImplementedError("Functions needs to be implemented") - def print_line(self, content): + def print_line (self, content): raise NotImplementedError("Functions needs to be implemented") - def print_err(self, exception, msg=""): - raise NotImplementedError("Functions needs to be implemented") + def print_err (self, exception, msg=""): + raise NotImplementedError("Functions needs to be implemented") \ No newline at end of file diff --git a/Calendar/DesignEntity.py b/Calendar/DesignEntity.py index ca0597b..9b8d9ef 100644 --- a/Calendar/DesignEntity.py +++ b/Calendar/DesignEntity.py @@ -3,19 +3,11 @@ from Assets import colors masking_threshold = 200 - class DesignEntity (object): """General entity that can be drawn on to a panel design or other design entities.""" - - def __init__(self, size, mask=False, invert_mask=False, color_key=False): + def __init__ (self, size, mask=False, invert_mask=False, color_key=False): self.size = size - # Are dimensions >= 0? - if self.size[0] < 0: - self.size = (0, self.size[1]) - if self.size[1] < 0: - self.size = (self.size[0], 0) - self.pos = (0, 0) self.mask = mask self.invert_mask = invert_mask @@ -23,39 +15,36 @@ class DesignEntity (object): self.__finished_image__ = False self.color_key = color_key - def __init_image__(self, color=colors["bg"]): - rounded_size = (int(self.size[0]), int(self.size[1])) - self.__image__ = Image.new('RGBA', rounded_size, color=color) + def __init_image__ (self, color = colors["bg"]): + rounded_size = (int(self.size[0]),int(self.size[1])) + self.__image__ = Image.new('RGB', rounded_size, color=color) - def get_image(self): + def get_image (self): if self.__finished_image__ is False: self.__finish_image__() self.__finished_image__ = True return self.__image__ - def draw(self, subimage, pos, mask=False, invert_mask=False, color_key=False): - rounded_pos = (int(pos[0]), int(pos[1])) + def draw (self, subimage, pos, mask=False, invert_mask=False, color_key=False): + rounded_pos = (int(pos[0]),int(pos[1])) img_mask = None if mask: - img_mask = self.__get_mask__( - subimage, invert_mask=invert_mask, color_key=color_key) + img_mask = self.__get_mask__(subimage, invert_mask=invert_mask, color_key=color_key) self.__image__.paste(subimage, rounded_pos, mask=img_mask) - def draw_design(self, entity): - self.draw(entity.get_image(), entity.pos, entity.mask, - entity.invert_mask, entity.color_key) + def draw_design (self, entity): + self.draw(entity.get_image(), entity.pos, entity.mask, entity.invert_mask, entity.color_key) - def draw_image(self, path, pos): + def draw_image (self, path, pos): self.draw(Image.open(path), pos) - def __finish_image__(self): + def __finish_image__ (self): pass - def __get_mask__(self, image, invert_mask, color_key): + def __get_mask__ (self, image, invert_mask, color_key): mask = image.convert('L') if color_key: - mask = mask.point(lambda p: 0 if p < - masking_threshold else 255, '1').convert('L') + mask = mask.point(lambda p : 0 if p < masking_threshold else 255, '1').convert('L') if invert_mask: mask = ImageOps.invert(mask) - return ImageOps.invert(mask) + return ImageOps.invert(mask) \ No newline at end of file diff --git a/Calendar/Dictionary.py b/Calendar/Dictionary.py index 68c1a3c..327f563 100644 --- a/Calendar/Dictionary.py +++ b/Calendar/Dictionary.py @@ -4,78 +4,78 @@ default_language = "en" '''Events''' more_events = { - 'en': '+ *0 more', - 'de': '+ *0 weitere' + 'en' : '+ *0 more', + 'de' : '+ *0 weitere' } multiday_events = { - 'en': 'Multi-day', - 'de': 'Mehrtägig' + 'en' : 'Multi-day', + 'de' : 'Mehrtägig' } allday_events = { - 'en': 'All-day', - 'de': 'Ganztägig' + 'en' : 'All-day', + 'de' : 'Ganztägig' } '''Weather''' rain_weather = { - 'en': 'Rain', - 'de': 'Regen' + 'en' : 'Rain', + 'de' : 'Regen' } clear_weather = { - 'en': 'Clear', - 'de': 'Klar' + 'en' : 'Clear', + 'de' : 'Klar' } clouds_weather = { - 'en': 'Clouds', - 'de': 'Wolken' + 'en' : 'Clouds', + 'de' : 'Wolken' } thunderstorm_weather = { - 'en': 'Thunderstorm', - 'de': 'Gewitter' + 'en' : 'Thunderstorm', + 'de' : 'Gewitter' } drizzle_weather = { - 'en': 'Drizzle', - 'de': 'Niesel' + 'en' : 'Drizzle', + 'de' : 'Niesel' } snow_weather = { - 'en': 'Snow', - 'de': 'Schnee' + 'en' : 'Snow', + 'de' : 'Schnee' } mist_weather = { - 'en': 'Mist', - 'de': 'Nebel' + 'en' : 'Mist', + 'de' : 'Nebel' } smoke_weather = { - 'en': 'Smoke', - 'de': 'Rauch' + 'en' : 'Smoke', + 'de' : 'Rauch' } haze_weather = { - 'en': 'Haze', - 'de': 'Nebel' + 'en' : 'Haze', + 'de' : 'Nebel' } dust_weather = { - 'en': 'Dust', - 'de': 'Staub' + 'en' : 'Dust', + 'de' : 'Staub' } fog_weather = { - 'en': 'Fog', - 'de': 'Nebel' + 'en' : 'Fog', + 'de' : 'Nebel' } sand_weather = { - 'en': 'Sand', - 'de': 'Sand' + 'en' : 'Sand', + 'de' : 'Sand' } ash_weather = { - 'en': 'Ash', - 'de': 'Asche' + 'en' : 'Ash', + 'de' : 'Asche' } squall_weather = { - 'en': 'Squall', - 'de': 'Sturm' + 'en' : 'Squall', + 'de' : 'Sturm' } tornado_weather = { - 'en': 'Tornado', - 'de': 'Tornado' + 'en' : 'Tornado', + 'de' : 'Tornado' } dictionary_collection = [ rain_weather, @@ -96,4 +96,4 @@ dictionary_collection = [ more_events, allday_events, multiday_events -] +] \ No newline at end of file diff --git a/Calendar/DictionaryMapper.py b/Calendar/DictionaryMapper.py index 6304610..3d7c12a 100644 --- a/Calendar/DictionaryMapper.py +++ b/Calendar/DictionaryMapper.py @@ -4,15 +4,13 @@ from settings import language '''Takes a collection of phrases and outputs the necessary text according to the language and inserts parameters.''' - def get_text(dictionary, *params): text = dictionary[default_language] if language in dictionary.keys(): text = dictionary[language] - + return __insert_params__(text, params) - def __insert_params__(text, params): index = 0 while '*%d' % index in text and index < len(params): @@ -26,4 +24,4 @@ def __insert_params__(text, params): else: text = splitted[0].rsplit(' ') index += 1 - return text + return text \ No newline at end of file diff --git a/Calendar/DisplayAdapter.py b/Calendar/DisplayAdapter.py index d8c3312..613ec75 100644 --- a/Calendar/DisplayAdapter.py +++ b/Calendar/DisplayAdapter.py @@ -1,12 +1,11 @@ class DisplayAdapter (object): """Interface for CalendarDesign output channels.""" - def __init__(self, width, height): self.width = width self.height = height - def render(self, design): + def render (self, design): raise NotImplementedError("Functions needs to be implemented") - def calibrate(self): - raise NotImplementedError("Functions needs to be implemented") + def calibrate (self): + raise NotImplementedError("Functions needs to be implemented") \ No newline at end of file diff --git a/Calendar/E-Paper.py b/Calendar/E-Paper.py index 1360310..a43bcc2 100644 --- a/Calendar/E-Paper.py +++ b/Calendar/E-Paper.py @@ -13,25 +13,20 @@ from Assets import path from LoopTimer import LoopTimer import locale from DebugConsole import DebugConsole -from settings import datetime_encoding, language, render_to_display, render_to_file, display_colours, location, api_key, owm_paid_subscription, choosen_design, ical_urls, highlighted_ical_urls, rss_feeds, update_interval, calibrate_hours, crypto_coins, max_loop_count, run_on_hour +from settings import datetime_encoding, language, render_to_display, render_to_file, display_colours, location, api_key, owm_paid_subscription, choosen_design, ical_urls, highlighted_ical_urls, rss_feeds, update_interval, calibrate_hours from MonthOvPanel import MonthOvPanel from DayListPanel import DayListPanel from DayViewPanel import DayViewPanel -from DayFocusListPanel import DayFocusListPanel from MonthViewPanel import MonthViewPanel from AgendaListPanel import AgendaListPanel -from ImageFramePanel import ImageFramePanel import OwmForecasts import IcalEvents import RssParserPosts -import GeckoCrypto all_locales = locale.locale_alias if language.lower() not in all_locales.keys(): - raise Exception( - "The locale for \"%s\" is currently not supported! If you need support, please open an issue on github." % language) -locale.setlocale(locale.LC_ALL, "%s.%s" % ( - all_locales[language.lower()].split('.')[0], datetime_encoding)) + raise Exception("The locale for \"%s\" is currently not supported! If you need support, please open an issue on github." % language) +locale.setlocale(locale.LC_ALL, "%s.%s" % (all_locales[language.lower()].split('.')[0], datetime_encoding)) debug = DebugConsole() output_adapters = [] @@ -52,27 +47,20 @@ if render_to_display: output_adapters.append(epd) available_panels = { - "day-list": DayListPanel, - "month-overview": MonthOvPanel, - "day-view": DayViewPanel, - "day-focus-list": DayFocusListPanel, - "agenda-list": AgendaListPanel, - "month-view": MonthViewPanel, - "image-frame": ImageFramePanel, + "day-list" : DayListPanel, + "month-overview" : MonthOvPanel, + "day-view" : DayViewPanel, + "agenda-list" : AgendaListPanel, + "month-view" : MonthViewPanel } -loop_timer = LoopTimer( - update_interval, run_on_hour=run_on_hour, max_loop_count=max_loop_count) +loop_timer = LoopTimer(update_interval, run_on_hour=True) """Main loop starts from here""" - - def main(): - owm = OwmForecasts.OwmForecasts( - location, api_key, paid_api=owm_paid_subscription) + owm = OwmForecasts.OwmForecasts(location, api_key, paid_api=owm_paid_subscription) events_cal = IcalEvents.IcalEvents(ical_urls, highlighted_ical_urls) rss = RssParserPosts.RssParserPosts(rss_feeds) - crypto = GeckoCrypto.GeckoCrypto(crypto_coins) while True: loop_timer.begin_loop() @@ -83,11 +71,10 @@ def main(): for output in output_adapters: output.calibrate() - if choosen_design in available_panels.keys(): + if choosen_design in available_panels.keys(): design = available_panels[choosen_design]((epd.width, epd.height)) else: - raise ImportError( - "choosen_design must be valid (" + choosen_design + ")") + raise ImportError("choosen_design must be valid (" + choosen_design + ")") debug.print_line("Fetching weather information from open weather map") owm.reload() @@ -101,35 +88,22 @@ def main(): rss.reload() design.add_rssfeed(rss) - debug.print_line('Fetching crypto prices from coin gecko') - crypto.reload() - design.add_crypto(crypto) - debug.print_line("\nStarting to render") for i, output in enumerate(output_adapters): try: output.render(design) - debug.print_line(str(i + 1) + " of " + - str(len(output_adapters)) + " rendered") + debug.print_line(str(i + 1) + " of " + str(len(output_adapters)) + " rendered") except BaseException as ex: - debug.print_err(ex, "Failed to render output " + - str(i + 1) + " of " + str(len(output_adapters))) + debug.print_err(ex, "Failed to render output " + str(i + 1) + " of " + str(len(output_adapters))) debug.print_line("=> Finished rendering" + "\n") loop_timer.end_loop() - - if loop_timer.was_last_loop(): - debug.print_line("Maximum loop count " + - str(loop_timer.loop_count) + " reached, exiting.") - return - sleep_time = loop_timer.time_until_next() - debug.print_line("This loop took " + - str(loop_timer.get_last_duration()) + " to execute.") + + debug.print_line("This loop took " + str(loop_timer.get_last_duration()) + " to execute.") debug.print_line("Sleeping " + str(sleep_time) + " until next loop.") sleep(sleep_time.total_seconds()) - if __name__ == '__main__': main() diff --git a/Calendar/EllipseDesign.py b/Calendar/EllipseDesign.py index 3d74565..5943df2 100644 --- a/Calendar/EllipseDesign.py +++ b/Calendar/EllipseDesign.py @@ -1,14 +1,10 @@ from BoxDesign import BoxDesign from PIL import ImageDraw, ImageOps - class EllipseDesign (BoxDesign): """Redefinition of ImageDraw.Draw.Rectangle""" - def __init__(self, size, fill=None, outline=None, width=0): - super(EllipseDesign, self).__init__( - size, fill=fill, outline=outline, width=width) + super(EllipseDesign, self).__init__(size, fill=fill, outline=outline, width=width) - def __finish_image__(self): - ImageDraw.Draw(self.__image__).ellipse( - self.corners, fill=self.fill, outline=self.outline, width=self.width) + def __finish_image__ (self): + ImageDraw.Draw(self.__image__).ellipse(self.corners, fill=self.fill, outline=self.outline, width=self.width) \ No newline at end of file diff --git a/Calendar/Epd7in5Adapter.py b/Calendar/Epd7in5Adapter.py index f1393a9..c247d79 100644 --- a/Calendar/Epd7in5Adapter.py +++ b/Calendar/Epd7in5Adapter.py @@ -2,12 +2,11 @@ from EpdAdapter import EpdAdapter, DISPLAY_REFRESH, DATA_START_TRANSMISSION_1 from settings import display_colours from PIL import Image, ImageDraw - class Epd7in5Adapter (EpdAdapter): - def __init__(self): + def __init__ (self): super(Epd7in5Adapter, self).__init__(384, 640) - def display_frame(self, frame_buffer): + def display_frame (self, frame_buffer): self.send_command(DATA_START_TRANSMISSION_1) for i in range(0, 30720): temp1 = frame_buffer[i] @@ -31,11 +30,11 @@ class Epd7in5Adapter (EpdAdapter): self.delay_ms(100) self.wait_until_idle() - def get_frame_buffer(self, image): + def get_frame_buffer (self, image): buf = [0x00] * int(self.height * self.width / 8) # Set buffer to value of Python Imaging Library image. # Image must be in mode 1. - image_monocolor = image.convert('L') # with ot withour dithering? + image_monocolor = image.convert('L') #with ot withour dithering? imwidth, imheight = image_monocolor.size if imwidth != self.height or imheight != self.width: raise ValueError('Image must be same dimensions as display \ @@ -45,11 +44,11 @@ class Epd7in5Adapter (EpdAdapter): for y in range(self.width): for x in range(self.height): # Set the bits for the column of pixels at the current position. - if pixels[x, y] >= 240: # White + if pixels[x, y] >= 240: #White buf[int((x + y * self.height) / 8)] |= 0x80 >> (x % 8) return buf - def calibrate(self): + def calibrate (self): for _ in range(2): self.init_render() black = Image.new('1', (self.height, self.width), 'black') @@ -62,4 +61,4 @@ class Epd7in5Adapter (EpdAdapter): print('calibrating white...') self.display_frame(self.get_frame_buffer(white)) self.sleep() - print('Calibration complete') + print('Calibration complete') \ No newline at end of file diff --git a/Calendar/Epd7in5bAdapter.py b/Calendar/Epd7in5bAdapter.py index 726b873..379fab4 100644 --- a/Calendar/Epd7in5bAdapter.py +++ b/Calendar/Epd7in5bAdapter.py @@ -4,17 +4,16 @@ from PIL import Image, ImageDraw from math import sqrt, pow import numpy as np - class Epd7in5bAdapter (EpdAdapter): - def __init__(self): + def __init__ (self): super(Epd7in5bAdapter, self).__init__(384, 640) - def display_frame(self, frame_buffer): + def display_frame (self, frame_buffer): self.send_command(DATA_START_TRANSMISSION_1) for i in range(0, int(self.height / 4 * self.width)): - # the above line had to be modified due to python2 -> python3 - # the issue lies in division, which returns integers in python2 - # but floats in python3 + #the above line had to be modified due to python2 -> python3 + #the issue lies in division, which returns integers in python2 + #but floats in python3 temp1 = frame_buffer[i] j = 0 while (j < 4): @@ -40,8 +39,8 @@ class Epd7in5bAdapter (EpdAdapter): self.delay_ms(100) self.wait_until_idle() - def get_frame_buffer(self, image): - buf = [0x00] * int(self.height * self.width / 4) + def get_frame_buffer (self, image): + buf = [ 0x00 ] * int(self.height * self.width / 4) imwidth, imheight = image.size if imwidth != self.height or imheight != self.width: raise ValueError('Image must be same dimensions as display \ @@ -52,26 +51,24 @@ class Epd7in5bAdapter (EpdAdapter): for y in range(self.height): # Set the bits for the column of pixels at the current # position. - if image_buf[x, y, 1] == 255: # White + if image_buf[x, y, 1] == 255: #White buf[int((y + x * self.height) / 4)] |= 0xC0 >> (y % 4 * 2) - elif image_buf[x, y, 0] == 0: # Black - buf[int((y + x * self.height) / 4) - ] &= ~(0xC0 >> (y % 4 * 2)) - else: # Red - buf[int((y + x * self.height) / 4) - ] &= ~(0xC0 >> (y % 4 * 2)) + elif image_buf[x, y, 0] == 0: #Black + buf[int((y + x * self.height) / 4)] &= ~(0xC0 >> (y % 4 * 2)) + else: #Red + buf[int((y + x * self.height) / 4)] &= ~(0xC0 >> (y % 4 * 2)) buf[int((y + x * self.height) / 4)] |= 0x40 >> (y % 4 * 2) return buf def __prepare_image__(self, image): buffer = np.array(image) - r, g = buffer[:, :, 0], buffer[:, :, 1] - buffer[np.logical_and(r > 220, g > 220)] = [255, 255, 255] - buffer[r > g] = [255, 0, 0] - buffer[r != 255] = [0, 0, 0] + r,g = buffer[:,:,0], buffer[:,:,1] + buffer[np.logical_and(r > 220, g > 220)] = [255,255,255] + buffer[r > g] = [255,0,0] + buffer[r != 255] = [0,0,0] return buffer - def calibrate(self): + def calibrate (self): for _ in range(2): self.init_render() black = Image.new('RGB', (self.height, self.width), 'black') @@ -89,4 +86,4 @@ class Epd7in5bAdapter (EpdAdapter): print('calibrating white...') self.display_frame(self.get_frame_buffer(white)) self.sleep() - print('Calibration complete') + print('Calibration complete') \ No newline at end of file diff --git a/Calendar/EpdAdapter.py b/Calendar/EpdAdapter.py index fa933c1..4817561 100644 --- a/Calendar/EpdAdapter.py +++ b/Calendar/EpdAdapter.py @@ -4,55 +4,53 @@ import RPi.GPIO as GPIO import time from PIL import Image -RST_PIN = 17 -DC_PIN = 25 -CS_PIN = 8 +RST_PIN = 17 +DC_PIN = 25 +CS_PIN = 8 BUSY_PIN = 24 # Commands -PANEL_SETTING = 0x00 -POWER_SETTING = 0x01 -POWER_OFF = 0x02 -POWER_OFF_SEQUENCE_SETTING = 0x03 -POWER_ON = 0x04 -POWER_ON_MEASURE = 0x05 -BOOSTER_SOFT_START = 0x06 -DEEP_SLEEP = 0x07 -DATA_START_TRANSMISSION_1 = 0x10 -DATA_STOP = 0x11 -DISPLAY_REFRESH = 0x12 -IMAGE_PROCESS = 0x13 -LUT_FOR_VCOM = 0x20 -LUT_BLUE = 0x21 -LUT_WHITE = 0x22 -LUT_GRAY_1 = 0x23 -LUT_GRAY_2 = 0x24 -LUT_RED_0 = 0x25 -LUT_RED_1 = 0x26 -LUT_RED_2 = 0x27 -LUT_RED_3 = 0x28 -LUT_XON = 0x29 -PLL_CONTROL = 0x30 -TEMPERATURE_SENSOR_COMMAND = 0x40 -TEMPERATURE_CALIBRATION = 0x41 -TEMPERATURE_SENSOR_WRITE = 0x42 -TEMPERATURE_SENSOR_READ = 0x43 -VCOM_AND_DATA_INTERVAL_SETTING = 0x50 -LOW_POWER_DETECTION = 0x51 -TCON_SETTING = 0x60 -TCON_RESOLUTION = 0x61 -SPI_FLASH_CONTROL = 0x65 -REVISION = 0x70 -GET_STATUS = 0x71 -AUTO_MEASUREMENT_VCOM = 0x80 -READ_VCOM_VALUE = 0x81 -VCM_DC_SETTING = 0x82 +PANEL_SETTING = 0x00 +POWER_SETTING = 0x01 +POWER_OFF = 0x02 +POWER_OFF_SEQUENCE_SETTING = 0x03 +POWER_ON = 0x04 +POWER_ON_MEASURE = 0x05 +BOOSTER_SOFT_START = 0x06 +DEEP_SLEEP = 0x07 +DATA_START_TRANSMISSION_1 = 0x10 +DATA_STOP = 0x11 +DISPLAY_REFRESH = 0x12 +IMAGE_PROCESS = 0x13 +LUT_FOR_VCOM = 0x20 +LUT_BLUE = 0x21 +LUT_WHITE = 0x22 +LUT_GRAY_1 = 0x23 +LUT_GRAY_2 = 0x24 +LUT_RED_0 = 0x25 +LUT_RED_1 = 0x26 +LUT_RED_2 = 0x27 +LUT_RED_3 = 0x28 +LUT_XON = 0x29 +PLL_CONTROL = 0x30 +TEMPERATURE_SENSOR_COMMAND = 0x40 +TEMPERATURE_CALIBRATION = 0x41 +TEMPERATURE_SENSOR_WRITE = 0x42 +TEMPERATURE_SENSOR_READ = 0x43 +VCOM_AND_DATA_INTERVAL_SETTING = 0x50 +LOW_POWER_DETECTION = 0x51 +TCON_SETTING = 0x60 +TCON_RESOLUTION = 0x61 +SPI_FLASH_CONTROL = 0x65 +REVISION = 0x70 +GET_STATUS = 0x71 +AUTO_MEASUREMENT_VCOM = 0x80 +READ_VCOM_VALUE = 0x81 +VCM_DC_SETTING = 0x82 - -class EpdAdapter (DisplayAdapter): +class EpdAdapter (DisplayAdapter): """Generalized adapter for epd7in5 and epd7in5b""" - - def __init__(self, width, height): + def __init__ (self, width, height): super(EpdAdapter, self).__init__(width, height) self.reset_pin = RST_PIN @@ -61,19 +59,19 @@ class EpdAdapter (DisplayAdapter): self.epd_init() - def display_frame(self, frame_buffer): + def display_frame (self, frame_buffer): raise NotImplementedError("Functions needs to be implemented") - def get_frame_buffer(self, image): + def get_frame_buffer (self, image): raise NotImplementedError("Functions needs to be implemented") - def render(self, design): + def render (self, design): self.init_render() time.sleep(5) - + print('Converting image to data and sending it to the display') print('This may take a while...' + '\n') - prepared_image = design.get_image().rotate(270, expand=1).convert("RGB") + prepared_image = design.get_image().rotate(270, expand=1) self.display_frame(self.get_frame_buffer(prepared_image)) # Powering off the E-Paper until the next loop @@ -81,7 +79,7 @@ class EpdAdapter (DisplayAdapter): print('Data sent successfully') print('Powering off the E-Paper until the next loop' + '\n') - def init_render(self): + def init_render (self): if (self.epd_init() != 0): return -1 self.reset() @@ -115,30 +113,30 @@ class EpdAdapter (DisplayAdapter): self.send_data(0x22) self.send_command(TCON_RESOLUTION) - self.send_data(0x02) # source 640 + self.send_data(0x02) #source 640 self.send_data(0x80) - self.send_data(0x01) # gate 384 + self.send_data(0x01) #gate 384 self.send_data(0x80) self.send_command(VCM_DC_SETTING) - self.send_data(0x1E) # decide by LUT file + self.send_data(0x1E) #decide by LUT file - self.send_command(0xe5) # FLASH MODE + self.send_command(0xe5) #FLASH MODE self.send_data(0x03) - def digital_write(self, pin, value): + def digital_write (self, pin, value): GPIO.output(pin, value) - def digital_read(self, pin): + def digital_read (self, pin): return GPIO.input(pin) - def delay_ms(self, delaytime): + def delay_ms (self, delaytime): time.sleep(delaytime / 1000.0) - def spi_transfer(self, data): + def spi_transfer (self, data): self.SPI.writebytes(data) - def epd_init(self): + def epd_init (self): # SPI device, bus = 0, device = 0 self.SPI = spidev.SpiDev(0, 0) #self.SPI.no_cs = True @@ -153,35 +151,30 @@ class EpdAdapter (DisplayAdapter): self.SPI.mode = 0b00 return 0 - def sleep(self): + def sleep (self): self.send_command(POWER_OFF) self.wait_until_idle() self.send_command(DEEP_SLEEP) self.send_data(0xa5) + + def wait_until_idle (self): + while(self.digital_read(self.busy_pin) == 0): # 0: busy, 1: idle + self.delay_ms(100) - def wait_until_idle(self, max_wait_seconds=60): - wait_ms = 100 - count = 0 - while(self.digital_read(self.busy_pin) == 0 and wait_ms * count < max_wait_seconds * 1000): # 0: busy, 1: idle - self.delay_ms(wait_ms) - count += 1 - if wait_ms * count >= max_wait_seconds * 1000: - print("Skipped idle confirmation") - - def reset(self): + def reset (self): self.digital_write(self.reset_pin, GPIO.LOW) # module reset self.delay_ms(200) self.digital_write(self.reset_pin, GPIO.HIGH) self.delay_ms(200) - def send_command(self, command): + def send_command (self, command): self.digital_write(self.dc_pin, GPIO.LOW) # the parameter type is list but not int # so use [command] instead of command - self.spi_transfer([command]) + self.spi_transfer([ command ]) - def send_data(self, data): + def send_data (self, data): self.digital_write(self.dc_pin, GPIO.HIGH) # the parameter type is list but not int # so use [data] instead of data - self.spi_transfer([data]) + self.spi_transfer([ data ]) \ No newline at end of file diff --git a/Calendar/EventListDesign.py b/Calendar/EventListDesign.py index b0c9948..13b602c 100644 --- a/Calendar/EventListDesign.py +++ b/Calendar/EventListDesign.py @@ -5,12 +5,10 @@ from TextFormatter import date_str from DictionaryMapper import get_text from Dictionary import more_events - class EventListDesign (DesignEntity): """Creates a TableDesign filled with event begin date and title""" - - def __init__(self, size, events, text_size=defaultfontsize, line_spacing=0, col_spacing=10, event_prefix_rel_dates=[], event_prefix_func=None, font_family=None, general_color=colors["fg"], background_color=colors["bg"], highlight_color=colors["hl"], show_more_info=False): + def __init__ (self, size, events, text_size = defaultfontsize, line_spacing = 0, col_spacing = 10, event_prefix_rel_dates = [], event_prefix_func = None, font_family = None, general_color = colors["fg"], background_color = colors["bg"], highlight_color = colors["hl"], show_more_info = False): super(EventListDesign, self).__init__(size) self.events = events self.__event_matrix__ = [] @@ -26,25 +24,22 @@ class EventListDesign (DesignEntity): self.event_prefix_func = event_prefix_func self.event_prefix_rel_dates = event_prefix_rel_dates if self.event_prefix_func is None: - self.event_prefix_func = lambda x, y: date_str(x.begin_datetime) + self.event_prefix_func = lambda x, y : date_str(x.begin_datetime) - def __finish_image__(self): - self.visible_event_count = int(int( - self.size[1] + self.line_spacing) // (self.line_spacing + int(self.text_size))) + def __finish_image__ (self): + self.visible_event_count = int(int(self.size[1] + self.line_spacing) // (self.line_spacing + int(self.text_size))) self.__fill_event_matrix__() - - col_hori_alignment = ['right', 'left'] - table_design = TableDesign(self.size, background_color=self.background_color, font=self.font_family, line_spacing=self.line_spacing, col_spacing=self.col_spacing, - matrix=self.__event_matrix__, fontsize=self.text_size, column_horizontal_alignments=col_hori_alignment, mask=False, truncate_cols=False, cell_properties=self.__props_matrix__) + + col_hori_alignment = [ 'right', 'left' ] + table_design = TableDesign(self.size, background_color = self.background_color, font=self.font_family, line_spacing=self.line_spacing, col_spacing=self.col_spacing, matrix=self.__event_matrix__, fontsize = self.text_size, column_horizontal_alignments=col_hori_alignment, mask=False, truncate_cols=False, cell_properties=self.__props_matrix__) self.draw_design(table_design) - - def __get_formatted_event__(self, event, index): - rel_date = None if index < 0 or index >= len( - self.event_prefix_rel_dates) else self.event_prefix_rel_dates[index] + + def __get_formatted_event__ (self, event, index): + rel_date = None if index < 0 or index >= len(self.event_prefix_rel_dates) else self.event_prefix_rel_dates[index] prefix = self.event_prefix_func(event, rel_date) - return [prefix, event.title] - - def __fill_event_matrix__(self): + return [ prefix, event.title ] + + def __fill_event_matrix__ (self): visible_events = self.events if self.show_more_info and len(visible_events) > self.visible_event_count: visible_events = visible_events[:self.visible_event_count - 1] @@ -59,16 +54,16 @@ class EventListDesign (DesignEntity): additional_events_count = len(self.events) - len(visible_events) more_text = " " + get_text(more_events, additional_events_count) if additional_events_count > 0: - self.__event_matrix__.append(["", more_text]) + self.__event_matrix__.append([ "", more_text ]) self.__props_matrix__.append(self.__get_row_props__()) - def __get_row_props__(self, event=None): + def __get_row_props__ (self, event = None): color = self.general_color bg_color = self.background_color if event is not None and event.highlight: color = self.highlight_color cell = { - "color": color, - "background_color": bg_color + "color" : color, + "background_color" : bg_color } - return [cell, cell] + return [ cell, cell ] \ No newline at end of file diff --git a/Calendar/GeckoCrypto.py b/Calendar/GeckoCrypto.py deleted file mode 100644 index 2505edc..0000000 --- a/Calendar/GeckoCrypto.py +++ /dev/null @@ -1,68 +0,0 @@ -from CryptoInterface import CryptoInterface -from datetime import datetime -from CryptoCoin import CryptoCoin -from urllib.request import urlopen -import json -import math - -api_test_url = "https://api.coingecko.com/api/v3/ping" -api_url = "https://api.coingecko.com/api/v3/" -api_metadata_url = api_url + "coins/list" -api_price_url = api_url + "simple/price" -price_currency = "usd" -price_currency_sign = "$" - - -class GeckoCrypto(CryptoInterface): - def __init__(self, coins): - self.coin_ids = coins - self.metadata = None - super(GeckoCrypto, self).__init__() - - def is_available(self): - try: - urlopen(api_test_url) - return True - except: - return False - - def __get_coins__(self): - if len(self.coin_ids) == 0: - return [] - - self.__prepare_metadata__() - coins = [] - for id in self.coin_ids: - try: - data = urlopen(api_price_url + "?include_24hr_change=true&ids=" + - self.metadata[id]['id'] + "&vs_currencies=" + price_currency).read() - dataJSON = json.loads(data.decode('utf-8')) - raw = dataJSON[id][price_currency] - price = math.ceil(raw*100) / 100 - change = dataJSON[id]['usd_24h_change'] - - coins.append(self.__build_coin__(id, price, change)) - except: - print("Gecko-Error [" + id + "]") - return coins - - def __build_coin__(self, id, value, change): - coin = CryptoCoin() - - coin.name = self.metadata[id]['name'] - coin.day_change = round(change, 2) - coin.price = value - - coin.datetime = datetime.now() - coin.fetch_datetime = datetime.now() - coin.currency = price_currency_sign - coin.symbol = self.metadata[id]['symbol'] - - return coin - - def __prepare_metadata__(self): - self.metadata = None - data = urlopen(api_metadata_url).read() - dataJSON = json.loads(data.decode('utf-8')) - self.metadata = {coin['id'].lower( - ): coin for coin in dataJSON if coin['id'].lower() in self.coin_ids} diff --git a/Calendar/HourListDesign.py b/Calendar/HourListDesign.py index f03e55c..b6e43b5 100644 --- a/Calendar/HourListDesign.py +++ b/Calendar/HourListDesign.py @@ -1,5 +1,5 @@ from DesignEntity import DesignEntity -from settings import hours, language, line_thickness +from settings import hours, language from TextDesign import TextDesign from PIL import ImageDraw from Assets import colors, defaultfontsize, fonts @@ -14,62 +14,59 @@ hoursubtext_height = 0.45 event_title_fontsize = defaultfontsize event_title_xpadding = 3 event_title_ypadding = 5 -line_thickness = line_thickness -currenttimeline_thickness = line_thickness +line_thickness = 1 +currenttimeline_thickness = 2 event_title_font = fonts['bold'] - class HourListDesign (DesignEntity): """Hours of a day are listed vertically and resemble a timeline.""" - - def __init__(self, size, first_hour=0, last_hour=23): + def __init__ (self, size, first_hour = 0, last_hour = 23): super(HourListDesign, self).__init__(size) self.first_hour = first_hour self.last_hour = last_hour self.__calc_parameters__() self.events = [] - def add_events(self, events): + def add_events (self, events): self.events.extend(events) - self.events.sort(key=lambda x: x.begin_datetime) + self.events.sort(key=lambda x : x.begin_datetime) - def __finish_image__(self): + def __finish_image__ (self): self.number_columns = self.__get_max_num_simultaneous_events__() self.__draw_lines__() self.__draw_events__() self.__draw_current_time_line__() self.__draw_hour_rows__() - def __calc_parameters__(self): + def __calc_parameters__ (self): self.hour_count = self.last_hour - self.first_hour + 1 self.row_size = (self.size[0], self.size[1] / self.hour_count) - def __get_hour_text__(self, hour): + def __get_hour_text__ (self, hour): if hour <= 12 or hours is "24": return str(hour) else: short = hour - 12 return str(short) if short > 0 else "12" - def __get_ypos_for_time__(self, hour, minute=0): + def __get_ypos_for_time__ (self, hour, minute = 0): return self.__get_height_for_duration__(hour, minute) - self.__get_height_for_duration__(self.first_hour) - def __get_height_for_duration__(self, hours, minutes=0): + def __get_height_for_duration__ (self, hours, minutes = 0): row_height = self.row_size[1] return row_height * (hours + minutes / 60) - def __draw_events__(self): + def __draw_events__ (self): column_events = [] for _ in range(self.number_columns): column_events.append(None) for event in self.events: - column_events = self.__update_columns_events__( - column_events, event) + column_events = self.__update_columns_events__(column_events, event) self.__draw_event__(event, column_events.index(event)) - def __update_columns_events__(self, column_events, new_event): + def __update_columns_events__ (self, column_events, new_event): current_time = new_event.begin_datetime new_event_added = False for index in range(len(column_events)): @@ -80,11 +77,11 @@ class HourListDesign (DesignEntity): new_event_added = True return column_events - def __draw_hour_rows__(self): + def __draw_hour_rows__ (self): for hour in range(self.first_hour, self.last_hour + 1): self.__draw_row__(hour) - def __draw_row__(self, hour): + def __draw_row__ (self, hour): subtext_height = self.row_size[1] * hoursubtext_height sub_fontsize = subtext_height * hoursubtext_fontsize ypadding = hour_ypadding * self.row_size[1] @@ -94,25 +91,22 @@ class HourListDesign (DesignEntity): pos = (0, self.__get_ypos_for_time__(hour) + ypadding) fontsize = size[1] * hour_box_fontsize - txt = TextDesign(size, text=self.__get_hour_text__( - hour), fontsize=fontsize, verticalalignment="bottom", horizontalalignment="center") + txt = TextDesign(size, text=self.__get_hour_text__(hour), fontsize=fontsize, verticalalignment="bottom", horizontalalignment="center") txt.pos = pos self.draw_design(txt) - sub = TextDesign((width, subtext_height), text=self.__get_hour_sub_text__( - hour), fontsize=sub_fontsize, verticalalignment="top", horizontalalignment="center") + sub = TextDesign((width, subtext_height), text=self.__get_hour_sub_text__(hour), fontsize=sub_fontsize, verticalalignment="top", horizontalalignment="center") sub.pos = (0, height + self.__get_ypos_for_time__(hour)) self.draw_design(sub) - def __draw_lines__(self): + def __draw_lines__ (self): for i in range(self.hour_count): ypos = i * self.row_size[1] line_start = (0, ypos) line_end = (self.size[0], ypos) - ImageDraw.Draw(self.__image__).line( - [line_start, line_end], fill=colors["fg"], width=line_thickness) + ImageDraw.Draw(self.__image__).line([ line_start, line_end ], fill=colors["fg"], width=line_thickness) - def __get_hour_sub_text__(self, hour): + def __get_hour_sub_text__ (self, hour): if hours == "12": return "AM" if hour < 12 else "PM" elif language is "de": @@ -121,7 +115,7 @@ class HourListDesign (DesignEntity): return "o'c" return "" - def __draw_event__(self, event, column=0): + def __draw_event__ (self, event, column = 0): xoffset = hourbox_y_width * self.row_size[1] column_width = (self.size[0] - xoffset) / self.number_columns @@ -138,42 +132,36 @@ class HourListDesign (DesignEntity): size = (column_width, time_height + yoffset_correction) if size[1] < 0: - return # Event not in shown time range + return #Event not in shown time range self.__draw_event_block__(pos, size, event) - def __draw_event_block__(self, pos, size, event): + def __draw_event_block__ (self, pos, size, event): box_color = colors["hl"] if event.highlight else colors["fg"] - box = BoxDesign(size, fill=box_color) + box = BoxDesign(size, fill = box_color) box.mask = False box.pos = pos self.draw_design(box) text = event.title text_color = colors["bg"] - textbox_size = (size[0] - event_title_xpadding, - size[1] - event_title_ypadding) - txt = TextDesign(textbox_size, text=text, font=event_title_font, - fontsize=event_title_fontsize, color=text_color, background_color=box_color, wrap=True) + textbox_size = (size[0] - event_title_xpadding, size[1] - event_title_ypadding) + txt = TextDesign(textbox_size, text = text, font=event_title_font, fontsize=event_title_fontsize, color=text_color, background_color=box_color, wrap=True) txt.mask = False - txt.pos = (pos[0] + event_title_xpadding, - pos[1] + event_title_ypadding) + txt.pos = (pos[0] + event_title_xpadding, pos[1] + event_title_ypadding) self.draw_design(txt) half_ypadding = int(event_title_ypadding / 2) line_start = (pos[0] + event_title_xpadding, pos[1] + half_ypadding) - line_end = (pos[0] + size[0] - event_title_xpadding, - pos[1] + half_ypadding) - ImageDraw.Draw(self.__image__).line( - [line_start, line_end], fill=colors["bg"], width=1) + line_end = (pos[0] + size[0] - event_title_xpadding, pos[1] + half_ypadding) + ImageDraw.Draw(self.__image__).line([ line_start, line_end ], fill=colors["bg"], width=1) - def __get_max_num_simultaneous_events__(self): + def __get_max_num_simultaneous_events__ (self): parallelity_count = 1 for index, event in enumerate(self.events): current_parallelity = 1 - # Assumption: Events are ordered chronologically - preceding = self.events[:index] + preceding = self.events[:index] #Assumption: Events are ordered chronologically for pre_event in preceding: if self.__are_simultaneous__(pre_event, event): current_parallelity += 1 @@ -181,19 +169,18 @@ class HourListDesign (DesignEntity): parallelity_count = current_parallelity return parallelity_count - def __are_simultaneous__(self, ev_a, ev_b): + def __are_simultaneous__ (self, ev_a, ev_b): if ev_a.begin_datetime > ev_b.begin_datetime: ev_a, ev_b = ev_b, ev_a - + mes_dur = ev_b.begin_datetime - ev_a.begin_datetime return mes_dur < ev_a.duration - def __draw_current_time_line__(self): + def __draw_current_time_line__ (self): now = datetime.now() ypos = self.__get_ypos_for_time__(now.hour, now.minute) - + line_start = (0, ypos) line_end = (self.size[0], ypos) - ImageDraw.Draw(self.__image__).line( - [line_start, line_end], fill=colors["hl"], width=currenttimeline_thickness) + ImageDraw.Draw(self.__image__).line([ line_start, line_end ], fill=colors["hl"], width=currenttimeline_thickness) \ No newline at end of file diff --git a/Calendar/IcalEvents.py b/Calendar/IcalEvents.py index 7ec6d22..ae348c1 100644 --- a/Calendar/IcalEvents.py +++ b/Calendar/IcalEvents.py @@ -6,10 +6,8 @@ import re from settings import week_starts_on from urllib.request import urlopen - class IcalEvents(CalendarInterface): """Fetches events from ical addresses.""" - def __init__(self, urls, highlighted_urls=None): self.urls = urls self.highlighted_urls = highlighted_urls @@ -51,7 +49,6 @@ class IcalEvents(CalendarInterface): ical = Calendar(decode) for event in ical.events: cal_event = CalendarEvent() - cal_event.calendar_url = calendar cal_event.fetch_datetime = datetime.now(timezone.utc) cal_event.begin_datetime = event.begin.datetime @@ -63,10 +60,8 @@ class IcalEvents(CalendarInterface): cal_event.allday = event.all_day cal_event.rrule = self.__extract_rrule__(event) - cal_event.begin_datetime = cal_event.begin_datetime.astimezone( - None) - cal_event.end_datetime = cal_event.end_datetime.astimezone( - None) + cal_event.begin_datetime = cal_event.begin_datetime.astimezone(None) + cal_event.end_datetime = cal_event.end_datetime.astimezone(None) if cal_event.allday: cal_event = self.__fix_allday__(cal_event) @@ -84,10 +79,8 @@ class IcalEvents(CalendarInterface): begin_utc = event.begin_datetime.astimezone(timezone.utc) end_utc = event.end_datetime.astimezone(timezone.utc) - event.begin_datetime = datetime( - begin_utc.year, begin_utc.month, begin_utc.day, 0, 0, 0, 0, local_tzinfo) - event.end_datetime = datetime( - end_utc.year, end_utc.month, end_utc.day, 0, 0, 0, 0, local_tzinfo) - timedelta(1) + event.begin_datetime = datetime(begin_utc.year, begin_utc.month, begin_utc.day, 0, 0, 0, 0, local_tzinfo) + event.end_datetime = datetime(end_utc.year, end_utc.month, end_utc.day, 0, 0, 0, 0, local_tzinfo) - timedelta(1) event.duration = event.end_datetime - event.begin_datetime return event @@ -102,21 +95,19 @@ class IcalEvents(CalendarInterface): beginAlarmIndex = decode.find(alarm_begin) if beginAlarmIndex >= 0: endAlarmIndex = decode.find(alarm_end, beginAlarmIndex) - decode = decode[:beginAlarmIndex] + \ - decode[endAlarmIndex + - len(alarm_end) + len(lineseparation):] + decode = decode[:beginAlarmIndex] + decode[endAlarmIndex + len(alarm_end) + len(lineseparation):] return decode def __extract_rrule__(self, event): - if re.search('RRULE', str(event)) is None: + if re.search('RRULE',str(event)) is None: return None - return re.search('RRULE:(.+?)\n', str(event)).group(1).rstrip() + return re.search('RRULE:(.+?)\n',str(event)).group(1).rstrip() - def __is_multiday__(self, event): + def __is_multiday__ (self, event): if event.allday and event.duration == timedelta(1): return False return event.begin_datetime.day != event.end_datetime.day or \ event.begin_datetime.month != event.end_datetime.month or \ - event.begin_datetime.year != event.end_datetime.year + event.begin_datetime.year != event.end_datetime.year \ No newline at end of file diff --git a/Calendar/ImageDesign.py b/Calendar/ImageDesign.py deleted file mode 100644 index 2705ac5..0000000 --- a/Calendar/ImageDesign.py +++ /dev/null @@ -1,86 +0,0 @@ -from DesignEntity import DesignEntity -from Assets import path as application_path -from PIL import Image, ExifTags - - -class ImageDesign (DesignEntity): - """Creates a TableDesign filled with rss post - date and title""" - - # fill: "none" : original size, "stretch" : strech to fill, "scale" : scale to fill, "border" : scale until one side touches border - def __init__(self, size, path, fill="none", color="RGBA", dither=None): - super(ImageDesign, self).__init__(size) - self.set_path(path) - self.fill = fill - self.color = color - self.dither = dither - - def set_path(self, path): - path = path.replace('\\', '/') - if path[0] != '/' and ':' not in path[0:3]: - path = application_path + '/' + path - self.path = path - - def __finish_image__(self): - img = Image.open(self.path) - img = img.convert(self.color, dither=self.dither) - - img = self.__fix_orientation__(img) - img = self.__resize_image__(img) - pos = self.__get_centered_position__(img) - - self.__init_image__("#00000000") - self.draw(img, pos) - - def __resize_image__(self, img): - if self.fill is "none": - return img - - if self.fill is "stretch": - img = img.resize(self.size, resample=Image.LANCZOS) - - if self.fill is "scale": - size = self.size - img_proportions = img.width / img.height - if img_proportions < size[0] / size[1]: - size = (size[0], int(size[0] * (1 / img_proportions))) - else: - size = (int(size[1] * img_proportions), size[1]) - img = img.resize(size, resample=Image.LANCZOS) - - if self.fill is "border": - size = self.size - img_proportions = img.width / img.height - if img_proportions < size[0] / size[1]: - size = (int(size[1] * img_proportions), size[1]) - else: - size = (size[0], int(size[0] * (1 / img_proportions))) - img = img.resize(size, resample=Image.LANCZOS) - - return img - - def __get_centered_position__(self, img): - screen_size = self.size - img_size = img.size - - delta_size = [s - i for s, i in zip(screen_size, img_size)] - delta_center_pos = [s / 2 for s in delta_size] - - return delta_center_pos - - def __fix_orientation__(self, img): - if "parsed_exif" not in img.info.keys(): - return img - - for orientation in ExifTags.TAGS.keys(): - if ExifTags.TAGS[orientation] == 'Orientation': - break - exif = img.info["parsed_exif"] - - if exif[orientation] == 3: - img = img.rotate(180, expand=True) - elif exif[orientation] == 6: - img = img.rotate(270, expand=True) - elif exif[orientation] == 8: - img = img.rotate(90, expand=True) - return img diff --git a/Calendar/ImageFileAdapter.py b/Calendar/ImageFileAdapter.py index 50043e0..f03554b 100644 --- a/Calendar/ImageFileAdapter.py +++ b/Calendar/ImageFileAdapter.py @@ -1,18 +1,16 @@ from DisplayAdapter import DisplayAdapter from Assets import path - class ImageFileAdapter (DisplayAdapter): """Saves design to an image file, can be used for debugging""" - - def __init__(self, file_path=""): + def __init__ (self, file_path = ""): super(ImageFileAdapter, self).__init__(384, 640) self.file_path = file_path if self.file_path == "": self.file_path = path - def render(self, design): + def render (self, design): design.get_image().save(self.file_path + 'design_exported.png') - def calibrate(self): - pass + def calibrate (self): + pass \ No newline at end of file diff --git a/Calendar/ImageFramePanel.py b/Calendar/ImageFramePanel.py deleted file mode 100644 index 3cb4e4d..0000000 --- a/Calendar/ImageFramePanel.py +++ /dev/null @@ -1,46 +0,0 @@ -from PanelDesign import PanelDesign -from settings import general_settings -from Assets import supported_img_formats, path as application_path -from os import listdir -from os.path import isfile, join -from ImageDesign import ImageDesign -from random import choice - - -class ImageFramePanel (PanelDesign): - """Converts the display into a digital frame and - shows a slide show of images, iterating on each update""" - - def __init__(self, size): - super(ImageFramePanel, self).__init__(size) - self.overlay_path = self.__complete_path__( - general_settings["overlay-image"]) - self.image_folder_path = self.__complete_path__( - general_settings["image-folder"]) - self.images = self.__extract_valid_img_paths__() - self.__first_render__() - - def __extract_valid_img_paths__(self): - images = [] - for file in listdir(self.image_folder_path): - file_path = join(self.image_folder_path, file).replace('\\', '/') - if isfile(file_path) and self.overlay_path != file_path: - if file.split('.')[-1].upper() in supported_img_formats: - images.append(file_path) - return images - - def __complete_path__(self, path): - path = path.replace('\\', '/') - if path[0] != '/' and ':' not in path[0:3]: - path = join(application_path, path) - return path - - def __first_render__(self): - current_image = choice(self.images) - img = ImageDesign(self.size, current_image, fill="scale", color="1") - self.draw_design(img) - - if self.overlay_path != "": - overlay = ImageDesign(self.size, self.overlay_path) - overlay.__finish_image__() - self.__image__.alpha_composite(overlay.__image__) diff --git a/Calendar/LoopTimer.py b/Calendar/LoopTimer.py index 76e093c..630dd3a 100644 --- a/Calendar/LoopTimer.py +++ b/Calendar/LoopTimer.py @@ -3,52 +3,39 @@ from datetime import datetime, timedelta min_sleep_minutes = 0 max_history_entries = 25 - class LoopTimer (object): """Manages loop times and sleeps until next loop.""" - - def __init__(self, loop_interval, run_on_hour=False, max_loop_count=0): + def __init__ (self, loop_interval, run_on_hour = False): self.interval = int(str(loop_interval)) self.on_hour = run_on_hour self.loop_history = [] - self.loop_count = 0 - self.max_loop_count = int(str(max_loop_count)) - def begin_loop(self): + def begin_loop (self): begin_time = datetime.now() - print('\n__________Starting new loop [' + str(self.loop_count) + ']__________') + print('\n__________Starting new loop__________') print('Datetime: ' + str(begin_time) + '\n') self.__add_beginning__(begin_time) - def __add_beginning__(self, time): + def __add_beginning__ (self, time): self.loop_history.append((time,)) if len(self.loop_history) > max_history_entries: dif = len(self.loop_history) - max_history_entries self.loop_history = self.loop_history[dif:] - def __add_ending__(self, time): + def __add_ending__ (self, time): current = self.get_current() self.loop_history[-1] = (current[0], time) - def end_loop(self): + def end_loop (self): end_time = datetime.now() self.__add_ending__(end_time) - - self.loop_count += 1 - while self.loop_count > 86400: - self.loop_count -= 86400 - def was_last_loop(self): - if self.max_loop_count == 0: - return False - return self.max_loop_count <= self.loop_count - - def get_current(self): + def get_current (self): return self.loop_history[-1] - def time_until_next(self): + def time_until_next (self): interval_duration = timedelta(minutes=self.interval) loop_duration = self.get_last_duration() sleep_time = interval_duration - loop_duration @@ -61,19 +48,19 @@ class LoopTimer (object): sleep_time = timedelta(0, 0, 0, 0, min_sleep_minutes) return sleep_time - def get_last_duration(self): + def get_last_duration (self): if len(self.loop_history) == 0: return begin, end = self.loop_history[-1] return end - begin - def get_time_to_next_hour(self): + def get_time_to_next_hour (self): cur = datetime.now() rounded = datetime(cur.year, cur.month, cur.day, cur.hour) next_hour_time = rounded + timedelta(hours=1) - return next_hour_time - datetime.now() + return next_hour_time - datetime.now() - def is_new_hour_loop(self): + def is_new_hour_loop (self): if len(self.loop_history) < 2: return False previous_loop = self.loop_history[-2] @@ -82,4 +69,4 @@ class LoopTimer (object): if previous_loop[0].hour != current_loop[0].hour: return True else: - return False + return False \ No newline at end of file diff --git a/Calendar/MonthBlockDesign.py b/Calendar/MonthBlockDesign.py index 56677c5..d0e6beb 100644 --- a/Calendar/MonthBlockDesign.py +++ b/Calendar/MonthBlockDesign.py @@ -11,12 +11,10 @@ dayhighlightboxsize = (0.143, 0.14) daynumbersize = daynumberboxsize[0] * 0.45 day_number_ypadding = -0.002 - class MonthBlockDesign (DesignEntity): """Creates a view containing one week of the month in one row""" - - def __init__(self, size, datetime_month, highlight_today=False): + def __init__(self, size, datetime_month, highlight_today = False): super(MonthBlockDesign, self).__init__(size, mask=True) self.month = datetime_month.month self.year = datetime_month.year @@ -26,53 +24,49 @@ class MonthBlockDesign (DesignEntity): def __finish_image__(self): self.__draw_month_overview__() - def __draw_month_overview__(self): + def __draw_month_overview__ (self): """Using the built-in calendar function, draw icons for each number of the month (1,2,3,...29,30,31)""" cal = callib.monthcalendar(self.year, self.month) for week in cal: for numbers in week: - self.__draw_day_number__(numbers, self.get_day_pos( - cal.index(week), week.index(numbers))) - + self.__draw_day_number__(numbers, self.get_day_pos(cal.index(week), week.index(numbers))) + if self.highlight_today: - self.__draw_highlight_box__(self.__abs_pos__( - dayhighlightboxsize), self.__get_today_box_pos__(), width=3) + self.__draw_highlight_box__(self.__abs_pos__(dayhighlightboxsize), self.__get_today_box_pos__(), width=3) - def __draw_highlight_box__(self, size, pos, color=colors["fg"], width=1): - design = BoxDesign(size, outline=color, width=width) + def __draw_highlight_box__ (self, size, pos, color=colors["fg"], width=1): + design = BoxDesign(size, outline=color, width = width) design.pos = pos self.draw_design(design) - def __draw_day_number__(self, number, pos): + def __draw_day_number__ (self, number, pos): if number <= 0: return - txt = TextDesign(self.__abs_pos__(daynumberboxsize), fontsize=daynumbersize * - self.size[0], text=str(number), verticalalignment="center", horizontalalignment="center") + txt = TextDesign(self.__abs_pos__(daynumberboxsize), fontsize=daynumbersize * self.size[0], text=str(number), verticalalignment="center", horizontalalignment="center") txt.pos = (pos[0], pos[1] + day_number_ypadding * self.size[1]) self.draw_design(txt) - - def get_day_pos(self, week_in_month, day_of_week, rel_pos=(0, 0)): + + def get_day_pos (self, week_in_month, day_of_week, rel_pos=(0,0)): maxwidth, maxheight = self.size partialwidth = maxwidth / 7 partialheight = maxheight / 5 return (int(rel_pos[0] + day_of_week * partialwidth), int(rel_pos[1] + week_in_month * partialheight)) - - def __get_today_box_pos__(self): - x, y = self.get_day_pos(self.__get_week_of_month__( - datetime.now()), self.__get_day_of_week__(datetime.now())) + + def __get_today_box_pos__ (self): + x, y = self.get_day_pos(self.__get_week_of_month__(datetime.now()), self.__get_day_of_week__(datetime.now())) return (x, int(y + (self.__abs_pos__(daynumberboxsize)[1] - self.__abs_pos__(dayhighlightboxsize)[1]) / 2)) - - def __get_week_of_month__(self, date): + + def __get_week_of_month__ (self, date): for wof, week in enumerate(callib.monthcalendar(date.year, date.month)): if date.day in week: return wof return 0 - def __get_day_of_week__(self, date): + def __get_day_of_week__ (self, date): return self.__week_days__.index(date.strftime("%a")) - def __get_week_days_ordered__(self): + def __get_week_days_ordered__ (self): cur_weekday = datetime.now().weekday() correction = -cur_weekday if week_starts_on == "Sunday": @@ -80,18 +74,17 @@ class MonthBlockDesign (DesignEntity): weekdays = [] for i in range(7): - weekdays.append( - (datetime.now() + timedelta(days=(i + correction))).strftime("%a")) + weekdays.append((datetime.now() + timedelta(days=(i + correction))).strftime("%a")) return weekdays - def __abs_pos__(self, pos, size=None): + def __abs_pos__ (self, pos, size = None): if size is None: size = self.size return (int(pos[0] * size[0]), int(pos[1] * size[1])) - def get_real_height(self): + def get_real_height (self): weeks_in_month = callib.monthcalendar(self.year, self.month) num_size = self.__abs_pos__(daynumberboxsize) num_pos = self.get_day_pos(len(weeks_in_month) - 1, 6) - return num_size[1] + num_pos[1] + return num_size[1] + num_pos[1] \ No newline at end of file diff --git a/Calendar/MonthOvPanel.py b/Calendar/MonthOvPanel.py index 437a997..b935b8f 100644 --- a/Calendar/MonthOvPanel.py +++ b/Calendar/MonthOvPanel.py @@ -12,10 +12,8 @@ from MonthBlockDesign import MonthBlockDesign, daynumberboxsize from EventListDesign import EventListDesign from RssPostListDesign import RssPostListDesign from settings import general_settings -from CryptoListDesign import CryptoListDesign - -weatherheadersize = (1, 0.113) +weatherheadersize = (1,0.113) monthboxsize = (1, 0.085) monthtextsize = monthboxsize[1] * 0.75 monthplace = (0, 0.11 - weatherheadersize[1]) @@ -29,19 +27,17 @@ weekdaytextpadding = -0.001 weekrownameboxsize = (0.143, 0.044) eventcirclehorizontalsize = 0.100 - class MonthOvPanel (PanelDesign): """Overview that focuses on the current month and some additional information in the bottom.""" - - def __init__(self, size): + def __init__ (self, size): super(MonthOvPanel, self).__init__(size) self.weather_header_height = 0 if general_settings["weather-info"]: self.weather_header_height = self.size[1] * weatherheadersize[1] self.__first_render__() - def __first_render__(self): + def __first_render__ (self): if week_starts_on == "Monday": callib.setfirstweekday(callib.MONDAY) elif week_starts_on == "Sunday": @@ -54,135 +50,102 @@ class MonthOvPanel (PanelDesign): if general_settings["weather-info"]: self.__draw_seperator__() - self.month_block = MonthBlockDesign(self.__abs_pos__( - monthovsize), datetime.now(), highlight_today=True) + self.month_block = MonthBlockDesign(self.__abs_pos__(monthovsize), datetime.now(), highlight_today = True) pos = self.__abs_pos__(monthovposition) pos = (pos[0], pos[1] + self.weather_header_height) self.month_block.pos = pos self.draw_design(self.month_block) - def add_weather(self, weather): + def add_weather (self, weather): if general_settings["weather-info"] == False: return - self.draw_design(WeatherHeaderDesign( - self.__abs_pos__(weatherheadersize), weather)) + self.draw_design(WeatherHeaderDesign(self.__abs_pos__(weatherheadersize), weather)) - def add_rssfeed(self, rss): + def add_rssfeed (self, rss): if general_settings["info-area"] is "rss": self.__draw_rss_post_list_to_bottom__(rss) - def add_crypto(self, crypto): - if general_settings["info-area"] is "crypto": - self.__draw_crypto_post_list_to_bottom__(crypto) - - def add_tasks(self, tasks): - pass - - def add_calendar(self, calendar): + def add_calendar (self, calendar): if general_settings["highlight-event-days"]: - month_events = list(set([(event.begin_datetime.day, event.begin_datetime.month, - event.begin_datetime.year) for event in calendar.get_month_events()])) + month_events = list(set([ (event.begin_datetime.day, event.begin_datetime.month, event.begin_datetime.year) for event in calendar.get_month_events()])) for event in month_events: self.__draw_highlight_event_day__(event) if general_settings["info-area"] is "events": self.__draw_event_list_to_bottom__(calendar) - def __draw_rss_post_list_to_bottom__(self, rss): + def __draw_rss_post_list_to_bottom__ (self, rss): month_pos = self.__abs_pos__(monthovposition) month_height = self.month_block.get_real_height() - size = (self.size[0], self.size[1] - (month_pos[1] + - month_height + self.weather_header_height)) + size = (self.size[0], self.size[1] - (month_pos[1] + month_height + self.weather_header_height)) info_list = RssPostListDesign(size, rss) - info_list.pos = ( - int(month_pos[0]), month_pos[1] + month_height + self.weather_header_height) + info_list.pos = (int(month_pos[0]), month_pos[1] + month_height + self.weather_header_height) self.draw_design(info_list) - def __draw_crypto_post_list_to_bottom__(self, crypto): + def __draw_event_list_to_bottom__ (self, calendar): month_pos = self.__abs_pos__(monthovposition) month_height = self.month_block.get_real_height() - size = (self.size[0], self.size[1] - (month_pos[1] + - month_height + self.weather_header_height)) - - info_list = CryptoListDesign(size, crypto) - list_height = info_list.get_estimated_height() - info_list.pos = (int(month_pos[0]), month_pos[1] + month_height + - self.weather_header_height + (size[1] - list_height)) - self.draw_design(info_list) - - def __draw_event_list_to_bottom__(self, calendar): - month_pos = self.__abs_pos__(monthovposition) - month_height = self.month_block.get_real_height() - size = (self.size[0], self.size[1] - (month_pos[1] + - month_height + self.weather_header_height)) + size = (self.size[0], self.size[1] - (month_pos[1] + month_height + self.weather_header_height)) events = calendar.get_upcoming_events() info_list = EventListDesign(size, events) - info_list.pos = (int(month_pos[0]), int( - month_pos[1] + month_height + self.weather_header_height)) + info_list.pos = (int(month_pos[0]), int(month_pos[1] + month_height + self.weather_header_height)) self.draw_design(info_list) - def __draw_highlight_event_day__(self, date): + def __draw_highlight_event_day__ (self, date): first_month_week = datetime(date[2], date[1], 1).isocalendar()[1] cur_date = datetime(date[2], date[1], date[0]) side_length = int(eventcirclehorizontalsize * self.size[0]) - circle_size = (side_length, side_length) - pos = self.month_block.get_day_pos(cur_date.isocalendar( - )[1] - first_month_week, self.__get_day_of_week__(cur_date), rel_pos=self.month_block.pos) - place_size = (self.month_block.size[0] * daynumberboxsize[0], - self.month_block.size[1] * daynumberboxsize[1]) - pos = (int(pos[0] + (place_size[0] - circle_size[0]) / 2), - int(pos[1] + (place_size[1] - circle_size[1]) / 2)) + circle_size = (side_length,side_length) + pos = self.month_block.get_day_pos(cur_date.isocalendar()[1] - first_month_week, self.__get_day_of_week__(cur_date), rel_pos = self.month_block.pos) + place_size = (self.month_block.size[0] * daynumberboxsize[0], self.month_block.size[1] * daynumberboxsize[1]) + pos = (int(pos[0] + (place_size[0] - circle_size[0]) / 2), int(pos[1] + (place_size[1] - circle_size[1]) / 2)) self.__draw_highlight_circle__(circle_size, pos, 'red', width=2) - def __abs_pos__(self, pos, size=None): + def __abs_pos__ (self, pos, size = None): if size is None: size = self.size return (int(pos[0] * size[0]), int(pos[1] * size[1])) - def __draw_seperator__(self): + def __draw_seperator__ (self): """Draw a line seperating the weather and Calendar section""" - ImageDraw.Draw(self.__image__).line([self.__abs_pos__( - seperatorplace), self.__abs_pos__((1, seperatorplace[1]))], fill='red', width=5) + ImageDraw.Draw(self.__image__).line([ self.__abs_pos__(seperatorplace), self.__abs_pos__((1, seperatorplace[1])) ], fill='red', width=5) - def __draw_month_name__(self): + def __draw_month_name__ (self): """Draw the icon with the current month's name""" month = datetime.now().strftime("%B") - txt = TextDesign(self.__abs_pos__(monthboxsize), fontsize=monthtextsize * - self.size[1], text=month, verticalalignment="center", horizontalalignment="center") + txt = TextDesign(self.__abs_pos__(monthboxsize), fontsize=monthtextsize * self.size[1], text=month, verticalalignment="center", horizontalalignment="center") pos = self.__abs_pos__(monthplace) txt.pos = (pos[0], pos[1] + self.weather_header_height) self.draw_design(txt) - def __draw_week_row__(self): + def __draw_week_row__ (self): for day_of_week, day in enumerate(self.__week_days__): - txt = TextDesign(self.__abs_pos__(weekrownameboxsize), fontsize=weekdaytextsize * - self.size[1], text=str(day), verticalalignment="center", horizontalalignment="center") + txt = TextDesign(self.__abs_pos__(weekrownameboxsize), fontsize=weekdaytextsize * self.size[1], text=str(day), verticalalignment="center", horizontalalignment="center") pos = self.__get_week_day_pos__(day_of_week) txt.pos = (pos[0], pos[1] + weekdaytextpadding * self.size[1]) self.draw_design(txt) + + self.__draw_highlight_box__(self.__abs_pos__(weekrownameboxsize), self.__get_week_day_pos__(self.__get_day_of_week__(datetime.now())), width=1) - self.__draw_highlight_box__(self.__abs_pos__(weekrownameboxsize), self.__get_week_day_pos__( - self.__get_day_of_week__(datetime.now())), width=1) - - def __get_week_day_pos__(self, day_of_week): + def __get_week_day_pos__ (self, day_of_week): maxwidth, _ = self.__abs_pos__(monthovsize) partialwidth = maxwidth / 7 posx, posy = self.__abs_pos__(weekdayrowpos) return (int(posx + day_of_week * partialwidth), int(posy + self.weather_header_height)) - def __draw_highlight_box__(self, size, pos, color=colors["fg"], width=1): - design = BoxDesign(size, outline=color, width=width) + def __draw_highlight_box__ (self, size, pos, color = colors["fg"], width = 1): + design = BoxDesign(size, outline=color, width = width) design.pos = pos self.draw_design(design) - def __draw_highlight_circle__(self, size, pos, color=colors["fg"], width=1): - design = EllipseDesign(size, outline=color, width=width) + def __draw_highlight_circle__ (self, size, pos, color = colors["fg"], width = 1): + design = EllipseDesign(size, outline=color, width = width) design.pos = pos self.draw_design(design) - def __get_week_days_ordered__(self): + def __get_week_days_ordered__ (self): cur_weekday = datetime.now().weekday() correction = -cur_weekday if week_starts_on == "Sunday": @@ -190,10 +153,9 @@ class MonthOvPanel (PanelDesign): weekdays = [] for i in range(7): - weekdays.append( - (datetime.now() + timedelta(days=(i + correction))).strftime("%a")) + weekdays.append((datetime.now() + timedelta(days=(i + correction))).strftime("%a")) return weekdays - def __get_day_of_week__(self, date): - return self.__week_days__.index(date.strftime("%a")) + def __get_day_of_week__ (self, date): + return self.__week_days__.index(date.strftime("%a")) \ No newline at end of file diff --git a/Calendar/MonthViewPanel.py b/Calendar/MonthViewPanel.py index 26b15f0..ff81348 100644 --- a/Calendar/MonthViewPanel.py +++ b/Calendar/MonthViewPanel.py @@ -1,5 +1,5 @@ from PanelDesign import PanelDesign -from settings import general_settings, week_starts_on, line_thickness +from settings import general_settings, week_starts_on from PIL import ImageDraw from datetime import date from Assets import colors @@ -8,19 +8,16 @@ from TableDesign import TableDesign from DayBoxDesign import DayBoxDesign from RssPostListDesign import RssPostListDesign from WeatherHeaderDesign import WeatherHeaderDesign -from CryptoListDesign import CryptoListDesign weather_height = 0.113 info_height = 0.25 info_padding = 5 -seperator_width = line_thickness - +seperator_width = 3 class MonthViewPanel (PanelDesign): """Displays a grid of the day of the current month with detailed event descriptions.""" - - def __init__(self, size, month=None, year=None): + def __init__(self, size, month = None, year = None): super(MonthViewPanel, self).__init__(size) self.day_table = [] self.month = month @@ -35,20 +32,18 @@ class MonthViewPanel (PanelDesign): def __init_sizes__(self): self.weather_height = 0 self.info_height = 0 - if general_settings["info-area"] in ["events", "rss", "crypto"]: + if general_settings["info-area"] in ["events", "rss"]: self.info_height = info_height if general_settings["weather-info"]: self.weather_height = weather_height self.day_area_height = 1 - self.weather_height - self.info_height self.day_area_ypos = self.weather_height - self.week_count = self.__get_week_count__() - area_height = self.size[1] * self.day_area_height area_width = self.size[0] - self.day_box_size = (area_width / 7, area_height / self.week_count) + self.day_box_size = (area_width / 7, area_height) - def add_weather(self, weather): + def add_weather (self, weather): if general_settings["weather-info"] == False: return size = (self.size[0], self.size[1] * self.weather_height) @@ -57,7 +52,7 @@ class MonthViewPanel (PanelDesign): self.draw_design(header) self.__draw_seperator__(size[1], colors["hl"]) - def add_calendar(self, calendar): + def add_calendar (self, calendar): self.__add_calendar_to_days__(calendar) def __add_calendar_to_days__(self, calendar): @@ -66,7 +61,7 @@ class MonthViewPanel (PanelDesign): if day != None: day.add_calendar(calendar) - def add_rssfeed(self, rss): + def add_rssfeed (self, rss): if general_settings["info-area"] != "rss": return @@ -77,36 +72,22 @@ class MonthViewPanel (PanelDesign): rss.pos = pos self.draw_design(rss) - def add_tasks(self, tasks): + def add_taks (self, tasks): pass - def add_crypto(self, crypto): - if general_settings["info-area"] == "crypto": - self.__draw_crypto__(crypto) - - def __draw_crypto__(self, crypto): - size = (self.size[0], self.size[1] * self.info_height) - pos = (0, self.size[1] - size[1]) - - crypto = CryptoListDesign(size, crypto) - crypto.pos = (pos[0], pos[1] + (size[1] - - crypto.get_estimated_height())) - self.draw_design(crypto) - def __finish_panel__(self): self.__draw_days__() def __draw_days__(self): size = (self.size[0], self.size[1] * self.day_area_height) - pos = (0, self.size[1] * self.day_area_ypos) + pos = (0, self.size[0] * self.day_area_ypos) - table = TableDesign(size, matrix=self.day_table) + table = TableDesign(size, matrix = self.day_table) table.pos = pos self.draw_design(table) - def __draw_seperator__(self, height, color): - ImageDraw.Draw(self.__image__).line( - [(0, height * self.size[1]), (self.size[0], height * self.size[1])], fill=color, width=seperator_width) + def __draw_seperator__ (self, height, color): + ImageDraw.Draw(self.__image__).line([ (0, height * self.size[1]), (self.size[0], height * self.size[1]) ], fill=color, width=seperator_width) def __init_day_boxes__(self): if week_starts_on == "Monday": @@ -124,10 +105,6 @@ class MonthViewPanel (PanelDesign): if day == None or day == 0: return None - design = DayBoxDesign(self.day_box_size, date( - self.year, self.month, int(day))) + design = DayBoxDesign(self.day_box_size, date(self.year, self.month, int(day))) - return design - - def __get_week_count__(self): - return len(callib.monthcalendar(self.year, self.month)) + return design \ No newline at end of file diff --git a/Calendar/OwmForecasts.py b/Calendar/OwmForecasts.py index f1be14b..dbc03ed 100644 --- a/Calendar/OwmForecasts.py +++ b/Calendar/OwmForecasts.py @@ -5,19 +5,16 @@ from datetime import datetime from settings import units, language from Translator import translate - class OwmForecasts (WeatherInterface): """Fetches weather through the Openweathermap-api.""" - - def __init__(self, location, api_key, paid_api=False): + def __init__ (self, location, api_key, paid_api=False): self.subscription = "pro" if paid_api else None self.api_key = api_key self.units = units self.location = location - self.api = pyowm.OWM( - self.api_key, subscription_type=self.subscription, language=language) + self.api = pyowm.OWM(self.api_key, subscription_type=self.subscription, language=language) - def is_available(self): + def is_available (self): try: return self.api.is_API_online() except: @@ -26,7 +23,7 @@ class OwmForecasts (WeatherInterface): def reload(self): pass - def get_today_forecast(self, location=None): + def get_today_forecast (self, location=None): if self.is_available() is False: return None @@ -40,7 +37,7 @@ class OwmForecasts (WeatherInterface): except: return None - def get_forecast_in_days(self, offset_by_days, location=None): + def get_forecast_in_days (self, offset_by_days, location=None): if offset_by_days is 0: return self.get_today_forecast(location) @@ -53,49 +50,37 @@ class OwmForecasts (WeatherInterface): target_weather = forecast.get_forecast().get_weathers()[-1] return self.__get_forecast_from_weather__(target_weather, location=location) - except: # only allowed for paid membership + except: # only allowed for paid membership return None - def __get_forecast_from_weather__(self, weather, location): + def __get_forecast_from_weather__ (self, weather, location): forecast_object = WeatherForecast() forecast_object.units = self.units forecast_object.fetch_datetime = datetime.now() forecast_object.location = location - forecast_object.datetime = weather.get_reference_time( - timeformat='date') + forecast_object.datetime = weather.get_reference_time(timeformat='date') forecast_object.icon = weather.get_weather_icon_name() forecast_object.air_humidity = str(weather.get_humidity()) forecast_object.clouds = str(weather.get_clouds()) - forecast_object.short_description = translate( - str(weather.get_status())) - forecast_object.detailed_description = str( - weather.get_detailed_status()) + forecast_object.short_description = translate(str(weather.get_status())) + forecast_object.detailed_description = str(weather.get_detailed_status()) forecast_object.air_pressure = str(weather.get_pressure()['press']) - if 'deg' in weather.get_wind().keys(): - forecast_object.wind_deg = str(int(weather.get_wind()['deg'])) + forecast_object.wind_deg = str(int(weather.get_wind()['deg'])) if forecast_object.units == "metric": - forecast_object.air_temperature = str( - int(weather.get_temperature(unit='celsius')['temp'])) - forecast_object.wind_speed = str( - int(weather.get_wind()['speed'])) # kmh + forecast_object.air_temperature = str(int(weather.get_temperature(unit='celsius')['temp'])) + forecast_object.wind_speed = str(int(weather.get_wind()['speed'])) #kmh if forecast_object.units == "aviation": - forecast_object.air_temperature = str( - int(weather.get_temperature(unit='celsius')['temp'])) - forecast_object.wind_speed = str( - int(weather.get_wind()['speed'] * 1.94384)) # knots + forecast_object.air_temperature = str(int(weather.get_temperature(unit='celsius')['temp'])) + forecast_object.wind_speed = str(int(weather.get_wind()['speed'] * 1.94384)) #knots if forecast_object.units == "imperial": - forecast_object.air_temperature = str( - int(weather.get_temperature('fahrenheit')['temp'])) - forecast_object.wind_speed = str( - int(weather.get_wind()['speed'] * 0.621)) # mph + forecast_object.air_temperature = str(int(weather.get_temperature('fahrenheit')['temp'])) + forecast_object.wind_speed = str(int(weather.get_wind()['speed'] * 0.621)) #mph - forecast_object.sunrise = datetime.fromtimestamp( - int(weather.get_sunrise_time(timeformat='unix'))) - forecast_object.sunset = datetime.fromtimestamp( - int(weather.get_sunset_time(timeformat='unix'))) + forecast_object.sunrise = datetime.fromtimestamp(int(weather.get_sunrise_time(timeformat='unix'))) + forecast_object.sunset = datetime.fromtimestamp(int(weather.get_sunset_time(timeformat='unix'))) return forecast_object diff --git a/Calendar/PanelDesign.py b/Calendar/PanelDesign.py index a360ef8..53e65dc 100644 --- a/Calendar/PanelDesign.py +++ b/Calendar/PanelDesign.py @@ -3,28 +3,23 @@ from TechnicalDataDesign import TechnicalDataDesign from settings import print_technical_data from datetime import datetime - class PanelDesign (DesignEntity): """Defined general interface for panel designs.""" - - def __init__(self, size): + def __init__ (self, size): super(PanelDesign, self).__init__(size) self.start_timestamp = datetime.now() - def add_weather(self, weather): - pass + def add_weather (self, weather): + raise NotImplementedError("Functions needs to be implemented") - def add_calendar(self, calendar): - pass + def add_calendar (self, calendar): + raise NotImplementedError("Functions needs to be implemented") - def add_rssfeed(self, rss): - pass + def add_rssfeed (self, rss): + raise NotImplementedError("Functions needs to be implemented") - def add_tasks(self, tasks): - pass - - def add_crypto(self, crypto): - pass + def add_taks (self, tasks): + raise NotImplementedError("Functions needs to be implemented") def __finish_panel__(self): pass @@ -33,7 +28,6 @@ class PanelDesign (DesignEntity): self.__finish_panel__() if print_technical_data: - td = TechnicalDataDesign( - self.size, self.start_timestamp, datetime.now()) + td = TechnicalDataDesign(self.size, self.start_timestamp, datetime.now()) td.mask = True - self.draw_design(td) + self.draw_design(td) \ No newline at end of file diff --git a/Calendar/RssInterface.py b/Calendar/RssInterface.py index 0bb03c8..9fe8c14 100644 --- a/Calendar/RssInterface.py +++ b/Calendar/RssInterface.py @@ -1,10 +1,8 @@ from DataSourceInterface import DataSourceInterface from datetime import datetime, timezone, timedelta - class RssInterface(DataSourceInterface): """Interface for fetching and processing rss post information.""" - def __init__(self): self.loaded_posts = [] @@ -24,7 +22,7 @@ class RssInterface(DataSourceInterface): return self.get_day_posts(datetime.now()) def get_day_posts(self, day): - return self.__get_posts_to_filter__(lambda x: x.datetime.strftime('%d-%m-%y') == day.strftime('%d-%m-%y')) + return self.__get_posts_to_filter__(lambda x : x.datetime.strftime('%d-%m-%y') == day.strftime('%d-%m-%y')) def __get_posts_to_filter__(self, post_filter): if self.loaded_posts is None: @@ -32,4 +30,4 @@ class RssInterface(DataSourceInterface): return [post for post in self.loaded_posts if post_filter(post)] def __sort_posts__(self): - self.loaded_posts.sort(key=lambda x: x.datetime, reverse=True) + self.loaded_posts.sort(key=lambda x : x.datetime, reverse=True) \ No newline at end of file diff --git a/Calendar/RssParserPosts.py b/Calendar/RssParserPosts.py index 5c415b4..11089fc 100644 --- a/Calendar/RssParserPosts.py +++ b/Calendar/RssParserPosts.py @@ -6,10 +6,8 @@ from urllib.request import urlopen max_range_days = 14 - class RssParserPosts (RssInterface): """Fetches posts from url-addresses via rss parser.""" - def __init__(self, urls): self.urls = urls super(RssParserPosts, self).__init__() @@ -54,4 +52,5 @@ class RssParserPosts (RssInterface): def __get_webpage__(self, link): start_index = link.find('://') + 3 end_index = link[start_index:].find('/') + start_index - return link[start_index: end_index] + return link[start_index : end_index] + \ No newline at end of file diff --git a/Calendar/RssPost.py b/Calendar/RssPost.py index b50505b..7851d6b 100644 --- a/Calendar/RssPost.py +++ b/Calendar/RssPost.py @@ -1,10 +1,9 @@ class RssPost(object): """Defines a rss post, independent of any implementation""" - - def __init__(self): + def __init__ (self): self.title = None self.description = None self.source = None self.datetime = None - self.fetch_datetime = None + self.fetch_datetime = None \ No newline at end of file diff --git a/Calendar/RssPostListDesign.py b/Calendar/RssPostListDesign.py index f4e06ea..724c915 100644 --- a/Calendar/RssPostListDesign.py +++ b/Calendar/RssPostListDesign.py @@ -2,35 +2,32 @@ from DesignEntity import DesignEntity from TableDesign import TableDesign from Assets import defaultfontsize - class RssPostListDesign (DesignEntity): """Creates a TableDesign filled with rss post date and title""" - - def __init__(self, size, rssfeed, text_size=defaultfontsize): + def __init__ (self, size, rssfeed, text_size = defaultfontsize): super(RssPostListDesign, self).__init__(size) self.rssfeed = rssfeed self.__post_matrix__ = [] self.text_size = text_size - def __finish_image__(self): + def __finish_image__ (self): self.__fill_post_matrix__() - table_design = TableDesign(self.size, line_spacing=2, col_spacing=3, matrix=self.__post_matrix__, - fontsize=self.text_size, mask=False, wrap=True, truncate_rows=True) + table_design = TableDesign(self.size, line_spacing=2, col_spacing=3, matrix=self.__post_matrix__, fontsize = self.text_size, mask=False, wrap=True, truncate_rows=True) self.draw_design(table_design) - - def __get_formatted_post__(self, post): + + def __get_formatted_post__ (self, post): date = post.datetime.strftime('%d %b') date = self.__remove_leading_zero__(date) - return ['', '•', post.title] + return [ '', '•', post.title ] def __remove_leading_zero__(self, text): while text[0] is '0': text = text[1:] return text - - def __fill_post_matrix__(self): + + def __fill_post_matrix__ (self): for post in self.rssfeed.get_latest_posts(): row = self.__get_formatted_post__(post) - self.__post_matrix__.append(row) + self.__post_matrix__.append(row) \ No newline at end of file diff --git a/Calendar/SingelDayEventListDesign.py b/Calendar/SingelDayEventListDesign.py index f3d7942..90f3547 100644 --- a/Calendar/SingelDayEventListDesign.py +++ b/Calendar/SingelDayEventListDesign.py @@ -5,11 +5,8 @@ from TextFormatter import event_prefix_str_sum font = fonts["regular"] - class SingelDayEventListDesign (EventListDesign): """Specialized event list for day list design.""" - - def __init__(self, size, events, font_size=defaultfontsize, line_spacing=0, event_prefix_rel_dates=[], col_spacing=5, general_color=colors["fg"], background_color=colors["bg"], highlight_color=colors["hl"]): - def prefix_func(x, rel_date): return event_prefix_str_sum(x, rel_date) - super().__init__(size, events, text_size=font_size, line_spacing=line_spacing, col_spacing=col_spacing, event_prefix_rel_dates=event_prefix_rel_dates, - event_prefix_func=prefix_func, font_family=font, show_more_info=True, general_color=general_color, background_color=background_color, highlight_color=highlight_color) + def __init__ (self, size, events, font_size = defaultfontsize, line_spacing=0, event_prefix_rel_dates = [], col_spacing=5, general_color=colors["fg"], background_color=colors["bg"], highlight_color=colors["hl"]): + prefix_func = lambda x, rel_date : event_prefix_str_sum(x, rel_date) + super().__init__(size, events, text_size=font_size, line_spacing=line_spacing, col_spacing=col_spacing, event_prefix_rel_dates = event_prefix_rel_dates, event_prefix_func=prefix_func, font_family=font, show_more_info=True, general_color=general_color, background_color=background_color, highlight_color = highlight_color) \ No newline at end of file diff --git a/Calendar/TableDesign.py b/Calendar/TableDesign.py index b654f2d..c08beb0 100644 --- a/Calendar/TableDesign.py +++ b/Calendar/TableDesign.py @@ -5,19 +5,16 @@ from DesignEntity import DesignEntity default_props = { - "color": colors["fg"], - "background_color": colors["bg"], - "font-size": defaultfontsize + "color" : colors["fg"], + "background_color" : colors["bg"], + "font-size" : defaultfontsize } - class TableDesign (TextDesign): """Gets a matrix with text or designs that is than displayed in a table without borders.""" - - def __init__(self, size, matrix, max_col_size=None, max_row_size=None, font=None, fontsize=defaultfontsize, column_horizontal_alignments=[], mask=True, line_spacing=0, col_spacing=0, truncate_rows=True, truncate_cols=True, wrap=False, truncate_text=True, truncate_suffix="...", cell_properties=None, background_color=colors["bg"]): - super(TableDesign, self).__init__( - size, font=font, fontsize=fontsize, mask=mask) + def __init__ (self, size, matrix, max_col_size = None, max_row_size = None, font = None, fontsize = defaultfontsize, column_horizontal_alignments = [], mask = True, line_spacing = 0, col_spacing = 0, truncate_rows = True, truncate_cols = True, wrap = False, truncate_text=True, truncate_suffix="...", cell_properties=None, background_color = colors["bg"]): + super(TableDesign, self).__init__(size, font=font, fontsize=fontsize, mask=mask) self.__init_image__(background_color) self.matrix = matrix self.max_col_size = max_col_size @@ -35,7 +32,7 @@ class TableDesign (TextDesign): self.cell_properties = cell_properties default_props["font-size"] = fontsize - def __finish_image__(self): + def __finish_image__ (self): if len(self.matrix) is 0: return self.__reform_col_size__() @@ -44,19 +41,19 @@ class TableDesign (TextDesign): self.max_col, self.max_row = self.__get_truncated_counts__() self.__print_table__(self.matrix) - def __reform_col_size__(self): + def __reform_col_size__ (self): if self.max_col_size is not None: return - + col_sizes = [] - for c in range(len(self.matrix[0])): # amout of columns + for c in range(len(self.matrix[0])): #amout of columns for r in range(len(self.matrix)): - row_col_size = self.__get_cell_size__(r, c)[0] + row_col_size = self.__get_cell_size__(r,c)[0] if len(col_sizes) - 1 < c: col_sizes.append(row_col_size) elif row_col_size > col_sizes[c]: col_sizes[c] = row_col_size - + for index, size in enumerate(col_sizes): preceding_size = sum(col_sizes[:index]) + index * self.col_spacing if preceding_size + size > self.size[0]: @@ -65,19 +62,19 @@ class TableDesign (TextDesign): self.max_col_size = col_sizes - def __reform_row_size__(self): + def __reform_row_size__ (self): if self.max_row_size is not None: return - + row_sizes = [] for r in range(len(self.matrix)): - for c in range(len(self.matrix[0])): # amout of columns - col_row_size = self.__get_cell_size__(r, c)[1] + for c in range(len(self.matrix[0])): #amout of columns + col_row_size = self.__get_cell_size__(r,c)[1] if len(row_sizes) - 1 < r: row_sizes.append(col_row_size) elif col_row_size > row_sizes[r]: row_sizes[r] = col_row_size - + self.max_row_size = row_sizes def __get_cell_size__(self, r, c): @@ -88,20 +85,18 @@ class TableDesign (TextDesign): return size elif type(content) == str: font = self.__get_font__() - # get width of text in that row/col - width = font.getsize(self.matrix[r][c])[0] + width = font.getsize(self.matrix[r][c])[0] #get width of text in that row/col if self.wrap and self.max_col_size != None: - content = wrap_text_with_font( - content, self.max_col_size[c], font) + content = wrap_text_with_font(content, self.max_col_size[c], font) line_count = content.count('\n') + 1 - height = font.font.height * line_count # get height of text in that col/row + height = font.font.height * line_count #get height of text in that col/row size = (width, height) - else: # DesignEntity + else: #DesignEntity size = content.size return size - def __get_truncated_counts__(self): + def __get_truncated_counts__ (self): max_col = 0 if self.truncate_cols: while max_col < len(self.matrix[0]) and self.__get_cell_pos__(0, max_col + 1)[0] - self.col_spacing <= self.size[0]: @@ -111,45 +106,44 @@ class TableDesign (TextDesign): max_row = 0 if self.truncate_rows: - while max_row < len(self.matrix) and self.__get_cell_pos__(max_row + 1, 0)[1] - self.line_spacing <= self.size[1]: + while max_row < len(self.matrix) and self.__get_cell_pos__(max_row + 1,0)[1] - self.line_spacing <= self.size[1]: max_row += 1 else: max_row = len(self.matrix) return (max_col, max_row) - def __print_table__(self, matrix): + def __print_table__ (self, matrix): for r in range(self.max_row): for c in range(self.max_col): - self.__draw_cell__(r, c) - - def __draw_text__(self, pos, size, row, col): + self.__draw_cell__(r,c) + + def __draw_text__ (self, pos, size, row, col): color = self.__get_cell_prop__(row, col, "color") bg_color = self.__get_cell_prop__(row, col, "background_color") fontsize = self.__get_cell_prop__(row, col, "font-size") - design = TextDesign(size, text=self.matrix[row][col], font=self.font_family, color=color, background_color=bg_color, fontsize=fontsize, - horizontalalignment=self.__get_col_hori_alignment__(col), wrap=self.wrap, truncate=self.truncate_text, truncate_suffix=self.truncate_suffix) + design = TextDesign(size, text=self.matrix[row][col], font=self.font_family, color=color, background_color=bg_color, fontsize=fontsize, horizontalalignment=self.__get_col_hori_alignment__(col), wrap=self.wrap, truncate=self.truncate_text, truncate_suffix=self.truncate_suffix) design.pos = pos design.mask = False self.draw_design(design) - - def __draw_design__(self, pos, size, row, col): + + def __draw_design__ (self, pos, size, row, col): bg_color = self.__get_cell_prop__(row, col, "background_color") source_design = self.matrix[row][col] source_design.mask = False - framed_design = DesignEntity(size, mask=False) + framed_design = DesignEntity(size, mask = False) framed_design.__init_image__(color=bg_color) framed_design.draw_design(source_design) framed_design.pos = pos self.draw_design(framed_design) - def __draw_cell__(self, row, col): + def __draw_cell__ (self, row, col): size = self.cell_sizes[row][col] - pos = self.__get_cell_pos__(row, col) + pos = self.__get_cell_pos__(row,col) if self.matrix[row][col] == None: return @@ -157,8 +151,8 @@ class TableDesign (TextDesign): self.__draw_text__(pos, size, row, col) else: self.__draw_design__(pos, size, row, col) - - def __get_cell_pos__(self, row, col): + + def __get_cell_pos__ (self, row, col): xpos, ypos = (0, 0) for c in range(col): xpos += self.cell_sizes[row][c][0] @@ -168,7 +162,7 @@ class TableDesign (TextDesign): ypos += self.line_spacing return (xpos, ypos) - def __get_cell_sizes__(self): + def __get_cell_sizes__ (self): size_matrix = [] for r in range(len(self.matrix)): size_matrix.append([]) @@ -177,7 +171,7 @@ class TableDesign (TextDesign): size_matrix[r].append(size) return size_matrix - def __get_col_hori_alignment__(self, c): + def __get_col_hori_alignment__ (self, c): if len(self.column_horizontal_alignments) <= c: return "left" return self.column_horizontal_alignments[c] @@ -185,8 +179,8 @@ class TableDesign (TextDesign): def __get_cell_prop__(self, r, c, prop): if self.cell_properties is None: return default_props[prop] - + if r < len(self.cell_properties) and c < len(self.cell_properties[r]) and prop in self.cell_properties[r][c].keys(): return self.cell_properties[r][c][prop] else: - return default_props[prop] + return default_props[prop] \ No newline at end of file diff --git a/Calendar/TechnicalDataDesign.py b/Calendar/TechnicalDataDesign.py index 4445fa7..f66f59a 100644 --- a/Calendar/TechnicalDataDesign.py +++ b/Calendar/TechnicalDataDesign.py @@ -4,12 +4,10 @@ from Assets import colors font_size = 20 - class TechnicalDataDesign(DesignEntity): '''Prints data about the current loop ontop of the panel''' - def __init__(self, size, start, stop): - super(TechnicalDataDesign, self).__init__(size, mask=True) + super(TechnicalDataDesign, self).__init__(size, mask = True) self.start = start self.stop = stop @@ -39,4 +37,4 @@ class TechnicalDataDesign(DesignEntity): data += str(dur) data += "\nSTOP: " data += str(self.stop) - return data + return data \ No newline at end of file diff --git a/Calendar/TextDesign.py b/Calendar/TextDesign.py index beac47a..9c44e70 100644 --- a/Calendar/TextDesign.py +++ b/Calendar/TextDesign.py @@ -5,13 +5,11 @@ from TextWraper import wrap_text_with_font truncateerror_fontsize = 0.5 - class TextDesign (DesignEntity): """Object that manages all information relevant to text and prints it to an image""" - - def __init__(self, size, color=colors["fg"], background_color=colors["bg"], font=None, fontsize=defaultfontsize, text="", horizontalalignment="left", verticalalignment="top", mask=True, truncate=False, truncate_suffix='...', wrap=False): - super(TextDesign, self).__init__(size, mask=mask) + def __init__ (self, size, color=colors["fg"], background_color=colors["bg"], font = None, fontsize = defaultfontsize, text = "", horizontalalignment = "left", verticalalignment = "top", mask=True, truncate=False, truncate_suffix = '...', wrap=False): + super(TextDesign, self).__init__(size, mask = mask) if font is None: font = defaultfont self.font_family = font @@ -25,7 +23,7 @@ class TextDesign (DesignEntity): self.color = color self.background_color = background_color - def __finish_image__(self): + def __finish_image__ (self): if self.color is "white": self.invert_mask = True if self.background_color not in ["white", "black"] or self.color in ["red"]: @@ -38,24 +36,21 @@ class TextDesign (DesignEntity): if self.wrap: self.__wrap_text__() pos = self.__pos_from_alignment__() - ImageDraw.Draw(self.__image__).text( - pos, self.text, fill=self.color, font=self.__font__) - - def __truncate_text__(self): - # does not need truncating - if self.__font__.getsize_multiline(self.text)[0] <= self.size[0]: + ImageDraw.Draw(self.__image__).text(pos, self.text, fill=self.color, font=self.__font__) + + def __truncate_text__ (self): + if self.__font__.getsize_multiline(self.text)[0] <= self.size[0]: #does not need truncating return - suffix_length = self.__font__.getsize_multiline( - self.truncate_suffix)[0] + suffix_length = self.__font__.getsize_multiline(self.truncate_suffix)[0] while len(self.text) > 1 and self.__font__.getsize_multiline(self.text)[0] + suffix_length >= self.size[0]: self.text = self.text[0:-1] self.text = self.text.rstrip(' ') self.text += self.truncate_suffix - def __pos_from_alignment__(self): + def __pos_from_alignment__ (self): width, height = self.__get_text_size__() x, y = 0, 0 - + if self.vertical_alignment == "center": y = int((self.size[1] / 2) - (height / 2)) elif self.vertical_alignment == "bottom": @@ -73,8 +68,8 @@ class TextDesign (DesignEntity): height = (self.text.count('\n') + 1) * self.__font__.font.height return widht, height - def __wrap_text__(self): + def __wrap_text__ (self): self.text = wrap_text_with_font(self.text, self.size[0], self.__font__) def __get_font__(self): - return ImageFont.truetype(path + self.font_family, int(self.font_size)) + return ImageFont.truetype(path + self.font_family, int(self.font_size)) \ No newline at end of file diff --git a/Calendar/TextFormatter.py b/Calendar/TextFormatter.py index e0e7f01..94e5b59 100644 --- a/Calendar/TextFormatter.py +++ b/Calendar/TextFormatter.py @@ -12,8 +12,7 @@ until_character = ' - ' allday_character = "•" multiday_character = allday_character + allday_character - -def time_str(dt): +def time_str (dt): if hours is "12": return dt.strftime("%I:%M%p") elif hours is "24": @@ -21,31 +20,29 @@ def time_str(dt): else: return str(dt) - -def event_prefix_str_md_dif(event, relative_date=None): +def event_prefix_str_md_dif (event, relative_date=None): if relative_date is None: relative_date = event.begin_datetime.date() if event.multiday is False: return event_time_summary(event) - - # Relative to - # First day + + #Relative to + #First day elif __equal__(event.begin_datetime, relative_date): return event_time_summary(event) + multiday_begin_character - # Last day + #Last day elif __equal__(event.end_datetime, relative_date) or \ - (__day_duration__(event.end_datetime) == timedelta(0) and __equal__(relative_date + timedelta(1), event.end_datetime)): + (__day_duration__(event.end_datetime) == timedelta(0) and __equal__(relative_date + timedelta(1), event.end_datetime)): return multiday_end_character + event_time_summary(event) - # Some day + #Some day else: event.allday = True return multiday_end_character + event_time_summary(event) + multiday_begin_character - -def event_prefix_str(event, relative_date=None): +def event_prefix_str (event, relative_date=None): if relative_date is None: relative_date = event.begin_datetime.date() @@ -54,8 +51,7 @@ def event_prefix_str(event, relative_date=None): else: return event_time_detailed(event) - -def event_prefix_str_sum(event, relative_date=None): +def event_prefix_str_sum (event, relative_date=None): if relative_date is None: relative_date = event.begin_datetime.date() @@ -64,30 +60,25 @@ def event_prefix_str_sum(event, relative_date=None): else: return event_time_summary(event) - -def event_time_summary(event): +def event_time_summary (event): if event.allday: return allday_character else: return time_str(event.begin_datetime) - -def event_time_detailed(event): +def event_time_detailed (event): if event.allday: return get_text(allday_events) else: return time_str(event.begin_datetime) + until_character + time_str(event.end_datetime) - def date_str(dt): return remove_leading_zero(dt.strftime('%d. %b')) - -def remove_leading_zero(text): - while text[0] is '0': - text = text[1:] - return text - +def remove_leading_zero (text): + while text[0] is '0': + text = text[1:] + return text def date_summary_str(dt): day = remove_leading_zero(dt.strftime("%d")) @@ -98,13 +89,11 @@ def date_summary_str(dt): else: return dt.strftime('%a ' + day + '. %b') - def __equal__(dt1, dt2): return dt1.day == dt2.day and \ dt1.month == dt2.month and \ dt1.year == dt2.year - def __day_duration__(dt): day_begin = datetime(dt.year, dt.month, dt.day, 0, 0, 0, 0, timezone.utc) - return dt - day_begin + return dt - day_begin \ No newline at end of file diff --git a/Calendar/TextWraper.py b/Calendar/TextWraper.py index dcb2970..b480a36 100644 --- a/Calendar/TextWraper.py +++ b/Calendar/TextWraper.py @@ -1,11 +1,10 @@ from Assets import path, defaultfont from PIL import ImageFont - -def wrap_text_with_font(text, width, font): +def wrap_text_with_font (text, width, font): words = text.split(' ') result = "" - for word in words: + for index, word in enumerate(words): until_current = (result + " " + word).strip() txt_width, _ = font.getsize_multiline(until_current) if txt_width > width: @@ -15,6 +14,5 @@ def wrap_text_with_font(text, width, font): result += word return result.strip() - -def wrap_text(text, width, font_size, font_family=defaultfont): - return wrap_text_with_font(text, width, ImageFont.truetype(path + font_family, int(font_size))) +def wrap_text (text, width, font_size, font_family = defaultfont): + return wrap_text_with_font(text, width, ImageFont.truetype(path + font_family, int(font_size))) \ No newline at end of file diff --git a/Calendar/Translator.py b/Calendar/Translator.py index 2eb4297..0a523de 100644 --- a/Calendar/Translator.py +++ b/Calendar/Translator.py @@ -4,8 +4,7 @@ from settings import language '''Looks up a phrase in a given dictionary-collection and returns the translated phrase''' - -def translate(phrase, target_lang=language, dictionary_collection=dictionary_collection): +def translate(phrase, target_lang = language, dictionary_collection = dictionary_collection) : dictionary = find_dictionary(phrase, dictionary_collection) if dictionary == None: @@ -18,9 +17,8 @@ def translate(phrase, target_lang=language, dictionary_collection=dictionary_col else: return dictionary[default_language] - -def find_dictionary(phrase, dictionary_collection=dictionary_collection): +def find_dictionary(phrase, dictionary_collection = dictionary_collection): for dictionary in dictionary_collection: if phrase in dictionary.values(): return dictionary - return None + return None \ No newline at end of file diff --git a/Calendar/WeatherColumnDesign.py b/Calendar/WeatherColumnDesign.py index 5de1232..3c5c165 100644 --- a/Calendar/WeatherColumnDesign.py +++ b/Calendar/WeatherColumnDesign.py @@ -13,15 +13,13 @@ info_yresize = -0.05 fontsize_static = defaultfontsize max_symbol_y_width = 0.15 - class WeatherColumnDesign (DesignEntity): """Displays weather information in a column""" - - def __init__(self, size, forecast): + def __init__ (self, size, forecast): super().__init__(size) self.forecast = forecast - def __finish_image__(self): + def __finish_image__ (self): if self.forecast is None: self.__draw_no_response__() return @@ -29,56 +27,42 @@ class WeatherColumnDesign (DesignEntity): self.__draw_icon__(self.forecast.icon) self.__draw_infos__(self.forecast) - def __draw_infos__(self, forecast): - temperature = forecast.air_temperature + \ - " " + self.__get_unit__(("°C", "°F")) + def __draw_infos__ (self, forecast): + temperature = forecast.air_temperature + " " + self.__get_unit__(("°C", "°F")) humidity = forecast.air_humidity + "%" - if forecast.units == "aviation": - if forecast.wind_deg == None: - forecast.wind_deg = "" - elif len(forecast.wind_deg) == 1: - forecast.wind_deg = "00" + forecast.wind_deg - elif len(forecast.wind_deg) == 2: - forecast.wind_deg = "0" + forecast.wind_deg - if int(forecast.wind_speed) < 10: - windspeed = forecast.wind_deg + "@" + "0" + forecast.wind_speed + \ - self.__get_unit__( - ("", "")) # added degrees, if wind<10 add a 0 to make two digit + if self.forecast.units== "aviation": + if int(forecast.wind_speed)<10: + windspeed = forecast.wind_deg + "@" + "0" + forecast.wind_speed + self.__get_unit__(("", "")) #added degrees, if wind<10 add a 0 to make two digit else: - windspeed = forecast.wind_deg + "@" + forecast.wind_speed + \ - self.__get_unit__(("", "")) # added degrees + windspeed = forecast.wind_deg + "@" + forecast.wind_speed + self.__get_unit__(("", "")) #added degrees else: - windspeed = forecast.wind_speed + " " + \ - self.__get_unit__(("km/h", "mph")) + windspeed = forecast.wind_speed + " " + self.__get_unit__(("km/h", "mph")) - numbers_list = [[forecast.short_description], - [temperature], - [humidity], - [windspeed]] + numbers_list = [ [ forecast.short_description ], + [ temperature ], + [ humidity ], + [ windspeed ] ] ypos = info_x_ypos * self.size[0] pos = (0, ypos) - size = (self.size[0], self.size[1] + - info_yresize * self.size[1] - pos[1]) - line_spacing = (size[1] - len(numbers_list) * - fontsize_static) / (len(numbers_list) + 1) + size = (self.size[0], self.size[1] + info_yresize * self.size[1] - pos[1]) + print(size) + line_spacing = (size[1] - len(numbers_list) * fontsize_static) / (len(numbers_list) + 1) - table = TableDesign(size, numbers_list, fontsize=fontsize_static, line_spacing=line_spacing, column_horizontal_alignments=[ - "center"], max_col_size=[size[0]], truncate_text=False, truncate_rows=False) + table = TableDesign(size, numbers_list, fontsize=fontsize_static, line_spacing=line_spacing, column_horizontal_alignments=[ "center" ], max_col_size=[ size[0] ], truncate_text=False, truncate_rows=False) table.pos = pos self.draw_design(table) - def __draw_icon__(self, icon_id): + def __draw_icon__ (self, icon_id): width = int(icon_width * self.size[0]) size = (width, width) xpos = icon_xpos * self.size[0] ypos = icon_x_ypos * self.size[0] pos = (xpos, ypos) - self.__draw_resized_path_at__( - wpath + weathericons[icon_id] + ".jpeg", pos, size) + self.__draw_resized_path_at__(wpath + weathericons[icon_id] + ".jpeg", pos, size) - def __draw_no_response__(self): + def __draw_no_response__ (self): width = int(icon_width * self.size[0]) size = (width, width) xpos = icon_xpos * self.size[0] @@ -87,25 +71,26 @@ class WeatherColumnDesign (DesignEntity): self.__draw_resized_image_at__(no_response, pos, size) - def __draw_resized_path_at__(self, path, pos, size): + def __draw_resized_path_at__ (self, path, pos, size): img = Image.open(path) self.__draw_resized_image_at__(img, pos, size) - def __draw_resized_image_at__(self, img, pos, size): + def __draw_resized_image_at__ (self, img, pos, size): size = (int(size[0]), int(size[1])) resized_img = img.resize(size, resample=Image.LANCZOS) self.draw(resized_img, pos) - def __get_unit__(self, tuple): - if self.forecast.units == "metric" or self.forecast.units == "aviation": + + def __get_unit__ (self, tuple): + if self.forecast.units == "metric": return tuple[0] else: return tuple[1] - def __abs_co__(self, coordinates): + def __abs_co__ (self, coordinates): return (coordinates[0] * self.size[0], coordinates[1] * self.size[1]) - def __get_time__(self, time): + def __get_time__ (self, time): if hours == "24": return time.strftime('%H:%M') else: diff --git a/Calendar/WeatherForecast.py b/Calendar/WeatherForecast.py index a2a192a..c1f4b34 100644 --- a/Calendar/WeatherForecast.py +++ b/Calendar/WeatherForecast.py @@ -1,7 +1,6 @@ class WeatherForecast (object): """Defines a weather forecast, independent of any implementation""" - - def __init__(self): + def __init__ (self): self.air_temperature = None self.air_pressure = None self.air_humidity = None @@ -12,7 +11,6 @@ class WeatherForecast (object): self.sunset = None self.moon_phase = None self.wind_speed = None - self.wind_deg = None self.clouds = None self.icon = None @@ -22,4 +20,4 @@ class WeatherForecast (object): self.units = None self.datetime = None self.location = None - self.fetch_datetime = None + self.fetch_datetime = None \ No newline at end of file diff --git a/Calendar/WeatherHeaderDesign.py b/Calendar/WeatherHeaderDesign.py index f8d5940..c6d21aa 100644 --- a/Calendar/WeatherHeaderDesign.py +++ b/Calendar/WeatherHeaderDesign.py @@ -10,16 +10,14 @@ windiconspace = (0.206, 0) sunriseplace = (0.55, 0) sunsetplace = (0.55, 0.486) - class WeatherHeaderDesign (DesignEntity): """Defines a top area that displays basic weather information""" - - def __init__(self, size, weather): + def __init__ (self, size, weather): super(WeatherHeaderDesign, self).__init__(size) self.__weather__ = weather self.__first_render__() - def __first_render__(self): + def __first_render__ (self): if self.__weather__.is_available() is False: self.__render_missing_connection__() return @@ -30,65 +28,47 @@ class WeatherHeaderDesign (DesignEntity): self.__render_missing_connection__() return - temperature = cur_weather.air_temperature + \ - " " + self.__get_unit__(("°C", "°F")) - if units == "aviation": # pick up aviation - if cur_weather.wind_deg == None: - cur_weather.wind_deg = "" - elif len(cur_weather.wind_deg) == 1: # if deg is 2, add two zeros for format - cur_weather.wind_deg = "00" + cur_weather.wind_deg - elif len(cur_weather.wind_deg) == 2: - cur_weather.wind_deg = "0" + cur_weather.wind_deg - if int(cur_weather.wind_speed) < 10: - windspeed = cur_weather.wind_deg + "@" + "0" + cur_weather.wind_speed + \ - self.__get_unit__( - ("", "")) # added degrees, if wind<10 add a 0 to make two digit + temperature = cur_weather.air_temperature + " " + self.__get_unit__(("°C", "°F")) + if units== "aviation": #pick up aviation + if int(cur_weather.wind_speed)<10: + windspeed = cur_weather.wind_deg + "@" + "0" + cur_weather.wind_speed + self.__get_unit__(("", "")) #added degrees, if wind<10 add a 0 to make two digit else: - windspeed = cur_weather.wind_deg + "@" + cur_weather.wind_speed + \ - self.__get_unit__(("", "")) # added degrees + windspeed = cur_weather.wind_deg + "@" + cur_weather.wind_speed + self.__get_unit__(("", "")) #added degrees else: - windspeed = cur_weather.wind_speed + " " + \ - self.__get_unit__(("km/h", "mph")) + windspeed = cur_weather.wind_speed + " " + self.__get_unit__(("km/h", "mph")) - self.__draw_text__(temperature, self.__abs_pos__((0.87, 0)), (50, 35)) - self.__draw_text__(windspeed, self.__abs_pos__( - (0.297, 0.05)), (100, 35)) - self.__draw_text__(self.__get_time__(cur_weather.sunrise), - self.__abs_pos__((0.64, 0)), (50, 35)) - self.__draw_text__(self.__get_time__(cur_weather.sunset), - self.__abs_pos__((0.64, 0.486)), (50, 35)) - self.__draw_text__(cur_weather.air_humidity + " %", - self.__abs_pos__((0.87, 0.486)), (50, 35)) - self.__draw_text__(cur_weather.short_description, - self.__abs_pos__((0.182, 0.486)), (144, 35)) + self.__draw_text__(temperature, self.__abs_pos__((0.87, 0)), (50,35)) + self.__draw_text__(windspeed, self.__abs_pos__((0.297, 0.05)), (100,35)) + self.__draw_text__(self.__get_time__(cur_weather.sunrise), self.__abs_pos__((0.64,0)), (50,35)) + self.__draw_text__(self.__get_time__(cur_weather.sunset), self.__abs_pos__((0.64,0.486)), (50,35)) + self.__draw_text__(cur_weather.air_humidity + " %", self.__abs_pos__((0.87,0.486)), (50,35)) + self.__draw_text__(cur_weather.short_description, self.__abs_pos__((0.182,0.486)), (144,35)) self.draw(windicon, self.__abs_pos__(windiconspace)) self.draw(sunseticon, self.__abs_pos__(sunsetplace)) self.draw(sunriseicon, self.__abs_pos__(sunriseplace)) self.draw(humicon, self.__abs_pos__(humplace)) self.draw(tempicon, self.__abs_pos__(tempplace)) - self.draw_image( - wpath + weathericons[cur_weather.icon] + '.jpeg', self.__abs_pos__(wiconplace)) + self.draw_image(wpath + weathericons[cur_weather.icon] + '.jpeg', self.__abs_pos__(wiconplace)) - def __render_missing_connection__(self): + def __render_missing_connection__ (self): self.draw(no_response, self.__abs_pos__(wiconplace)) - def __abs_pos__(self, pos): + def __abs_pos__ (self, pos): return (int(pos[0] * self.size[0]), int(pos[1] * self.size[1])) - def __draw_text__(self, text, pos, size): - txt = TextDesign(size, fontsize=18, text=text, - verticalalignment="center", horizontalalignment="center") + def __draw_text__ (self, text, pos, size): + txt = TextDesign(size, fontsize=18, text=text, verticalalignment="center", horizontalalignment="center") txt.pos = pos self.draw_design(txt) - def __get_unit__(self, tuple): - if units == "metric" or units == "aviation": + def __get_unit__ (self, tuple): + if units == "metric": return tuple[0] else: return tuple[1] - def __get_time__(self, time): + def __get_time__ (self, time): if hours == "24": return time.strftime('%H:%M') else: diff --git a/Calendar/WeatherInterface.py b/Calendar/WeatherInterface.py index cb02fc9..bd3fc6e 100644 --- a/Calendar/WeatherInterface.py +++ b/Calendar/WeatherInterface.py @@ -1,11 +1,9 @@ from DataSourceInterface import DataSourceInterface - class WeatherInterface (DataSourceInterface): """Interface for fetching and processing weather forecast information.""" - - def get_forecast_in_days(self, offset_by_days, location=None): + def get_forecast_in_days (self, offset_by_days, location=None): raise NotImplementedError("Functions needs to be implemented") - def get_today_forecast(self, location=None): - raise NotImplementedError("Functions needs to be implemented") + def get_today_forecast (self, location=None): + raise NotImplementedError("Functions needs to be implemented") \ No newline at end of file diff --git a/Calendar/settings.py.sample b/Calendar/settings.py.sample index a683a38..5a72c4d 100644 --- a/Calendar/settings.py.sample +++ b/Calendar/settings.py.sample @@ -1,43 +1,36 @@ """ To quickly get started, fill in the following details:""" ical_urls = [ + "https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics" ] highlighted_ical_urls = [ ] rss_feeds = [ -] - -crypto_coins = [ + "http://feeds.bbci.co.uk/news/world/rss.xml#" ] api_key = "" owm_paid_subscription = False -location = "Berlin, DE" +location = "Julich, DE" week_starts_on = "Monday" display_colours = "bwr" language = "en" datetime_encoding = "UTF-8" # UTF-8 -units = "metric" # aviation (celcius,degrees/knots), metric (celcius,kmh), imperial(f,mph) +units = "metric" #aviation (celcius,degrees/knots), metric (celcius,kmh), imperial(f,mph) hours = "24" update_interval = 60 -max_loop_count = 0 # From 0 to 86400 representing the maximum number the loop is executed, with 0 being unlimited -run_on_hour = True # If True, updates calendar every full hour, ignoring differing update_interval """DESIGN""" font_size = 14 # does not affect every text font_boldness = "semibold" # extralight, light, regular, semibold, bold, extrabold -line_thickness = 1 # 1-3 Thickness advised -choosen_design = "month-overview" # month-overview, day-list, day-view, day-focus-list, agenda-list, month-view, image-frame +choosen_design = "month-overview" # month-overview, day-list, day-view, agenda-list, month-view general_settings = { # General settings that designs may use - "info-area" : "rss", # empty, events, rss, crypto + "info-area" : "rss", # empty, events, rss "highlight-event-days" : True, - "weather-info" : True, - "image-folder" : "", - "overlay-image" : "", # Size must be 384x640px with default display - "extra-excluded-urls" : [] + "weather-info" : True } diff --git a/Gallery/day-focus-list_example.png b/Gallery/day-focus-list_example.png deleted file mode 100644 index 9d3686a..0000000 Binary files a/Gallery/day-focus-list_example.png and /dev/null differ diff --git a/Gallery/image-frame_example.png b/Gallery/image-frame_example.png deleted file mode 100644 index fbbc68d..0000000 Binary files a/Gallery/image-frame_example.png and /dev/null differ diff --git a/README.md b/README.md index f2a9c5e..e2df946 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,8 @@ -# NOT ACTIVELY MAINTAINED ANY LONGER -Since [aceisace/Inkycal](https://github.com/aceisace/Inkycal) has grown significantly and is far superior in many ways, I will no longer focus on mainting my own fork. The ical implementation might also not be super reliable in this project. - # E-Paper Calendar [![Codacy Badge](https://api.codacy.com/project/badge/Grade/0962be8b02e947e9aa4326e73b387e01)](https://app.codacy.com/app/m.giller.dev/E-Paper-Calendar?utm_source=github.com&utm_medium=referral&utm_content=mgfcf/E-Paper-Calendar&utm_campaign=Badge_Grade_Dashboard) -This is a software written in python3 that allows you to transform an E-Paper display (like the kindle) into an information display. It fetches live data from Openweathermap (a weather info provider), rss-feeds and your Online Calendar (Google/Yahoo Calendar/...) and displays them on a large, beautiful and ultra-low power E-Paper display. It's ideal for staying organised and keeping track of important details without having to check them up online. It can also be used as an image frame. +This is a software written in python3 that allows you to transform an E-Paper display (like the kindle) into an information display. It fetches live data from Openweathermap (a weather info provider), rss-feeds and your Online Calendar (Google/Yahoo Calendar/...) and displays them on a large, beautiful and ultra-low power E-Paper display. It's ideal for staying organised and keeping track of important details without having to check them up online. This software fully supports the 3-Colour **and** 2-Colour version of the 7.5" E-Paper display from waveshare/gooddisplay and works with Raspberry Pi 2, 3 and 0 (Zero, Zero W, Zero WH). @@ -18,21 +15,18 @@ This software fully supports the 3-Colour **and** 2-Colour version of the 7.5" E * **Added Support for the 2-Colour E-Paper Display as well!** (Late September 2018) * **Added Support for Raspbian Stretch lite.** (Late September 2018) - + -1.: Day-List Panel  -2.: Month-Overview Panel  -3.: Agenda-List Panel
-4.: Day-View Panel  -5.: Day-Focus-List Panel  -6.: Image-Frame Panel +1.: Day-List Panel +2.: Month-Overview Panel +3.: Agenda-List Panel +4.: Day-View Panel ## Main features * Display a calendar with one of multiple designes * Optionally get RSS-Feed fetched and shown * Syncronise events from any online calendar (like google, yahoo, etc.) * Get live weather data (including temperature, humidity, etc.) using openweathermap api -* Only show a slideshow of images ## Hardware required * 7.5" 3-Colour E-Paper Display (Black, White, Red/Yellow) with driver hat from [waveshare](https://www.waveshare.com/product/7.5inch-e-paper-hat-b.htm) @@ -74,35 +68,29 @@ Once the packages are installed, navigate to the home directory, open 'E-Paper-M | Parameter | Description | | --- | --- | | ical_urls | Your iCalendar URL/s. To add more than one URL, seperate each with a comma. | -| highlighted_ical_urls | Your iCalendar URL/s that should be higlighted in comparison to the ical_urls. To add more than one URL, seperate each with a comma. | +| highlighted_ical_urls | Your iCalendar URL/s that should be higlighted in comparison the ical_urls. To add more than one URL, seperate each with a comma. | | rss_feeds | All the sources for your rss-feed. To add more than one URL, seperate each with a comma. | | api_key | Your __personal__ openweathermap API-key which you can generate and find in your Account info. | | owm_paid_subscription | If you have a paid owm subscription you can set it to `True` and in some panels receive forecast information. | | location | Location refers to the closest weather station from your place. It isn't necessarily the place you live in. To find this location, type your city name in the search box on [openweathermap](https://openweathermap.org/). The output should be in the following format: City Name, Country ISO-Code. Not sure what your ISO code is? Check here: [(find iso-code)](https://countrycode.org/). | -| week_starts_on | When does the work start in your region? Possible options are `"Monday"` or `"Sunday"`. | +| week_starts_on | When does the work start on your Region? Possible options are `"Monday"` or `"Sunday"`. | | display_colours | This should normally be set by the installer when you choose the type of your display. Options include `"bw"` if you're using the black and white E-Paper or `"bwr"` when you're using the black-white-red or black-white-yellow E-Paper.| | language | Sets the language and the related locale for datetime-information. Some texts depend on additional translations that can be added to the dictionary-file.| | datetime_encoding | Sets the encoding that will be used in the datetime-texts (month, weekday, ...). Default is `"UTF-8"`. Not to confuse with the full locale, this is only the encoding. | -|units| Selecting units allows switching units from km/h (kilometer per hour) and °C (degree Celcius) to mph (miles per hour) and °F (degree Fahrenheit). Possible options are `"metric"`, `"imperial"` or `"aviation"` (Celsius, Degree/Knots). | +|units| Selecting units allows switching units from km/h (kilometer per hour) and °C (degree Celcius) to mph (miles per hour) and °F (degree Fahrenheit). Possible options are `"metric"` or `"imperial"`. | |hours | Which time format do you prefer? This will change the sunrise and sunset times from 24-hours format to 12-hours format. Possible options are `"24"` for 24-hours and `"12"` for 12-hours.| |update_interval | The update delay between two updates in minutes. By default there is always an update on a full hour.| -|max_loop_count | From 0 to 86400 representing the maximum number of times the loop is going to be executed, with `0` being unlimited.| -|run_on_hour | If `True`, updates calendar every full hour, ignoring differing update_interval.| ### Design | Parameter | Description | | --- | --- | | font_size | Varies the size of the font. Can be any number >0. In some cases the size of the font is fixed by the design. Default value is `14`. | | font_boldness | Varies the boldness of the font. Can be one of `extralight`, `light`, `regular`, `semibold`, `bold` or `extrabold`. In some cases the boldness of the font is fixed by the design. Default value is `regular`. | -| choosen_design | Sets the desired panel design. | -| line_thickness | Varies the boldness of separation lines in the chosen design. | +| choosen_design | Sets the desired panel design. Can be one of `month-overview` or `day-list`. | | general_settings | A dictionary containing options that some designs use to optimize there design. Possible options are as follows: | | `"info-area"` | Defines the content type of an additionaly info area on the design. Can be one of `"rss"`, `"events"` or empty, to remove this area or keep it clean. | | `"highlight-event-days"` | If set to `True`, days with events are highlighted in contrast to days without events. | | `"weather-info"` | If set to `False`, weather info areas disappear and make room for events/rss/etc. (depends on the design). | -| `"image-folder"` | Set a relative or absolute path to a folder containing images that you want to see fullscreen with the `"image-frame"` design activated. | -| `"overlay-image"` | Set a relative or absolute path to an image with the same size as the screen (default: 384x640px) to show some static information over every image shown in the `"image-frame"` design. If the overlay image is contained within the `"image-folder"`, it will not be included into the slideshow. | -| `"extra-excluded-urls"` | A list of calendar urls that may be excluded in some panels in certain areas. | ### Debug | Parameter | Description |