Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bullpen screen rotation PoC #523

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion coordinates/w64h64.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@
"width": 64
},
"time": {
"x": 40,
"x": 32,
"y": 7
},
"conditions": {
Expand Down
4 changes: 2 additions & 2 deletions data/standings.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,11 @@ def populated(self):
def is_postseason(self):
return self.date > self.playoffs_start_date

def __standings_for(self, division_name):
def standings_for(self, division_name):
return next(division for division in self.standings if division.name == division_name)

def current_standings(self):
return self.__standings_for(self.preferred_divisions[self.current_division_index])
return self.standings_for(self.preferred_divisions[self.current_division_index])

def advance_to_next_standings(self):
self.current_division_index = self.__next_division_index()
Expand Down
6 changes: 5 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import sys
import Bullpen
import screens

from data.screens import ScreenType
import debug
Expand Down Expand Up @@ -167,7 +169,9 @@ def __refresh_gameday(render_thread, data): # type: (threading.Thread, Data) ->


def __render_main(matrix, data):
MainRenderer(matrix, data).render()
rotator = screens.ScreenRotator(matrix, data)

rotator.start()


if __name__ == "__main__":
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ MLB_StatsAPI>=1.6.1
pyowm==3.3.0
RGBMatrixEmulator>=0.8.4
tzlocal==4.2
Bullpen
80 changes: 80 additions & 0 deletions screens/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from screens.news import NewsScreen
from screens.standings import *

import Bullpen

class ScreenRotator:

def __init__(self, matrix, data):
self.matrix = matrix
self.data = data
self.canvas = matrix.CreateFrameCanvas()

self.generate_rotations()
self.manager = Bullpen.Manager(entrypoint=self.entrypoint)

@property
def entrypoint(self):
return self.screens[0]

def start(self):
self.manager.perform()

def generate_rotations(self):
# ****************
# * SCREEN SETUP *
# ****************

# NEWS
news = NewsScreen(self.matrix, self.canvas, self.data)

# NL DIVISION STANDINGS
nl_central_standings = DivisionStandingsScreen(self.matrix, self.canvas, self.data, division="NL Central")
nl_west_standings = DivisionStandingsScreen(self.matrix, self.canvas, self.data, division="NL West")
nl_east_standings = DivisionStandingsScreen(self.matrix, self.canvas, self.data, division="NL East")

# AL DIVISION STANDINGS
al_central_standings = DivisionStandingsScreen(self.matrix, self.canvas, self.data, division="AL Central")
al_west_standings = DivisionStandingsScreen(self.matrix, self.canvas, self.data, division="AL West")
al_east_standings = DivisionStandingsScreen(self.matrix, self.canvas, self.data, division="AL East")

self.screens = [
news,
nl_central_standings,
nl_west_standings,
nl_east_standings,
nl_central_standings,
nl_west_standings,
nl_east_standings
]

# ********************
# * Transition SETUP *
# ********************

# NEWS
news.add_transitions(
Bullpen.Transition(to=nl_central_standings, on=Bullpen.Condition.Timer(10))
)

# NL DIVISION STANDINGS
nl_central_standings.add_transitions(
Bullpen.Transition(to=nl_east_standings, on=Bullpen.Condition.Timer(10))
)
nl_east_standings.add_transitions(
Bullpen.Transition(to=nl_west_standings, on=Bullpen.Condition.Timer(10))
)
nl_west_standings.add_transitions(
Bullpen.Transition(to=al_central_standings, on=Bullpen.Condition.Timer(10))
)

# AL DIVISION STANDINGS
al_central_standings.add_transitions(
Bullpen.Transition(to=al_east_standings, on=Bullpen.Condition.Timer(10))
)
al_east_standings.add_transitions(
Bullpen.Transition(to=al_west_standings, on=Bullpen.Condition.Timer(10))
)
al_west_standings.add_transitions(
Bullpen.Transition(to=news, on=Bullpen.Condition.Timer(10))
)
51 changes: 51 additions & 0 deletions screens/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Bullpen
from driver import graphics

class MLBLEDScoreboardScreen(Bullpen.Action):

SCROLLABLE = False
DEFAULT_REFRESH_RATE = 0.05 # seconds

def __init__(self, matrix, canvas, data):
self.matrix = matrix
self.canvas = canvas
self.data = data
self.scroll_position = self.canvas.width

self.on_perform = self._on_render

def on_render(self):
raise NotImplementedError

def _on_render(self):
self.canvas.Fill(
self.background_color.red,
self.background_color.green,
self.background_color.blue
)

self.on_render()

self.canvas = self.matrix.SwapOnVSync(self.canvas)

def ready_to_rotate(self):
if self.SCROLLABLE and self.data.config.rotation_scroll_until_finished:
return self.data.scrolling_finished

return True

def update_scroll_position(self, text_length, end):
after_scroll = self.scroll_position - 1

if after_scroll + text_length < 0:
self.data.scrolling_finished = True

if after_scroll + text_length < -10:
self.scroll_position = end
return

self.scroll_position = after_scroll

@property
def background_color(self):
return self.data.config.scoreboard_colors.graphics_color("default.background")
107 changes: 107 additions & 0 deletions screens/news.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from screens.base import MLBLEDScoreboardScreen
from driver import graphics
from renderers import scrollingtext
from utils import center_text_position

import PIL, time

class NewsScreen(MLBLEDScoreboardScreen):

NAME = "news"
SCROLLABLE = True

class ScrollConfig:
def __init__(self, coords, color, bgcolor, font):
self.coords = coords
self.color = graphics.Color(color["r"], color["g"], color["b"])
self.bgcolor = graphics.Color(bgcolor["r"], bgcolor["g"], bgcolor["b"])
self.font = font


def on_render(self):
self.__render_ticker()
self.__render_clock()
self.__render_weather()

def __render_ticker(self):
remaining_length = scrollingtext.render_text(
self.canvas,
self.scroll_config.coords["x"],
self.scroll_config.coords["y"],
self.scroll_config.coords["width"],
self.scroll_config.font,
self.scroll_config.color,
self.scroll_config.bgcolor,
self.headline_text,
self.scroll_position
)

self.update_scroll_position(remaining_length, self.canvas.width)

def __render_clock(self):
time_format = "{}:%M".format(self.data.config.time_format)
if self.data.config.time_format == "%I":
time_format += "%p"

time_str = time.strftime(time_format)
coords = self.data.config.layout.coords("offday.time")
font = self.data.config.layout.font("offday.time")
color = self.data.config.scoreboard_colors.graphics_color("offday.time")
position = center_text_position(time_str, coords["x"], font["size"]["width"])

graphics.DrawText(self.canvas, font["font"], position, coords["y"], color, time_str)

def __render_weather(self):
if self.weather.available():
image_path = self.weather.icon_filename()
weather_icon = PIL.Image.open(image_path)
self.__render_weather_icon(weather_icon)
self.__render_weather_text(self.weather.conditions, "conditions")
self.__render_weather_text(self.weather.temperature_string(), "temperature")
self.__render_weather_text(self.weather.wind_speed_string(), "wind_speed")
self.__render_weather_text(self.weather.wind_dir_string(), "wind_dir")
self.__render_weather_text(self.weather.wind_string(), "wind")

def __render_weather_icon(self, weather_icon):
coords = self.data.config.layout.coords("offday.weather_icon")
color = self.data.config.scoreboard_colors.color("offday.weather_icon")
resize = coords.get("rescale_icon")

if resize:
weather_icon = weather_icon.resize(
(weather_icon.width * resize, weather_icon.height * resize), PIL.Image.NEAREST
)
for x in range(weather_icon.width):
for y in range(weather_icon.height):
pixel = weather_icon.getpixel((x, y))
if pixel[3] > 0:
self.canvas.SetPixel(coords["x"] + x, coords["y"] + y, color["r"], color["g"], color["b"])

def __render_weather_text(self, text, keyname):
coords = self.data.config.layout.coords("offday.{}".format(keyname))
font = self.data.config.layout.font("offday.{}".format(keyname))
color = self.data.config.scoreboard_colors.color("offday.{}".format(keyname))
text_x = center_text_position(text, coords["x"], font["size"]["width"])

color_tuple = (color["r"], color["g"], color["b"])
graphics.DrawText(self.canvas, font["font"], text_x, coords["y"], color_tuple, text)

@property
def weather(self):
return self.data.weather

@property
def scroll_config(self):
if not hasattr(self, "_scroll_config"):
self._scroll_config = NewsScreen.ScrollConfig(
self.data.config.layout.coords("offday.scrolling_text"),
self.data.config.scoreboard_colors.color("offday.scrolling_text"),
self.data.config.scoreboard_colors.color("default.background"),
self.data.config.layout.font("offday.scrolling_text")
)

return self._scroll_config

@property
def headline_text(self):
return self.data.headlines.ticker_string()
Loading
Loading