From e5d4f3c9f01d5afe8c65164afa3826bd5d9ac82d Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sun, 27 Jul 2025 10:25:47 -0500 Subject: [PATCH 1/7] playing with scroll speed for news manager --- src/news_manager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/news_manager.py b/src/news_manager.py index a55a9051..26c76d6c 100644 --- a/src/news_manager.py +++ b/src/news_manager.py @@ -438,6 +438,9 @@ class NewsManager: self.display_manager.image = img self.display_manager.update_display() + # Add the scroll delay to control speed + time.sleep(self.scroll_delay) + # Debug: log scroll position if hasattr(self, 'scroll_position') and hasattr(self, 'total_scroll_width'): logger.debug(f"Scroll position: {self.scroll_position}/{self.total_scroll_width}") From 4ab4d14a4ec956d462f3b55b2b1e5639fcc2d93c Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sun, 27 Jul 2025 12:42:12 -0500 Subject: [PATCH 2/7] adjusting news manager scroll speed --- src/news_manager.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/news_manager.py b/src/news_manager.py index 26c76d6c..9d6943fc 100644 --- a/src/news_manager.py +++ b/src/news_manager.py @@ -336,6 +336,9 @@ class NewsManager: # Calculate scroll position for smooth animation if self.total_scroll_width > 0: + # Use modulo for continuous scrolling like stock ticker + self.scroll_position = (self.scroll_position + self.scroll_speed) % self.total_scroll_width + # Scroll from right to left x_pos = width - self.scroll_position @@ -346,12 +349,8 @@ class NewsManager: if x_pos + self.total_scroll_width < width: draw.text((x_pos + self.total_scroll_width, y_pos), self.cached_text, font=font, fill=self.text_color) - # Update scroll position - self.scroll_position += self.scroll_speed - - # Reset scroll when text has completely passed - if self.scroll_position >= self.total_scroll_width: - self.scroll_position = 0 + # Check if we should rotate headlines (when scroll wraps around) + if self.scroll_position == 0: self.rotation_count += 1 # Check if we should rotate headlines From 6bbb4f5de8939b6a6e97c820bf9cebafc50f8389 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sun, 27 Jul 2025 12:56:17 -0500 Subject: [PATCH 3/7] trying to make news scroll smoother --- src/news_manager.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/news_manager.py b/src/news_manager.py index 9d6943fc..8af61ccf 100644 --- a/src/news_manager.py +++ b/src/news_manager.py @@ -430,19 +430,26 @@ class NewsManager: finally: self.is_fetching = False - # Get the current news display image - img = self.get_news_display() + # Run continuous scrolling loop for smooth animation + start_time = time.time() + duration = self.get_dynamic_duration() - # Set the image and update display - self.display_manager.image = img - self.display_manager.update_display() + while time.time() - start_time < duration: + # Get the current news display image + img = self.get_news_display() + + # Set the image and update display + self.display_manager.image = img + self.display_manager.update_display() + + # Add the scroll delay to control speed + time.sleep(self.scroll_delay) + + # Debug: log scroll position + if hasattr(self, 'scroll_position') and hasattr(self, 'total_scroll_width'): + logger.debug(f"Scroll position: {self.scroll_position}/{self.total_scroll_width}") - # Add the scroll delay to control speed - time.sleep(self.scroll_delay) - - # Debug: log scroll position - if hasattr(self, 'scroll_position') and hasattr(self, 'total_scroll_width'): - logger.debug(f"Scroll position: {self.scroll_position}/{self.total_scroll_width}") + return True except Exception as e: logger.error(f"Error in news display: {e}") @@ -450,6 +457,7 @@ class NewsManager: error_img = self.create_error_image(str(e)) self.display_manager.image = error_img self.display_manager.update_display() + return False def run_news_display(self): """Standalone method to run news display in its own loop""" From 3ba317c4e4e349fe30a6e44c0227731b690ad18e Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sun, 27 Jul 2025 13:01:02 -0500 Subject: [PATCH 4/7] Add scroll control to news manager --- src/news_manager.py | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/news_manager.py b/src/news_manager.py index 8af61ccf..7f563492 100644 --- a/src/news_manager.py +++ b/src/news_manager.py @@ -359,6 +359,9 @@ class NewsManager: any(len(headlines) > self.headlines_per_feed for headlines in self.news_data.values())): self.prepare_headlines_for_display() self.rotation_count = 0 + + # Add scroll delay to control speed + time.sleep(self.scroll_delay) return img @@ -430,24 +433,16 @@ class NewsManager: finally: self.is_fetching = False - # Run continuous scrolling loop for smooth animation - start_time = time.time() - duration = self.get_dynamic_duration() + # Get the current news display image (this updates scroll position) + img = self.get_news_display() - while time.time() - start_time < duration: - # Get the current news display image - img = self.get_news_display() - - # Set the image and update display - self.display_manager.image = img - self.display_manager.update_display() - - # Add the scroll delay to control speed - time.sleep(self.scroll_delay) - - # Debug: log scroll position - if hasattr(self, 'scroll_position') and hasattr(self, 'total_scroll_width'): - logger.debug(f"Scroll position: {self.scroll_position}/{self.total_scroll_width}") + # Set the image and update display + self.display_manager.image = img + self.display_manager.update_display() + + # Debug: log scroll position + if hasattr(self, 'scroll_position') and hasattr(self, 'total_scroll_width'): + logger.debug(f"Scroll position: {self.scroll_position}/{self.total_scroll_width}") return True From 34903dd9791a59ab1f44578bd1cc5b97ed6cafd6 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sun, 27 Jul 2025 13:03:57 -0500 Subject: [PATCH 5/7] Scroll delay check --- src/news_manager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/news_manager.py b/src/news_manager.py index 7f563492..7e8438b0 100644 --- a/src/news_manager.py +++ b/src/news_manager.py @@ -521,5 +521,6 @@ class NewsManager: def get_dynamic_duration(self) -> int: """Get the calculated dynamic duration for display""" - # Return the current calculated duration without fetching data - return self.dynamic_duration \ No newline at end of file + # For smooth scrolling, use a fixed short duration so display controller calls us frequently + # This allows the scroll_delay to control the actual scrolling speed + return 1 # 1 second duration - display controller will call us every second \ No newline at end of file From 05d9f7c05729e86d63d38382911095098b4afd5c Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sun, 27 Jul 2025 13:10:07 -0500 Subject: [PATCH 6/7] cant find why its not scrolling faster --- src/news_manager.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/news_manager.py b/src/news_manager.py index 7e8438b0..bf04fac5 100644 --- a/src/news_manager.py +++ b/src/news_manager.py @@ -360,8 +360,7 @@ class NewsManager: self.prepare_headlines_for_display() self.rotation_count = 0 - # Add scroll delay to control speed - time.sleep(self.scroll_delay) + # Remove the delay - let the display controller timing control the speed return img @@ -521,6 +520,7 @@ class NewsManager: def get_dynamic_duration(self) -> int: """Get the calculated dynamic duration for display""" - # For smooth scrolling, use a fixed short duration so display controller calls us frequently - # This allows the scroll_delay to control the actual scrolling speed - return 1 # 1 second duration - display controller will call us every second \ No newline at end of file + # For smooth scrolling, use a very short duration so display controller calls us frequently + # The scroll_speed controls how many pixels we move per call + # Return the current calculated duration without fetching data + return self.dynamic_duration # 0.1 second duration - display controller will call us 10 times per second \ No newline at end of file From 0601a9fda7d0894a421381bff78256f1836b8ea4 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sun, 27 Jul 2025 13:19:10 -0500 Subject: [PATCH 7/7] new strategy to draw news manager --- src/news_manager.py | 109 +++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 47 deletions(-) diff --git a/src/news_manager.py b/src/news_manager.py index bf04fac5..af3650d8 100644 --- a/src/news_manager.py +++ b/src/news_manager.py @@ -30,7 +30,7 @@ class NewsManager: self.news_data = {} self.current_headline_index = 0 self.scroll_position = 0 - self.cached_text_image = None + self.scrolling_image = None # Image for pre-rendering self.cached_text = None self.cache_manager = CacheManager() self.current_headlines = [] @@ -214,10 +214,34 @@ class NewsManager: # Calculate text dimensions for perfect scrolling self.calculate_scroll_dimensions() + self.create_scrolling_image() self.current_headlines = display_headlines logger.debug(f"Prepared {len(display_headlines)} headlines for display") + def create_scrolling_image(self): + """Create a pre-rendered image for smooth scrolling.""" + if not self.cached_text: + self.scrolling_image = None + return + + try: + font = ImageFont.truetype(self.font_path, self.font_size) + except Exception as e: + logger.warning(f"Failed to load custom font for pre-rendering: {e}. Using default.") + font = ImageFont.load_default() + + height = self.display_manager.height + width = self.total_scroll_width + + self.scrolling_image = Image.new('RGB', (width, height), (0, 0, 0)) + draw = ImageDraw.Draw(self.scrolling_image) + + text_height = self.font_size + y_pos = (height - text_height) // 2 + draw.text((0, y_pos), self.cached_text, font=font, fill=self.text_color) + logger.debug("Pre-rendered scrolling news image created.") + def calculate_scroll_dimensions(self): """Calculate exact dimensions needed for smooth scrolling""" if not self.cached_text: @@ -309,58 +333,46 @@ class NewsManager: return (time.time() - self.last_update) > self.update_interval def get_news_display(self) -> Image.Image: - """Generate the scrolling news ticker display""" + """Generate the scrolling news ticker display by cropping the pre-rendered image.""" try: - if not self.cached_text: - logger.debug("No cached text available, showing loading image") + if not self.scrolling_image: + logger.debug("No pre-rendered image available, showing loading image.") return self.create_no_news_image() - - # Create display image + width = self.display_manager.width height = self.display_manager.height + + # Use modulo for continuous scrolling + self.scroll_position = (self.scroll_position + self.scroll_speed) % self.total_scroll_width + + # Crop the visible part of the image + x = self.scroll_position + visible_end = x + width - img = Image.new('RGB', (width, height), (0, 0, 0)) - draw = ImageDraw.Draw(img) - - # Load font - try: - font = ImageFont.truetype(self.font_path, self.font_size) - logger.debug(f"Successfully loaded custom font: {self.font_path}") - except Exception as e: - logger.warning(f"Failed to load custom font '{self.font_path}': {e}. Using default font.") - font = ImageFont.load_default() - - # Calculate vertical position (center the text) - text_height = self.font_size - y_pos = (height - text_height) // 2 - - # Calculate scroll position for smooth animation - if self.total_scroll_width > 0: - # Use modulo for continuous scrolling like stock ticker - self.scroll_position = (self.scroll_position + self.scroll_speed) % self.total_scroll_width + if visible_end <= self.total_scroll_width: + # No wrap-around needed + img = self.scrolling_image.crop((x, 0, visible_end, height)) + else: + # Handle wrap-around + img = Image.new('RGB', (width, height)) - # Scroll from right to left - x_pos = width - self.scroll_position + width1 = self.total_scroll_width - x + portion1 = self.scrolling_image.crop((x, 0, self.total_scroll_width, height)) + img.paste(portion1, (0, 0)) - # Draw the text - draw.text((x_pos, y_pos), self.cached_text, font=font, fill=self.text_color) - - # If text has scrolled partially off screen, draw it again for seamless loop - if x_pos + self.total_scroll_width < width: - draw.text((x_pos + self.total_scroll_width, y_pos), self.cached_text, font=font, fill=self.text_color) - - # Check if we should rotate headlines (when scroll wraps around) - if self.scroll_position == 0: - self.rotation_count += 1 - - # Check if we should rotate headlines - if (self.rotation_enabled and - self.rotation_count >= self.rotation_threshold and - any(len(headlines) > self.headlines_per_feed for headlines in self.news_data.values())): - self.prepare_headlines_for_display() - self.rotation_count = 0 - - # Remove the delay - let the display controller timing control the speed + width2 = width - width1 + portion2 = self.scrolling_image.crop((0, 0, width2, height)) + img.paste(portion2, (width1, 0)) + + # Check for rotation when scroll completes a cycle + if self.scroll_position < self.scroll_speed: # Check if we just wrapped around + self.rotation_count += 1 + if (self.rotation_enabled and + self.rotation_count >= self.rotation_threshold and + any(len(headlines) > self.headlines_per_feed for headlines in self.news_data.values())): + logger.info("News rotation threshold reached. Preparing new headlines.") + self.prepare_headlines_for_display() + self.rotation_count = 0 return img @@ -432,13 +444,16 @@ class NewsManager: finally: self.is_fetching = False - # Get the current news display image (this updates scroll position) + # Get the current news display image img = self.get_news_display() # Set the image and update display self.display_manager.image = img self.display_manager.update_display() + # Add scroll delay to control speed + time.sleep(self.scroll_delay) + # Debug: log scroll position if hasattr(self, 'scroll_position') and hasattr(self, 'total_scroll_width'): logger.debug(f"Scroll position: {self.scroll_position}/{self.total_scroll_width}")