# Copyright (C) 2020 Fintic, finticofficial@gmail.com # # This file is part of Fintic project, developed by Neythen Treloar and Justin Dunn # # stockTicker can not be copied and/or distributed without the express # permission of Fintic import sys, select import os import threading from PIL import Image, ImageDraw, ImageFont, ImageSequence 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.delay = 0.02 self.greenORred = (255, 255, 255) #self.blank = Image.open('logos/blank.png') self.blank = Image.new('RGB', (10, 32)) self.running = True settings = json.load(open('csv/general_settings.json', 'r')) self.brightness = settings['brightness']*10 # 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 = 2 options.brightness = self.brightness self.matrix = RGBMatrix(options = options) #sys.exit() self.points = True # display crypto change in points or percent self.functions = {'Stocks': self.getStockImage, 'Crypto': self.getCryptoImage, 'Forex': self.getForexImage, 'Daily Forecast':self.getDailyWeatherImage, 'Current Weather': self.getTodayWeatherImage, 'Sports (Team Stats)':lambda : self.getLeagueTableImage('premier_league'), 'Sports (Past Games)': lambda:self.getLeagueImage('NBA', 'past'), 'Sports (Upcoming Games)': lambda : self.getLeagueImage('NHL', 'future'), 'Sports (Live Games)': lambda: self.getLeagueImage('NBA', 'live'), 'News':self.getNewsImage, 'Custom Messages': self.getUserMessages, 'Stocks Prof': self.getStockProfessional, 'Crypto Prof': self.getCryptoProfessional, 'Forex Prof': self.getForexProfessional, 'Current Weather Prof': self.getTodayWeatherProfessional, 'News Prof':self.getNewsProfessional} self.JSONs = {'Stocks': 'csv/stocks_settings.json', 'Crypto': 'csv/crypto_settings.json', 'Forex': 'csv/forex_settings.json', 'Daily Forecast':'csv/daily_weather.json', 'Current Weather': 'csv/current_weather.json', 'Sports (Team Stats)': 'csv/league_tables.json', 'Sports (Past Games)': 'csv/past_games.json', 'Sports (Upcoming Games)': 'csv/upcoming_games.json', 'Sports (Live Games)': 'csv/live_games.json', 'News':'csv/news_settings.json', 'Custom Images': 'csv/image_settings.json', 'Custom GIFs': 'csv/GIF_settings.json', 'Custom Messages': 'csv/message_settings.json', 'Stocks Prof': 'csv/stocks_settings.json', 'Crypto Prof': 'csv/crypto_settings.json', 'Forex Prof': 'csv/forex_settings.json', 'Current Weather Prof': 'csv/current_weather.json', 'News Prof':'csv/news_settings.json'} def openImage(self, image_file): image = Image.open(image_file) image = image.convert('RGB') # 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, frame_skip = 10, gif = False, pause_frames = 0): img_width, img_height = image.size kill = False while offset_x > -(img_width+1): if offset_x == 0: while pause_frames > 0: if pause_frames%frame_skip == 0: self.incrementGIF(image) pause_frames -=1 if gif: self.double_buffer.SetImage(image.convert('RGB'), offset_x, offset_y) else: self.double_buffer.SetImage(image, offset_x, offset_y) for y in range(self.matrix.height): self.matrix.SetPixel(offset_x + img_width +1 , y , 0,0,0) self.matrix.SetPixel(offset_x + img_width , y , 0,0,0) self.double_buffer = self.matrix.SwapOnVSync(self.double_buffer) time.sleep(self.delay) kill = self.checkKilled() if kill: break if kill: break # for animation in gifs if offset_x%frame_skip == 0: self.incrementGIF(image) #image = image.convert('RGB') offset_x -= 1 #self.setImage(image.convert('RGB'), offset_x = offset_x, offset_y = offset_y) if gif: self.double_buffer.SetImage(image.convert('RGB'), offset_x, offset_y) else: self.double_buffer.SetImage(image, offset_x, offset_y) buff = 0 # remove the ppixels behind the image, to stop trailing self.double_buffer = self.matrix.SwapOnVSync(self.double_buffer) for y in range(self.matrix.height): self.matrix.SetPixel(offset_x + img_width +1 , y , 0,0,0) self.matrix.SetPixel(offset_x + img_width , y , 0,0,0) time.sleep(self.delay) kill = self.checkKilled() if kill: break return kill def scrollImageY(self, image, direction = 1, offset_x = 0, offset_y = 0, frame_skip = 10, gif = False): kill = False while offset_y != 0: # for animation in gifs if offset_y%frame_skip == 0: self.incrementGIF(image) offset_y += direction if gif: self.double_buffer.SetImage(image.convert('RGB'), offset_x, offset_y) else: self.double_buffer.SetImage(image, offset_x, offset_y) self.double_buffer = self.matrix.SwapOnVSync(self.double_buffer) time.sleep(self.delay) kill = self.checkKilled() if kill: break 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() update_process.terminate() if current_img == 1: current_img = 2 elif current_img == 2: current_img = 1 offset_x = 0 update_process.join() update_process.terminate() 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 updateMultiple(self, options): for option in options: if option not in ['Custom GIFs', 'Custom Images', 'Custom Messages']: # these images are already saved in user uploads, dodnt need to update them img = self.functions[option]() img.save('./display_images/'+ option+ '.ppm') def incrementGIF(self, image): try: image.seek(self.frame) except EOFError: self.frame = 0 image.seek(self.frame) self.frame +=1 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 set_delay(self,speed): if speed.lower() == 'slow': self.delay = 0.03 elif speed.lower() == 'medium': self.delay = 0.02 elif speed.lower() == 'fast': self.delay = 0.01 return self.delay def scrollFunctionsAnimated(self, options, animation = 'down', repeat = True): # 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 self.double_buffer = self.matrix.CreateFrameCanvas() while True: update_process = Process(target = self.updateMultiple, args = ([options[(i+1) % len(options)]],)) update_process.start() f = open(self.JSONs[options[(i) % len(options)]]) settings = json.load(f) f.close() self.set_delay(settings['speed']) animation = settings['animation'].lower() if options[i % len(options)] == 'Custom Images': images = self.getUserImages() elif options[i % len(options)] == 'Custom GIFs': images = self.getUserGIFs() elif options[i % len(options)] == 'Custom Messages': images = self.getUserMessages() else: #these options just have a single ppm image image = self.openImage('./display_images/' + options[i % len(options)] +'.ppm') images = [image] for image in images: img_width, img_height = image.size offset_x = 0 if animation == 'continuous': offset_x = 128 elif animation in ['up', 'down']: offset_x = max(0, 128-img_width) offset_y = 0 #first scroll image in from bottom frame_skip = int((1/15)/self.delay) #controls how fast gifs run self.frame = 0 pause_frames = int(0.5/self.delay) if animation == 'up': offset_y = 33 direction = -1 kill = self.scrollImageY(image, direction = direction, offset_x = offset_x, offset_y = offset_y, frame_skip = frame_skip, gif = options[i % len(options)] == 'Custom GIFs') elif animation == 'down': direction = 1 offset_y = -33 kill = self.scrollImageY(image, direction = direction, offset_x = offset_x, offset_y = offset_y, frame_skip = frame_skip, gif = options[i % len(options)] == 'Custom GIFs') if kill: break offset_y = 0 if animation in ['up', 'down']: while pause_frames > 0: if pause_frames%frame_skip == 0: self.incrementGIF(image) pause_frames -=1 if options[i % len(options)] != 'Custom GIFs': self.double_buffer.SetImage(image, offset_x, offset_y) else: self.double_buffer.SetImage(image.convert('RGB'), offset_x, offset_y) self.double_buffer = self.matrix.SwapOnVSync(self.double_buffer) time.sleep(self.delay) kill = self.checkKilled() if kill: break if kill: break if kill: break try: pause = float(settings['pause']) except: pause = 0 pause_frames = int(float(pause)/self.delay) kill = self.scrollImage(image, offset_x = offset_x, offset_y = offset_y, frame_skip = frame_skip, gif = options[i % len(options)] == 'Custom GIFs', pause_frames = pause_frames) if kill: break if kill:break if not repeat: break update_process.join() update_process.terminate() i+=1 def scrollProfessionalAnimated(self, options, animation = 'on'): # scrolls trhough all functions with animation. Updates functions and remakes images when each function not being dispplayed top = options[0] bottom = options[1] self.updateMultiple([top[0], bottom[0]]) kill = False i1 = 0 # keep track of which image we are displaying i2 = 0 # keep track of which image we are displaying self.double_buffer = self.matrix.CreateFrameCanvas() update_process = Process(target = self.updateMultiple, args = ([top[(i1+1) % len(top)]+ ' Prof', bottom[(i2+1) % len(bottom)]+ ' Prof' ],)) update_process.start() self.updateMultiple([top[i1 % len(top)]+ ' Prof', bottom[i2 % len(bottom)]+ ' Prof' ]) image1 = self.openImage('./display_images/' + top[i1 % len(top)] +' Prof.ppm') image1 = image1.convert('RGB') image2 = self.openImage('./display_images/' + bottom[i2 % len(bottom)] +' Prof.ppm') image2 = image2.convert('RGB') settings1 = json.load(open(self.JSONs[top[i1 % len(top)]])) delay_t1 = self.set_delay(settings1['speed']) animation1 = settings1['animation'].lower() settings2 = json.load(open(self.JSONs[bottom[i2 % len(bottom)] ])) try: delay_t2 = self.set_delay(settings2['speed2']) except: delay_t2 = self.set_delay(settings2['speed']) animation2 = settings2['animation'].lower() if animation1 == 'continuous': offset_y1 = 0 offset_x1 = 128 else: offset_y1 = -16 offset_x1 = 0 if animation2 == 'continuous': offset_y2 = 16 offset_x2 = 128 else: offset_y2 = 32 offset_x2 = 0 frame_skip = int((1/15)/self.delay) #controls how fast gifs run self.frame = 0 img_width1, img_height1 = image1.size img_width2, img_height2 = image2.size kill = False update_t1 = time.time() update_t2 = time.time() while True: if offset_x1 < -(img_width1+1): i1 += 1 settings1 = json.load(open(self.JSONs[top[i1 % len(top)]])) delay_t1 = self.set_delay(settings1['speed']) animation1 = settings1['animation'].lower() if animation1 == 'continuous': offset_y1 = 0 offset_x1 = 128 else: offset_y1 = -16 offset_x1 = 0 update_process.join() update_process.terminate() update_process = Process(target = self.updateMultiple, args = ([top[(i1+1) % len(top)]+ ' Prof'],)) update_process.start() image1 = self.openImage('./display_images/' + top[i1 % len(top)] +' Prof.ppm') image1 = image1.convert('RGB') img_width1, img_height1 = image1.size if offset_x2 < -(img_width2+1): i2 += 1 settings2 = json.load(open(self.JSONs[bottom[(i2) % len(bottom)]])) try: delay_t2 = self.set_delay(settings2['speed2']) except: delay_t2 = self.set_delay(settings2['speed']) animation2 = settings2['animation'].lower() if animation2 == 'continuous': offset_y2 = 16 offset_x2 = 128 else: offset_y2 = 32 offset_x2 = 0 update_process.join() update_process.terminate() update_process = Process(target = self.updateMultiple, args = ([bottom[i2 % len(bottom)]+ ' Prof'],)) update_process.start() image2 = self.openImage('./display_images/' + bottom[i2 % len(bottom)] +' Prof.ppm') image2 = image2.convert('RGB') img_width2, img_height2 = image2.size if time.time() - update_t1 > delay_t1: update_t1 = time.time() if offset_y1 < 0: offset_y1+=1 else: offset_x1 -= 1 if time.time() - update_t2 > delay_t2: update_t2 = time.time() if offset_y2 > 16: offset_y2-=1 else: offset_x2 -= 1 if kill: break #image = image.convert('RGB') self.double_buffer.SetImage(image1, offset_x1, offset_y1) self.double_buffer.SetImage(image2, offset_x2, offset_y2) buff = 0 # remove the ppixels behind the image, to stop trailing self.double_buffer = self.matrix.SwapOnVSync(self.double_buffer) for y in range(16): self.matrix.SetPixel(offset_x1 + img_width1 +1 , y , 0,0,0) self.matrix.SetPixel(offset_x1 + img_width1 , y , 0,0,0) for y in range(16,32): self.matrix.SetPixel(offset_x2 + img_width2 +1 , y , 0,0,0) self.matrix.SetPixel(offset_x2 + img_width2 , y , 0,0,0) kill = self.checkKilled() if kill: break if kill: break def scrollMultiple(self, animation = 'down'): # scrolls trhough all functions with animation. Updates functions and remakes images when each function not being dispplayed # read lines from csv images = [] delays = [] kinds = [] f = open('csv/multiple.csv', 'r') CSV = csv.reader(f) next(CSV) font = ImageFont.load("./fonts/texgyre-27.pil") for row in CSV: kind, content, delay = row delays.append(delay) kinds.append(kind) if kind == 'text': images.append(self.textImage(content, font, 255, 255, 0, True, w_buff = 50)) elif kind == 'image': images.append(self.openImage(content).convert('RGB')) elif kind == 'gif': images.append(self.openImage(content)) f.close() kill = False i = 0 # keep track of which image we are displaying self.double_buffer = self.matrix.CreateFrameCanvas() while True: image = images[i%len(images)] delay = delays[i%len(images)] kind = kinds[i%len(images)] img_width, img_height = image.size 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 frame_skip = int((1/15)/self.delay) #controls how fast gifs run self.frame = 0 pause_frames = int(float(delay)/self.delay) if animation == 'up': offset_y = 33 direction = -1 kill = self.scrollImageY(image, direction = direction, offset_x = offset_x, offset_y = offset_y, frame_skip = frame_skip, gif = kind=='gif') elif animation == 'down': direction = 1 offset_y = -33 kill = self.scrollImageY(image, direction = direction, offset_x = offset_x, offset_y = offset_y, frame_skip = frame_skip, gif = kind=='gif') offset_y = 0 if kill: break kill = self.scrollImage(image, offset_x = offset_x, offset_y = offset_y, frame_skip = frame_skip, gif = kind=='gif', pause_frames = pause_frames) if kill: break i+=1 def textImage(self, text, font, r = 255, g = 255, b = 255, matrix_height = False, w_buff = 3, h_buff = 3, background = False, location = False): ''' 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) if background: br, bg, bb = background d.rectangle(xy = (0, 0, width + w_buff, height + h_buff), fill = (br, bg, bb)) #outline = (255, 255, 255), #width = 0) #use outline and width to add a border if location: d.text(location, text, fill=(r, g, b), font=font) else: d.text((0, 0), text, fill=(r, g, b), font=font) return img def getUserMessages(self): ''' displays the text entered in the webpage by the user. ''' f = open('csv/message_settings.json', 'r') all_settings = json.load(f) f.close() colours = {'Black':(0,0,0), 'White':(255,255,255), 'Red':(255,0,0), 'Green':(0,255,0), 'Blue':(0,0,255), 'Purple':(255,0,255), 'Yellow':(255,255,0), 'Cyan':(0,255,255)} imgs = [] for ind, message in enumerate(all_settings['messages']): font = ImageFont.load("./fonts/10x20.pil") location = (0, 4) if message["size"] == 'Large': font = ImageFont.load("./fonts/texgyre-27.pil") location = (0, -2) elif message["size"] == 'Small': font = ImageFont.load("./fonts/6x13.pil") location = (0, 7) r,g,b = colours[message['text_colour']] background = colours[message['background_colour']] img = self.textImage(message['text'], font, int(r), int(g), int(b), True, w_buff = 5, background = background, location = location) if all_settings['title'] and ind == 0: title_img = self.openImage('feature_titles/message.png') imgs.append(self.stitchImage([title_img, img])) else: imgs.append(img) return imgs def displayGIF(self, gif, delay = 0.5, repeat = True): # To iterate through the entire gif i = 0 while True: try: gif.seek(i) except EOFError: if not repeat: break i = 0 gif.seek(i) # do something to im self.setImage(gif.convert('RGB')) time.sleep(delay) 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-1.png')) self.greenORred = (0, 255, 0) else: Arrow = Image.open(os.path.join(logos_path, 'down-1.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, ARROW, percent_change = False, point_change = False, font = ImageFont.load("./fonts/10x20.pil")): w1, text_height = self.get_text_dimensions(TICKER, font) w2, text_height = self.get_text_dimensions(CURRENT, font) text_width_current = max(w1,w2) img = Image.new('RGB', (text_width_current +1000 , 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) if percent_change: d.text(((w1+7), 14 - text_height), percent_change, fill=self.greenORred, font=font) w, h = self.get_text_dimensions(percent_change, font) w1 += 7 + w if point_change: img.paste(ARROW, ((w2+ 9),18)) d.text(((w2+29), 16), point_change, fill=self.greenORred, font=font) w,h = self.get_text_dimensions(point_change, font) w2 += 29 + w img = img.crop((0,0,max(w1, w2) + 20,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): f = open('csv/crypto_settings.json', 'r') all_crypto_settings = json.load(f) f.close() if all_crypto_settings['title']: title_img = self.openImage('feature_titles/crypto.png') image_list = [title_img] image_list.append(self.blank) else: image_list = [] coin_info = all_crypto_settings['symbols'] coin_bases = list(coin_info.keys()) for i, cb in enumerate(coin_bases): try: ticker, base = cb.split(',') current = float(coin_info[cb]["current"]) point_change = float(coin_info[cb]["24hr_change"]) percent_change = float(coin_info[cb]["percent_change"]) arrow, change = self.getArrow(point_change) percent_change = '%.2f' % abs(percent_change) + '%' point_change = str(abs(point_change)) current = str(current) if not all_crypto_settings['percent']: percent_change = False if not all_crypto_settings['point']: point_change = False midFrame = self.textToImage(ticker + '(' + base + ')', current, arrow, percent_change, point_change) #IMAGE THE TEXT if all_crypto_settings['logos']: try: logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'crypto') logo = self.openImage(os.path.join(logos_path, ticker + '.png')) stitchedStock = self.stitchImage([logo,midFrame]) except Exception as e: stitchedStock = midFrame else: stitchedStock = midFrame image_list.append(stitchedStock) image_list.append(self.blank) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() finalDisplayImage = self.stitchImage(image_list) return finalDisplayImage def getCryptoProfessional(self): self.blank = Image.new('RGB', (0, 16)) f = open('csv/crypto_settings.json', 'r') all_crypto_settings = json.load(f) f.close() if all_crypto_settings['title']: title_img = self.openImage('feature_titles/small_feature_titles/crypto.png') image_list = [title_img] image_list.append(self.blank) else: image_list = [] coin_info = all_crypto_settings['symbols'] coin_bases = list(coin_info.keys()) for i, cb in enumerate(coin_bases): try: ticker, base = cb.split(',') current = float(coin_info[cb]["current"]) change = float(coin_info[cb]["24hr_change"]) arrow, change = self.getArrow(change, professional=True) if all_crypto_settings["percent"]: # convert percent to points change = '%.2f' % abs(float(coin_info[cb]['percent_change'])) + '%' else: change = str(abs(change)) current = str(current) midFrame = self.textToImageProf(ticker + '(' + base + ')', current, change, arrow, font=ImageFont.load("./fonts/6x10.pil")) #IMAGE THE TEXT if all_crypto_settings['logos']: try: logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'crypto') logo = self.openImage(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 else: stitchedStock = midFrame image_list.append(stitchedStock) image_list.append(self.blank) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() finalDisplayImage = self.stitchImage(image_list) self.blank = Image.new('RGB', (10, 32)) return finalDisplayImage def getForexImage(self): f = open('csv/forex_settings.json', 'r') all_forex_settings = json.load(f) f.close() if all_forex_settings['title']: title_img = self.openImage('feature_titles/forex.png') image_list = [title_img] image_list.append(self.blank) else: image_list = [] forex_settings = all_forex_settings['symbols'] symbol_bases = list(forex_settings.keys()) for i, sb in enumerate(symbol_bases): try: symbol, base = sb.split(',') current = float(forex_settings[sb]['current']) change = float(forex_settings[sb]['24hr_change']) percent_change = '%.2f' % abs(float(forex_settings[sb]['percent_change'])) +'%' point_change = str(abs(change)) if not all_forex_settings['percent']: percent_change = False if not all_forex_settings['point']: point_change = False arrow, change = self.getArrow(change) current = str(current) midFrame = self.textToImage(symbol+ '(' + base + ')', current, arrow, percent_change, point_change) #IMAGE THE TEXT if all_forex_settings['logos']: 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, symbol.upper() + '.png')) bse = Image.open(os.path.join(logos_path, base.upper() + '.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: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() image_list.append(midFrame) image_list.append(self.blank) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() finalDisplayImage = self.stitchImage(image_list) return finalDisplayImage def getForexProfessional(self): self.blank = Image.new('RGB', (0, 16)) f = open('csv/forex_settings.json', 'r') all_forex_settings = json.load(f) f.close() if all_forex_settings['title']: title_img = self.openImage('feature_titles/small_feature_titles/forex.png') image_list = [title_img] image_list.append(self.blank) else: image_list = [] forex_settings = all_forex_settings['symbols'] symbol_bases = list(forex_settings.keys()) for i, sb in enumerate(symbol_bases): try: symbol, base = sb.split(',') current = float(forex_settings[sb]['current']) change = float(forex_settings[sb]['24hr_change']) arrow, change = self.getArrow(change, professional = True) if all_forex_settings["percent"]: change = abs(float(forex_settings[sb]['percent_change'])) change = '%.2f' % change + '%' else: change = str(abs(change)) current = str(current) midFrame = self.textToImageProf(symbol + '(' + base + ')', current, change, arrow, font = ImageFont.load("./fonts/6x10.pil")) #IMAGE THE TEXT if all_forex_settings['logos']: 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, symbol.upper() + '.png')) bse = Image.open(os.path.join(logos_path, base.upper() + '.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: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() image_list.append(midFrame) image_list.append(self.blank) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() finalDisplayImage = self.stitchImage(image_list) self.blank = Image.new('RGB', (10, 32)) return finalDisplayImage def getStockImage(self): f = open('csv/stocks_settings.json', 'r') all_stocks_settings = json.load(f) f.close() if all_stocks_settings['title']: title_img = self.openImage('feature_titles/stocks.png') image_list = [title_img] image_list.append(self.blank) else: image_list = [] stock_info = all_stocks_settings['symbols'] symbols = list(stock_info.keys()) for i, symbol in enumerate(symbols): try: info = stock_info[symbol] change = float(info['change']) #TEXT ticker = symbol #TEXT arrow, change = self.getArrow(change) percent_change = '%.2f' % abs(float(info['percent_change'])) + '%' point_change = '%.2f' % abs(change) current = '%.2f' % float(info['current']) #TEXT if not all_stocks_settings['percent']: percent_change = False if not all_stocks_settings['point']: point_change = False midFrame = self.textToImage(ticker, current, arrow, percent_change, point_change) #IMAGE THE TEXT if all_stocks_settings['logos']: try: logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'stocks') logo = self.openImage(os.path.join(logos_path, ticker + '.png')) stitchedStock = self.stitchImage([logo,midFrame]) except Exception as e: stitchedStock = midFrame else: stitchedStock = midFrame image_list.append(stitchedStock) image_list.append(self.blank) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() finalDisplayImage = self.stitchImage(image_list) return finalDisplayImage def getStockProfessional(self): self.blank = Image.new('RGB', (0, 16)) f = open('csv/stocks_settings.json', 'r') all_stocks_settings = json.load(f) f.close() if all_stocks_settings['title']: title_img = self.openImage('feature_titles/small_feature_titles/stocks.png') image_list = [title_img, Image.new('RGB', (5, 16))] image_list.append(self.blank) else: image_list = [] stock_info = all_stocks_settings['symbols'] symbols = list(stock_info.keys()) for i, symbol in enumerate(symbols): try: info = stock_info[symbol] change = float(info['change'])#TEXT ticker = symbol #TEXT arrow, change = self.getArrow(change, professional=True) if all_stocks_settings["percent"]: change = '%.2f' % abs(float(info['percent_change'])) + '%' else: change = '%.2f' % abs(change) current = '%.2f' % float(info['current']) #TEXT midFrame = self.textToImageProf(ticker, current, change, arrow, font=ImageFont.load("./fonts/6x10.pil")) #IMAGE THE TEXT if all_stocks_settings['logos']: 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 else: stitchedStock = midFrame image_list.append(stitchedStock) image_list.append(self.blank) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() finalDisplayImage = self.stitchImage(image_list) self.blank = Image.new('RGB', (10, 32)) return finalDisplayImage def getNewsImage(self): f = open('csv/news_settings.json', 'r') all_settings = json.load(f) f.close() if all_settings['title']: title_img = self.openImage('feature_titles/news.png') image_list = [title_img] else: image_list = [] headline_font = ImageFont.load("./fonts/6x13.pil") source_font = ImageFont.load("./fonts/6x13.pil") headline_info = all_settings['headlines'] headlines = [] source_date_times = [] source = [] for i, hi in enumerate(headline_info): try: headline, source, date_time = hi date, time = date_time.split('T') time = time[:-1] source_date_time = source + ': ' + date + ' ' + time 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_time, source_font, r=255, g=255, b=0, matrix_height = True) try: logo_name = source.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) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() news_image = self.stitchImage(image_list) return news_image def getNewsProfessional(self): f = open('csv/news_settings.json', 'r') all_settings = json.load(f) f.close() if all_settings['title']: title_img = self.openImage('feature_titles/small_feature_titles/news.png') image_list = [title_img] else: image_list = [] headline_font = ImageFont.load("./fonts/6x13.pil") source_font = ImageFont.load("./fonts/6x13.pil") headline_info = all_settings['headlines'] headlines = [] source_date_times = [] source = [] blank = Image.new('RGB', (0, 16)) for i, hi in enumerate(headline_info): try: headline, source, date_time = hi date, time = date_time.split('T') time = time[:-1] source_date_time = source + ': ' + date + ' ' + time 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_time+ ':', source_font, r=255, g=255, b=0, matrix_height = True) try: logo_name = source.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) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() news_image = self.stitchImage(image_list) return news_image def getLeagueImage(self, league=False, time = 'past'): if time in ['past', 'live']: filepath = 'csv/{}_games.json'.format(time) else: filepath = 'csv/upcoming_games.json' f = open(filepath, 'r') all_settings = json.load(f) f.close() title_img = self.openImage('feature_titles/sports_'+ time + '.png') if all_settings['title']: title_img = self.openImage('feature_titles/sports_{}.png'.format(time)) imgs = [title_img, self.blank] else: imgs = [] leagues_info = all_settings['leagues'] leagues = list(leagues_info.keys()) for league in leagues: try: x_offset = 0 img = Image.new('RGB', (10000, 32)) league_info = leagues_info[league] try: league_logo = Image.open('logos/sports/league_logos/{}.png'.format(league)).convert('RGB') img.paste(league_logo, (x_offset,0)) x_offset += league_logo.size[0] +self.blank.size[0] except: pass 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 for match in league_info: strHomeTeam = match['home_team'] strAwayTeam = match['away_team'] if time != 'future': intHomeScore = str(match['home_score']) intAwayScore = str(match['away_score']) dateEvent = match['date'].replace('-', '.') try: home_logo = Image.open('logos/sports/{}/{}'.format(league, sports_info[strHomeTeam]['logo'])) except Exception as e: home_logo = self.textImage(strHomeTeam.replace(' ', '\n'), small_font, r = 255, g = 255, b = 255) try: away_logo = Image.open('logos/sports/{}/{}'.format(league, sports_info[strAwayTeam]['logo'])) except Exception as e: away_logo = self.textImage(strAwayTeam.replace(' ', '\n'), small_font, r = 255, g = 255, b = 255) date_timage = self.textImage(dateEvent, 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[strHomeTeam]['colour'].replace(' ', '')) a_colour = mcolors.to_rgb(sports_info[strAwayTeam]['colour'].replace(' ', '')) hc_timage = self.textImage(sports_info[strHomeTeam]['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[strAwayTeam]['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(intHomeScore + '-' + intAwayScore, 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[strHomeTeam]['colour'].replace(' ', '')) a_colour = mcolors.to_rgb(sports_info[strAwayTeam]['colour'].replace(' ', '')) hc_timage = self.textImage(sports_info[strHomeTeam]['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[strAwayTeam]['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)) imgs.append(img) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() return self.stitchImage(imgs) def getLeagueTableImage(self, league = False): f = open('csv/league_tables.json', 'r') all_settings = json.load(f) f.close() leagues_info = all_settings['leagues'] leagues = list(leagues_info.keys()) if all_settings['title']: title_img = self.openImage('feature_titles/sports_team_stats.png') imgs = [title_img, self.blank] else: imgs = [] for league in leagues: try: x_offset = 0 img = Image.new('RGB', (10000, 32)) league_logo = Image.open('logos/sports/league_logos/{}.png'.format(league)).convert('RGB') img.paste(league_logo, (x_offset,0)) x_offset += league_logo.size[0] +self.blank.size[0] team_info = leagues_info[league] 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) # gets colour and symbol info etc from csv buff_size = 20 for team in team_info: try: logo = Image.open('logos/sports/{}/{}'.format(league, sports_info[team['name']]['logo'])) img.paste(logo, (x_offset, 0)) x_offset += logo.size[0] + 2 except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() name_timage = self.textImage(team['name'], med_font, r = 255, g = 255, b = 0) wins_timage = self.textImage('Wins:' + str(team['wins']), small_font, r = 0, g = 255, b = 0) loss_timage = self.textImage('Losses:' + str(team['loss']), small_font, r = 255, g = 0, b = 0) draw_timage = self.textImage('Draws:' + str(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:' + str(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 += standing_timage.size[0] if name_timage.size[0] > max( wins_timage.size[0], loss_timage.size[0], draw_timage.size[0]) + standing_timage.size[0]: x_offset += name_timage.size[0] - (max( wins_timage.size[0], loss_timage.size[0], draw_timage.size[0]) + standing_timage.size[0]) x_offset += buff_size img = img.crop((0,0,x_offset ,32)) imgs.append(img) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() return self.stitchImage(imgs) def convertTemp(self,temp, setting): if setting == 'kelvin': temp = temp+ 273.15 elif setting == 'fahrenheit': temp = temp*9/5 + 32 return temp def getTodayWeatherImage(self): f = open('csv/current_weather.json', 'r') all_settings = json.load(f) f.close() if all_settings['title']: title_img = self.openImage('feature_titles/weather.png') imgs = [title_img, self.blank] else: imgs = [] current_weathers = all_settings['locations'] locations = list(current_weathers.keys()) for i, location in enumerate(locations): try: current_weather = current_weathers[location] 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) 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 = self.convertTemp(current_weather['temp'], all_settings['temp']) feels_temp = self.convertTemp(current_weather['feels_like'], all_settings['temp']) temp_img = self.textImage(str("{0:.0f}".format(temp)), large_font) deg_img = self.textImage('o', small_font) main = current_weather['main_weather'] main_img = self.textImage(main.upper(), small_font) feels_img = self.textImage('Feels like:'.upper() + str("{0:.0f}".format(feels_temp)), small_font) min_img = self.textImage( "{0:.0f}".format(self.convertTemp(current_weather['min_temp'], all_settings['temp'])), small_font, r=0, g=0, b=255) max_img = self.textImage( "{0:.0f}".format(self.convertTemp(current_weather['max_temp'], all_settings['temp'])), small_font, r=255, g=0, b=0) hum_img = Image.open(weather_dir + '/humidity.png') htext_img = self.textImage(str(current_weather['humidity']) + '%', small_font) uv_img = Image.open(weather_dir + '/uv.png') utext_img = self.textImage(str(round(current_weather['uv'], 1)) , 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(), small_font) rain_img = Image.open(weather_dir + '/rain-chance.png') rtext_img = self.textImage(str(int(current_weather['rain_chance']*100)) + '%', 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') w_speed = current_weather['wind_speed']*3.6 w_unit = 'K/H' if all_settings["wind_speed"] == "miles/hour": w_speed = current_weather['wind_speed']*2.236936 w_unit = 'M/H' wtext_img = self.textImage("{0:.0f}".format(w_speed) + w_unit, small_font) wdir_img = self.textImage(self.degreesToCompass(current_weather['wind_direction']).upper(), 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 = Image.new('RGB', (1000, 32)) img.paste(weather_img, (5,9)) x_offset = 5 + weather_img.size[0] + 1 img.paste(main_img, (x_offset, 26)) img.paste(temp_img, (x_offset + main_img.size[0]//2 - temp_img.size[0]//2,9)) img.paste(deg_img, (x_offset + main_img.size[0]//2 - temp_img.size[0]//2 + temp_img.size[0], 8)) x_offset += max( main_img.size[0], temp_img.size[0] + deg_img.size[0]) img.paste(min_img, (x_offset - 1, 12)) img.paste(max_img, (x_offset - 1, 21)) x_offset += max(min_img.size[0], max_img.size[0]) + 2 img.paste(hum_img, ( x_offset, 8)) img.paste(uv_img, ( x_offset, 22)) img.paste(htext_img, (x_offset + hum_img.size[0], 10)) img.paste(utext_img, (x_offset + uv_img.size[0], 23)) x_offset += max(hum_img.size[0], uv_img.size[0]+2) x_offset += max(htext_img.size[0], utext_img.size[0]) + 6 img.paste(rain_img, (x_offset,8)) img.paste(cloud_img, (x_offset,20)) img.paste(ctext_img, (x_offset + cloud_img.size[0] + 2, 22)) img.paste(rtext_img, (x_offset + rain_img.size[0]+ 2, 10)) x_offset += max(cloud_img.size[0], rain_img.size[0])+6 x_offset += max(ctext_img.size[0], rtext_img.size[0])+6 img.paste(wind_img, (x_offset,8)) img.paste(vis_img, (x_offset,20)) img.paste(wtext_img, (x_offset + wind_img.size[0] + 2, 10)) img.paste(vtext_img, (x_offset + vis_img.size[0] + 2, 22)) x_offset += wind_img.size[0] + wtext_img.size[0] + 4 img.paste(wdir_img, (x_offset, 10)) img.paste(location_img, (5,0)) img.paste(feels_img, (location_img.size[0] + 10, 0)) img.paste(date_img, (location_img.size[0] + feels_img.size[0] + 15, 0)) img = img.crop((0,0,max(x_offset +wdir_img.size[0] + 4, location_img.size[0] + feels_img.size[0] + 15 + date_img.size[0]) ,32)) imgs.append(img) imgs.append(self.blank) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() return self.stitchImage(imgs) def getTodayWeatherProfessional(self): f = open('csv/current_weather.json', 'r') all_settings = json.load(f) f.close() if all_settings['title']: title_img = self.openImage('feature_titles/small_feature_titles/weather.png') image_list = [title_img, Image.new('RGB', (3, 16))] else: image_list = [] current_weathers = all_settings['locations'] locations = list(current_weathers.keys()) 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): try: img = Image.new('RGB', (1000, 32)) current_weather = current_weathers[location] 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 = self.convertTemp(current_weather['temp'], all_settings['temp']) temp_img = self.textImage(str("{0:.0f}".format(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(self.convertTemp(current_weather['min_temp'], all_settings['temp'])), small_font, r=0, g=0, b=255) img.paste(min_img, (x_offset+2, 2)) max_img = self.textImage( "{0:.0f}".format(self.convertTemp(current_weather['max_temp'], all_settings['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') w_speed = current_weather['wind_speed']*3.6 w_unit = 'K/H' if all_settings["wind_speed"] == "miles/hour": w_speed = current_weather['wind_speed']*2.236936 w_unit = 'M/H' wtext_img = self.textImage("{0:.0f}".format(w_speed) + w_unit, 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) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() 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/daily_weather.json', 'r') all_settings = json.load(f) f.close() if all_settings['title']: title_img = self.openImage('feature_titles/forecast.png') imgs = [title_img, self.blank] else: imgs = [] f = open('csv/daily_weather.json', 'r') daily_weathers = json.load(f) f.close() locations = list(daily_weathers['locations'].keys()) for i, location in enumerate(locations): try: img = Image.new('RGB', (1000, 32)) daily_weather = daily_weathers['locations'][location] 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 = daily_weather[0]['main_weather'] if main == 'Clouds': main = daily_weather[0]['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 = self.convertTemp(daily_weather[0]['temp'], daily_weathers['temp']) min_temp = self.convertTemp(daily_weather[0]['min_temp'], daily_weathers['temp']) max_temp = self.convertTemp(daily_weather[0]['max_temp'], daily_weathers['temp']) temp_img = self.textImage(str("{0:.0f}".format(temp)), large_font) deg_img = self.textImage('o', small_font) min_img = self.textImage( "{0:.0f}".format(min_temp, daily_weathers['temp']), small_font, r=0, g=0, b=255) max_img = self.textImage( "{0:.0f}".format(max_temp, daily_weathers['temp']), small_font, r=255, g=0, b=0) main = daily_weather[0]['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(daily_weather[0]['rain_chance']*100)) + '%', extra_small_font) hum_img = Image.open(weather_dir + '/humidity.png') htext_img = self.textImage(str(daily_weather[0]['humidity']) + '%', extra_small_font) wind_img = Image.open(weather_dir + '/wind.png') wtext_img = self.textImage(str(daily_weather[0]['wind_speed']) + 'm/s'.upper(), extra_small_font) uv_img = Image.open(weather_dir + '/uv.png') utext_img = self.textImage(str(round(daily_weather[0]['uv'], 1)) , small_font) cloud_img = Image.open(weather_dir + '/clouds.png') ctext_img = self.textImage(str(daily_weather[0]['clouds']) + '%', small_font) wind_img = Image.open(weather_dir + '/wind.png') w_speed = daily_weather[0]['wind_speed']*3.6 w_unit = 'K/H' if daily_weathers['wind_speed'] == "miles/hour": w_speed = daily_weather[0]['wind_speed']*2.236936 w_unit = 'M/H' wtext_img = self.textImage("{0:.0f}".format(w_speed) + w_unit, small_font) wdir_img = self.textImage(self.degreesToCompass(daily_weather[0]['wind_direction']), small_font) vis_img = Image.open(weather_dir + '/visibility.png') vtext_img = self.textImage(str(round(daily_weather[0]['visibility']/1000, 1)) + 'km'.upper(), small_font) img.paste(location_img, (0,0)) x_offset = location_img.size[0]+2 img.paste(weather_img, (5,9)) x_offset = 5 + weather_img.size[0] + 1 img.paste(main_img, (x_offset, 26)) img.paste(temp_img, (x_offset + main_img.size[0]//2 - temp_img.size[0]//2,9)) img.paste(deg_img, (x_offset + main_img.size[0]//2 - temp_img.size[0]//2 + temp_img.size[0], 8)) x_offset += max( main_img.size[0], temp_img.size[0] + deg_img.size[0]) img.paste(min_img, (x_offset - 1, 12)) img.paste(max_img, (x_offset - 1, 21)) x_offset += max(min_img.size[0], max_img.size[0]) + 2 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 crop_x = x_offset #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 = 77 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'] min_temp = self.convertTemp(weather['min_temp'], daily_weathers['temp']) max_temp = self.convertTemp(weather['max_temp'], daily_weathers['temp']) weather_img = Image.open(weather_dir + '/small_icons/' + weather_ids[main] + '.png') min_img = self.textImage( "{0:.0f}".format(min_temp), small_font, r=0, g=0, b=255) max_img = self.textImage( "{0:.0f}".format(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 += 43 img1 = img.crop((0,0,max(x_offset, crop_x) ,32)) imgs.append(img1) imgs.append(self.blank) # add the image text except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() return self.stitchImage(imgs) 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] f.close() return team_info def displayDailyWeatherAlt(self): img0, img1 = self.getDailyWeatherImageAlt() 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 getUserImages(self): f = open('csv/image_settings.json', 'r') all_settings = json.load(f) f.close() imgs = [] for ind,image in enumerate(all_settings['images']): try: img = self.openImage(os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'user_uploads'), image)) img.thumbnail((99999, 32)) if all_settings['title'] and ind == 0: title_img = self.openImage('feature_titles/images.png') imgs.append(self.stitchImage([title_img, img])) else: imgs.append(img) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() return imgs def getUserGIFs(self): f = open('csv/GIF_settings.json', 'r') all_settings = json.load(f) f.close() GIFs = [] for ind,fle in enumerate(all_settings['images']): try: GIF = Image.open(os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'user_uploads'), fle)) frames = [] if ind == 0 and all_settings['title']: title_img = self.openImage('feature_titles/gifs.png') for i, frame in enumerate(ImageSequence.Iterator(GIF)): frame = frame.convert('RGB') frame.thumbnail((128, 32)) f = self.stitchImage([title_img, frame]) frames.append(f) else: for i, frame in enumerate(ImageSequence.Iterator(GIF)): frame = frame.convert('RGB') frame.thumbnail((128, 32)) #frame = frame.resize((99999, 32)) f = self.stitchImage([frame]) frames.append(f) frames[0].save('./display_images/working_gif{}.gif'.format(str(ind)), save_all=True, append_images=frames[1:], loop=0, optimize = False) GIF = Image.open('./display_images/working_gif{}.gif'.format(str(ind))) GIFs.append(GIF) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close() #below code stitches title and GIF together ''' frames = [] for i, frame in enumerate(ImageSequence.Iterator(gif)): if all_settings['title']: f = self.stitchImage([title_img, frame]) else: f = self.stitchImage([frame]) frames.append(f) frames[0].save('./display_images/Custom GIFs.gif', save_all=True, append_images=frames[1:], loop=0, optimize = False) ''' return GIFs 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] self.double_buffer = self.matrix.CreateFrameCanvas() 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 = (float(msg)+1)*10 self.matrix.brightness = self.brightness 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 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 self.scrollFunctionsAnimated(['past_games', 'past_games'],animation = 'traditional') elif msg == 'l': # future league self.scrollFunctionsAnimated(['future_games', 'future_games'],animation = 'traditional') elif msg == 'L': # live game self.scrollFunctionsAnimated(['live_games', 'live_games'],animation = 'traditional') elif msg == 't': #legue tble self.scrollFunctionsAnimated(['league_table', 'league_table'],animation = 'traditional') 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 f = open('csv/display_settings.json', 'r') user_settings = json.load(f) f.close() if 'Professional'==user_settings[0] and len(user_settings[1][0])>0 and len(user_settings[1][1])>0: #if professional display self.scrollProfessionalAnimated(user_settings[1]) elif len(user_settings[1][0])>0 and 'Standard'==user_settings[0]: self.scrollFunctionsAnimated(user_settings[1][0], animation = 'down') elif msg == 'b': userSettings = json.load(open('csv/display_settings.json')) self.scrollProfessionalAnimated(userSettings) #self.displayProfessional() elif msg == '+': stock_ticker.scrollMultiple() elif msg == 'K': # kill self.resetMatrix() elif msg =='-': self.run_intro_screen() elif msg == '*': # # start_GIF = Image.open('./logos/startup_logo1.gif') self.displayGIF(start_GIF, delay = 0.02, repeat = False) #stock_ticker.setImage(start_image) time.sleep(2) self.resetMatrix() def run_intro_screen(self): dct = {"feature": "Custom Messages", "speed": "Medium", "animation": "Down", "title": False, "messages": [{"name": "welcome", "text": "Welcome to Fintic!", "text_colour": "White", "size": "Large", "background_colour": "Black"}, {"name": "get_started", "text": "To get started, connect your device to the \"Fintic Hotspot\" and access \"fintic.local:1024\" on your web browser. You can connect your ticker to Wi-Fi there.", "text_colour": "White", "size": "Small", "background_colour": "Black"}]} json.dump(dct, open('csv/message_settings.json', 'w')) self.scrollFunctionsAnimated(['Custom Messages'], repeat = True) if __name__ == '__main__': try: stock_ticker = StockTicker() while True: msg = getInput() stock_ticker.process_msg(msg) except Exception as e: logf = open('log.txt', "a") exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logf.write(str(e)) logf.write('. file: ' + fname) logf.write('. line: ' + str(exc_tb.tb_lineno)) logf.write('. type: ' + str(exc_type)) logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))) logf.close()