Skip to content

Instantly share code, notes, and snippets.

@apocolipse
Created October 5, 2020 00:00
Show Gist options
  • Save apocolipse/b9bf86a8b5a54ee5fc288846016e397b to your computer and use it in GitHub Desktop.
Save apocolipse/b9bf86a8b5a54ee5fc288846016e397b to your computer and use it in GitHub Desktop.
RainCclok - a hacky weather/time display for RaspiZeroW
#!/usr/bin/env python3
# encoding=utf8
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtSvg import *
from datetime import datetime
import requests
import lxml.html
import sys
TWC_COLOR_VARS = { "moon": "#e3e3e3", "star": "#e3e3e3", "cloud": "#d3d3d3", "na": "#d3d3d3", "fog": "#d3d3d3", "hail": "#d3d3d3",
"tornado": "#d3d3d3", "wind": "#d3d3d3", "storm": "#d3d3d3", "lightning": "#ebdb00", "sun": "#ebdb00", "drop": "#6adef8",
"snowflake": "#d3d3d3","thunderstorm-mask": "#2b2b2b",}
class WeatherInfo:
def __init__(self, tempString="", phrase="", svg="", precipPhrase="", title=""):
self.tempString, self.phrase, self.precipPhrase, self.svg, self.title = (tempString,phrase,precipPhrase,svg,title)
class RainClok(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('clok')
self.min = 0
self.currentWeather = WeatherInfo()
self.todayWeather = [WeatherInfo(),WeatherInfo(),WeatherInfo(),WeatherInfo()]
self.timers=[]
self._make_timer(333, self.update)
self._make_timer(10000, self._update_weather)
def _make_timer(self, interval, connector):
connector()
t=QTimer()
t.setInterval(interval)
t.timeout.connect(connector)
t.start()
self.timers.append(t)
def _update_weather(self):
n = datetime.now().minute
if n != self.min:
self.min = n
try:
# Replace with weather.com URL for your locale
resp = requests.get('https://weather.com/weather/today/l/cc72367e165efc130e5d4745b27a98955b97d33bf7a8215eeb74589f67f9cb2e')
# replace the color vars for SVG's used later on
text = resp.text
for key,value in TWC_COLOR_VARS.items():
text = text.replace("var(--color-%s)" % key, value)
tree = lxml.html.fromstring(text)
cc = tree.xpath('//div[contains(@id, "CurrentConditions")]')[0]
self.currentWeather.tempString = cc.xpath('.//span[@data-testid="TemperatureValue"]/text()')[0]
self.currentWeather.phrase = cc.xpath('.//div[@data-testid="wxPhrase"]/text()')[0]
self.currentWeather.precipPhrase = (cc.xpath('.//div[@data-testid="precipPhrase"]/span/text()')[0:1] or ("",))[0]
self.currentWeather.svg = self._svg_from_element_string(tree, lxml.html.tostring(cc.xpath('.//svg[@set="weather"]')[0]))
today=tree.xpath('//div[contains(@id, "TodayWeatherCard")]')[0]
for i, li in enumerate(today.xpath('.//li')):
title, temp, precip = li.xpath('.//span/text()')
self.todayWeather[i] = WeatherInfo(tempString=temp, title=title, precipPhrase=precip,
svg=self._svg_from_element_string(tree, lxml.html.tostring(li.xpath('.//svg[@set="weather"]')[0])))
except Exception as e:
print("Errror n shit %s" % e)
self.update()
def _svg_from_element_string(self, tree, element_string, width='100%', height='100%'):
svg = lxml.html.fromstring(element_string)
if len(svg.xpath('./defs')) > 0:
defs = svg.xpath('./defs')[0]
mask = defs.xpath('./mask')[0]
umask = lxml.html.tostring(mask)
defs.remove(mask)
defs.append(lxml.html.fromstring(self._svg_from_element_string(tree, umask)))
for i, use in enumerate(svg.xpath('./use')):
symb = lxml.html.tostring(tree.xpath('//symbol[@id="%s"]' % (use.attrib['xlink:href'].strip('#')))[0])
symb = lxml.html.fromstring(self._svg_from_element_string(tree, symb))
for c in symb.getchildren():
for k, v in use.attrib.items():
if k != 'xlink:href':
c.attrib[k]=v
svg.remove(use)
symb.tag = 'g'
svg.append(symb)
svg.attrib['width'], svg.attrib['height'] = width, height
return lxml.html.tostring(svg)
def _draw_weather(self, painter):
painter.save()
painter.setPen(Qt.white)
fontSize = 70
painter.setFont(QFont("SF Pro Display", fontSize, QFont.Normal))
x, y = 40, 170
w, h = int(fontSize * 2.5), int(fontSize * 1.5)
painter.drawText(x, y, w, h, Qt.AlignLeft, self.currentWeather.tempString)
painter.setFont(QFont("SF Pro Display", 20, QFont.Normal))
painter.drawText(x, y + h, w, 30, Qt.AlignLeft, self.currentWeather.phrase)
painter.setFont(QFont("SF Pro Display", 14, QFont.Light))
painter.drawText(x, y + h + 30, w*4, 20, Qt.AlignLeft, self.currentWeather.precipPhrase)
renderer = QSvgRenderer(self.currentWeather.svg)
renderer.render(painter, QRectF(x+w,y,50,50))
tw, ty = 90, y+180
for i, w in enumerate(self.todayWeather):
painter.setFont(QFont("SF Pro Display", 25, QFont.Bold))
painter.drawText(x+tw*i-20, ty-10, tw, 30, Qt.AlignCenter, w.tempString)
painter.setFont(QFont("SF Pro Display", 14, QFont.Normal))
painter.drawText(x+tw*i-20, ty+70, tw, 30, Qt.AlignCenter, w.title)
renderer = QSvgRenderer(w.svg)
renderer.render(painter, QRectF(x+tw*i,ty+20,25,25))
painter.restore()
def _draw_time(self, painter):
painter.save()
fontSize, y, ind = 95, 20, 320
h, dw, sw = int(fontSize * 1.5), fontSize * 2 - 15, 18
is24, displaySecs = False, False
n = datetime.now()
hour = n.hour
if not is24:
isAM = True if n.hour < 12 else False
hour = hour % 12 if hour != 0 else 12
painter.setPen(Qt.white)
painter.setFont(QFont("SF Pro Display", fontSize, QFont.Normal))
painter.drawText(ind, y, dw, h, Qt.AlignRight | Qt.AlignBottom, str(hour).zfill(1))
painter.drawText(ind+dw, y - 15, sw, h, Qt.AlignCenter | Qt.AlignTop, ":" if displaySecs else (":" if n.second % 2 == 0 else ""))
painter.drawText(ind+dw+sw, y, dw, h, Qt.AlignCenter | Qt.AlignBottom, str(n.minute).zfill(2))
if displaySecs:
painter.drawText(ind+dw+sw+dw, y-15, sw, h, Qt.AlignCenter | Qt.AlignTop, ":")
painter.drawText(ind+dw+sw+dw+sw, y, dw, h, Qt.AlignLeft | Qt.AlignBottom, str(n.second).zfill(2))
if not is24:
apx = ind+dw+sw+dw+sw+dw if displaySecs else ind+dw+sw+dw
painter.setFont(QFont("SF Pro Display", int(fontSize / 2), QFont.Normal))
painter.drawText(apx + 10, y - 7, dw, h, Qt.AlignLeft | Qt.AlignBottom, "AM" if isAM else "PM")
painter.restore()
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
painter.setPen(QColor('#555753'))
painter.setBrush(QColor('#555753'))
painter.drawRect(self.rect())
self._draw_time(painter)
self._draw_weather(painter)
if __name__ == '__main__':
QApplication.setAttribute(Qt.AA_DisableHighDpiScaling)
app = QApplication(sys.argv)
w = RainClok()
if '--fullscreen' in sys.argv:
w.showFullScreen()
w.setCursor(Qt.BlankCursor)
else:
w.show()
w.resize(800, 480)
app.exec()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment