From 448860d77de96dc71b1e7f4032904d7d043fdb95 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Tue, 8 Apr 2025 18:18:06 -0500 Subject: [PATCH] Flicker reduction flicker improvements --- src/display_controller.py | 42 +++++++++++++------------- src/weather_manager.py | 62 +++++++++++++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 34 deletions(-) diff --git a/src/display_controller.py b/src/display_controller.py index 9951aa5e..255d2311 100644 --- a/src/display_controller.py +++ b/src/display_controller.py @@ -20,9 +20,10 @@ class DisplayController: self.current_display = 'clock' self.last_switch = time.time() self.scroll_position = 0 - self.scroll_speed = 2 # Pixels per update + self.scroll_speed = 1 # Reduced scroll speed self.last_scroll = time.time() - self.scroll_interval = 0.05 # 50ms between scroll updates + self.scroll_interval = 0.1 # Increased scroll interval for smoother scrolling + self.force_clear = False logger.info("DisplayController initialized with display_manager: %s", id(self.display_manager)) def run(self): @@ -46,37 +47,36 @@ class DisplayController: logger.info("Switching display to: %s", self.current_display) self.last_switch = current_time - self.display_manager.clear() # Clear display when switching modes - force_clear = True - else: - force_clear = False + self.force_clear = True # Set force clear flag instead of clearing immediately # Update scroll position for hourly forecast if needed - if self.current_display == 'hourly': - if current_time - self.last_scroll > self.scroll_interval: - self.scroll_position += self.scroll_speed - self.last_scroll = current_time - - # Reset scroll position if we've gone through all forecasts - if self.scroll_position > self.display_manager.matrix.width * 3: - self.scroll_position = 0 - force_clear = True # Clear when resetting scroll + if self.current_display == 'hourly' and current_time - self.last_scroll > self.scroll_interval: + self.scroll_position += self.scroll_speed + self.last_scroll = current_time + + # Reset scroll position if we've gone through all forecasts + if self.scroll_position > self.display_manager.matrix.width * 3: + self.scroll_position = 0 + self.force_clear = True # Display current screen if self.current_display == 'clock': - self.clock.display_time(force_clear=force_clear) + self.clock.display_time(force_clear=self.force_clear) elif self.current_display == 'weather': - self.weather.display_weather(force_clear=force_clear) + self.weather.display_weather(force_clear=self.force_clear) elif self.current_display == 'hourly': - self.weather.display_hourly_forecast(self.scroll_position, force_clear=force_clear) + self.weather.display_hourly_forecast(self.scroll_position, force_clear=self.force_clear) else: # daily - self.weather.display_daily_forecast(force_clear=force_clear) + self.weather.display_daily_forecast(force_clear=self.force_clear) + + # Reset force clear flag after use + self.force_clear = False # Sleep longer when not scrolling if self.current_display == 'hourly': - time.sleep(0.05) # 50ms for smooth scrolling + time.sleep(self.scroll_interval) # Use scroll interval for consistent timing else: - time.sleep(0.1) # 100ms for static displays + time.sleep(0.2) # Longer sleep for static displays except KeyboardInterrupt: print("\nDisplay stopped by user") diff --git a/src/weather_manager.py b/src/weather_manager.py index 0075ff71..7e3f89ab 100644 --- a/src/weather_manager.py +++ b/src/weather_manager.py @@ -50,6 +50,28 @@ class WeatherManager: 'temp_high': (255, 100, 100), 'temp_low': (100, 100, 255) } + # Initialize image buffer + self.image = None + self.draw = None + self._init_buffer() + + def _init_buffer(self): + """Initialize or reset the image buffer.""" + if self.display_manager and self.display_manager.matrix: + self.image = Image.new('RGB', (self.display_manager.matrix.width, self.display_manager.matrix.height)) + self.draw = ImageDraw.Draw(self.image) + + def _clear_buffer(self): + """Clear the image buffer without updating display.""" + if self.image and self.draw: + self.draw.rectangle((0, 0, self.image.size[0], self.image.size[1]), fill=(0, 0, 0)) + + def _update_display(self): + """Update the display with current buffer contents.""" + if self.image: + self.display_manager.image = self.image.copy() + self.display_manager.draw = ImageDraw.Draw(self.display_manager.image) + self.display_manager.update_display() def _fetch_weather(self) -> None: """Fetch current weather and forecast data from OpenWeatherMap API.""" @@ -172,8 +194,9 @@ class WeatherManager: # Only update display if forced or data changed if force_clear or not hasattr(self, 'last_temp') or temp != self.last_temp or condition != self.last_condition: - if force_clear: - self.display_manager.clear() + # Reset buffer + self._init_buffer() + self._clear_buffer() # Calculate layout display_width = self.display_manager.matrix.width @@ -181,10 +204,14 @@ class WeatherManager: # Draw main temperature in large format temp_text = f"{temp}°" + x_pos = display_width // 4 + y_pos = display_height // 2 - 4 + + # Draw to buffer self.display_manager.draw_text( temp_text, - x=display_width // 4, - y=display_height // 2 - 4, + x=x_pos, + y=y_pos, color=self.COLORS['highlight'], small_font=False ) @@ -204,6 +231,9 @@ class WeatherManager: small_font=True ) + # Update display once + self._update_display() + # Update cache self.last_temp = temp self.last_condition = condition @@ -219,9 +249,9 @@ class WeatherManager: if not force_clear and current_time - self.last_draw_time < 0.1: return - # Clear display when starting new scroll - if force_clear: - self.display_manager.clear() + # Reset buffer + self._init_buffer() + self._clear_buffer() # Calculate layout parameters display_width = self.display_manager.matrix.width @@ -238,7 +268,7 @@ class WeatherManager: ) # Draw separator line - self.display_manager.draw.line( + self.draw.line( [(0, 8), (display_width, 8)], fill=self.COLORS['separator'] ) @@ -283,12 +313,13 @@ class WeatherManager: if i < len(self.hourly_forecast) - 1: sep_x = x_pos + forecast_width - 1 if 0 <= sep_x <= display_width: - self.display_manager.draw.line( + self.draw.line( [(sep_x, 8), (sep_x, display_height)], fill=self.COLORS['separator'] ) - self.display_manager.update_display() + # Update display once + self._update_display() self.last_draw_time = current_time def display_daily_forecast(self, force_clear: bool = False) -> None: @@ -302,6 +333,10 @@ class WeatherManager: if not force_clear and current_time - self.last_draw_time < 0.1: return + # Reset buffer + self._init_buffer() + self._clear_buffer() + # Calculate layout parameters display_width = self.display_manager.matrix.width display_height = self.display_manager.matrix.height @@ -317,7 +352,7 @@ class WeatherManager: ) # Draw separator line - self.display_manager.draw.line( + self.draw.line( [(0, 8), (display_width, 8)], fill=self.COLORS['separator'] ) @@ -373,10 +408,11 @@ class WeatherManager: # Draw separator lines if i < len(self.daily_forecast) - 1: sep_x = x_base + section_width - 1 - self.display_manager.draw.line( + self.draw.line( [(sep_x, 8), (sep_x, display_height)], fill=self.COLORS['separator'] ) - self.display_manager.update_display() + # Update display once + self._update_display() self.last_draw_time = current_time \ No newline at end of file