2774 lines
115 KiB
Python
Executable File
2774 lines
115 KiB
Python
Executable File
# 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 = 4
|
||
options.brightness = self.brightness
|
||
self.matrix = RGBMatrix(options = options)
|
||
print(dir(self.matrix))
|
||
#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):
|
||
print('options', 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()
|
||
|
||
settings = json.load(open(self.JSONs[options[(i) % len(options)]]))
|
||
|
||
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)] ]))
|
||
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)]]))
|
||
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+1) % 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)
|
||
print(kind, content, delay)
|
||
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 = str(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:
|
||
|
||
exc_type, exc_obj, exc_tb = sys.exc_info()
|
||
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
|
||
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 = str(abs(float(coin_info[cb]['percent_change']))) + '%'
|
||
else:
|
||
|
||
change = str(abs(float(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:
|
||
exc_type, exc_obj, exc_tb = sys.exc_info()
|
||
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
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 = str(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(base+ '(' + symbol + ')', 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:
|
||
print(e)
|
||
|
||
image_list.append(midFrame)
|
||
image_list.append(self.blank)
|
||
|
||
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]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
|
||
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"]:
|
||
# convert percent to points
|
||
change = abs(float(forex_settings[sb]['percent_change']))
|
||
|
||
change = str(change) + '%'
|
||
else:
|
||
change = str(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:
|
||
print(e)
|
||
|
||
image_list.append(midFrame)
|
||
|
||
image_list.append(self.blank)
|
||
|
||
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]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
|
||
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)
|
||
|
||
print(percent_change, point_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:
|
||
print(str(e))
|
||
stitchedStock = midFrame
|
||
else:
|
||
stitchedStock = midFrame
|
||
|
||
image_list.append(stitchedStock)
|
||
|
||
|
||
image_list.append(self.blank)
|
||
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]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
|
||
|
||
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:
|
||
exc_type, exc_obj, exc_tb = sys.exc_info()
|
||
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
|
||
finalDisplayImage = self.stitchImage(image_list)
|
||
|
||
|
||
self.blank = Image.new('RGB', (10, 32))
|
||
return finalDisplayImage
|
||
|
||
def getNewsImage(self):
|
||
|
||
all_settings = json.load(open('csv/news_settings.json', 'r'))
|
||
|
||
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:
|
||
exc_type, exc_obj, exc_tb = sys.exc_info()
|
||
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
news_image = self.stitchImage(image_list)
|
||
|
||
return news_image
|
||
|
||
def getNewsProfessional(self):
|
||
|
||
all_settings = json.load(open('csv/news_settings.json', 'r'))
|
||
|
||
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[i].lower().replace(' ', '-')
|
||
logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'news_logos')
|
||
|
||
logo = Image.open(os.path.join(logos_path, logo_name + '.png'))
|
||
|
||
except Exception as e:
|
||
logo_name = 'default'
|
||
logos_path = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos'), 'news_logos')
|
||
|
||
logo = Image.open(os.path.join(logos_path, logo_name + '.png'))
|
||
|
||
width, height = logo.size
|
||
|
||
logo = logo.resize((int(width/2), int(height/2)))
|
||
|
||
img = Image.new('RGB', (headline_img.size[0]+ source_img.size[0] + logo.size[0] +5, 32))
|
||
img.paste(headline_img, (source_img.size[0]+ logo.size[0] -5, 3))
|
||
img.paste(source_img, (2,3))
|
||
|
||
img= self.stitchImage([logo,img])
|
||
|
||
|
||
|
||
|
||
image_list.append(img)
|
||
image_list.append(blank)
|
||
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]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
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:
|
||
print(time)
|
||
filepath = 'csv/upcoming_games.json'
|
||
|
||
|
||
all_settings = json.load(open(filepath, 'r'))
|
||
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)
|
||
print(sports_info)
|
||
|
||
buff_size = 25
|
||
|
||
for match in league_info:
|
||
home_team = match['home_team']
|
||
away_team = match['away_team']
|
||
|
||
if time != 'future':
|
||
home_score = str(match['home_score'])
|
||
away_score = str(match['away_score'])
|
||
|
||
date = match['date'].replace('-', '.')
|
||
|
||
#rond = match['round']
|
||
|
||
|
||
try:
|
||
|
||
home_logo = Image.open('logos/sports/{}/{}'.format(league, sports_info[home_team]['logo']))
|
||
|
||
except Exception as e:
|
||
home_logo = self.textImage(home_team.replace(' ', '\n'), small_font, r = 255, g = 255, b = 255)
|
||
|
||
|
||
try:
|
||
|
||
away_logo = Image.open('logos/sports/{}/{}'.format(league, sports_info[away_team]['logo']))
|
||
|
||
except Exception as e:
|
||
away_logo = self.textImage(away_team.replace(' ', '\n'), small_font, r = 255, g = 255, b = 255)
|
||
|
||
|
||
date_timage = self.textImage(date, small_font, r = 255, g = 255, b = 255)
|
||
|
||
img.paste(home_logo, (x_offset,0))
|
||
|
||
x_offset += home_logo.size[0] + 2
|
||
|
||
|
||
if time == 'future':
|
||
img.paste(date_timage, (x_offset+5, 0))
|
||
|
||
|
||
h_colour = mcolors.to_rgb(sports_info[home_team]['colour'].replace(' ', ''))
|
||
a_colour = mcolors.to_rgb(sports_info[away_team]['colour'].replace(' ', ''))
|
||
|
||
|
||
hc_timage = self.textImage(sports_info[home_team]['code'], med_font, r = int(h_colour[0]*255), g = int(h_colour[1]*255), b = int(h_colour[2]*255))
|
||
ac_timage = self.textImage(sports_info[away_team]['code'], med_font, r = int(a_colour[0]*255), g = int(a_colour[1]*255), b = int(a_colour[2]*255))
|
||
vs_timage = self.textImage('vs', med_font, r = 255, g = 255, b = 255, h_buff = 5)
|
||
|
||
img.paste(hc_timage, (x_offset, 9))
|
||
img.paste(vs_timage, (x_offset + hc_timage.size[0], 9))
|
||
img.paste(ac_timage, (x_offset + hc_timage.size[0] + vs_timage.size[0], 9))
|
||
x_offset += max( date_timage.size[0], hc_timage.size[0] + vs_timage.size[0] + ac_timage.size[0])
|
||
else:
|
||
|
||
score_image = self.textImage(home_score + '-' + away_score, large_font, h_buff = 5, r = 255, g = 255, b = 255)
|
||
|
||
#vs_timage = self.textImage(sports_info[home_team]['code'] + 'vs' + sports_info[away_team]['code'], small_font, r = 255, g = 255, b = 255)
|
||
|
||
h_colour = mcolors.to_rgb(sports_info[home_team]['colour'].replace(' ', ''))
|
||
a_colour = mcolors.to_rgb(sports_info[away_team]['colour'].replace(' ', ''))
|
||
|
||
hc_timage = self.textImage(sports_info[home_team]['code'], small_font, r = int(h_colour[0]*255), g = int(h_colour[1]*255), b = int(h_colour[2]*255))
|
||
ac_timage = self.textImage(sports_info[away_team]['code'], small_font, r = int(a_colour[0]*255), g = int(a_colour[1]*255), b = int(a_colour[2]*255))
|
||
vs_timage = self.textImage('vs', small_font, r = 255, g = 255, b = 255)
|
||
|
||
if date_timage.size[0] > score_image.size[0]:
|
||
img.paste(date_timage, (x_offset+2, 0))
|
||
img.paste(hc_timage, (x_offset+6, 9))
|
||
img.paste(vs_timage, (x_offset+5 + hc_timage.size[0], 9))
|
||
img.paste(ac_timage, (x_offset+6 + hc_timage.size[0] + vs_timage.size[0], 9))
|
||
img.paste(score_image, (x_offset + 2 + int((date_timage.size[0] - score_image.size[0])/2), 15))
|
||
else:
|
||
|
||
img.paste(date_timage, (x_offset+1+int((score_image.size[0] - date_timage.size[0] )/2), 0))
|
||
vs_size = hc_timage.size[0] + vs_timage.size[0] + ac_timage.size[0]
|
||
img.paste(hc_timage, (x_offset + 1 + int((score_image.size[0] - vs_size)/2), 9))
|
||
img.paste(vs_timage, (x_offset + int((score_image.size[0] - vs_size)/2) + hc_timage.size[0], 9))
|
||
img.paste(ac_timage, (x_offset+1 + int((score_image.size[0] - vs_size)/2) + hc_timage.size[0] + vs_timage.size[0], 9))
|
||
|
||
img.paste(score_image, (x_offset+1, 15))
|
||
|
||
|
||
x_offset += max( date_timage.size[0]+4, hc_timage.size[0] + vs_timage.size[0] + ac_timage.size[0]+4, 2 + int(score_image.size[0]))
|
||
|
||
#img.paste(vs_timage, (x_offset+4, 9))
|
||
|
||
#if league == 'NHL':
|
||
#
|
||
#img.paste(round_timage, (x_offset+ 7, 8))
|
||
#x_offset += max(home_timage.size[0], away_timage.size[0], date_timage.size[0], round_timage.size[0], score_image.size[0])
|
||
|
||
img.paste(away_logo, (x_offset,0))
|
||
|
||
x_offset += away_logo.size[0]
|
||
x_offset += buff_size
|
||
img = img.crop((0,0,x_offset ,32))
|
||
imgs.append(img)
|
||
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]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
|
||
|
||
|
||
return self.stitchImage(imgs)
|
||
|
||
def getLeagueTableImage(self, league = False):
|
||
|
||
|
||
all_settings = json.load(open('csv/league_tables.json', 'r'))
|
||
|
||
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:
|
||
|
||
print('no logo for:', team['name'])
|
||
|
||
|
||
name_timage = self.textImage(team['name'], med_font, r = 255, g = 255, b = 0)
|
||
wins_timage = self.textImage('Wins:' + 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 += max(points_timage.size[0], standing_timage.size[0]) + buff_size
|
||
x_offset += standing_timage.size[0] + buff_size
|
||
|
||
|
||
img = img.crop((0,0,x_offset ,32))
|
||
imgs.append(img)
|
||
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]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
return self.stitchImage(imgs)
|
||
|
||
def getTodayWeatherImage(self):
|
||
all_settings = json.load(open('csv/current_weather.json', 'r'))
|
||
|
||
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 = current_weather['temp']
|
||
feels_temp = current_weather['feels_like']
|
||
if all_settings['temp'] == 'kelvin':
|
||
temp = current_weather['temp'] + 273.15
|
||
elif all_settings['temp'] == 'fahrenheit':
|
||
feels_temp = current_weather['feels_like']*9/5 + 32
|
||
temp = current_weather['temp']*9/5 + 32
|
||
|
||
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(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)
|
||
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:
|
||
exc_type, exc_obj, exc_tb = sys.exc_info()
|
||
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
|
||
return self.stitchImage(imgs)
|
||
|
||
def getTodayWeatherProfessional(self):
|
||
all_settings = json.load(open('csv/current_weather.json', 'r'))
|
||
|
||
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 = current_weather['temp']
|
||
if all_settings['temp'] == 'kelvin':
|
||
temp = current_weather['temp'] + 273.15
|
||
elif all_settings['temp'] == 'fahrenheit':
|
||
temp = current_weather['temp']*9/5 + 32
|
||
|
||
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(current_weather['min_temp']), small_font, r=0, g=0, b=255)
|
||
img.paste(min_img, (x_offset+2, 2))
|
||
|
||
max_img = self.textImage( "{0:.0f}".format(current_weather['max_temp']), small_font, r=255, g=0, b=0)
|
||
img.paste(max_img, (x_offset+2, 8))
|
||
|
||
x_offset += max_img.size[0] + 2
|
||
|
||
hum_img = Image.open(weather_dir + '/humidity.png')
|
||
htext_img = self.textImage(str(current_weather['humidity']) + '%', font)
|
||
uv_img = Image.open(weather_dir + '/uv.png')
|
||
utext_img = self.textImage(str(round(current_weather['uv'], 1)), font)
|
||
|
||
|
||
rain_img = Image.open(weather_dir + '/rain-chance.png')
|
||
rtext_img = self.textImage(str(int(current_weather['rain_chance']*100)) + '%', font)
|
||
cloud_img = Image.open(weather_dir + '/clouds.png')
|
||
ctext_img = self.textImage(str(current_weather['clouds']) + '%', font)
|
||
wind_img = Image.open(weather_dir + '/wind.png')
|
||
|
||
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(wind_speed) + wind_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:
|
||
exc_type, exc_obj, exc_tb = sys.exc_info()
|
||
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
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):
|
||
|
||
|
||
all_settings = json.load(open('csv/daily_weather.json', 'r'))
|
||
|
||
if all_settings['title']:
|
||
title_img = self.openImage('feature_titles/forecast.png')
|
||
imgs = [title_img, self.blank]
|
||
else:
|
||
imgs = []
|
||
|
||
|
||
|
||
daily_weathers = json.load(open('csv/daily_weather.json', 'r'))
|
||
|
||
locations = list(daily_weathers['locations'].keys())
|
||
|
||
for i, location in enumerate(locations):
|
||
print(location)
|
||
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 = daily_weather[0]['temp']
|
||
min_temp = daily_weather[0]['min_temp']
|
||
max_temp = daily_weather[0]['max_temp']
|
||
|
||
if daily_weathers['temp'] == 'kelvin':
|
||
temp = daily_weather[0]['temp'] + 273.15
|
||
min_temp = daily_weather[0]['min_temp'] + 273.15
|
||
max_temp = daily_weather[0]['max_temp'] + 273.15
|
||
elif daily_weathers['temp'] == 'fahrenheit':
|
||
temp = daily_weather[0]['temp']*9/5 + 32
|
||
min_temp = daily_weather[0]['min_temp']*9/5 + 32
|
||
max_temp = daily_weather[0]['max_temp']*9/5 + 32
|
||
|
||
|
||
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), 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)
|
||
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 = 70
|
||
|
||
for i in range(1,len(daily_weather)-1):
|
||
weekday = weekdays[(datetime.today().weekday() + i)%7]
|
||
|
||
day_img = self.textImage( weekday.upper(), small_font)
|
||
weather = daily_weather[i]
|
||
main = weather['main_weather']
|
||
|
||
|
||
if main == 'Clouds':
|
||
main = weather['description']
|
||
|
||
|
||
|
||
min_temp = weather['min_temp']
|
||
max_temp = weather['max_temp']
|
||
|
||
if daily_weathers['temp'] == 'kelvin':
|
||
|
||
min_temp = weather['min_temp'] + 273.15
|
||
max_temp = weather['max_temp'] + 273.15
|
||
elif daily_weathers['temp'] == 'fahrenheit':
|
||
|
||
min_temp = weather['min_temp']*9/5 + 32
|
||
max_temp = weather['max_temp']*9/5 + 32
|
||
|
||
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:
|
||
print(str(e))
|
||
exc_type, exc_obj, exc_tb = sys.exc_info()
|
||
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
|
||
|
||
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()
|
||
#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 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))
|
||
print(img)
|
||
img.thumbnail((99999, 32))
|
||
print(img)
|
||
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:
|
||
exc_type, exc_obj, exc_tb = sys.exc_info()
|
||
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
|
||
|
||
|
||
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:
|
||
print(str(e))
|
||
exc_type, exc_obj, exc_tb = sys.exc_info()
|
||
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
|
||
self.logf.write(str(e))
|
||
self.logf.write('. file: ' + fname)
|
||
self.logf.write('. line: ' + str(exc_tb.tb_lineno))
|
||
self.logf.write('. type: ' + str(exc_type))
|
||
self.logf.write('\n ' + "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])))
|
||
|
||
|
||
|
||
#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
|
||
userSettings = json.load(open('csv/display_settings.json'))
|
||
if 'Professional'==userSettings[0] and len(userSettings[1][0])>0 and len(userSettings[1][1])>0: #if professional display
|
||
self.scrollProfessionalAnimated(userSettings[1])
|
||
elif len(userSettings[1][0])>0 and 'Standard'==userSettings[0]:
|
||
self.scrollFunctionsAnimated(userSettings[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__':
|
||
|
||
with open('log.txt', "a") as log:
|
||
try:
|
||
stock_ticker = StockTicker()
|
||
stock_ticker.logf = log
|
||
|
||
|
||
|
||
#stock_ticker.getLeagueImage('NHL', 'future')
|
||
#stock_ticker.getCryptoImage()
|
||
|
||
#
|
||
|
||
#stock_ticker.process_msg('*')
|
||
#time.sleep(8)
|
||
|
||
#stock_ticker.process_msg('-')
|
||
#stock_ticker.process_msg('W')
|
||
#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])))
|
||
|
||
|
||
|