Formatted code
This commit is contained in:
parent
5d881753b8
commit
1e0f59739f
54 changed files with 968 additions and 722 deletions
|
@ -8,8 +8,10 @@ from settings import line_thickness
|
|||
|
||||
separator_width = line_thickness
|
||||
|
||||
|
||||
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):
|
||||
super(AgendaListDesign, self).__init__(size)
|
||||
self.calendar = calendar
|
||||
|
@ -60,7 +62,8 @@ class AgendaListDesign (DesignEntity):
|
|||
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)
|
||||
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):
|
||||
|
@ -73,7 +76,8 @@ class AgendaListDesign (DesignEntity):
|
|||
pos = (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):
|
||||
color = colors["fg"]
|
||||
|
|
|
@ -13,14 +13,17 @@ seperator_width = line_thickness
|
|||
infolist_size = (1, 0.24)
|
||||
infolist_padding = 0
|
||||
|
||||
|
||||
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):
|
||||
self.weather = weather
|
||||
|
@ -65,7 +68,8 @@ class AgendaListPanel (PanelDesign):
|
|||
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)
|
||||
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):
|
||||
if size is None:
|
||||
|
@ -73,7 +77,8 @@ class AgendaListPanel (PanelDesign):
|
|||
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])
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
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
|
||||
|
@ -17,4 +19,5 @@ class BoxDesign (DesignEntity):
|
|||
self.corners = [topleft, bottomright]
|
||||
|
||||
def __finish_image__(self):
|
||||
ImageDraw.Draw(self.__image__).rectangle(self.corners, fill=self.fill, outline=self.outline, width=self.width)
|
||||
ImageDraw.Draw(self.__image__).rectangle(
|
||||
self.corners, fill=self.fill, outline=self.outline, width=self.width)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
class CalendarEvent (object):
|
||||
"""Defines a calendar event, independent of any implementation"""
|
||||
|
||||
def __init__(self):
|
||||
self.begin_datetime = None
|
||||
self.end_datetime = None
|
||||
|
|
|
@ -4,8 +4,10 @@ from dateutil.rrule import rrulestr
|
|||
from dateutil.parser import parse
|
||||
import calendar
|
||||
|
||||
|
||||
class CalendarInterface (DataSourceInterface):
|
||||
"""Interface for fetching and processing calendar event information."""
|
||||
|
||||
def __init__(self):
|
||||
self.events = []
|
||||
|
||||
|
@ -22,7 +24,8 @@ class CalendarInterface (DataSourceInterface):
|
|||
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):
|
||||
|
@ -41,9 +44,11 @@ class CalendarInterface (DataSourceInterface):
|
|||
|
||||
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):
|
||||
|
@ -54,7 +59,8 @@ class CalendarInterface (DataSourceInterface):
|
|||
|
||||
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):
|
||||
|
@ -66,7 +72,8 @@ class CalendarInterface (DataSourceInterface):
|
|||
|
||||
events_in_range = []
|
||||
for event in self.events:
|
||||
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)
|
||||
|
||||
|
@ -109,12 +116,14 @@ class CalendarInterface (DataSourceInterface):
|
|||
for occurrence in rule:
|
||||
if occurrence - end > timedelta(0):
|
||||
return occurrences
|
||||
merged_event = self.__merge_event_data__(event, start=occurrence)
|
||||
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)
|
||||
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
|
||||
|
||||
def __merge_event_data__(self, event, start=None):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from DataSourceInterface import DataSourceInterface
|
||||
|
||||
|
||||
class CryptoInterface(DataSourceInterface):
|
||||
def __init__(self):
|
||||
self.crypto_coins = []
|
||||
|
|
|
@ -6,6 +6,7 @@ from settings import crypto_coins
|
|||
|
||||
xpadding = 5
|
||||
|
||||
|
||||
class CryptoListDesign (DesignEntity):
|
||||
def __init__(self, size, crypto, text_size=defaultfontsize):
|
||||
super(CryptoListDesign, self).__init__(size)
|
||||
|
@ -18,7 +19,8 @@ class CryptoListDesign (DesignEntity):
|
|||
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 = 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)
|
||||
|
||||
|
@ -26,7 +28,8 @@ class CryptoListDesign (DesignEntity):
|
|||
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) ]
|
||||
row = [coin.symbol.upper(), coin.name, coin.currency + " " +
|
||||
str(coin.price), "% " + str(coin.day_change)]
|
||||
matrix.append(row)
|
||||
return matrix
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
class DataSourceInterface (object):
|
||||
"""Interface for child interfaces that fetch data."""
|
||||
|
||||
def is_available(self):
|
||||
raise NotImplementedError("Functions needs to be implemented")
|
||||
|
||||
|
|
|
@ -4,8 +4,10 @@ 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
|
||||
|
|
|
@ -27,8 +27,10 @@ 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):
|
||||
super(DayHeaderDesign, self).__init__(size)
|
||||
self.weather_column_width = 0
|
||||
|
@ -38,9 +40,11 @@ class DayHeaderDesign (DesignEntity):
|
|||
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)
|
||||
|
@ -50,8 +54,10 @@ class DayHeaderDesign (DesignEntity):
|
|||
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):
|
||||
self.__draw_event_list__(events)
|
||||
|
@ -73,12 +79,15 @@ class DayHeaderDesign (DesignEntity):
|
|||
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)
|
||||
|
||||
|
@ -118,7 +127,8 @@ 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)
|
||||
|
@ -131,7 +141,8 @@ class DayHeaderDesign (DesignEntity):
|
|||
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)
|
||||
|
|
|
@ -24,9 +24,11 @@ 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):
|
||||
super(DayListPanel, self).__init__(size)
|
||||
self.__day_rows__ = []
|
||||
|
@ -60,7 +62,8 @@ class DayListPanel (PanelDesign):
|
|||
pass
|
||||
|
||||
def __draw_rss_infoarea__(self, rss):
|
||||
height = infoarea_replacedrowscount * self.dayrow_size[1] * self.size[1] - rss_y_padding
|
||||
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)
|
||||
|
@ -70,7 +73,8 @@ class DayListPanel (PanelDesign):
|
|||
self.draw_design(design)
|
||||
|
||||
def __draw_crypto_infoarea__(self, crypto):
|
||||
height = infoarea_replacedrowscount * self.dayrow_size[1] * self.size[1] - crypto_y_padding
|
||||
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)
|
||||
|
@ -80,7 +84,8 @@ class DayListPanel (PanelDesign):
|
|||
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]))
|
||||
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):
|
||||
|
@ -111,7 +116,8 @@ class DayListPanel (PanelDesign):
|
|||
return following_days
|
||||
|
||||
def __draw_today_header__(self):
|
||||
header = DayHeaderDesign(self.__abs_co__(todayheader_size), date.today())
|
||||
header = DayHeaderDesign(self.__abs_co__(
|
||||
todayheader_size), date.today())
|
||||
header.pos = self.__abs_co__(todayheader_pos)
|
||||
self.__day_rows__.append(header)
|
||||
|
||||
|
@ -122,7 +128,8 @@ class DayListPanel (PanelDesign):
|
|||
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__:
|
||||
|
|
|
@ -20,8 +20,10 @@ eventlist_y_fontsize = 0.2
|
|||
|
||||
font = fonts["light"]
|
||||
|
||||
|
||||
class DayRowDesign (DesignEntity):
|
||||
"""Detailed view of a given date."""
|
||||
|
||||
def __init__(self, size, date):
|
||||
super(DayRowDesign, self).__init__(size)
|
||||
self.__init_image__()
|
||||
|
@ -50,12 +52,14 @@ class DayRowDesign (DesignEntity):
|
|||
|
||||
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)
|
||||
forecast = weather.get_forecast_in_days(
|
||||
self.date.day - datetime.today().day)
|
||||
|
||||
if forecast is None:
|
||||
return
|
||||
|
@ -74,14 +78,16 @@ class DayRowDesign (DesignEntity):
|
|||
|
||||
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)
|
||||
|
@ -89,13 +95,15 @@ class DayRowDesign (DesignEntity):
|
|||
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)
|
||||
|
||||
|
|
|
@ -18,9 +18,11 @@ 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):
|
||||
super(DayViewPanel, self).__init__(size)
|
||||
self.shownhours_count = default_shownhours_count
|
||||
|
@ -36,7 +38,8 @@ class DayViewPanel (PanelDesign):
|
|||
self.__header__.add_weather(weather)
|
||||
|
||||
def add_calendar(self, calendar):
|
||||
allday_ev, timed_ev = self.__split_events__(calendar.get_today_events())
|
||||
allday_ev, timed_ev = self.__split_events__(
|
||||
calendar.get_today_events())
|
||||
self.__header__.add_events(allday_ev)
|
||||
self.__hourlist__.add_events(timed_ev)
|
||||
|
||||
|
@ -60,10 +63,12 @@ class DayViewPanel (PanelDesign):
|
|||
|
||||
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])
|
||||
|
||||
|
@ -72,7 +77,8 @@ class DayViewPanel (PanelDesign):
|
|||
self.draw_design(rss)
|
||||
|
||||
def __draw_crypto_feed__(self, crypto):
|
||||
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])
|
||||
|
||||
|
@ -81,9 +87,9 @@ class DayViewPanel (PanelDesign):
|
|||
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])
|
||||
|
||||
|
@ -99,13 +105,15 @@ class DayViewPanel (PanelDesign):
|
|||
self.draw_design(self.__hourlist__)
|
||||
|
||||
def __init_header__(self):
|
||||
self.__header__ = DayHeaderDesign(self.__abs_co__(header_size), date.today())
|
||||
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)
|
||||
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])
|
||||
|
|
|
@ -3,8 +3,10 @@ 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):
|
||||
print("\nCalendarEvent:")
|
||||
print("---------------------")
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
class DebugInterface (object):
|
||||
"""Defines general interface for debugging operations"""
|
||||
|
||||
def print_event(self, event):
|
||||
raise NotImplementedError("Functions needs to be implemented")
|
||||
|
||||
|
|
|
@ -3,9 +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):
|
||||
self.size = size
|
||||
self.pos = (0, 0)
|
||||
|
@ -29,11 +31,13 @@ class DesignEntity (object):
|
|||
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)
|
||||
self.draw(entity.get_image(), entity.pos, entity.mask,
|
||||
entity.invert_mask, entity.color_key)
|
||||
|
||||
def draw_image(self, path, pos):
|
||||
self.draw(Image.open(path), pos)
|
||||
|
@ -44,7 +48,8 @@ class DesignEntity (object):
|
|||
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)
|
|
@ -4,6 +4,7 @@ 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():
|
||||
|
@ -11,6 +12,7 @@ def get_text(dictionary, *params):
|
|||
|
||||
return __insert_params__(text, params)
|
||||
|
||||
|
||||
def __insert_params__(text, params):
|
||||
index = 0
|
||||
while '*%d' % index in text and index < len(params):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
class DisplayAdapter (object):
|
||||
"""Interface for CalendarDesign output channels."""
|
||||
|
||||
def __init__(self, width, height):
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
|
|
@ -27,8 +27,10 @@ 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 = []
|
||||
|
@ -60,8 +62,11 @@ available_panels = {
|
|||
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)
|
||||
|
@ -78,7 +83,8 @@ def main():
|
|||
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()
|
||||
|
@ -100,18 +106,22 @@ def main():
|
|||
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()
|
||||
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()
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
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)
|
||||
ImageDraw.Draw(self.__image__).ellipse(
|
||||
self.corners, fill=self.fill, outline=self.outline, width=self.width)
|
||||
|
|
|
@ -2,6 +2,7 @@ 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):
|
||||
super(Epd7in5Adapter, self).__init__(384, 640)
|
||||
|
|
|
@ -4,6 +4,7 @@ from PIL import Image, ImageDraw
|
|||
from math import sqrt, pow
|
||||
import numpy as np
|
||||
|
||||
|
||||
class Epd7in5bAdapter (EpdAdapter):
|
||||
def __init__(self):
|
||||
super(Epd7in5bAdapter, self).__init__(384, 640)
|
||||
|
@ -54,9 +55,11 @@ class Epd7in5bAdapter (EpdAdapter):
|
|||
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))
|
||||
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)
|
||||
] &= ~(0xC0 >> (y % 4 * 2))
|
||||
buf[int((y + x * self.height) / 4)] |= 0x40 >> (y % 4 * 2)
|
||||
return buf
|
||||
|
||||
|
|
|
@ -48,8 +48,10 @@ AUTO_MEASUREMENT_VCOM = 0x80
|
|||
READ_VCOM_VALUE = 0x81
|
||||
VCM_DC_SETTING = 0x82
|
||||
|
||||
|
||||
class EpdAdapter (DisplayAdapter):
|
||||
"""Generalized adapter for epd7in5 and epd7in5b"""
|
||||
|
||||
def __init__(self, width, height):
|
||||
super(EpdAdapter, self).__init__(width, height)
|
||||
|
||||
|
|
|
@ -5,9 +5,11 @@ 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):
|
||||
super(EventListDesign, self).__init__(size)
|
||||
self.events = events
|
||||
|
@ -27,15 +29,18 @@ class EventListDesign (DesignEntity):
|
|||
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)))
|
||||
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__)
|
||||
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]
|
||||
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]
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ api_price_url = api_url + "simple/price"
|
|||
price_currency = "usd"
|
||||
price_currency_sign = "$"
|
||||
|
||||
|
||||
class GeckoCrypto(CryptoInterface):
|
||||
def __init__(self, coins):
|
||||
self.coin_ids = coins
|
||||
|
@ -33,7 +34,8 @@ class GeckoCrypto(CryptoInterface):
|
|||
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()
|
||||
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
|
||||
|
@ -62,4 +64,5 @@ class GeckoCrypto(CryptoInterface):
|
|||
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 }
|
||||
self.metadata = {coin['id'].lower(
|
||||
): coin for coin in dataJSON if coin['id'].lower() in self.coin_ids}
|
||||
|
|
|
@ -19,9 +19,11 @@ currenttimeline_thickness = line_thickness
|
|||
|
||||
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):
|
||||
super(HourListDesign, self).__init__(size)
|
||||
self.first_hour = first_hour
|
||||
|
@ -63,7 +65,8 @@ class HourListDesign (DesignEntity):
|
|||
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):
|
||||
|
@ -91,11 +94,13 @@ 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)
|
||||
|
||||
|
@ -104,7 +109,8 @@ class HourListDesign (DesignEntity):
|
|||
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):
|
||||
if hours == "12":
|
||||
|
@ -145,23 +151,29 @@ class HourListDesign (DesignEntity):
|
|||
|
||||
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):
|
||||
parallelity_count = 1
|
||||
|
||||
for index, event in enumerate(self.events):
|
||||
current_parallelity = 1
|
||||
preceding = self.events[:index] #Assumption: Events are ordered chronologically
|
||||
# Assumption: Events are ordered chronologically
|
||||
preceding = self.events[:index]
|
||||
for pre_event in preceding:
|
||||
if self.__are_simultaneous__(pre_event, event):
|
||||
current_parallelity += 1
|
||||
|
@ -183,4 +195,5 @@ class HourListDesign (DesignEntity):
|
|||
|
||||
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)
|
||||
|
|
|
@ -6,8 +6,10 @@ 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
|
||||
|
@ -60,8 +62,10 @@ 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)
|
||||
|
@ -79,8 +83,10 @@ 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
|
||||
|
@ -95,7 +101,9 @@ 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):
|
||||
|
|
|
@ -2,10 +2,13 @@ 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"""
|
||||
def __init__ (self, size, path, fill = "none", color="RGBA", dither=None): # fill: "none" : original size, "stretch" : strech to fill, "scale" : scale to fill, "border" : scale until one side touches border
|
||||
|
||||
# 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
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
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=""):
|
||||
super(ImageFileAdapter, self).__init__(384, 640)
|
||||
self.file_path = file_path
|
||||
|
|
|
@ -10,10 +10,13 @@ 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.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__()
|
||||
|
||||
|
|
|
@ -3,9 +3,11 @@ 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):
|
||||
self.interval = int(str(loop_interval))
|
||||
self.on_hour = run_on_hour
|
||||
|
|
|
@ -11,9 +11,11 @@ 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):
|
||||
super(MonthBlockDesign, self).__init__(size, mask=True)
|
||||
self.month = datetime_month.month
|
||||
|
@ -30,10 +32,12 @@ class MonthBlockDesign (DesignEntity):
|
|||
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)
|
||||
|
@ -43,7 +47,8 @@ class MonthBlockDesign (DesignEntity):
|
|||
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)
|
||||
|
||||
|
@ -54,7 +59,8 @@ class MonthBlockDesign (DesignEntity):
|
|||
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()))
|
||||
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):
|
||||
|
@ -74,7 +80,8 @@ 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
|
||||
|
||||
|
|
|
@ -29,9 +29,11 @@ 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):
|
||||
super(MonthOvPanel, self).__init__(size)
|
||||
self.weather_header_height = 0
|
||||
|
@ -52,7 +54,8 @@ 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
|
||||
|
@ -61,7 +64,8 @@ class MonthOvPanel (PanelDesign):
|
|||
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):
|
||||
if general_settings["info-area"] is "rss":
|
||||
|
@ -76,7 +80,8 @@ class MonthOvPanel (PanelDesign):
|
|||
|
||||
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)
|
||||
|
||||
|
@ -86,29 +91,35 @@ class MonthOvPanel (PanelDesign):
|
|||
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):
|
||||
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 = 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))
|
||||
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):
|
||||
|
@ -117,9 +128,12 @@ class MonthOvPanel (PanelDesign):
|
|||
|
||||
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))
|
||||
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):
|
||||
|
@ -129,24 +143,28 @@ class MonthOvPanel (PanelDesign):
|
|||
|
||||
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):
|
||||
"""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):
|
||||
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):
|
||||
maxwidth, _ = self.__abs_pos__(monthovsize)
|
||||
|
@ -172,7 +190,8 @@ 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
|
||||
|
||||
|
|
|
@ -15,9 +15,11 @@ info_height = 0.25
|
|||
info_padding = 5
|
||||
seperator_width = line_thickness
|
||||
|
||||
|
||||
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):
|
||||
super(MonthViewPanel, self).__init__(size)
|
||||
self.day_table = []
|
||||
|
@ -87,7 +89,8 @@ class MonthViewPanel (PanelDesign):
|
|||
pos = (0, self.size[1] - size[1])
|
||||
|
||||
crypto = CryptoListDesign(size, crypto)
|
||||
crypto.pos = (pos[0],pos[1] + (size[1] - crypto.get_estimated_height()))
|
||||
crypto.pos = (pos[0], pos[1] + (size[1] -
|
||||
crypto.get_estimated_height()))
|
||||
self.draw_design(crypto)
|
||||
|
||||
def __finish_panel__(self):
|
||||
|
@ -102,7 +105,8 @@ class MonthViewPanel (PanelDesign):
|
|||
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)
|
||||
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":
|
||||
|
@ -120,7 +124,8 @@ 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
|
||||
|
||||
|
|
|
@ -5,14 +5,17 @@ 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):
|
||||
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):
|
||||
try:
|
||||
|
@ -58,30 +61,41 @@ class OwmForecasts (WeatherInterface):
|
|||
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']))
|
||||
|
||||
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
|
||||
|
|
|
@ -3,8 +3,10 @@ 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):
|
||||
super(PanelDesign, self).__init__(size)
|
||||
self.start_timestamp = datetime.now()
|
||||
|
@ -31,6 +33,7 @@ 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)
|
|
@ -1,8 +1,10 @@
|
|||
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 = []
|
||||
|
||||
|
|
|
@ -6,8 +6,10 @@ 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__()
|
||||
|
@ -53,4 +55,3 @@ class RssParserPosts (RssInterface):
|
|||
start_index = link.find('://') + 3
|
||||
end_index = link[start_index:].find('/') + start_index
|
||||
return link[start_index: end_index]
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
class RssPost(object):
|
||||
"""Defines a rss post, independent of any implementation"""
|
||||
|
||||
def __init__(self):
|
||||
self.title = None
|
||||
self.description = None
|
||||
|
|
|
@ -2,9 +2,11 @@ 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):
|
||||
super(RssPostListDesign, self).__init__(size)
|
||||
self.rssfeed = rssfeed
|
||||
|
@ -14,7 +16,8 @@ class RssPostListDesign (DesignEntity):
|
|||
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):
|
||||
|
|
|
@ -5,8 +5,11 @@ 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"]):
|
||||
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)
|
||||
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)
|
||||
|
|
|
@ -10,11 +10,14 @@ default_props = {
|
|||
"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)
|
||||
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
|
||||
|
@ -85,9 +88,11 @@ class TableDesign (TextDesign):
|
|||
return size
|
||||
elif type(content) == str:
|
||||
font = self.__get_font__()
|
||||
width = font.getsize(self.matrix[r][c])[0] #get width of text in that row/col
|
||||
# get width of text in that row/col
|
||||
width = font.getsize(self.matrix[r][c])[0]
|
||||
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
|
||||
size = (width, height)
|
||||
|
@ -123,7 +128,8 @@ class TableDesign (TextDesign):
|
|||
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)
|
||||
|
|
|
@ -4,8 +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)
|
||||
self.start = start
|
||||
|
|
|
@ -5,9 +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)
|
||||
if font is None:
|
||||
|
@ -36,12 +38,15 @@ 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__)
|
||||
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
|
||||
# does not need truncating
|
||||
if self.__font__.getsize_multiline(self.text)[0] <= self.size[0]:
|
||||
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(' ')
|
||||
|
|
|
@ -12,6 +12,7 @@ until_character = ' - '
|
|||
allday_character = "•"
|
||||
multiday_character = allday_character + allday_character
|
||||
|
||||
|
||||
def time_str(dt):
|
||||
if hours is "12":
|
||||
return dt.strftime("%I:%M%p")
|
||||
|
@ -20,6 +21,7 @@ def time_str (dt):
|
|||
else:
|
||||
return str(dt)
|
||||
|
||||
|
||||
def event_prefix_str_md_dif(event, relative_date=None):
|
||||
if relative_date is None:
|
||||
relative_date = event.begin_datetime.date()
|
||||
|
@ -42,6 +44,7 @@ def event_prefix_str_md_dif (event, relative_date=None):
|
|||
event.allday = True
|
||||
return multiday_end_character + event_time_summary(event) + multiday_begin_character
|
||||
|
||||
|
||||
def event_prefix_str(event, relative_date=None):
|
||||
if relative_date is None:
|
||||
relative_date = event.begin_datetime.date()
|
||||
|
@ -51,6 +54,7 @@ def event_prefix_str (event, relative_date=None):
|
|||
else:
|
||||
return event_time_detailed(event)
|
||||
|
||||
|
||||
def event_prefix_str_sum(event, relative_date=None):
|
||||
if relative_date is None:
|
||||
relative_date = event.begin_datetime.date()
|
||||
|
@ -60,26 +64,31 @@ def event_prefix_str_sum (event, relative_date=None):
|
|||
else:
|
||||
return 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):
|
||||
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 date_summary_str(dt):
|
||||
day = remove_leading_zero(dt.strftime("%d"))
|
||||
if language is "en":
|
||||
|
@ -89,11 +98,13 @@ 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
|
|
@ -1,10 +1,11 @@
|
|||
from Assets import path, defaultfont
|
||||
from PIL import ImageFont
|
||||
|
||||
|
||||
def wrap_text_with_font(text, width, font):
|
||||
words = text.split(' ')
|
||||
result = ""
|
||||
for index, word in enumerate(words):
|
||||
for word in words:
|
||||
until_current = (result + " " + word).strip()
|
||||
txt_width, _ = font.getsize_multiline(until_current)
|
||||
if txt_width > width:
|
||||
|
@ -14,5 +15,6 @@ 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)))
|
|
@ -4,6 +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):
|
||||
dictionary = find_dictionary(phrase, dictionary_collection)
|
||||
|
||||
|
@ -17,6 +18,7 @@ def translate(phrase, target_lang = language, dictionary_collection = dictionary
|
|||
else:
|
||||
return dictionary[default_language]
|
||||
|
||||
|
||||
def find_dictionary(phrase, dictionary_collection=dictionary_collection):
|
||||
for dictionary in dictionary_collection:
|
||||
if phrase in dictionary.values():
|
||||
|
|
|
@ -13,8 +13,10 @@ 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):
|
||||
super().__init__(size)
|
||||
self.forecast = forecast
|
||||
|
@ -28,7 +30,8 @@ class WeatherColumnDesign (DesignEntity):
|
|||
self.__draw_infos__(self.forecast)
|
||||
|
||||
def __draw_infos__(self, forecast):
|
||||
temperature = forecast.air_temperature + " " + self.__get_unit__(("°C", "°F"))
|
||||
temperature = forecast.air_temperature + \
|
||||
" " + self.__get_unit__(("°C", "°F"))
|
||||
humidity = forecast.air_humidity + "%"
|
||||
if forecast.units == "aviation":
|
||||
if forecast.wind_deg == None:
|
||||
|
@ -38,11 +41,15 @@ class WeatherColumnDesign (DesignEntity):
|
|||
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
|
||||
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],
|
||||
|
@ -51,10 +58,13 @@ class WeatherColumnDesign (DesignEntity):
|
|||
|
||||
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])
|
||||
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)
|
||||
|
||||
|
@ -65,7 +75,8 @@ class WeatherColumnDesign (DesignEntity):
|
|||
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):
|
||||
width = int(icon_width * self.size[0])
|
||||
|
@ -85,7 +96,6 @@ class WeatherColumnDesign (DesignEntity):
|
|||
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":
|
||||
return tuple[0]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
class WeatherForecast (object):
|
||||
"""Defines a weather forecast, independent of any implementation"""
|
||||
|
||||
def __init__(self):
|
||||
self.air_temperature = None
|
||||
self.air_pressure = None
|
||||
|
|
|
@ -10,8 +10,10 @@ 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):
|
||||
super(WeatherHeaderDesign, self).__init__(size)
|
||||
self.__weather__ = weather
|
||||
|
@ -28,7 +30,8 @@ class WeatherHeaderDesign (DesignEntity):
|
|||
self.__render_missing_connection__()
|
||||
return
|
||||
|
||||
temperature = cur_weather.air_temperature + " " + self.__get_unit__(("°C", "°F"))
|
||||
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 = ""
|
||||
|
@ -37,25 +40,35 @@ class WeatherHeaderDesign (DesignEntity):
|
|||
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
|
||||
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__(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):
|
||||
self.draw(no_response, self.__abs_pos__(wiconplace))
|
||||
|
@ -64,7 +77,8 @@ class WeatherHeaderDesign (DesignEntity):
|
|||
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")
|
||||
txt = TextDesign(size, fontsize=18, text=text,
|
||||
verticalalignment="center", horizontalalignment="center")
|
||||
txt.pos = pos
|
||||
self.draw_design(txt)
|
||||
|
||||
|
|
|
@ -1,7 +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):
|
||||
raise NotImplementedError("Functions needs to be implemented")
|
||||
|
||||
|
|
Loading…
Reference in a new issue