# Copyright (C) 2020 Daniel Richardson richardson.daniel@hotmail.co.uk # # This file is part of stockTicker project for justinodunn. # # stockTicker can not be copied and/or distributed without the express # permission of Daniel Richardson import sys, select import os import threading from PIL import Image, ImageDraw, ImageFont Image.init() import time import csv import requests import pexpect from rgbmatrix import RGBMatrix, RGBMatrixOptions from rgbmatrix.graphics import * from multiprocessing import Process import traceback import json from datetime import datetime import matplotlib.colors as mcolors def getInput(Block=False): if Block or select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []): msg = sys.stdin.read(1) #sys.stdin.flush() else: msg = '' return msg class StockTicker(): def __init__(self): #Define global resources self.symbols = [] self.greenORred = (255, 255, 255) #self.blank = Image.open('logos/blank.png') self.blank = Image.new('RGB', (10, 32)) self.running = True self.brightness = 1.0 self.delay = 0.02 # Configuration for the matrix options = RGBMatrixOptions() options.rows = 32 options.cols = 64 options.chain_length = 2 options.parallel = 1 options.hardware_mapping = 'adafruit-hat' # If you have an Adafruit HAT: 'adafruit-hat' options.gpio_slowdown = 4 self.matrix = RGBMatrix(options = options) self.points = True # display crypto change in points or percent self.functions = {'stocks': self.getStockImage, 'crypto': self.getCryptoImage, 'forex': self.getForexImage, 'daily_weather':self.getDailyWeatherImage, 'today_weather': self.getTodayWeatherImage, 'league_table': self.getLeagueTableImage, 'league_games': self.getLeagueImage, 'news':self.getNewsImage, 'text': self.getUserText, 'display_image': self.getUserImage, 'display_gif':self.getUserGIF} #put this somewhere else def openImage(self, image_file): image = Image.open(image_file) # Make image fit our screen. #image.thumbnail((self.matrix.width, self.matrix.height), Image.ANTIALIAS) #image = image.convert('RGB') return image def degreesToCompass(self, deg): dirs = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'] return dirs[int((deg+22.5)//45)%8] def setImage(self, image, offset_x = 0, offset_y = 0, unsafe=True, min_x = 0, max_x = 128, min_y = 0, max_y = 32): if (image.mode != "RGB"): image = image.convert('RGB') if unsafe: #In unsafe mode we directly acceshow to send commands to running python processs the underlying PIL image array #in cython, which is considered unsafe pointer accecss, #however it's super fast and seems to work fine #https://groups.google.com/forum/#!topic/cython-users/Dc1ft5W6KM4 img_width, img_height = image.size self.matrix.SetPixelsPillow(offset_x, offset_y, img_width, img_height, image) else: # First implementation of a SetImage(). OPTIMIZE_ME: A more native # implementation that directly reads the buffer and calls the underlying # C functions can certainly be faster. img_width, img_height = image.size pixels = image.load() for y in range(max(0, -offset_y), min(img_height, self.matrix.height - offset_y)): for x in range(max(0, -offset_x), min(img_width, self.matrix.width - offset_x)): if min_x <= x + offset_x <= max_x and min_y <= y + offset_y <= max_y: (r, g, b) = pixels[x, y] self.matrix.SetPixel(x + offset_x, y + offset_y, r*self.brightness, g*self.brightness, b*self.brightness) def scrollImage(self, image, offset_x = 0, offset_y = 0): img_width, img_height = image.size double_buffer = self.matrix.CreateFrameCanvas() image = image.convert('RGB') while offset_x > -img_width: offset_x -= 1 double_buffer.SetImage(image, offset_x, offset_y) double_buffer = self.matrix.SwapOnVSync(double_buffer) # remove the ppixels behind the image, to stop trailing for x in range(offset_x + img_width, 128): for y in range(self.matrix.height): self.matrix.SetPixel(x , y , 0,0,0) kill = self.checkKilled() if kill: break time.sleep(self.delay) return kill def scrollImageStacked(self, image, offset_x = 0, offset_y = 0): img_width, img_height = image.size while offset_x > -img_width - 128: offset_x -= 1 self.setImage(image, offset_x = offset_x+128, offset_y = offset_y) self.setImage(image, offset_x = offset_x, offset_y = offset_y+20) kill = self.checkKilled() time.sleep(self.delay) return kill def scrollImageTransition(self, image_files, offset_x = 0, offset_y = 0, stocks = True): # use two image files and switch between them with a seemless transition current_img = 1 image1 = self.openImage(image_files[0]).convert('RGB') image2 = self.openImage(image_files[1]).convert('RGB') double_buffer = self.matrix.CreateFrameCanvas() kill = False while True: if current_img == 1: if stocks: update_process = Process(target = self.getFullStockImage, args = (1,)) update_process.start() image1 = self.openImage(image_files[0]).convert('RGB') image2 = self.openImage(image_files[1]).convert('RGB') elif current_img == 2: if stocks: update_process = Process(target = self.getFullStockImage, args = (2,)) update_process.start() image1 = self.openImage(image_files[1]).convert('RGB') image2 = self.openImage(image_files[0]).convert('RGB') img_width, img_height = image1.size while offset_x > -img_width: offset_x -= 1 double_buffer.SetImage(image1, offset_x, offset_y) if offset_x + img_width < self.matrix.width: # if the image is ending double_buffer.SetImage(image2, offset_x, offset_y) double_buffer = self.matrix.SwapOnVSync(double_buffer) time.sleep(self.delay) kill = self.checkKilled() if kill: break if stocks: image1.close() image2.close() if kill: break if stocks: update_process.join() if current_img == 1: current_img = 2 elif current_img == 2: current_img = 1 offset_x = 0 def updateMultiple(self, options): for option in options: if option not in ['display_gif']: # aving the gif like this kills the animation img = self.functions[option]() img.save('./display_images/'+ option+ '.ppm') def checkKilled(self): kill = False try: msg = getInput() if msg == 'K': self.resetMatrix() kill = True self.process_msg(msg) except KeyboardInterrupt: sys.stdout.flush() pass return kill def scrollFunctionsAnimated(self, options, animation = 'continuous'): # scrolls trhough all functions with animation. Updates functions and remakes images when each function not being dispplayed self.updateMultiple([options[0]]) kill = False i = 0 # keep track of which image we are displaying while True: update_process = Process(target = self.updateMultiple, args = ([options[(i+1) % len(options)]],)) update_process.start() if options[i % len(options)] != 'display_gif': image = self.openImage('./display_images/' + options[i % len(options)] +'.ppm') image = image.convert('RGB') else: image = self.openImage('./display_images/user_gif.gif') img_width, img_height = image.size double_buffer = self.matrix.CreateFrameCanvas() offset_x = 0 if animation == 'traditional': offset_x = 128 elif animation == 'continuous': offset_x = 0 elif animation in ['up', 'down']: offset_x = max(0, 128-img_width) offset_y = 0 #first scroll image in from bottom frames = 10 #controls how fast gifs run frame = 0 pause_frames = int(0.5/self.delay) if animation == 'up': offset_y = 33 while offset_y > 0: # for animation in gifs if offset_y%frames == 0: try: image.seek(frame) except EOFError: frame = 0 image.seek(frame) frame +=1 offset_y -= 1 if options[i % len(options)] != 'display_gif': double_buffer.SetImage(image, offset_x, offset_y) else: double_buffer.SetImage(image.convert('RGB'), offset_x, offset_y) double_buffer = self.matrix.SwapOnVSync(double_buffer) time.sleep(self.delay) kill = self.checkKilled() if kill: break while pause_frames > 0: if pause_frames%frames == 0: try: image.seek(frame) except EOFError: frame = 0 image.seek(frame) frame +=1 pause_frames -=1 if options[i % len(options)] != 'display_gif': double_buffer.SetImage(image, offset_x, offset_y) else: double_buffer.SetImage(image.convert('RGB'), offset_x, offset_y) double_buffer = self.matrix.SwapOnVSync(double_buffer) time.sleep(self.delay) kill = self.checkKilled() if kill: break elif animation == 'down': offset_y = -33 while offset_y < 0: # for animation in gifs if offset_y%frames == 0: try: image.seek(frame) except EOFError: frame = 0 image.seek(frame) frame +=1 offset_y += 1 if options[i % len(options)] != 'display_gif': double_buffer.SetImage(image, offset_x, offset_y) else: double_buffer.SetImage(image.convert('RGB'), offset_x, offset_y) double_buffer = self.matrix.SwapOnVSync(double_buffer) time.sleep(self.delay) kill = self.checkKilled() if kill: break if kill: break while pause_frames > 0: if pause_frames%frames == 0: try: image.seek(frame) except EOFError: frame = 0 image.seek(frame) frame +=1 pause_frames -=1 if options[i % len(options)] != 'display_gif': double_buffer.SetImage(image, offset_x, offset_y) else: double_buffer.SetImage(image.convert('RGB'), offset_x, offset_y) double_buffer = self.matrix.SwapOnVSync(double_buffer) time.sleep(self.delay) kill = self.checkKilled() if kill: break if kill: break while offset_x > -img_width: # for animation in gifs if offset_x%frames == 0: try: print(frame) image.seek(frame) except EOFError: frame = 0 image.seek(frame) frame +=1 #image = image.convert('RGB') offset_x -= 1 #self.setImage(image.convert('RGB'), offset_x = offset_x, offset_y = offset_y) if options[i % len(options)] != 'display_gif': double_buffer.SetImage(image, offset_x, offset_y) else: double_buffer.SetImage(image.convert('RGB'), offset_x, offset_y) buff = 0 # remove the ppixels behind the image, to stop trailing double_buffer = self.matrix.SwapOnVSync(double_buffer) for y in range(self.matrix.height): self.matrix.SetPixel(offset_x + img_width , y , 0,0,0) self.matrix.SetPixel(offset_x + img_width +1 , y , 0,0,0) self.matrix.SetPixel(offset_x + img_width +2 , y , 0,0,0) time.sleep(self.delay) kill = self.checkKilled() if kill: break if kill: break update_process.join() i+=1 def textImage(self, text, font, r = 255, g = 255, b = 255, matrix_height = False, w_buff = 3, h_buff = 3): ''' creates and returns a ppm image containing the text in the supplied font and colour ''' width, height = self.get_text_dimensions(text, font) if matrix_height: height = 32 img = Image.new('RGB', (width + w_buff, height + h_buff)) d = ImageDraw.Draw(img) d.text((0, 0), text, fill=(r, g, b), font=font) return img def getUserText(self): ''' displays the text entered in the webpage by the user. ''' f = open('csv/scroll_text.csv', 'r') CSV = csv.reader(f) text, r, g, b = next(CSV) f.close() title_img = self.openImage('feature_titles/message.png') font = ImageFont.load("./fonts/texgyre-27.pil") img = self.textImage(text, font, int(r), int(g), int(b), True, w_buff = 50) return self.stitchImage([title_img, img]) def displayGIF(self, gif): # To iterate through the entire gif i = 0 while True: try: gif.seek(i) except EOFError: i = 0 gif.seek(i) # do something to im self.setImage(gif.convert('RGB')) time.sleep(0.5) i += 1 try: msg = getInput() if msg == 'K': gif.close() self.resetMatrix() break self.process_msg(msg) except KeyboardInterrupt: sys.stdout.flush() pass def scrollGIF(self, gif, offset_x = 0, offset_y = 0): # To iterate through the entire gif i = 0 img_width, img_height = gif.size while offset_x > -img_width: offset_x -= 1 try: gif.seek(i) except EOFError: i = 0 gif.seek(i) # do something to im self.setImage(gif.convert('RGB'), offset_x = offset_x) time.sleep(self.delay) if offset_x % 20 == 0: i += 1 for x in range(offset_x + img_width, 128): for y in range(self.matrix.height): self.matrix.SetPixel(x , y , 0,0,0) kill = self.checkKilled() if kill: break return kill #Using change between min and day price give appropriate arrow #and set the overall change colour def getArrow(self, CHANGE, professional = False): self.greenORred logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'stocks') if(CHANGE>0): Arrow = Image.open(os.path.join(logos_path, 'up.png')) self.greenORred = (0, 255, 0) else: Arrow = Image.open(os.path.join(logos_path, 'down.png')) self.greenORred = (255, 0, 0) CHANGE = (CHANGE * -1) if professional: w, h = Arrow.size Arrow = Arrow.resize((int(w/2), int(h/2))) return Arrow, CHANGE def get_text_dimensions(self, text_string, font): canvas = Image.new('RGB', (10000,100)) draw = ImageDraw.Draw(canvas) monospace = font text = text_string white = (255,255,255) draw.text((0, 0), text, font=monospace, fill=white) bbox = canvas.getbbox() width = bbox[2]-bbox[0] height = bbox[3]-bbox[1] return width,height #Draw Ticker, current and change onto one image def textToImage(self, TICKER, CURRENT, CHANGE, ARROW, font = ImageFont.load("./fonts/10x20.pil")): text_width_current, text_height = self.get_text_dimensions(CURRENT, font) img = Image.new('RGB', (text_width_current +100 , 32)) d = ImageDraw.Draw(img) d.text((4, 0), TICKER, fill=(255, 255, 255), font=font) d.text((4, 16), CURRENT, fill=self.greenORred, font=font) img.paste(ARROW, ((text_width_current + 9),18)) d.text(((text_width_current+29), 16), CHANGE, fill=self.greenORred, font=font) text_width_change, text_height = self.get_text_dimensions(CHANGE, font) newWidth = text_width_current + text_width_change +30 img = img.crop((0,0,newWidth,32)) return img def textToImageProf(self, TICKER, CURRENT, CHANGE, ARROW, font): text_width_current, text_height = self.get_text_dimensions(CURRENT, font) img = Image.new('RGB', (text_width_current +100 , 32)) d = ImageDraw.Draw(img) d.text((4, 0), TICKER, fill=(255, 255, 255), font=font) d.text((4, 8), CURRENT, fill=self.greenORred, font=font) img.paste(ARROW, ((text_width_current + 7),10)) d.text(((text_width_current+18), 8), CHANGE, fill=self.greenORred, font=font) text_width_change, text_height = self.get_text_dimensions(CHANGE, font) newWidth = text_width_current + text_width_change +30 img = img.crop((0,0,newWidth,32)) return img #Stitch the logo & prices picture into one image def stitchImage(self, image_list): widths, heights = zip(*(i.size for i in image_list)) total_width = sum(widths) max_height = max(heights) new_im = Image.new('RGB', (total_width, max_height)) x_offset = 0 for im in image_list: new_im.paste(im, (x_offset,0)) x_offset += im.size[0] return new_im def resetMatrix(self): for x in range(self.matrix.width): for y in range(self.matrix.height): self.matrix.SetPixel(x , y , 0,0,0) def getCryptoImage(self): title_img = self.openImage('feature_titles/crypto.png') image_list = [title_img] image_list.append(self.blank) start = time.time() self.readCryptoCSV() for i, coin in enumerate(self.coins): info = self.coin_info[coin] change = float(info[3]) #TEXT ticker = info[0] #TEXT current = float(info[2]) #TEXT base = info[1].upper() if self.points: # convert percent to points change = change/100 * current current = '%.2f' % current arrow, change = self.getArrow(change) change = '%.2f' % change midFrame = self.textToImage(ticker + '(' + base + ')', current, change, arrow) #IMAGE THE TEXT try: logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'crypto') logo = Image.open(os.path.join(logos_path, ticker + '.png')) stitchedStock = self.stitchImage([logo,midFrame]) except: stitchedStock = midFrame image_list.append(stitchedStock) image_list.append(self.blank) finalDisplayImage = self.stitchImage(image_list) return finalDisplayImage def getCryptoProfessional(self): title_img = self.openImage('feature_titles/small_feature_titles/crypto.png') self.blank = Image.new('RGB', (0, 16)) image_list = [title_img, Image.new('RGB', (5, 16))] start = time.time() self.readCryptoCSV() for i, coin in enumerate(self.coins): info = self.coin_info[coin] change = float(info[3]) #TEXT ticker = info[0] #TEXT current = float(info[2]) #TEXT base = info[1].upper() if self.points: # convert percent to points change = change/100 * current current = '%.2f' % current arrow, change = self.getArrow(change, professional=True) change = '%.2f' % change midFrame = self.textToImageProf(ticker + '(' + base + ')', current, change, arrow, font=ImageFont.load("./fonts/6x10.pil")) #IMAGE THE TEXT try: logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'crypto') logo = Image.open(os.path.join(logos_path, ticker + '.png')) w,h = logo.size logo=logo.resize((int(w/2), int(h/2))) stitchedStock = self.stitchImage([logo,midFrame]) except: stitchedStock = midFrame image_list.append(stitchedStock) image_list.append(self.blank) finalDisplayImage = self.stitchImage(image_list) self.blank = Image.new('RGB', (10, 32)) return finalDisplayImage def getForexImage(self): title_img = self.openImage('feature_titles/forex.png') image_list = [title_img] image_list.append(self.blank) start = time.time() base, currency_info = json.load(open('csv/currency.json', 'r')) currencies = ['AUD', 'CAD', 'CHF', 'EUR', 'GBP', 'JPY', 'NZD'] for i, currency in enumerate(currencies): current, yesterday = currency_info[currency] change = 1/current - 1/yesterday current = 1/current current = '%.3f' % current arrow, change = self.getArrow(change) change = '%.6f' % change midFrame = self.textToImage(currency + '(' + base + ')', current, change, arrow) #IMAGE THE TEXT try: logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'currencies') logo = Image.open(os.path.join(logos_path, currency.lower() + '.png')) bse = Image.open(os.path.join(logos_path, base.lower() + '.png')) new_im = Image.new('RGB', (32, 32)) new_im.paste(bse, (0,10), bse.convert('RGBA')) new_im.paste(logo, (10,0), logo.convert('RGBA')) stitchedStock = self.stitchImage([new_im, midFrame]) image_list.append(new_im) except Exception as e: print(e) image_list.append(midFrame) image_list.append(self.blank) finalDisplayImage = self.stitchImage(image_list) return finalDisplayImage def getForexProfessional(self): title_img = self.openImage('feature_titles/small_feature_titles/forex.png') self.blank = Image.new('RGB', (0, 16)) image_list = [title_img, Image.new('RGB', (5, 16))] base, currency_info = json.load(open('csv/currency.json', 'r')) currencies = ['AUD', 'CAD', 'CHF', 'EUR', 'GBP', 'JPY', 'NZD'] for i, currency in enumerate(currencies): current, yesterday = currency_info[currency] change = 1/current - 1/yesterday current = 1/current current = '%.3f' % current arrow, change = self.getArrow(change, professional = True) change = '%.6f' % change midFrame = self.textToImageProf(currency + '(' + base + ')', current, change, arrow, font = ImageFont.load("./fonts/6x10.pil")) #IMAGE THE TEXT try: logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'currencies') logo = Image.open(os.path.join(logos_path, currency.lower() + '.png')) bse = Image.open(os.path.join(logos_path, base.lower() + '.png')) new_im = Image.new('RGB', (32, 32)) new_im.paste(bse, (0,10), bse.convert('RGBA')) new_im.paste(logo, (10,0), logo.convert('RGBA')) width, height = new_im.size new_im = new_im.resize((int(width/2), int(height/2))) stitchedStock = self.stitchImage([new_im, midFrame]) image_list.append(new_im) except Exception as e: print(e) image_list.append(midFrame) image_list.append(self.blank) finalDisplayImage = self.stitchImage(image_list) self.blank = Image.new('RGB', (10, 32)) return finalDisplayImage def getStockImage(self): title_img = self.openImage('feature_titles/stocks.png') image_list = [title_img] image_list.append(self.blank) start = time.time() self.readStocksCSV() for i, symbol in enumerate(self.symbols): info = self.stock_info[symbol] change = float(info[0])-float(info[1]) #TEXT ticker = symbol #TEXT current = '%.2f' % float(info[0]) #TEXT arrow, change = self.getArrow(change) change = '%.2f' % change midFrame = self.textToImage(ticker, current, change, arrow) #IMAGE THE TEXT try: logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'stocks') logo = Image.open(os.path.join(logos_path, ticker + '.png')) stitchedStock = self.stitchImage([logo,midFrame]) except: stitchedStock = midFrame image_list.append(stitchedStock) image_list.append(self.blank) finalDisplayImage = self.stitchImage(image_list) return finalDisplayImage def getStockProfessional(self): title_img = self.openImage('feature_titles/small_feature_titles/stocks.png') image_list = [title_img, Image.new('RGB', (5, 16))] self.readStocksCSV() self.blank = Image.new('RGB', (0, 16)) for i, symbol in enumerate(self.symbols): info = self.stock_info[symbol] change = float(info[0])-float(info[1]) #TEXT ticker = symbol #TEXT current = '%.2f' % float(info[0]) #TEXT arrow, change = self.getArrow(change, professional=True) change = '%.2f' % change midFrame = self.textToImageProf(ticker, current, change, arrow, font=ImageFont.load("./fonts/6x10.pil")) #IMAGE THE TEXT try: try: #load the tiny logo logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'tiny_stocks') logo = Image.open(os.path.join(logos_path, ticker + '.png')) except: # load the big logo and scale it logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'stocks') logo = Image.open(os.path.join(logos_path, ticker + '.png')) # half the size of the logo width, height = logo.size logo = logo.resize((int(width/2), int(height/2))) stitchedStock = self.stitchImage([logo,midFrame]) except Exception as e: stitchedStock = midFrame image_list.append(stitchedStock) image_list.append(self.blank) finalDisplayImage = self.stitchImage(image_list) self.blank = Image.new('RGB', (10, 32)) return finalDisplayImage def getNewsImage(self): title_img = self.openImage('feature_titles/news.png') headline_font = ImageFont.load("./fonts/6x13.pil") source_font = ImageFont.load("./fonts/6x13.pil") image_list = [title_img] headlines = [] source_date_times = [] sources = [] f = open('csv/news.csv', 'r') CSV = csv.reader(f) next(CSV) for row in CSV: headline, source, date, time = row headlines.append(headline) source_date_times.append(source + ': ' + date + ' ' + time) sources.append(source) f.close() for i, headline in enumerate(headlines): headline = headline.replace("^", ",") headline = headline.replace("’", "'") headline = headline.replace("‘", "'") headline = headline.replace('“', '"') headline = headline.replace('”', '"') headline = headline.replace('—', '-') headline = headline.replace('–', '-') headline = ''.join([h for h in headline if ord(h) < 256]) lst = headline.rsplit('-', 1) #remove source name at end of headline headline = lst[0] headline_img = self.textImage(headline, headline_font, matrix_height = True) source_img = self.textImage(source_date_times[i], source_font, r=255, g=255, b=0, matrix_height = True) try: logo_name = sources[i].lower().replace(' ', '-') logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'news_logos') logo = Image.open(os.path.join(logos_path, logo_name + '.png')) except Exception as e: logo_name = 'default' logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'news_logos') logo = Image.open(os.path.join(logos_path, logo_name + '.png')) img = Image.new('RGB', (headline_img.size[0], 32)) img.paste(headline_img, (2, 0)) img.paste(source_img, (2,16)) img= self.stitchImage([logo,img]) image_list.append(img) image_list.append(self.blank) news_image = self.stitchImage(image_list) return news_image def getNewsProfessional(self): headline_font = ImageFont.load("./fonts/6x10.pil") source_font = ImageFont.load("./fonts/6x10.pil") title_img = self.openImage('feature_titles/small_feature_titles/news.png') image_list = [title_img, Image.new('RGB', (5, 16))] headlines = [] sources = [] f = open('csv/news.csv', 'r') CSV = csv.reader(f) next(CSV) for row in CSV: headline, source, date, time = row headlines.append(headline) sources.append(source) f.close() blank = Image.new('RGB', (0, 16)) for i, headline in enumerate(headlines): headline = headline.replace("^", ",") headline = headline.replace("’", "'") headline = headline.replace("‘", "'") headline = headline.replace('“', '"') headline = headline.replace('”', '"') headline = headline.replace('—', '-') headline = headline.replace('–', '-') headline = ''.join([h for h in headline if ord(h) < 256]) lst = headline.rsplit('-', 1) #remove source name at end of headline headline = lst[0] headline_img = self.textImage(headline, headline_font, matrix_height = True) source_img = self.textImage(sources[i] + ':', source_font, r=255, g=255, b=0, matrix_height = True) try: logo_name = sources[i].lower().replace(' ', '-') logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'news_logos') logo = Image.open(os.path.join(logos_path, logo_name + '.png')) except Exception as e: logo_name = 'default' logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'news_logos') logo = Image.open(os.path.join(logos_path, logo_name + '.png')) width, height = logo.size logo = logo.resize((int(width/2), int(height/2))) img = Image.new('RGB', (headline_img.size[0]+ source_img.size[0] + logo.size[0] +5, 32)) img.paste(headline_img, (source_img.size[0]+ logo.size[0] -5, 3)) img.paste(source_img, (2,3)) img= self.stitchImage([logo,img]) image_list.append(img) image_list.append(blank) news_image = self.stitchImage(image_list) return news_image def getLeagueImage(self, league=False, time = 'past'): if not league: f = open( "csv/league.txt", 'r' ) league = f.read().replace('\n', '') f.close() img = Image.new('RGB', (10000, 32)) league_info = json.load(open('csv/sports/{}/{}_games.json'.format(league, time), 'r')) small_font = ImageFont.load("./fonts/5x7.pil") med_font = ImageFont.load("./fonts/7x14B.pil") large_font = ImageFont.load("./fonts/9x18B.pil") sports_info = self.readSportsCSV(league) buff_size = 25 x_offset = 0 for match in league_info[-15:]: home_team = match['home_team'] away_team = match['away_team'] home_score = match['home_score'] away_score = match['away_score'] date = match['date'].replace('-', '.') #rond = match['round'] try: home_logo = Image.open('logos/sports/{}/{}'.format(league, sports_info[home_team]['logo'])) except Exception as e: home_logo = self.textImage(home_team.replace(' ', '\n'), small_font, r = 255, g = 255, b = 255) try: away_logo = Image.open('logos/sports/{}/{}'.format(league, sports_info[away_team]['logo'])) except Exception as e: away_logo = self.textImage(away_team.replace(' ', '\n'), small_font, r = 255, g = 255, b = 255) date_timage = self.textImage(date, small_font, r = 255, g = 255, b = 255) img.paste(home_logo, (x_offset,0)) x_offset += home_logo.size[0] + 2 if time == 'future': img.paste(date_timage, (x_offset+5, 0)) h_colour = mcolors.to_rgb(sports_info[home_team]['colour'].replace(' ', '')) a_colour = mcolors.to_rgb(sports_info[away_team]['colour'].replace(' ', '')) hc_timage = self.textImage(sports_info[home_team]['code'], med_font, r = int(h_colour[0]*255), g = int(h_colour[1]*255), b = int(h_colour[2]*255)) ac_timage = self.textImage(sports_info[away_team]['code'], med_font, r = int(a_colour[0]*255), g = int(a_colour[1]*255), b = int(a_colour[2]*255)) vs_timage = self.textImage('vs', med_font, r = 255, g = 255, b = 255, h_buff = 5) img.paste(hc_timage, (x_offset, 9)) img.paste(vs_timage, (x_offset + hc_timage.size[0], 9)) img.paste(ac_timage, (x_offset + hc_timage.size[0] + vs_timage.size[0], 9)) x_offset += max( date_timage.size[0], hc_timage.size[0] + vs_timage.size[0] + ac_timage.size[0]) else: score_image = self.textImage(home_score + '-' + away_score, large_font, h_buff = 5, r = 255, g = 255, b = 255) #vs_timage = self.textImage(sports_info[home_team]['code'] + 'vs' + sports_info[away_team]['code'], small_font, r = 255, g = 255, b = 255) h_colour = mcolors.to_rgb(sports_info[home_team]['colour'].replace(' ', '')) a_colour = mcolors.to_rgb(sports_info[away_team]['colour'].replace(' ', '')) hc_timage = self.textImage(sports_info[home_team]['code'], small_font, r = int(h_colour[0]*255), g = int(h_colour[1]*255), b = int(h_colour[2]*255)) ac_timage = self.textImage(sports_info[away_team]['code'], small_font, r = int(a_colour[0]*255), g = int(a_colour[1]*255), b = int(a_colour[2]*255)) vs_timage = self.textImage('vs', small_font, r = 255, g = 255, b = 255) if date_timage.size[0] > score_image.size[0]: img.paste(date_timage, (x_offset+2, 0)) img.paste(hc_timage, (x_offset+6, 9)) img.paste(vs_timage, (x_offset+5 + hc_timage.size[0], 9)) img.paste(ac_timage, (x_offset+6 + hc_timage.size[0] + vs_timage.size[0], 9)) img.paste(score_image, (x_offset + 2 + int((date_timage.size[0] - score_image.size[0])/2), 15)) else: img.paste(date_timage, (x_offset+1+int((score_image.size[0] - date_timage.size[0] )/2), 0)) vs_size = hc_timage.size[0] + vs_timage.size[0] + ac_timage.size[0] img.paste(hc_timage, (x_offset + 1 + int((score_image.size[0] - vs_size)/2), 9)) img.paste(vs_timage, (x_offset + int((score_image.size[0] - vs_size)/2) + hc_timage.size[0], 9)) img.paste(ac_timage, (x_offset+1 + int((score_image.size[0] - vs_size)/2) + hc_timage.size[0] + vs_timage.size[0], 9)) img.paste(score_image, (x_offset+1, 15)) x_offset += max( date_timage.size[0]+4, hc_timage.size[0] + vs_timage.size[0] + ac_timage.size[0]+4, 2 + int(score_image.size[0])) #img.paste(vs_timage, (x_offset+4, 9)) #if league == 'NHL': # #img.paste(round_timage, (x_offset+ 7, 8)) #x_offset += max(home_timage.size[0], away_timage.size[0], date_timage.size[0], round_timage.size[0], score_image.size[0]) img.paste(away_logo, (x_offset,0)) x_offset += away_logo.size[0] x_offset += buff_size img = img.crop((0,0,x_offset ,32)) font = ImageFont.load("./fonts/texgyre-27.pil") title_img = self.textImage(time.upper() + ' GAMES', font, matrix_height = True) return self.stitchImage([title_img, img]) def getLeagueTableImage(self, league = False): if not league: f = open( "csv/table_league.txt", 'r' ) league = f.read().replace('\n', '') f.close() img = Image.new('RGB', (10000, 32)) team_info = json.load(open('csv/sports/{}/team_stats.json'.format(league), 'r')) small_font = ImageFont.load("./fonts/5x7.pil") med_font = ImageFont.load("./fonts/8x13B.pil") large_font = ImageFont.load("./fonts/10x20.pil") if league =='NHL': # read the NHl info from the csv, prem will need this as well sports_info = self.readSportsCSV(league) buff_size = 20 x_offset = 0 for team in team_info: try: if league == 'NHL': logo = Image.open('logos/sports/{}/{}'.format(league, sports_info[team['name']]['logo'])) elif league == 'premier_league': logo = Image.open('logos/sports/{}/{}.png'.format(league, team['name'])) img.paste(logo, (x_offset, 0)) x_offset += logo.size[0] + 2 except Exception as e: print('no logo for:', team['name']) name_timage = self.textImage(team['name'], med_font, r = 255, g = 255, b = 0) wins_timage = self.textImage('Wins:' + team['wins'], small_font, r = 0, g = 255, b = 0) loss_timage = self.textImage('Losses:' + team['loss'], small_font, r = 255, g = 0, b = 0) draw_timage = self.textImage('Draws:' + team['draw'], small_font, r = 0, g = 0, b = 255) points_timage = self.textImage('Points:' + team['points'], small_font, r = 255, g = 255, b = 255) standing_timage = self.textImage('Standing:' + team['standing'], small_font, r = 255, g = 255, b = 255) img.paste(name_timage, (x_offset, -1)) img.paste(wins_timage, (x_offset, 12)) img.paste(loss_timage, (x_offset, 19)) img.paste(draw_timage, (x_offset, 26)) x_offset += max( wins_timage.size[0], loss_timage.size[0], draw_timage.size[0]) img.paste(points_timage, (x_offset, 14)) img.paste(standing_timage, (x_offset, 22)) x_offset += max(points_timage.size[0], standing_timage.size[0]) + buff_size img = img.crop((0,0,x_offset ,32)) font = ImageFont.load("./fonts/texgyre-27.pil") title_img = self.textImage('LEAGUE TABLE', font, matrix_height = True) return self.stitchImage([title_img, img]) def getTodayWeatherImage(self): f = open( "csv/weather_location.txt", 'r' ) line = next(f) locations = line.split(',') f.close() title_img = self.openImage('feature_titles/weather.png') imgs = [title_img, self.blank] current_weathers = json.load(open('csv/current_weather.json', 'r')) for i, location in enumerate(locations): img = Image.new('RGB', (203, 32)) current_weather = current_weathers[i] small_font = ImageFont.load("./fonts/5x7.pil") large_font = ImageFont.load("./fonts/10x20.pil") location_img = self.textImage(location.upper(), small_font, r = 255, g = 255, b = 0) img.paste(location_img, (5,0)) main = current_weather['main_weather'] if main == 'Clouds': main = current_weather['description'] weather_ids = {'Clear': '01', 'few clouds': '02', 'scattered clouds': '03', 'broken clouds':'04', 'overcast clouds':'04', 'Drizzle':'09', 'Rain':'10', 'Thunderstorm':'11', 'Snow':'13', 'Mist': '50', 'Smoke': '50', 'Haze': '50', 'Dust': '50', 'Fog': '50', 'Sand': '50', 'Ash': '50', 'Squall': '50', 'Tornado': '50'} weather_dir = './logos/weather_icons' weather_img = Image.open(weather_dir + '/weather_type_icons/' + weather_ids[main] + '.png') img.paste(weather_img, (5,9)) temp_img = self.textImage(str("{0:.0f}".format(current_weather['temp'])), large_font) img.paste(temp_img, (39,9)) deg_img = self.textImage('o', small_font) img.paste(deg_img, (59, 8)) main = current_weather['main_weather'] main_img = self.textImage(main.upper(), small_font) img.paste(main_img, (35, 26)) feels_img = self.textImage('Feels like:'.upper() + str("{0:.0f}".format(current_weather['feels_like'])), small_font) img.paste(feels_img, (location_img.size[0] + 10, 0)) min_img = self.textImage( "{0:.0f}".format(current_weather['min_temp']), small_font, r=0, g=0, b=255) img.paste(min_img, (66, 12)) max_img = self.textImage( "{0:.0f}".format(current_weather['max_temp']), small_font, r=255, g=0, b=0) img.paste(max_img, (66, 21)) hum_img = Image.open(weather_dir + '/humidity.png') img.paste(hum_img, ( 82, 8)) htext_img = self.textImage(str(current_weather['humidity']) + '%', small_font) img.paste(htext_img, (95, 10)) uv_img = Image.open(weather_dir + '/uv.png') img.paste(uv_img, ( 82, 22)) utext_img = self.textImage(str(round(current_weather['uv'], 1)) , small_font) img.paste(utext_img, (95, 23)) weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] months =['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] month = months[int(datetime.now().strftime('%m'))-1] date = str(int(datetime.now().strftime('%d'))) weekday = weekdays[datetime.today().weekday()] date_img = self.textImage((month + ' ' + date + ',' + weekday).upper(), small_font) img.paste(date_img, (132, 0)) rain_img = Image.open(weather_dir + '/rain-chance.png') img.paste(rain_img, (118,8)) rtext_img = self.textImage(str(int(current_weather['rain_chance']*100)) + '%', small_font) img.paste(rtext_img, (131, 10)) cloud_img = Image.open(weather_dir + '/clouds.png') img.paste(cloud_img, (118,20)) ctext_img = self.textImage(str(current_weather['clouds']) + '%', small_font) img.paste(ctext_img, (131, 22)) wind_img = Image.open(weather_dir + '/wind.png') img.paste(wind_img, (154,8)) wtext_img = self.textImage("{0:.0f}".format(current_weather['wind_speed']) + 'm/s'.upper(), small_font) img.paste(wtext_img, (168, 10)) wdir_img = self.textImage(self.degreesToCompass(current_weather['wind_direction']).upper(), small_font) img.paste(wdir_img, (191, 10)) vis_img = Image.open(weather_dir + '/visibility.png') img.paste(vis_img, (154,20)) vtext_img = self.textImage(str(round(current_weather['visibility']/1000, 1)) + 'km'.upper(), small_font) img.paste(vtext_img, (168, 22)) imgs.append(img) imgs.append(self.blank) return self.stitchImage(imgs) def getTodayWeatherProfessional(self): f = open( "csv/weather_location.txt", 'r' ) line = next(f) locations = line.split(',') f.close() title_img = self.openImage('feature_titles/small_feature_titles/weather.png') image_list = [title_img, Image.new('RGB', (3, 16))] current_weathers = json.load(open('csv/current_weather.json', 'r')) weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] months =['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] month = months[int(datetime.now().strftime('%m'))-1] date = str(int(datetime.now().strftime('%d'))) weekday = weekdays[datetime.today().weekday()] for i, location in enumerate(locations): img = Image.new('RGB', (1000, 32)) current_weather = current_weathers[i] small_font = ImageFont.load("./fonts/4x6.pil") font = ImageFont.load("./fonts/6x10.pil") large_font = ImageFont.load("./fonts/10x20.pil") main = current_weather['main_weather'] if main == 'Clouds': main = current_weather['description'] weather_ids = {'Clear': '01', 'few clouds': '02', 'scattered clouds': '03', 'broken clouds':'04', 'overcast clouds':'04', 'Drizzle':'09', 'Rain':'10', 'Thunderstorm':'11', 'Snow':'13', 'Mist': '50', 'Smoke': '50', 'Haze': '50', 'Dust': '50', 'Fog': '50', 'Sand': '50', 'Ash': '50', 'Squall': '50', 'Tornado': '50'} weather_dir = './logos/weather_icons' location_img = self.textImage(location.upper(), font, r = 255, g = 255, b = 0) img.paste(location_img, (5,3)) x_offset = location_img.size[0] + 8 date_img = self.textImage((month + ' ' + date + ',' + weekday).upper(), font) img.paste(date_img, (x_offset, 3)) x_offset += date_img.size[0] + 2 weather_img = Image.open(weather_dir + '/weather_type_icons/' + weather_ids[main] + '.png') w, h = weather_img.size weather_img = weather_img.resize((int(w/2), int(h/2))) main = current_weather['main_weather'] main_img = self.textImage(main.upper(), font) img.paste(main_img, (x_offset, 3)) x_offset += main_img.size[0] + 2 img.paste(weather_img, (x_offset,3)) x_offset += weather_img.size[0] + 2 temp_img = self.textImage(str("{0:.0f}".format(current_weather['temp'])), font) img.paste(temp_img, (x_offset,3)) x_offset += temp_img.size[0]-3 deg_img = self.textImage('o', small_font) img.paste(deg_img, (x_offset+1, 1)) x_offset += deg_img.size[0] -2 min_img = self.textImage( "{0:.0f}".format(current_weather['min_temp']), small_font, r=0, g=0, b=255) img.paste(min_img, (x_offset+2, 2)) max_img = self.textImage( "{0:.0f}".format(current_weather['max_temp']), small_font, r=255, g=0, b=0) img.paste(max_img, (x_offset+2, 8)) x_offset += max_img.size[0] + 2 hum_img = Image.open(weather_dir + '/humidity.png') htext_img = self.textImage(str(current_weather['humidity']) + '%', font) uv_img = Image.open(weather_dir + '/uv.png') utext_img = self.textImage(str(round(current_weather['uv'], 1)), font) rain_img = Image.open(weather_dir + '/rain-chance.png') rtext_img = self.textImage(str(int(current_weather['rain_chance']*100)) + '%', font) cloud_img = Image.open(weather_dir + '/clouds.png') ctext_img = self.textImage(str(current_weather['clouds']) + '%', font) wind_img = Image.open(weather_dir + '/wind.png') wtext_img = self.textImage("{0:.0f}".format(current_weather['wind_speed']) + 'm/s', font) wdir_img = self.textImage(self.degreesToCompass(current_weather['wind_direction']).upper(), font) vis_img = Image.open(weather_dir + '/visibility.png') vtext_img = self.textImage(str(current_weather['visibility']/1000) + 'km', font) for image in [hum_img, htext_img, uv_img, utext_img, rain_img, rtext_img, cloud_img, ctext_img, wind_img, wtext_img, wdir_img, vis_img, vtext_img]: img.paste(image, (x_offset, 3)) x_offset += image.size[0] + 2 img = img.crop((0,0,x_offset +image.size[0] ,16)) image_list.append(img) return self.stitchImage(image_list) def getDailyWeatherImageAlt(self): f = open( "csv/weather_location.txt", 'r' ) location = f.read() f.close() img = Image.new('RGB', (128, 32)) current_weather = json.load(open('csv/current_weather.json', 'r')) small_font = ImageFont.load("./fonts/5x7.pil") extra_small_font = ImageFont.load("./fonts/4x6.pil") large_font = ImageFont.load("./fonts/10x20.pil") location_img = self.textImage(location, extra_small_font, r = 255, g = 255, b = 0) main = current_weather['main_weather'] if main == 'Clouds': main = current_weather['description'] weather_ids = {'Clear': '01', 'few clouds': '02', 'scattered clouds': '03', 'broken clouds':'04', 'overcast clouds':'04', 'Drizzle':'09', 'Rain':'10', 'Thunderstorm':'11', 'Snow':'13', 'Mist': '50', 'Smoke': '50', 'Haze': '50', 'Dust': '50', 'Fog': '50', 'Sand': '50', 'Ash': '50', 'Squall': '50', 'Tornado': '50'} weather_dir = './logos/weather_icons' weather_img = Image.open(weather_dir + '/weather_type_icons/' + weather_ids[main] + '.png') temp_img = self.textImage(str("{0:.0f}".format(current_weather['temp'])), large_font) deg_img = self.textImage('o', small_font) min_img = self.textImage( "{0:.0f}".format(current_weather['min_temp']), small_font, r=0, g=0, b=255) max_img = self.textImage( "{0:.0f}".format(current_weather['max_temp']), small_font, r=255, g=0, b=0) main = current_weather['main_weather'] main_img = self.textImage(main, small_font) weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] months =['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] month = months[int(datetime.now().strftime('%m'))] date = str(int(datetime.now().strftime('%d'))) weekday = weekdays[datetime.today().weekday()] date_img = self.textImage(month + ' ' + date + ',' + weekday, extra_small_font) rain_img = Image.open(weather_dir + '/rain-chance.png') rtext_img = self.textImage(str(int(current_weather['rain_chance']*100)) + '%', extra_small_font) hum_img = Image.open(weather_dir + '/humidity.png') htext_img = self.textImage(str(current_weather['humidity']) + '%', extra_small_font) uv_img = Image.open(weather_dir + '/uv.png') utext_img = self.textImage(str(current_weather['uv']) , extra_small_font) wind_img = Image.open(weather_dir + '/wind.png') wtext_img = self.textImage(str(current_weather['wind_speed']) + 'm/s', extra_small_font) img.paste(location_img, (0,0)) img.paste(weather_img, (0,12)) img.paste(temp_img, (30,9)) img.paste(deg_img, (50, 8)) img.paste(min_img, (55, 10)) img.paste(main_img, (30, 26)) img.paste(max_img, (55, 19)) img.paste(date_img, (location_img.size[0], 1)) img.paste(rain_img, (75,0)) img.paste(rtext_img, (88, 1)) img.paste(hum_img, (102, 0)) img.paste(htext_img, (113, 1)) #img.paste(uv_img, ( 96, 0)) #img.paste(utext_img, (109, 0)) #img.paste(wind_img, (96,0)) #img.paste(wtext_img, (109,0)) img0 = img img = Image.new('RGB', (1000, 32)) daily_weather = json.load(open('csv/daily_weather.json', 'r')) #img.paste(date_img, (70, 0)) x_offset = 64 for i in range(0,len(daily_weather)-1): weekday = weekdays[(datetime.today().weekday() + i)%7] day_img = self.textImage( weekday, small_font) weather = daily_weather[i] main = weather['main_weather'] if main == 'Clouds': main = weather['description'] weather_img = Image.open(weather_dir + '/small_icons/' + weather_ids[main] + '.png') min_img = self.textImage( "{0:.0f}".format(weather['min_temp']), small_font, r=0, g=0, b=255) max_img = self.textImage( "{0:.0f}".format(weather['max_temp']), small_font, r=255, g=0, b=0) img.paste(day_img, (x_offset +5, 9)) img.paste(weather_img, (x_offset +5, 16)) img.paste(min_img, (x_offset + 25, 10)) img.paste(max_img, (x_offset + 25, 20)) x_offset += 35 img1 = img.crop((64,0,x_offset ,32)) img1.save('display_images/weather.ppm') return img0, img1 def getDailyWeatherImage(self): f = open( "csv/weather_location.txt", 'r' ) line = next(f) locations = line.split(',') f.close() title_img = self.openImage('feature_titles/forecast.png') imgs = [title_img, self.blank] current_weathers = json.load(open('csv/current_weather.json', 'r')) daily_weathers = json.load(open('csv/daily_weather.json', 'r')) for i, location in enumerate(locations): img = Image.new('RGB', (1000, 32)) current_weather = current_weathers[i] daily_weather = daily_weathers[i] small_font = ImageFont.load("./fonts/5x7.pil") extra_small_font = ImageFont.load("./fonts/4x6.pil") large_font = ImageFont.load("./fonts/10x20.pil") location_img = self.textImage(location.upper(), extra_small_font, r = 255, g = 255, b = 0) main = current_weather['main_weather'] if main == 'Clouds': main = current_weather['description'] weather_ids = {'Clear': '01', 'few clouds': '02', 'scattered clouds': '03', 'broken clouds':'04', 'overcast clouds':'04', 'Drizzle':'09', 'Rain':'10', 'Thunderstorm':'11', 'Snow':'13', 'Mist': '50', 'Smoke': '50', 'Haze': '50', 'Dust': '50', 'Fog': '50', 'Sand': '50', 'Ash': '50', 'Squall': '50', 'Tornado': '50'} weather_dir = './logos/weather_icons' weather_img = Image.open(weather_dir + '/weather_type_icons/' + weather_ids[main] + '.png') temp_img = self.textImage(str("{0:.0f}".format(current_weather['temp'])), large_font) deg_img = self.textImage('o', small_font) min_img = self.textImage( "{0:.0f}".format(current_weather['min_temp']), small_font, r=0, g=0, b=255) max_img = self.textImage( "{0:.0f}".format(current_weather['max_temp']), small_font, r=255, g=0, b=0) main = current_weather['main_weather'] main_img = self.textImage(main.upper(), small_font) weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] months =['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] month = months[int(datetime.now().strftime('%m'))-1] date = str(int(datetime.now().strftime('%d'))) weekday = weekdays[datetime.today().weekday()] date_img = self.textImage((month + ' ' + date + ',' + weekday).upper(), extra_small_font) rain_img = Image.open(weather_dir + '/rain-chance.png') rtext_img = self.textImage(str(int(current_weather['rain_chance']*100)) + '%', extra_small_font) hum_img = Image.open(weather_dir + '/humidity.png') htext_img = self.textImage(str(current_weather['humidity']) + '%', extra_small_font) wind_img = Image.open(weather_dir + '/wind.png') wtext_img = self.textImage(str(current_weather['wind_speed']) + 'm/s'.upper(), extra_small_font) uv_img = Image.open(weather_dir + '/uv.png') utext_img = self.textImage(str(round(current_weather['uv'], 1)) , small_font) cloud_img = Image.open(weather_dir + '/clouds.png') ctext_img = self.textImage(str(current_weather['clouds']) + '%', small_font) wind_img = Image.open(weather_dir + '/wind.png') wtext_img = self.textImage("{0:.0f}".format(current_weather['wind_speed']) + 'm/s'.upper(), small_font) wdir_img = self.textImage(self.degreesToCompass(current_weather['wind_direction']), small_font) vis_img = Image.open(weather_dir + '/visibility.png') vtext_img = self.textImage(str(round(current_weather['visibility']/1000, 1)) + 'km'.upper(), small_font) img.paste(location_img, (0,0)) x_offset = location_img.size[0]+2 img.paste(weather_img, (0,12)) img.paste(temp_img, (34,9)) img.paste(deg_img, (55, 8)) img.paste(min_img, (61, 10)) img.paste(main_img, (31, 26)) img.paste(max_img, (61, 18)) img.paste(date_img, (x_offset, 1)) x_offset += date_img.size[0]+2 img.paste(rain_img, (x_offset,0)) x_offset += rain_img.size[0]+2 img.paste(rtext_img, (x_offset, 1)) x_offset += rtext_img.size[0]+2 img.paste(hum_img, (x_offset, 0)) x_offset += hum_img.size[0]+2 img.paste(htext_img, (x_offset, 1)) x_offset+= htext_img.size[0]+2 img.paste(uv_img, ( x_offset, 0)) x_offset += uv_img.size[0]+2 img.paste(utext_img, (x_offset, 1)) x_offset += utext_img.size[0]+2 img.paste(cloud_img, (x_offset,0)) x_offset += cloud_img.size[0]+2 img.paste(ctext_img, (x_offset, 1)) x_offset += ctext_img.size[0]+2 img.paste(wind_img, (x_offset,0)) x_offset += wind_img.size[0]+2 img.paste(wtext_img, (x_offset, 1)) x_offset += wtext_img.size[0]+2 img.paste(wdir_img, (x_offset, 1)) x_offset+= wdir_img.size[0]+2 img.paste(vis_img, (x_offset,0)) x_offset+= vis_img.size[0]+2 img.paste(vtext_img, (x_offset, 1)) x_offset += vtext_img.size[0] +2 #img.paste(uv_img, ( 96, 0)) #img.paste(utext_img, (109, 0)) #img.paste(wind_img, (96,0)) #img.paste(wtext_img, (109,0)) #img.paste(date_img, (70, 0)) x_offset = 70 for i in range(1,len(daily_weather)-1): weekday = weekdays[(datetime.today().weekday() + i)%7] day_img = self.textImage( weekday.upper(), small_font) weather = daily_weather[i] main = weather['main_weather'] if main == 'Clouds': main = weather['description'] weather_img = Image.open(weather_dir + '/small_icons/' + weather_ids[main] + '.png') min_img = self.textImage( "{0:.0f}".format(weather['min_temp']), small_font, r=0, g=0, b=255) max_img = self.textImage( "{0:.0f}".format(weather['max_temp']), small_font, r=255, g=0, b=0) img.paste(day_img, (x_offset +5, 9)) img.paste(weather_img, (x_offset +5, 16)) img.paste(min_img, (x_offset + 25, 10)) img.paste(max_img, (x_offset + 25, 18)) x_offset += 40 img1 = img.crop((0,0,x_offset ,32)) imgs.append(img1) imgs.append(self.blank) # add the image text return self.stitchImage(imgs) #Retrieve symbols and stock info from the csv file def readStocksCSV(self): self.symbols = [] self.stock_info = {} f = open('csv/tickers.csv', 'r') CSV = csv.reader(f) next(CSV) for row in CSV: try: symbol, current_price, opening_price = row self.symbols.append(symbol) self.stock_info[symbol] = [current_price, opening_price] except: pass # we dont want to include incomplete information f.close() def readCryptoCSV(self): self.coins = [] self.coin_info = {} f = open('csv/crypto.csv', 'r') CSV = csv.reader(f) next(CSV) for row in CSV: try: symbol, coin, base, current_price, day_change = row self.coins.append(coin) self.coin_info[coin] = [symbol, base, current_price, day_change] except: pass f.close() def readSportsCSV(self, league): team_info = {} f = open('csv/sports/{}/team_info.csv'.format(league), 'r') CSV = csv.reader(f) next(CSV) for row in CSV: team_info[row[0]] = {} team_info[row[0]]['id'] = row[1] team_info[row[0]]['code'] = row[2] team_info[row[0]]['logo'] = row[4] team_info[row[0]]['colour'] = row[3] return team_info def displayDailyWeatherAlt(self): img0, img1 = self.getDailyWeatherImageAlt() #img = stock_ticker.getTodayWeatherImage() while True: self.setImage(img0, offset_x = 0, offset_y = 0) image = img1 img_width, img_height = image.size offset_x = 64 offset_y = 0 while offset_x > 64-img_width: offset_x -= 1 self.setImage(image, offset_x = offset_x, offset_y = offset_y, min_x = 64, min_y = 9) if offset_x + img_width < self.matrix.width: # if the image is ending self.setImage(image, offset_x = offset_x + img_width, offset_y = offset_y, min_x = 64, min_y = 9) try: msg = getInput() if msg == 'K': self.resetMatrix() return True self.process_msg(msg) except KeyboardInterrupt: sys.stdout.flush() pass time.sleep(self.delay*1.1) def getUserImage(self): title_img = self.openImage('feature_titles/images.png') image = self.openImage(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'display_images/user_image.ppm')) return self.stitchImage([title_img, image]) def getUserGIF(self): gif = Image.open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'display_images/user_gif.gif')) return gif def displayStocks(self): self.scrollImageTransition(['final.ppm', 'final.ppm'], offset_x = 0, offset_y = 0) def displayProfessional(self): forex = self.getForexProfessional() crypto = self.getCryptoProfessional() news = self.getNewsProfessional() stock = self.getStockProfessional() weather = self.getTodayWeatherProfessional() x_offset = 0 news.paste(weather, (x_offset, 16)) x_offset += weather.size[0] news.paste(crypto, (x_offset, 16)) x_offset += crypto.size[0] news.paste(stock, (x_offset, 16)) x_offset += stock.size[0] news.paste(forex, (x_offset, 16)) x_offset += forex.size[0] while True: kill = stock_ticker.scrollImage(news, offset_x = 128) if kill: break def process_msg(self, msg): if msg == 'S': # stocks self.scrollFunctionsAnimated(['stocks', 'stocks'], animation = 'traditional') elif msg == 'C': # crypto self.scrollFunctionsAnimated(['crypto', 'crypto'], animation = 'traditional') elif msg == 'F': # forex self.scrollFunctionsAnimated(['forex', 'forex'], animation = 'traditional') elif msg == 'N': #news self.scrollFunctionsAnimated(['news', 'news'], animation = 'traditional') # speed settings elif msg == 's': self.delay = 0.03 elif msg == 'm': self.delay = 0.02 elif msg == 'f': self.delay = 0.01 elif msg in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']: # birghtness ettings self.brightness = min(1.0, float(msg)/10 + 0.1) elif msg == 'T':# text self.scrollFunctionsAnimated(['text', 'text'], animation = 'traditional') elif msg == 'I': # image self.scrollFunctionsAnimated(['display_image', 'display_image'], animation = 'traditional') elif msg == 'G': # gif image = self.openImage('./display_images/user_gif.gif') #self.displayGIF(image) self.scrollFunctionsAnimated(['display_gif', 'display_gif'], animation = 'traditional') elif msg == 'W': # weather self.scrollFunctionsAnimated(['today_weather', 'today_weather'], animation = 'traditional') elif msg == 'D': # daily weather self.scrollFunctionsAnimated(['daily_weather', 'daily_weather'],animation = 'traditional') elif msg == 'P': # past league img = self.getLeagueImage('NBA', 'past') img.save('display_images/league.ppm') stock_ticker.scrollImageTransition(['display_images/league.ppm', 'display_images/league.ppm'], stocks = False) elif msg == 'l': # future league img = self.getLeagueImage('NHL', 'future') img.save('display_images/league.ppm') stock_ticker.scrollImageTransition(['display_images/league.ppm', 'display_images/league.ppm'], stocks = False) elif msg == 'L': # live game img = self.getLeagueImage('NBA', 'live') img.save('display_images/league.ppm') stock_ticker.scrollImageTransition(['display_images/league.ppm', 'display_images/league.ppm'], stocks = False) elif msg == 't': #legue tble img = self.getLeagueTableImage('premier_league') img.save('display_images/teams.ppm') stock_ticker.scrollImageTransition(['display_images/teams.ppm', 'display_images/teams.ppm'], stocks = False) elif msg == 'A': #everything userSettings = ['display_gif', 'text', 'display_image', 'stocks', 'crypto', 'forex', 'today_weather', 'daily_weather', 'league_table', 'league_games', 'news'] # these wil be read from csv, just for demo #userSettings = [ 'daily_weather'] #userSettings = ['crypto', 'stocks'] # these wil be read from csv, just for demo #userSettings = [ 'display_image', 'news'] # these wil be read from csv, just for demo self.scrollFunctionsAnimated(userSettings, animation = 'down') elif msg == 'b': self.displayProfessional() elif msg == 'K': # kill self.resetMatrix() if __name__ == '__main__': with open('log.txt', "w") as log: try: stock_ticker = StockTicker() # #stock_ticker.process_msg('b') #stock_ticker.process_msg('G') #stock_ticker.process_msg('f') #stock_ticker.process_msg('W') while True: msg = getInput() stock_ticker.process_msg(msg) except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] log.write(str(e)) log.write('. file: ' + fname) log.write('. line: ' + str(exc_tb.tb_lineno)) log.write('. type: ' + str(exc_type)) log.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) raise(e)