fintic-tracker/stockTicker.py
2021-06-30 22:00:14 +01:00

1680 lines
62 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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()
print(Image.SAVE.keys())
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 = 3
self.matrix = RGBMatrix(options = options)
self.points = True # display crypto change in points or percent
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=False, 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)):
(r, g, b) = pixels[x, y]
if min_x <= x + offset_x <= max_x and min_y <= y + offset_y <= max_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
while offset_x > -img_width:
offset_x -= 1
self.setImage(image, offset_x = offset_x, offset_y = offset_y)
# 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()
time.sleep(self.delay)
return False
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])
image2 = self.openImage(image_files[1])
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])
image2 = self.openImage(image_files[1])
elif current_img == 2:
if stocks:
update_process = Process(target = self.getFullStockImage, args = (2,))
update_process.start()
image1 = self.openImage(image_files[1])
image2 = self.openImage(image_files[0])
img_width, img_height = image1.size
while offset_x > -img_width:
offset_x -= 1
self.setImage(image1, offset_x = offset_x, offset_y = offset_y)
if offset_x + img_width < self.matrix.width: # if the image is ending
self.setImage(image2, offset_x = offset_x + img_width, offset_y = offset_y)
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:
print(option)
print('display_images/' + option + '.ppm')
if option not in ['display_gif']:
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()
image = self.openImage('./display_images/' + options[i % len(options)] +'.ppm')
if animation == 'continuous':
image2 = self.openImage('./display_images/' + options[(i + 1) % len(options)] +'.ppm')
img_width, img_height = image.size
offset_x = 0
if animation in ['traditional', '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
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:
print(frame)
try:
image.seek(frame)
except EOFError:
print('finished')
frame = 0
image.seek(frame)
frame +=1
offset_y -= 1
self.setImage(image.convert('RGB'), offset_x = offset_x, offset_y = offset_y)
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
self.setImage(image.convert('RGB'), offset_x = offset_x, offset_y = offset_y)
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
self.setImage(image.convert('RGB'), offset_x = offset_x, offset_y = offset_y)
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
self.setImage(image.convert('RGB'), offset_x = offset_x, offset_y = offset_y)
time.sleep(self.delay)
kill = self.checkKilled()
if kill: break
while offset_x > -img_width:
# for animation in gifs
if offset_x%frames == 0:
try:
image.seek(frame)
except EOFError:
frame = 0
image.seek(frame)
frame +=1
#image = image.convert('RGB')
offset_x -= 1
# remove the ppixels behind the image, to stop trailing
for x in range(0,offset_x ):
for y in range(self.matrix.height):
self.matrix.SetPixel(x , y , 0,0,0)
self.setImage(image.convert('RGB'), offset_x = offset_x, offset_y = offset_y)
buff = 0
if offset_x + img_width + buff< self.matrix.width and animation == 'continuous': # if the image is ending
self.setImage(image2, offset_x = offset_x + img_width + buff, offset_y = offset_y)
else:
# 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)
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:
print('finished')
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:
print('finished')
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)
try:
msg = getInput()
if msg == 'K':
gif.close()
self.resetMatrix()
return True
self.process_msg(msg)
except KeyboardInterrupt:
sys.stdout.flush()
pass
return False
#Using change between min and day price give appropriate arrow
#and set the overall change colour
def getArrow(self, CHANGE):
self.greenORred
logos_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos')
if(CHANGE>0):
Arrow = Image.open(os.path.join(logos_path, 'up.png'))
self.greenORred = (0, 255, 0)
return Arrow, CHANGE
Arrow = Image.open(os.path.join(logos_path, 'down.png'))
self.greenORred = (255, 0, 0)
CHANGE = (CHANGE * -1)
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
#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 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)
print('image', len(image_list))
finalDisplayImage = self.stitchImage(image_list)
return finalDisplayImage
#Connect all the pieces togeather creating 1 long final stock image
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.dirname(os.path.abspath(__file__)), 'logos')
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 getNewsImage(self):
title_img = self.openImage('feature_titles/news.png')
headline_font = ImageFont.load("./fonts/10x20.pil")
source_font = ImageFont.load("./fonts/10x20.pil")
image_list = [title_img]
headlines = []
source_date_times = []
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)
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_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:
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, 'techcrunch' + '.png'))
img = Image.new('RGB', (headline_img.size[0], 32))
img.paste(headline_img, (2, -3))
img.paste(source_img, (2,16))
img= self.stitchImage([logo,img])
except Exception as e:
print(e)
img = Image.new('RGB', (headline_img.size[0], 32))
img.paste(headline_img, (0,0))
img.paste(source_img, (0,16))
image_list.append(img)
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'))
print(league_info[0].keys())
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
print(len(league_info))
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)
print(e)
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)
print(e)
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)
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'])
print(e)
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]
for location in locations:
img = Image.new('RGB', (200, 32))
current_weather = json.load(open('csv/current_weather.json', 'r'))
small_font = ImageFont.load("./fonts/5x7.pil")
large_font = ImageFont.load("./fonts/10x20.pil")
location_img = self.textImage(location, 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, (36,9))
deg_img = self.textImage('o', small_font)
img.paste(deg_img, (56, 8))
main = current_weather['main_weather']
main_img = self.textImage(main, small_font)
img.paste(main_img, (34, 26))
feels_img = self.textImage('Feels like:' + 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(current_weather['uv']) , 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'))]
date = str(int(datetime.now().strftime('%d')))
weekday = weekdays[datetime.today().weekday()]
date_img = self.textImage(month + ' ' + date + ',' + weekday, 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', small_font)
img.paste(wtext_img, (168, 10))
wdir_img = self.textImage(self.degreesToCompass(current_weather['wind_direction']), small_font)
img.paste(wdir_img, (183, 10))
vis_img = Image.open(weather_dir + '/visibility.png')
img.paste(vis_img, (154,20))
vtext_img = self.textImage(str(current_weather['visibility']/1000) + 'km', small_font)
img.paste(vtext_img, (168, 22))
imgs.append(img)
return self.stitchImage(imgs)
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/weather.png')
imgs = [title_img]
for location in locations:
img = Image.new('RGB', (1000, 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)
uv_img = Image.open(weather_dir + '/uv.png')
utext_img = self.textImage(str(current_weather['uv']) , 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', 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(current_weather['visibility']/1000) + 'km', 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, (90,0))
img.paste(rtext_img, (103, 1))
img.paste(hum_img, (117, 0))
img.paste(htext_img, (128, 1))
img.paste(uv_img, ( 143, 0))
img.paste(utext_img, (156, 1))
img.paste(cloud_img, (169,0))
img.paste(ctext_img, (182, 1))
img.paste(wind_img, (205,0))
img.paste(wtext_img, (218, 1))
img.paste(wdir_img, (231, 1))
img.paste(vis_img, (243,0))
img.paste(vtext_img, (256, 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))
daily_weather = json.load(open('csv/daily_weather.json', 'r'))
#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, 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 += 40
img1 = img.crop((0,0,x_offset ,32))
imgs.append(img1)
# 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/display_gif.ppm'))
return gif
def displayStocks(self):
self.scrollImageTransition(['final.ppm', 'final.ppm'], offset_x = 0, offset_y = 0)
def process_msg(self, msg):
if msg == 'S': # stocks
img = self.getStockImage()
img.save('display_images/stocks.ppm')
self.scrollImageTransition(['display_images/stocks.ppm', 'display_images/stocks.ppm'], stocks = False)
elif msg == 'N': #news
img = self.getNewsImage()
img.save('display_images/news.ppm')
self.scrollImageTransition(['display_images/news.ppm', 'display_images/news.ppm'], stocks = False)
# speed settings
elif msg == 's':
self.delay = 0.03
elif msg == 'm':
self.delay = 0.01
elif msg == 'f':
self.delay = 0.005
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
img = self.getUserText()
img.save('display_images/scroll_text.ppm')
self.scrollImageTransition(['display_images/scroll_text.ppm', 'display_images/scroll_text.ppm'], offset_x = 128, offset_y = 0, stocks = False)
elif msg == 'I': # image
image = self.openImage(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'display_images/display_image'))
#self.setImage( image)
while True:
kill = self.scrollImage(image, offset_x = 128)
if kill:
break
elif msg == 'G': # gif
gif = Image.open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'display_images/display_gif.ppm'))
#self.displayGIF(gif)
while True:
kill = self.scrollGIF(gif, offset_x = 128)
if kill:
break
elif msg == 'W': # weather
img = self.getTodayWeatherImage()
img.save('display_images/weather.ppm')
self.scrollImageTransition(['display_images/weather.ppm', 'display_images/weather.ppm'], stocks = False)
elif msg == 'D': # daily weather
#self.displayDailyWeatherAlt()
img = self.getDailyWeatherImage()
img.save('display_images/weather.ppm')
#self.setImage( image)
self.scrollImageTransition(['display_images/weather.ppm', 'display_images/weather.ppm'], stocks = False)
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 == 'F': # 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
self.functions = {'stocks': self.getStockImage, 'crypto': self.getCryptoImage, 'forex': self.getForexImage,
'daily_weather':self.getDailyWeatherImage, 'today_weather': self.getDailyWeatherImage, '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
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 = ['stocks', 'crypto'] # 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 == 'K': # kill
self.resetMatrix()
if __name__ == '__main__':
with open('log.txt', "w") as log:
try:
stock_ticker = StockTicker()
#stock_ticker.process_msg(P')
#stock_ticker.process_msg('G')
#stock_ticker.process_msg('f')
#stock_ticker.process_msg('A')
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)