2019-03-17 22:05:49 +01:00
|
|
|
from DesignEntity import DesignEntity
|
2019-04-04 21:27:29 +02:00
|
|
|
from settings import hours, language
|
|
|
|
from TextDesign import TextDesign
|
|
|
|
from PIL import ImageDraw
|
2019-04-09 09:15:53 +02:00
|
|
|
from Assets import colors, defaultfontsize
|
|
|
|
from BoxDesign import BoxDesign
|
2019-04-09 10:57:51 +02:00
|
|
|
from datetime import timedelta, datetime
|
2019-04-04 21:27:29 +02:00
|
|
|
|
|
|
|
hourbox_y_width = 1
|
2019-04-09 12:49:57 +02:00
|
|
|
hour_box_fontsize = 0.8
|
|
|
|
hoursubtext_fontsize = 0.8
|
|
|
|
hoursubtext_height = 0.38
|
2019-04-09 09:15:53 +02:00
|
|
|
event_title_fontsize = defaultfontsize
|
2019-04-09 13:52:14 +02:00
|
|
|
event_title_xpadding = 3
|
|
|
|
event_title_ypadding = 5
|
2019-04-04 21:27:29 +02:00
|
|
|
line_thickness = 1
|
2019-04-09 13:35:32 +02:00
|
|
|
currenttimeline_thickness = 2
|
2019-03-17 22:05:49 +01:00
|
|
|
|
|
|
|
class HourListDesign (DesignEntity):
|
|
|
|
"""Hours of a day are listed vertically and
|
|
|
|
resemble a timeline."""
|
2019-04-05 11:37:51 +02:00
|
|
|
def __init__ (self, size, first_hour = 0, last_hour = 23):
|
2019-03-17 22:05:49 +01:00
|
|
|
super(HourListDesign, self).__init__(size)
|
2019-04-04 21:27:29 +02:00
|
|
|
self.first_hour = first_hour
|
|
|
|
self.last_hour = last_hour
|
2019-04-09 12:39:48 +02:00
|
|
|
self.__calc_parameters__()
|
2019-04-09 09:15:53 +02:00
|
|
|
self.events = []
|
2019-04-04 21:27:29 +02:00
|
|
|
|
|
|
|
def add_events (self, events):
|
2019-04-09 09:15:53 +02:00
|
|
|
self.events.extend(events)
|
2019-04-29 18:47:47 +02:00
|
|
|
self.events.sort(key=lambda x : x.begin_datetime)
|
2019-04-04 21:27:29 +02:00
|
|
|
|
2019-04-09 09:15:53 +02:00
|
|
|
def __finish_image__ (self):
|
2019-04-09 12:39:48 +02:00
|
|
|
self.number_columns = self.__get_max_num_simultaneous_events__()
|
2019-04-09 10:57:51 +02:00
|
|
|
self.__draw_lines__()
|
2019-04-09 12:05:27 +02:00
|
|
|
self.__draw_events__()
|
2019-04-09 10:57:51 +02:00
|
|
|
self.__draw_current_time_line__()
|
2019-04-09 13:52:14 +02:00
|
|
|
self.__draw_hour_rows__()
|
2019-04-09 09:15:53 +02:00
|
|
|
|
|
|
|
def __calc_parameters__ (self):
|
2019-04-09 13:37:39 +02:00
|
|
|
self.hour_count = self.last_hour - self.first_hour + 1
|
2019-04-09 12:16:57 +02:00
|
|
|
self.row_size = (self.size[0], self.size[1] / self.hour_count)
|
2019-04-04 21:27:29 +02:00
|
|
|
|
|
|
|
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):
|
|
|
|
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):
|
2019-04-09 12:16:57 +02:00
|
|
|
row_height = self.row_size[1]
|
2019-04-04 21:27:29 +02:00
|
|
|
return row_height * (hours + minutes / 60)
|
|
|
|
|
2019-04-09 10:48:59 +02:00
|
|
|
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)
|
|
|
|
self.__draw_event__(event, column_events.index(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)):
|
|
|
|
if column_events[index] != None and column_events[index].end_datetime <= current_time:
|
|
|
|
column_events[index] = None
|
|
|
|
if new_event_added == False and column_events[index] == None:
|
|
|
|
column_events[index] = new_event
|
|
|
|
new_event_added = True
|
|
|
|
return column_events
|
|
|
|
|
2019-04-04 21:27:29 +02:00
|
|
|
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):
|
2019-04-09 12:16:57 +02:00
|
|
|
subtext_height = self.row_size[1] * hoursubtext_height
|
2019-04-04 21:27:29 +02:00
|
|
|
sub_fontsize = subtext_height * hoursubtext_fontsize
|
2019-04-09 12:16:57 +02:00
|
|
|
width = hourbox_y_width * self.row_size[1]
|
|
|
|
height = self.row_size[1] - subtext_height
|
2019-04-04 21:27:29 +02:00
|
|
|
size = (width, height)
|
|
|
|
pos = (0, self.__get_ypos_for_time__(hour))
|
|
|
|
fontsize = size[1] * hour_box_fontsize
|
|
|
|
|
|
|
|
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.pos = (0, height + self.__get_ypos_for_time__(hour))
|
|
|
|
self.draw_design(sub)
|
|
|
|
|
2019-04-09 09:15:53 +02:00
|
|
|
def __draw_lines__ (self):
|
2019-04-04 21:27:29 +02:00
|
|
|
for i in range(self.hour_count):
|
2019-04-09 12:16:57 +02:00
|
|
|
ypos = i * self.row_size[1]
|
2019-04-04 21:27:29 +02:00
|
|
|
line_start = (0, ypos)
|
|
|
|
line_end = (self.size[0], ypos)
|
2019-04-09 09:15:53 +02:00
|
|
|
ImageDraw.Draw(self.__image__).line([ line_start, line_end ], fill=colors["fg"], width=line_thickness)
|
2019-03-17 22:05:49 +01:00
|
|
|
|
2019-04-09 09:15:53 +02:00
|
|
|
def __get_hour_sub_text__ (self, hour):
|
2019-04-09 12:48:13 +02:00
|
|
|
if hours == "12":
|
|
|
|
return "AM" if hour < 12 else "PM"
|
|
|
|
elif language is "de":
|
2019-04-04 21:27:29 +02:00
|
|
|
return "Uhr"
|
|
|
|
elif language is "en":
|
2019-04-09 12:48:13 +02:00
|
|
|
return "o'c"
|
2019-04-09 13:41:29 +02:00
|
|
|
return ""
|
2019-04-09 09:15:53 +02:00
|
|
|
|
|
|
|
def __draw_event__ (self, event, column = 0):
|
2019-04-09 12:16:57 +02:00
|
|
|
xoffset = hourbox_y_width * self.row_size[1]
|
2019-04-09 09:15:53 +02:00
|
|
|
column_width = (self.size[0] - xoffset) / self.number_columns
|
|
|
|
|
|
|
|
begin = event.begin_datetime
|
|
|
|
time_ypos = self.__get_ypos_for_time__(begin.hour, begin.minute)
|
|
|
|
hours = event.duration.total_seconds() / 3600
|
|
|
|
time_height = self.__get_height_for_duration__(hours)
|
|
|
|
|
2019-04-09 10:57:51 +02:00
|
|
|
yoffset_correction = 0
|
|
|
|
if time_ypos < 0:
|
|
|
|
yoffset_correction = time_ypos
|
|
|
|
|
|
|
|
pos = (xoffset + column_width * column, time_ypos - yoffset_correction)
|
|
|
|
size = (column_width, time_height + yoffset_correction)
|
2019-04-09 09:15:53 +02:00
|
|
|
|
2019-04-09 12:16:57 +02:00
|
|
|
if size[1] < 0:
|
|
|
|
return #Event not in shown time range
|
|
|
|
|
2019-04-09 09:15:53 +02:00
|
|
|
self.__draw_event_block__(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.mask = False
|
|
|
|
box.pos = pos
|
|
|
|
self.draw_design(box)
|
|
|
|
|
|
|
|
text = event.title
|
|
|
|
text_color = colors["bg"]
|
2019-04-09 13:52:14 +02:00
|
|
|
textbox_size = (size[0] - event_title_xpadding, size[1] - event_title_ypadding)
|
2019-04-09 10:48:59 +02:00
|
|
|
txt = TextDesign(textbox_size, text = text, fontsize=event_title_fontsize, color=text_color, background_color=box_color, wrap=True)
|
2019-04-09 11:35:35 +02:00
|
|
|
txt.mask = False
|
2019-04-09 13:52:14 +02:00
|
|
|
txt.pos = (pos[0] + event_title_xpadding, pos[1] + event_title_ypadding)
|
2019-04-09 09:34:00 +02:00
|
|
|
self.draw_design(txt)
|
|
|
|
|
2019-04-09 13:52:14 +02:00
|
|
|
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)
|
|
|
|
|
2019-04-09 10:48:59 +02:00
|
|
|
def __get_max_num_simultaneous_events__ (self):
|
2019-04-09 09:34:00 +02:00
|
|
|
parallelity_count = 1
|
|
|
|
|
|
|
|
for index, event in enumerate(self.events):
|
|
|
|
current_parallelity = 1
|
|
|
|
preceding = self.events[:index] #Assumption: Events are ordered chronologically
|
|
|
|
for pre_event in preceding:
|
2019-04-29 18:47:47 +02:00
|
|
|
if self.__are_simultaneous__(pre_event, event):
|
2019-04-09 09:34:00 +02:00
|
|
|
current_parallelity += 1
|
|
|
|
if parallelity_count < current_parallelity:
|
|
|
|
parallelity_count = current_parallelity
|
|
|
|
return parallelity_count
|
|
|
|
|
2019-04-09 10:48:59 +02:00
|
|
|
def __are_simultaneous__ (self, ev_a, ev_b):
|
2019-04-09 09:34:00 +02:00
|
|
|
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
|
|
|
|
|
2019-04-09 10:57:51 +02:00
|
|
|
return mes_dur < ev_a.duration
|
|
|
|
|
2019-04-09 13:52:14 +02:00
|
|
|
def __draw_current_time_line__ (self):
|
2019-04-09 10:57:51 +02:00
|
|
|
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)
|