From e24c46b9f4354adb9974180a46da3e5818c06683 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Thu, 10 Apr 2025 20:19:10 -0500 Subject: [PATCH] Scroll Performance Tuning news scrolling performance --- config/config.json | 4 +-- src/news_manager.py | 64 +++++++++++++++++++++++++++++++++---------- test_smooth_scroll.py | 59 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 16 deletions(-) create mode 100644 test_smooth_scroll.py diff --git a/config/config.json b/config/config.json index 8241f199..b7e189ed 100644 --- a/config/config.json +++ b/config/config.json @@ -54,8 +54,8 @@ "news": { "enabled": true, "update_interval": 300, - "scroll_speed": 10, - "scroll_delay": 0.05, + "scroll_speed": 2, + "scroll_delay": 0.03, "max_headlines_per_symbol": 1 } } \ No newline at end of file diff --git a/src/news_manager.py b/src/news_manager.py index d0f109c5..abe05e07 100644 --- a/src/news_manager.py +++ b/src/news_manager.py @@ -9,6 +9,7 @@ import os import urllib.parse import re from src.config_manager import ConfigManager +from PIL import Image, ImageDraw # Configure logging logging.basicConfig(level=logging.INFO) @@ -108,6 +109,23 @@ class NewsManager: else: logger.error("Failed to fetch news for any configured stocks") + def _create_text_image(self, text: str, color: Tuple[int, int, int] = (255, 255, 255)) -> Image.Image: + """Create an image containing the text for efficient scrolling.""" + # Get text dimensions + bbox = self.display_manager.draw.textbbox((0, 0), text, font=self.display_manager.small_font) + text_width = bbox[2] - bbox[0] + text_height = bbox[3] - bbox[1] + + # Create a new image with the text + text_image = Image.new('RGB', (text_width, self.display_manager.matrix.height), (0, 0, 0)) + text_draw = ImageDraw.Draw(text_image) + + # Draw the text centered vertically + y = (self.display_manager.matrix.height - text_height) // 2 + text_draw.text((0, y), text, font=self.display_manager.small_font, fill=color) + + return text_image + def display_news(self): """Display news headlines by scrolling them across the screen.""" if not self.news_config.get('enabled', False): @@ -140,23 +158,41 @@ class NewsManager: # Format the news text news_text = f"{current_news['symbol']}: {current_news['title']}" - # Get text dimensions - bbox = self.display_manager.draw.textbbox((0, 0), news_text, font=self.display_manager.small_font) - text_width = bbox[2] - bbox[0] + # Create a text image for efficient scrolling + text_image = self._create_text_image(news_text) + text_width = text_image.width - # Clear the display - self.display_manager.clear() + # Calculate the visible portion of the text + visible_width = min(self.display_manager.matrix.width, text_width) - # Draw the news text at the current scroll position - self.display_manager.draw_text( - news_text, - x=self.display_manager.matrix.width - self.scroll_position, - y=16, # Center vertically - color=(255, 255, 255), # White - small_font=True - ) + # If this is the first time displaying this news item, clear the screen + if self.scroll_position == 0: + self.display_manager.clear() + self.display_manager.update_display() - # Update the display + # Create a new image for the current frame + frame_image = Image.new('RGB', (self.display_manager.matrix.width, self.display_manager.matrix.height), (0, 0, 0)) + + # Calculate the source and destination regions for the visible portion + src_x = max(0, text_width - self.scroll_position - visible_width) + src_width = min(visible_width, text_width - src_x) + + # Copy the visible portion of the text to the frame + if src_width > 0: + src_region = text_image.crop((src_x, 0, src_x + src_width, self.display_manager.matrix.height)) + frame_image.paste(src_region, (0, 0)) + + # If we need to wrap around to the beginning of the text + if src_x == 0 and self.scroll_position > text_width: + remaining_width = self.display_manager.matrix.width - src_width + if remaining_width > 0: + wrap_src_width = min(remaining_width, text_width) + wrap_region = text_image.crop((0, 0, wrap_src_width, self.display_manager.matrix.height)) + frame_image.paste(wrap_region, (src_width, 0)) + + # Update the display with the new frame + self.display_manager.image = frame_image + self.display_manager.draw = ImageDraw.Draw(frame_image) self.display_manager.update_display() # Update scroll position diff --git a/test_smooth_scroll.py b/test_smooth_scroll.py new file mode 100644 index 00000000..4ee1670b --- /dev/null +++ b/test_smooth_scroll.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +import time +import sys +import os +from src.config_manager import ConfigManager +from src.display_manager import DisplayManager +from src.news_manager import NewsManager + +def main(): + """Test the smooth scrolling performance of the news ticker.""" + try: + # Load configuration + config_manager = ConfigManager() + config = config_manager.config + + # Initialize display manager + display_manager = DisplayManager(config.get('display', {})) + + # Initialize news manager + news_manager = NewsManager(config, display_manager) + + print("Smooth scrolling test started. Press Ctrl+C to exit.") + print("Displaying news headlines with optimized scrolling...") + + # Clear the display first + display_manager.clear() + display_manager.update_display() + + # Display news headlines for a longer time to test scrolling performance + start_time = time.time() + frame_count = 0 + + while time.time() - start_time < 60: # Run for 1 minute + news_manager.display_news() + frame_count += 1 + + # Print FPS every 5 seconds + elapsed = time.time() - start_time + if int(elapsed) % 5 == 0 and int(elapsed) > 0: + fps = frame_count / elapsed + print(f"FPS: {fps:.2f}") + frame_count = 0 + start_time = time.time() + + print("Test completed successfully.") + + except KeyboardInterrupt: + print("\nTest interrupted by user.") + except Exception as e: + print(f"Error: {e}") + finally: + # Clean up + if 'display_manager' in locals(): + display_manager.clear() + display_manager.update_display() + display_manager.cleanup() + +if __name__ == "__main__": + main() \ No newline at end of file