minor improvements
fixed a few things in rss module, minor improvements to other modules. Added test file for use without installing
This commit is contained in:
		
							
								
								
									
										26
									
								
								dev_tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								dev_tests.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | from inkycal.modules.inkycal_rss import rss | ||||||
|  | from inkycal.modules.inkycal_calendar import calendar | ||||||
|  | from inkycal.modules.inkycal_agenda import agenda | ||||||
|  |  | ||||||
|  | # Test rss module: | ||||||
|  | rss_size = (384, 160) | ||||||
|  | rss_config = {'rss_urls': ['http://feeds.bbci.co.uk/news/world/rss.xml#']} | ||||||
|  | rss = rss(rss_size, rss_config) | ||||||
|  | rss.generate_image() | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Test calendar module: | ||||||
|  |  | ||||||
|  | calendar_size = (400, 520) | ||||||
|  | calendar_config = {'week_starts_on': 'Monday', 'ical_urls': ['https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics']} | ||||||
|  | calendar = calendar(calendar_size, calendar_config) | ||||||
|  | calendar.generate_image() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Test agenda module: | ||||||
|  |  | ||||||
|  | agenda_size = (400, 520) | ||||||
|  | agenda_config = {'week_starts_on': 'Monday', 'ical_urls': ['https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics']} | ||||||
|  | agenda = agenda(agenda_size, agenda_config) | ||||||
|  | agenda.generate_image() | ||||||
| @@ -8,7 +8,7 @@ Copyright by aceisace | |||||||
| from inkycal.custom import * | from inkycal.custom import * | ||||||
| import calendar as cal | import calendar as cal | ||||||
| import arrow | import arrow | ||||||
| from ical_parser import icalendar | from inkycal.modules.ical_parser import icalendar | ||||||
|  |  | ||||||
| size = (400, 520) | size = (400, 520) | ||||||
| config = {'week_starts_on': 'Monday', 'ical_urls': ['https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics']} | config = {'week_starts_on': 'Monday', 'ical_urls': ['https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics']} | ||||||
|   | |||||||
| @@ -26,15 +26,14 @@ class calendar: | |||||||
|     self.name = os.path.basename(__file__).split('.py')[0] |     self.name = os.path.basename(__file__).split('.py')[0] | ||||||
|     self.config = section_config |     self.config = section_config | ||||||
|     self.width, self.height = section_size |     self.width, self.height = section_size | ||||||
|  |  | ||||||
|     self.background_colour =  'white' |  | ||||||
|     self.font_colour = 'black' |  | ||||||
|     self.fontsize = 12 |     self.fontsize = 12 | ||||||
|     self.font = ImageFont.truetype( |     self.font = ImageFont.truetype( | ||||||
|       fonts['NotoSans-SemiCondensed'], size = self.fontsize) |       fonts['NotoSans-SemiCondensed'], size = self.fontsize) | ||||||
|     self.padding_x = 0.02 |     self.padding_x = 0.02 | ||||||
|     self.padding_y = 0.05 |     self.padding_y = 0.05 | ||||||
|  |  | ||||||
|  |     self.num_font = ImageFont.truetype( | ||||||
|  |       fonts['NotoSans-SemiCondensed'], size = self.fontsize) | ||||||
|     self.weekstart = 'Monday' |     self.weekstart = 'Monday' | ||||||
|     self.show_events = True |     self.show_events = True | ||||||
|     self.date_format = 'D MMM' # used for dates  |     self.date_format = 'D MMM' # used for dates  | ||||||
| @@ -42,9 +41,7 @@ class calendar: | |||||||
|     self.language = 'en' # Grab from settings file? |     self.language = 'en' # Grab from settings file? | ||||||
|      |      | ||||||
|     self.timezone = get_system_tz() |     self.timezone = get_system_tz() | ||||||
|     # urls of icalendars |  | ||||||
|     self.ical_urls = config['ical_urls'] |     self.ical_urls = config['ical_urls'] | ||||||
|     # filepaths of icalendar files |  | ||||||
|     self.ical_files = [] |     self.ical_files = [] | ||||||
|     print('{0} loaded'.format(self.name)) |     print('{0} loaded'.format(self.name)) | ||||||
|  |  | ||||||
| @@ -84,7 +81,7 @@ class calendar: | |||||||
|     logging.info('Image size: {0}'.format(im_size)) |     logging.info('Image size: {0}'.format(im_size)) | ||||||
|  |  | ||||||
|     # Create an image for black pixels and one for coloured pixels |     # Create an image for black pixels and one for coloured pixels | ||||||
|     im_black = Image.new('RGB', size = im_size, color = self.background_colour) |     im_black = Image.new('RGB', size = im_size, color = 'white') | ||||||
|     im_colour = Image.new('RGB', size = im_size, color = 'white') |     im_colour = Image.new('RGB', size = im_size, color = 'white') | ||||||
|  |  | ||||||
|     # Allocate space for month-names, weekdays etc. |     # Allocate space for month-names, weekdays etc. | ||||||
| @@ -105,11 +102,11 @@ class calendar: | |||||||
|  |  | ||||||
|     # Create grid and calculate icon sizes |     # Create grid and calculate icon sizes | ||||||
|     calendar_rows, calendar_cols = 6, 7 |     calendar_rows, calendar_cols = 6, 7 | ||||||
|     icon_width = self.width // calendar_cols |     icon_width = im_width // calendar_cols | ||||||
|     icon_height = calendar_height // calendar_rows |     icon_height = calendar_height // calendar_rows | ||||||
|  |  | ||||||
|     # Calculate spacings for calendar area |     # Calculate spacings for calendar area | ||||||
|     x_spacing_calendar = int((im_width % icon_width) / 2) |     x_spacing_calendar = int((im_width % calendar_cols) / 2) | ||||||
|     y_spacing_calendar = int((im_height % calendar_rows) / 2) |     y_spacing_calendar = int((im_height % calendar_rows) / 2) | ||||||
|  |  | ||||||
|     # Calculate positions for days of month |     # Calculate positions for days of month | ||||||
| @@ -122,7 +119,6 @@ class calendar: | |||||||
|     weekday_pos = [(grid_start_x + icon_width*_, month_name_height) for _ in |     weekday_pos = [(grid_start_x + icon_width*_, month_name_height) for _ in | ||||||
|                    range(calendar_cols)] |                    range(calendar_cols)] | ||||||
|  |  | ||||||
|  |  | ||||||
|     now = arrow.now(tz = self.timezone) |     now = arrow.now(tz = self.timezone) | ||||||
|  |  | ||||||
|     # Set weekstart of calendar to specified weekstart |     # Set weekstart of calendar to specified weekstart | ||||||
| @@ -169,28 +165,27 @@ class calendar: | |||||||
|           grid[i], |           grid[i], | ||||||
|           (icon_width,icon_height), |           (icon_width,icon_height), | ||||||
|           str(calendar_flat[i]), |           str(calendar_flat[i]), | ||||||
|           font = self.font, |           font = self.num_font, fill_height = 0.5 | ||||||
|           ) |           ) | ||||||
|  |  | ||||||
|     # Draw a red/black circle with the current day of month in white |     # Draw a red/black circle with the current day of month in white | ||||||
|     icon = Image.new('RGBA', (icon_width, icon_height)) |     icon = Image.new('RGBA', (icon_width, icon_height)) | ||||||
|     current_day_pos = grid[calendar_flat.index(now.day)] |     current_day_pos = grid[calendar_flat.index(now.day)] | ||||||
|     x_circle,y_circle = int(icon_width/2), int(icon_height/2) |     x_circle,y_circle = int(icon_width/2), int(icon_height/2) | ||||||
|     radius = int(icon_width * 0.25) |     radius = int(icon_width * 0.3) | ||||||
|     text_width, text_height = self.font.getsize(str(now.day)) |     ImageDraw.Draw(icon).ellipse( | ||||||
|     x_text = int((icon_width / 2) - (text_width / 2)) |       (x_circle-radius, y_circle-radius, x_circle+radius, y_circle+radius), | ||||||
|     y_text = int((icon_height / 2) - (text_height / 1.7)) |       fill= 'black', outline=None) | ||||||
|     ImageDraw.Draw(icon).ellipse((x_circle-radius, y_circle-radius, |     write(icon, (0,0), (icon_width, icon_height), str(now.day), | ||||||
|       x_circle+radius, y_circle+radius), fill= 'black', outline=None) |           font=self.num_font, fill_height = 0.5, colour='white') | ||||||
|     ImageDraw.Draw(icon).text((x_text, y_text), str(now.day), fill='white', |  | ||||||
|       font=self.font) |  | ||||||
|     im_colour.paste(icon, current_day_pos, icon) |     im_colour.paste(icon, current_day_pos, icon) | ||||||
|      |      | ||||||
|  |  | ||||||
|     # If events should be loaded and shown... |     # If events should be loaded and shown... | ||||||
|     if self.show_events == True: |     if self.show_events == True: | ||||||
|  |  | ||||||
|       # import the ical-parser |       # import the ical-parser | ||||||
|       from ical_parser import icalendar |       from inkycal.modules.ical_parser import icalendar | ||||||
|  |  | ||||||
|       # find out how many lines can fit at max in the event section |       # find out how many lines can fit at max in the event section | ||||||
|       line_spacing = 0 |       line_spacing = 0 | ||||||
| @@ -215,7 +210,7 @@ class calendar: | |||||||
|       # Filter events for full month (even past ones) for drawing event icons |       # Filter events for full month (even past ones) for drawing event icons | ||||||
|       month_events = parser.get_events(month_start, month_end) |       month_events = parser.get_events(month_start, month_end) | ||||||
|       parser.sort() |       parser.sort() | ||||||
|       # parser.show_events() # uncomment to show events |       self.month_events = month_events | ||||||
|  |  | ||||||
|       # find out on which days of this month events are taking place |       # find out on which days of this month events are taking place | ||||||
|       days_with_events = [int(events['begin'].format('D')) for events in |       days_with_events = [int(events['begin'].format('D')) for events in | ||||||
| @@ -223,12 +218,7 @@ class calendar: | |||||||
|  |  | ||||||
|       # remove duplicates (more than one event in a single day) |       # remove duplicates (more than one event in a single day) | ||||||
|       list(set(days_with_events)).sort() |       list(set(days_with_events)).sort() | ||||||
|       print('days with events:', days_with_events) |       self._days_with_events = days_with_events | ||||||
|  |  | ||||||
| ##      # calculate sizes for event-markers |  | ||||||
| ##      square_size = int(icon_width * 0.6) |  | ||||||
| ##      center_x = int((icon_width - square_size) / 2) |  | ||||||
| ##      center_y = int((icon_height - square_size) / 2) |  | ||||||
|  |  | ||||||
|       # Draw a border with specified parameters around days with events |       # Draw a border with specified parameters around days with events | ||||||
|       for days in days_with_events: |       for days in days_with_events: | ||||||
| @@ -236,23 +226,15 @@ class calendar: | |||||||
|           im_colour, |           im_colour, | ||||||
|           grid[calendar_flat.index(days)], |           grid[calendar_flat.index(days)], | ||||||
|           (icon_width, icon_height), |           (icon_width, icon_height), | ||||||
|           radius = 4, |           radius = 6, | ||||||
|           thickness= 1, |           thickness= 1, | ||||||
|           shrinkage = (0.4, 0.4) |           shrinkage = (0.4, 0.4) | ||||||
|           ) |           ) | ||||||
|  |  | ||||||
| ##         |  | ||||||
| ##        draw_square((int(grid[calendar_flat.index(days)][0]+center_x), |  | ||||||
| ##           int(grid[calendar_flat.index(days)][1] + center_y )), |  | ||||||
| ##           8, square_size , square_size, colour='black') |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|       # Filter upcoming events until 4 weeks in the future |       # Filter upcoming events until 4 weeks in the future | ||||||
|       parser.clear_events() |       parser.clear_events() | ||||||
|       upcoming_events = parser.get_events(now, now.shift(weeks=4)) |       upcoming_events = parser.get_events(now, now.shift(weeks=4)) | ||||||
|       parser.show_events() |       self._upcoming_events = upcoming_events | ||||||
|  |  | ||||||
|       # delete events which won't be able to fit (more events than lines) |       # delete events which won't be able to fit (more events than lines) | ||||||
|       upcoming_events[max_event_lines:] |       upcoming_events[max_event_lines:] | ||||||
| @@ -313,11 +295,6 @@ class calendar: | |||||||
|               (im_width, self.font.getsize(symbol)[1]), symbol, |               (im_width, self.font.getsize(symbol)[1]), symbol, | ||||||
|               font = self.font) |               font = self.font) | ||||||
|  |  | ||||||
|  |  | ||||||
| ################################################################### |  | ||||||
| ## Exception handling |  | ||||||
| ################################################################# |  | ||||||
|  |  | ||||||
|     # Save image of black and colour channel in image-folder |     # Save image of black and colour channel in image-folder | ||||||
|     im_black.save(images+self.name+'.png') |     im_black.save(images+self.name+'.png') | ||||||
|     im_colour.save(images+self.name+'_colour.png') |     im_colour.save(images+self.name+'_colour.png') | ||||||
| @@ -325,3 +302,6 @@ class calendar: | |||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|   print('running {0} in standalone mode'.format( |   print('running {0} in standalone mode'.format( | ||||||
|     os.path.basename(__file__).split('.py')[0])) |     os.path.basename(__file__).split('.py')[0])) | ||||||
|  |  | ||||||
|  | ##a = calendar(size, config) | ||||||
|  | ##a.generate_image() | ||||||
|   | |||||||
							
								
								
									
										179
									
								
								inkycal/modules/inkycal_image.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								inkycal/modules/inkycal_image.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | |||||||
|  | #!/usr/bin/python3 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | """ | ||||||
|  | Image module for inkycal Project | ||||||
|  | Copyright by aceisace | ||||||
|  | Development satge: Beta | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | from os import path | ||||||
|  | from PIL import ImageOps | ||||||
|  | import requests | ||||||
|  | import numpy | ||||||
|  |  | ||||||
|  | """----------------------------------------------------------------""" | ||||||
|  | #path = 'https://github.com/aceisace/Inky-Calendar/raw/master/Gallery/Inky-Calendar-logo.png' | ||||||
|  | #path  ='/home/pi/Inky-Calendar/images/canvas.png' | ||||||
|  | path      = inkycal_image_path | ||||||
|  | path_body = inkycal_image_path_body | ||||||
|  | mode = 'auto'         # 'horizontal' # 'vertical' # 'auto' | ||||||
|  | upside_down = False    # Flip image by 180 deg (upside-down) | ||||||
|  | alignment = 'center'  # top_center, top_left, center_left, bottom_right etc. | ||||||
|  | colours = 'bwr'       # bwr # bwy # bw | ||||||
|  | render = True         # show image on E-Paper? | ||||||
|  | """----------------------------------------------------------------""" | ||||||
|  |  | ||||||
|  | # First determine dimensions | ||||||
|  | if mode == 'horizontal': | ||||||
|  |   display_width, display_height == display_height, display_width | ||||||
|  |  | ||||||
|  | if mode == 'vertical': | ||||||
|  |   raise NotImplementedError('Vertical mode is not currenctly supported') | ||||||
|  |  | ||||||
|  | # .. Then substitute possibly parameterized path | ||||||
|  | # TODO Get (assigned) panel dimensions instead of display dimensions | ||||||
|  | path = path.replace('{model}', model).replace('{width}',str(display_width)).replace('{height}',str(display_height)) | ||||||
|  | print(path) | ||||||
|  |  | ||||||
|  | """Try to open the image if it exists and is an image file""" | ||||||
|  | try: | ||||||
|  |   if 'http' in path: | ||||||
|  |     if path_body is None: | ||||||
|  |       # Plain GET | ||||||
|  |       im = Image.open(requests.get(path, stream=True).raw) | ||||||
|  |     else: | ||||||
|  |       # POST request, passing path_body in the body | ||||||
|  |       im = Image.open(requests.post(path, json=path_body, stream=True).raw) | ||||||
|  |   else: | ||||||
|  |     im = Image.open(path) | ||||||
|  | except FileNotFoundError: | ||||||
|  |   print('Your file could not be found. Please check the path to your file.') | ||||||
|  |   raise | ||||||
|  | except OSError: | ||||||
|  |   print('Please check if the path points to an image file.') | ||||||
|  |   raise | ||||||
|  |  | ||||||
|  | """Turn image upside-down if specified""" | ||||||
|  | if upside_down == True: | ||||||
|  |   im.rotate(180, expand = True) | ||||||
|  |  | ||||||
|  | if mode == 'auto': | ||||||
|  |   if (im.width > im.height) and (display_width < display_height): | ||||||
|  |     print('display vertical, image horizontal -> flipping image') | ||||||
|  |     im = im.rotate(90, expand=True) | ||||||
|  |   if (im.width < im.height) and (display_width > display_height): | ||||||
|  |     print('display horizontal, image vertical -> flipping image') | ||||||
|  |     im = im.rotate(90, expand=True) | ||||||
|  |  | ||||||
|  | def fit_width(image, width): | ||||||
|  |   """Resize an image to desired width""" | ||||||
|  |   print('resizing width from', image.width, 'to', end = ' ') | ||||||
|  |   wpercent = (display_width/float(image.width)) | ||||||
|  |   hsize = int((float(image.height)*float(wpercent))) | ||||||
|  |   img = image.resize((width, hsize), Image.ANTIALIAS) | ||||||
|  |   print(img.width) | ||||||
|  |   return img | ||||||
|  |  | ||||||
|  | def fit_height(image, height): | ||||||
|  |   """Resize an image to desired height""" | ||||||
|  |   print('resizing height from', image.height, 'to', end = ' ') | ||||||
|  |   hpercent = (height / float(image.height)) | ||||||
|  |   wsize = int(float(image.width) * float(hpercent)) | ||||||
|  |   img = image.resize((wsize, height), Image.ANTIALIAS) | ||||||
|  |   print(img.height) | ||||||
|  |   return img | ||||||
|  |  | ||||||
|  | if im.width > display_width: | ||||||
|  |   im = fit_width(im, display_width) | ||||||
|  | if im.height > display_height: | ||||||
|  |   im = fit_height(im, display_height) | ||||||
|  |  | ||||||
|  | if alignment == 'center': | ||||||
|  |   x,y = int((display_width-im.width)/2), int((display_height-im.height)/2) | ||||||
|  | elif alignment == 'center_right': | ||||||
|  |   x, y = display_width-im.width, int((display_height-im.height)/2) | ||||||
|  | elif alignment == 'center_left': | ||||||
|  |   x, y = 0, int((display_height-im.height)/2) | ||||||
|  |  | ||||||
|  | elif alignment == 'top_center': | ||||||
|  |   x, y = int((display_width-im.width)/2), 0 | ||||||
|  | elif alignment == 'top_right': | ||||||
|  |   x, y = display_width-im.width, 0 | ||||||
|  | elif alignment == 'top_left': | ||||||
|  |   x, y = 0, 0 | ||||||
|  |  | ||||||
|  | elif alignment == 'bottom_center': | ||||||
|  |   x, y = int((display_width-im.width)/2), display_height-im.height | ||||||
|  | elif alignment == 'bottom_right': | ||||||
|  |   x, y = display_width-im.width, display_height-im.height | ||||||
|  | elif alignment == 'bottom_left': | ||||||
|  |   x, y = display_width-im.width, display_height-im.height | ||||||
|  |  | ||||||
|  | if len(im.getbands()) == 4: | ||||||
|  |   print('removing transparency') | ||||||
|  |   bg = Image.new('RGBA', (im.width, im.height), 'white') | ||||||
|  |   im = Image.alpha_composite(bg, im) | ||||||
|  |  | ||||||
|  | image.paste(im, (x,y)) | ||||||
|  | im = image | ||||||
|  |  | ||||||
|  | if colours == 'bw': | ||||||
|  |   """For black-white images, use monochrome dithering""" | ||||||
|  |   black = im.convert('1', dither=True) | ||||||
|  | elif colours == 'bwr': | ||||||
|  |   """For black-white-red images, create corresponding palette""" | ||||||
|  |   pal = [255,255,255, 0,0,0, 255,0,0, 255,255,255] | ||||||
|  | elif colours == 'bwy': | ||||||
|  |   """For black-white-yellow images, create corresponding palette""" | ||||||
|  |   pal = [255,255,255, 0,0,0, 255,255,0, 255,255,255] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | """Map each pixel of the opened image to the Palette""" | ||||||
|  | if colours != 'bw': | ||||||
|  |   palette_im = Image.new('P', (3,1)) | ||||||
|  |   palette_im.putpalette(pal * 64) | ||||||
|  |   quantized_im = im.quantize(palette=palette_im) | ||||||
|  |   quantized_im.convert('RGB') | ||||||
|  |  | ||||||
|  |   """Create buffer for coloured pixels""" | ||||||
|  |   buffer1 = numpy.array(quantized_im.convert('RGB')) | ||||||
|  |   r1,g1,b1 = buffer1[:, :, 0], buffer1[:, :, 1], buffer1[:, :, 2] | ||||||
|  |  | ||||||
|  |   """Create buffer for black pixels""" | ||||||
|  |   buffer2 = numpy.array(quantized_im.convert('RGB')) | ||||||
|  |   r2,g2,b2 = buffer2[:, :, 0], buffer2[:, :, 1], buffer2[:, :, 2] | ||||||
|  |  | ||||||
|  |   if colours == 'bwr': | ||||||
|  |     """Create image for only red pixels""" | ||||||
|  |     buffer2[numpy.logical_and(r2 ==  0, b2 == 0)] = [255,255,255] # black->white | ||||||
|  |     buffer2[numpy.logical_and(r2 ==  255, b2 == 0)] = [0,0,0] #red->black | ||||||
|  |     colour = Image.fromarray(buffer2) | ||||||
|  |     """Create image for only black pixels""" | ||||||
|  |     buffer1[numpy.logical_and(r1 ==  255, b1 == 0)] = [255,255,255] | ||||||
|  |     black = Image.fromarray(buffer1) | ||||||
|  |  | ||||||
|  |   if colours == 'bwy': | ||||||
|  |     """Create image for only yellow pixels""" | ||||||
|  |     buffer2[numpy.logical_and(r2 ==  0, b2 == 0)] = [255,255,255] # black->white | ||||||
|  |     buffer2[numpy.logical_and(g2 == 255, b2 == 0)] = [0,0,0] #yellow -> black | ||||||
|  |     colour = Image.fromarray(buffer2) | ||||||
|  |     """Create image for only black pixels""" | ||||||
|  |     buffer1[numpy.logical_and(g1 == 255, b1 == 0)] = [255,255,255] | ||||||
|  |     black = Image.fromarray(buffer1) | ||||||
|  | ## | ||||||
|  | ##if render == True: | ||||||
|  | ##  epaper = driver.EPD() | ||||||
|  | ##  print('Initialising E-Paper...', end = '') | ||||||
|  | ##  epaper.init() | ||||||
|  | ##  print('Done') | ||||||
|  | ## | ||||||
|  | ##  print('Sending image data and refreshing display...', end='') | ||||||
|  | ##  if three_colour_support == True: | ||||||
|  | ##    epaper.display(epaper.getbuffer(black), epaper.getbuffer(colour)) | ||||||
|  | ##  else: | ||||||
|  | ##    epaper.display(epaper.getbuffer(black)) | ||||||
|  | ##  print('Done') | ||||||
|  | ## | ||||||
|  | ##  print('Sending E-Paper to deep sleep...', end = '') | ||||||
|  | ##  epaper.sleep() | ||||||
|  | print('Done') | ||||||
| @@ -18,7 +18,8 @@ except ImportError: | |||||||
| # Debug Data (not for production use!) | # Debug Data (not for production use!) | ||||||
| size = (384, 160) | size = (384, 160) | ||||||
| config = {'rss_urls': ['http://feeds.bbci.co.uk/news/world/rss.xml#']} | config = {'rss_urls': ['http://feeds.bbci.co.uk/news/world/rss.xml#']} | ||||||
|  | #config = {'rss_urls': ['http://www.tagesschau.de/xml/atom/']} | ||||||
|  | #https://www.tagesschau.de/xml/rss2 | ||||||
|  |  | ||||||
| class rss: | class rss: | ||||||
|   """RSS class |   """RSS class | ||||||
| @@ -34,14 +35,15 @@ class rss: | |||||||
|     self.name = os.path.basename(__file__).split('.py')[0] |     self.name = os.path.basename(__file__).split('.py')[0] | ||||||
|     self.config = section_config |     self.config = section_config | ||||||
|     self.width, self.height = section_size |     self.width, self.height = section_size | ||||||
|  |  | ||||||
|     self.background_colour =  'white' |  | ||||||
|     self.font_colour = 'black' |  | ||||||
|     self.fontsize = 12 |     self.fontsize = 12 | ||||||
|     self.font = ImageFont.truetype(fonts['NotoSans-SemiCondensed'], |  | ||||||
|                                    size = self.fontsize) |  | ||||||
|     self.padding_x = 0.02 |     self.padding_x = 0.02 | ||||||
|     self.padding_y = 0.05 |     self.padding_y = 0.05 | ||||||
|  |     self.font = ImageFont.truetype(fonts['NotoSans-SemiCondensed'], | ||||||
|  |                                    size = self.fontsize) | ||||||
|  |  | ||||||
|  |     # module specifc config | ||||||
|  |     self.shuffle_feeds = True | ||||||
|  |  | ||||||
|     print('{0} loaded'.format(self.name)) |     print('{0} loaded'.format(self.name)) | ||||||
|  |  | ||||||
|   def set(self, **kwargs): |   def set(self, **kwargs): | ||||||
| @@ -76,15 +78,14 @@ class rss: | |||||||
|     logging.info('image size: {} x {} px'.format(im_width, im_height)) |     logging.info('image size: {} x {} px'.format(im_width, im_height)) | ||||||
|  |  | ||||||
|     # Create an image for black pixels and one for coloured pixels |     # Create an image for black pixels and one for coloured pixels | ||||||
|     im_black = Image.new('RGB', size = im_size, color = self.background_colour) |     im_black = Image.new('RGB', size = im_size, color = 'white') | ||||||
|     im_colour = Image.new('RGB', size = im_size, color = 'white') |     im_colour = Image.new('RGB', size = im_size, color = 'white') | ||||||
|  |  | ||||||
|     # Check if internet is available |     # Check if internet is available | ||||||
|     if internet_available() == True: |     if internet_available() == True: | ||||||
|       logging.info('Connection test passed') |       logging.info('Connection test passed') | ||||||
|     else: |     else: | ||||||
|       logging.error('Network could not be reached :/') |       raise Exception('Network could not be reached :/') | ||||||
|       raise Exception('Network could not be reached :(') |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # Set some parameters for formatting rss feeds |     # Set some parameters for formatting rss feeds | ||||||
| @@ -101,35 +102,42 @@ class rss: | |||||||
|       (0, spacing_top + _ * line_height ) for _ in range(max_lines)] |       (0, spacing_top + _ * line_height ) for _ in range(max_lines)] | ||||||
|  |  | ||||||
|  |  | ||||||
|     try: |  | ||||||
|     # Create list containing all rss-feeds from all rss-feed urls |     # Create list containing all rss-feeds from all rss-feed urls | ||||||
|     parsed_feeds = [] |     parsed_feeds = [] | ||||||
|     for feeds in self.config['rss_urls']: |     for feeds in self.config['rss_urls']: | ||||||
|       text = feedparser.parse(feeds) |       text = feedparser.parse(feeds) | ||||||
|       for posts in text.entries: |       for posts in text.entries: | ||||||
|         parsed_feeds.append('•{0}: {1}'.format(posts.title, posts.summary)) |         parsed_feeds.append('•{0}: {1}'.format(posts.title, posts.summary)) | ||||||
|       # print(parsed_feeds) |  | ||||||
|  |     self._parsed_feeds = parsed_feeds | ||||||
|  |  | ||||||
|     # Shuffle the list to prevent showing the same content |     # Shuffle the list to prevent showing the same content | ||||||
|  |     if self.shuffle_feeds == True: | ||||||
|       shuffle(parsed_feeds) |       shuffle(parsed_feeds) | ||||||
|  |  | ||||||
|     # Trim down the list to the max number of lines |     # Trim down the list to the max number of lines | ||||||
|     del parsed_feeds[max_lines:] |     del parsed_feeds[max_lines:] | ||||||
|  |  | ||||||
|  |  | ||||||
|     # Wrap long text from feeds (line-breaking) |     # Wrap long text from feeds (line-breaking) | ||||||
|     flatten = lambda z: [x for y in z for x in y] |     flatten = lambda z: [x for y in z for x in y] | ||||||
|     filtered_feeds, counter = [], 0 |     filtered_feeds, counter = [], 0 | ||||||
|  |  | ||||||
|     for posts in parsed_feeds: |     for posts in parsed_feeds: | ||||||
|       wrapped = text_wrap(posts, font = self.font, max_width = line_width) |       wrapped = text_wrap(posts, font = self.font, max_width = line_width) | ||||||
|         counter += len(filtered_feeds) + len(wrapped) |       counter += len(wrapped) | ||||||
|       if counter < max_lines: |       if counter < max_lines: | ||||||
|         filtered_feeds.append(wrapped) |         filtered_feeds.append(wrapped) | ||||||
|     filtered_feeds = flatten(filtered_feeds) |     filtered_feeds = flatten(filtered_feeds) | ||||||
|  |     self._filtered_feeds = filtered_feeds | ||||||
|  |  | ||||||
|  |     # Check if feeds could be parsed and can be displayed | ||||||
|  |     if len(filtered_feeds) == 0 and len(parsed_feeds) > 0: | ||||||
|  |       print('Feeds could be parsed, but the text is too long to be displayed:/') | ||||||
|  |     elif len(filtered_feeds) == 0 and len(parsed_feeds) == 0: | ||||||
|  |       print('No feeds could be parsed :/') | ||||||
|  |     else: | ||||||
|       # Write rss-feeds on image |       # Write rss-feeds on image | ||||||
|       """Write the correctly formatted text on the display""" |  | ||||||
|       for _ in range(len(filtered_feeds)): |       for _ in range(len(filtered_feeds)): | ||||||
|         write(im_black, line_positions[_], (line_width, line_height), |         write(im_black, line_positions[_], (line_width, line_height), | ||||||
|               filtered_feeds[_], font = self.font, alignment= 'left') |               filtered_feeds[_], font = self.font, alignment= 'left') | ||||||
| @@ -137,11 +145,6 @@ class rss: | |||||||
|     # Cleanup |     # Cleanup | ||||||
|     del filtered_feeds, parsed_feeds, wrapped, counter, text |     del filtered_feeds, parsed_feeds, wrapped, counter, text | ||||||
|  |  | ||||||
|     except Exception as e: |  | ||||||
|       print('Error in {0}'.format(self.name)) |  | ||||||
|       print('Reason: ',e) |  | ||||||
|       write(im_black, (0,0), (im_width, im_height), str(e), font = self.font) |  | ||||||
|  |  | ||||||
|     # Save image of black and colour channel in image-folder |     # Save image of black and colour channel in image-folder | ||||||
|     im_black.save(images+self.name+'.png', 'PNG') |     im_black.save(images+self.name+'.png', 'PNG') | ||||||
|     im_colour.save(images+self.name+'_colour.png', 'PNG') |     im_colour.save(images+self.name+'_colour.png', 'PNG') | ||||||
| @@ -149,3 +152,6 @@ class rss: | |||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|   print('running {0} in standalone mode'.format( |   print('running {0} in standalone mode'.format( | ||||||
|     os.path.basename(__file__).split('.py')[0])) |     os.path.basename(__file__).split('.py')[0])) | ||||||
|  |  | ||||||
|  | ##a = rss(size, config) | ||||||
|  | ##a.generate_image() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user