From 8b8b087f9a8026bbbdae148d45d7aadc90874eaf Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sun, 20 Jul 2025 21:28:29 -0500 Subject: [PATCH] dynamically define time on screen to rotate through odds ticker display --- src/display_controller.py | 6 ++++ src/odds_ticker_manager.py | 56 ++++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/display_controller.py b/src/display_controller.py index 91e5a339..79a53ef1 100644 --- a/src/display_controller.py +++ b/src/display_controller.py @@ -447,6 +447,12 @@ class DisplayController: def get_current_duration(self) -> int: """Get the duration for the current display mode.""" mode_key = self.current_display_mode + + if mode_key == 'odds_ticker' and self.odds_ticker: + dynamic_duration = self.odds_ticker.get_dynamic_duration() + if dynamic_duration > 0: + return dynamic_duration + # Simplify weather key handling if mode_key.startswith('weather_'): return self.display_durations.get(mode_key, 15) diff --git a/src/odds_ticker_manager.py b/src/odds_ticker_manager.py index a4a563e7..221ca1f2 100644 --- a/src/odds_ticker_manager.py +++ b/src/odds_ticker_manager.py @@ -42,6 +42,7 @@ class OddsTickerManager: self.current_game_index = 0 self.ticker_image = None # This will hold the single, wide image self.last_display_time = 0 + self.dynamic_display_duration = 0 # Font setup self.fonts = self._load_fonts() @@ -404,7 +405,7 @@ class OddsTickerManager: vs_text = "vs." vs_width = int(temp_draw.textlength(vs_text, font=vs_font)) - # Format date and time + # Format date and time into 3 parts game_time = game['start_time'] timezone_str = self.config.get('timezone', 'UTC') try: @@ -416,7 +417,9 @@ class OddsTickerManager: game_time = game_time.replace(tzinfo=pytz.UTC) local_time = game_time.astimezone(tz) - datetime_text = local_time.strftime("%a %-m/%d %-I:%M%p").lower() + day_text = local_time.strftime("%a").lower() + date_text = local_time.strftime("%-m/%d") + time_text = local_time.strftime("%-I:%M%p").lower() # Team and record text away_team_text = f"{game.get('away_team', 'N/A')} ({game.get('away_record', '') or 'N/A'})" @@ -424,8 +427,13 @@ class OddsTickerManager: away_team_width = int(temp_draw.textlength(away_team_text, font=team_font)) home_team_width = int(temp_draw.textlength(home_team_text, font=team_font)) - datetime_width = int(temp_draw.textlength(datetime_text, font=datetime_font)) - team_info_width = max(away_team_width, home_team_width, datetime_width) + team_info_width = max(away_team_width, home_team_width) + + # Datetime column width + day_width = int(temp_draw.textlength(day_text, font=datetime_font)) + date_width = int(temp_draw.textlength(date_text, font=datetime_font)) + time_width = int(temp_draw.textlength(time_text, font=datetime_font)) + datetime_col_width = max(day_width, date_width, time_width) # Odds text odds = game.get('odds') or {} @@ -471,7 +479,7 @@ class OddsTickerManager: # --- Calculate total width --- total_width = (logo_size + logo_padding + vs_width + vs_padding + logo_size + section_padding + - team_info_width + section_padding + odds_width + section_padding) + team_info_width + section_padding + odds_width + section_padding + datetime_col_width + section_padding) # --- Create final image --- image = Image.new('RGB', (int(total_width), height), color=(0, 0, 0)) @@ -499,17 +507,9 @@ class OddsTickerManager: # Team Info (stacked) team_font_height = team_font.size if hasattr(team_font, 'size') else 8 - datetime_font_height = datetime_font.size if hasattr(datetime_font, 'size') else 6 - - total_text_height = team_font_height + datetime_font_height + team_font_height - padding_y = (height - total_text_height) // 2 # Center the whole block vertically - - away_y = padding_y - datetime_y = away_y + team_font_height - home_y = datetime_y + datetime_font_height - + away_y = 2 + home_y = height - team_font_height - 2 draw.text((current_x, away_y), away_team_text, font=team_font, fill=(255, 255, 255)) - draw.text((current_x, datetime_y), datetime_text, font=datetime_font, fill=(255, 255, 255)) draw.text((current_x, home_y), home_team_text, font=team_font, fill=(255, 255, 255)) current_x += team_info_width + section_padding @@ -523,6 +523,20 @@ class OddsTickerManager: draw.text((current_x, odds_y_away), away_odds_text, font=odds_font, fill=odds_color) draw.text((current_x, odds_y_home), home_odds_text, font=odds_font, fill=odds_color) + current_x += odds_width + section_padding + + # Datetime (stacked, 3 rows) + datetime_font_height = datetime_font.size if hasattr(datetime_font, 'size') else 6 + total_dt_height = 3 * datetime_font_height + 4 # Padding between lines + dt_padding_y = (height - total_dt_height) // 2 + + day_y = dt_padding_y + date_y = day_y + datetime_font_height + 2 + time_y = date_y + datetime_font_height + 2 + + draw.text((current_x, day_y), day_text, font=datetime_font, fill=(255, 255, 255)) + draw.text((current_x, date_y), date_text, font=datetime_font, fill=(255, 255, 255)) + draw.text((current_x, time_y), time_text, font=datetime_font, fill=(255, 255, 255)) return image @@ -548,6 +562,18 @@ class OddsTickerManager: self.ticker_image.paste(img, (current_x, 0)) current_x += img.width + gap_width + if self.ticker_image and self.scroll_speed > 0 and self.scroll_delay > 0: + # Duration for the ticker to scroll its full width + self.dynamic_display_duration = (self.ticker_image.width / self.scroll_speed) * self.scroll_delay + logger.info(f"Calculated dynamic display duration: {self.dynamic_display_duration:.2f} seconds for a width of {self.ticker_image.width}px") + else: + # Fallback to the configured duration if something is wrong + self.dynamic_display_duration = self.display_duration + + def get_dynamic_duration(self) -> float: + """Return the calculated dynamic duration for the ticker to complete one full scroll.""" + return self.dynamic_display_duration + def _draw_text_with_outline(self, draw: ImageDraw.Draw, text: str, position: tuple, font: ImageFont.FreeTypeFont, fill: tuple = (255, 255, 255), outline_color: tuple = (0, 0, 0)) -> None: """Draw text with a black outline for better readability."""