display draw settings

fighting flicker
This commit is contained in:
Chuck
2025-04-08 18:30:08 -05:00
parent f569881394
commit 902b53ec86
2 changed files with 60 additions and 95 deletions

View File

@@ -23,11 +23,11 @@ class Clock:
self.timezone = self._get_timezone()
self.last_time = None
self.last_date = None
# Colors for different elements
# Colors for different elements - using brighter colors
self.COLORS = {
'time': (255, 255, 255), # White for time
'ampm': (200, 200, 200), # Light gray for AM/PM
'date': (150, 150, 150) # Darker gray for date
'time': (255, 255, 255), # Bright white for time
'ampm': (200, 200, 255), # Light blue for AM/PM
'date': (255, 200, 200) # Light red for date
}
def _get_timezone(self) -> pytz.timezone:
@@ -108,21 +108,21 @@ class Clock:
display_width = self.display_manager.matrix.width
display_height = self.display_manager.matrix.height
# Draw time (large, centered)
# Draw time (large, centered, near top)
self.display_manager.draw_text(
time_str,
y=4, # Near top
y=2, # Slightly higher
color=self.COLORS['time'],
small_font=False
)
# Draw AM/PM (small, next to time)
time_width = self.display_manager.font.getlength(time_str)
ampm_x = (display_width + time_width) // 2 + 2 # Right of time
ampm_x = (display_width + time_width) // 2 + 1 # Closer to time
self.display_manager.draw_text(
ampm,
x=ampm_x,
y=6, # Align with time
y=4, # Align better with time
color=self.COLORS['ampm'],
small_font=True
)
@@ -130,7 +130,7 @@ class Clock:
# Draw date (small, centered below time)
self.display_manager.draw_text(
date_str,
y=display_height - 10, # Near bottom
y=display_height - 8, # Slightly higher from bottom
color=self.COLORS['date'],
small_font=True
)

View File

@@ -1,7 +1,7 @@
from rgbmatrix import RGBMatrix, RGBMatrixOptions
from PIL import Image, ImageDraw, ImageFont
import time
from typing import Dict, Any, List
from typing import Dict, Any, List, Tuple
import logging
import math
from .weather_icons import WeatherIcons
@@ -19,15 +19,11 @@ class DisplayManager:
cls._instance = super(DisplayManager, cls).__new__(cls)
return cls._instance
def __init__(self, config: Dict[str, Any]):
# Only initialize once
if not DisplayManager._initialized:
self.config = config
logger.info("Initializing DisplayManager with config: %s", config)
self._setup_matrix()
self._load_fonts()
DisplayManager._initialized = True
def __init__(self, config: Dict[str, Any] = None):
self.config = config or {}
self._setup_matrix()
self._load_fonts()
def _setup_matrix(self):
"""Initialize the RGB matrix with configuration settings."""
options = RGBMatrixOptions()
@@ -35,40 +31,40 @@ class DisplayManager:
# Hardware configuration
hardware_config = self.config.get('hardware', {})
options.rows = hardware_config.get('rows', 32)
options.cols = hardware_config.get('cols', 64) # Each panel is 64 columns
options.cols = hardware_config.get('cols', 64)
options.chain_length = hardware_config.get('chain_length', 2)
options.parallel = hardware_config.get('parallel', 1)
options.hardware_mapping = hardware_config.get('hardware_mapping', 'adafruit-hat-pwm')
logger.info("Setting hardware mapping to: %s", options.hardware_mapping)
options.brightness = hardware_config.get('brightness', 60)
options.pwm_bits = hardware_config.get('pwm_bits', 8)
# Increase brightness and optimize display quality
options.brightness = hardware_config.get('brightness', 100) # Maximum brightness
options.pwm_bits = hardware_config.get('pwm_bits', 11) # Maximum color depth
options.pwm_lsb_nanoseconds = hardware_config.get('pwm_lsb_nanoseconds', 130)
options.led_rgb_sequence = hardware_config.get('led_rgb_sequence', 'RGB')
options.pixel_mapper_config = hardware_config.get('pixel_mapper_config', '')
options.row_address_type = hardware_config.get('row_addr_type', 0)
options.multiplexing = hardware_config.get('multiplexing', 0)
options.disable_hardware_pulsing = hardware_config.get('disable_hardware_pulsing', True)
options.show_refresh_rate = hardware_config.get('show_refresh_rate', True)
options.limit_refresh_rate_hz = hardware_config.get('limit_refresh_rate_hz', 100)
options.disable_hardware_pulsing = True # Reduce flickering
options.show_refresh_rate = False
options.limit_refresh_rate_hz = hardware_config.get('limit_refresh_rate_hz', 120) # Higher refresh rate
# Runtime configuration
runtime_config = self.config.get('runtime', {})
options.gpio_slowdown = runtime_config.get('gpio_slowdown', 2)
logger.info("Setting GPIO slowdown to: %d", options.gpio_slowdown)
# Initialize the matrix
logger.info("Initializing RGB matrix with options...")
self.matrix = RGBMatrix(options=options)
logger.info("RGB matrix initialized successfully")
logger.info(f"Matrix dimensions: {self.matrix.width}x{self.matrix.height}")
options.gpio_slowdown = runtime_config.get('gpio_slowdown', 1) # Reduce slowdown
# Create double buffer
# Initialize the matrix
self.matrix = RGBMatrix(options=options)
# Create double buffer for smooth updates
self.offscreen_canvas = self.matrix.CreateFrameCanvas()
# Create image with full chain width
self.image = Image.new('RGB', (self.matrix.width, self.matrix.height))
self.draw = ImageDraw.Draw(self.image)
# Set matrix to use luminance correction for better color reproduction
self.matrix.set_luminance_correct(True)
# Initialize font
try:
self.font = ImageFont.truetype("DejaVuSans.ttf", 14)
@@ -101,16 +97,17 @@ class DisplayManager:
time.sleep(2)
def update_display(self):
"""Update the display using double buffering."""
"""Update the display using double buffering for smooth transitions."""
# Copy the current image to the offscreen canvas
self.offscreen_canvas.SetImage(self.image)
# Swap the canvases
# Swap the canvases on VSync for smooth transition
self.offscreen_canvas = self.matrix.SwapOnVSync(self.offscreen_canvas)
def clear(self):
"""Clear the display."""
self.draw.rectangle((0, 0, self.matrix.width, self.matrix.height), fill=(0, 0, 0))
self.update_display()
"""Clear the display completely."""
self.image = Image.new('RGB', (self.matrix.width, self.matrix.height))
self.draw = ImageDraw.Draw(self.image)
self.update_display() # Ensure the clear is displayed
def _load_fonts(self):
"""Load fonts for different text sizes."""
@@ -126,68 +123,36 @@ class DisplayManager:
self.font = ImageFont.load_default()
self.small_font = self.font
def draw_text(self, text: str, x: int = None, y: int = None, color: tuple = (255, 255, 255),
force_clear: bool = False, small_font: bool = False):
"""Draw text on the display with automatic centering."""
if force_clear:
self.clear()
else:
# Just create a new blank image without updating display
self.image = Image.new('RGB', (self.matrix.width, self.matrix.height))
self.draw = ImageDraw.Draw(self.image)
def draw_text(self, text: str, x: int = None, y: int = None, color: Tuple[int, int, int] = (255, 255, 255), small_font: bool = False) -> None:
"""Draw text on the display with improved visibility."""
# Ensure maximum brightness for text
if isinstance(color, tuple) and len(color) == 3:
# Increase brightness of colors while maintaining relative ratios
color = tuple(min(255, int(c * 1.2)) for c in color)
# Select font based on small_font parameter
font = self.small_font if small_font else self.font
# Split text into lines if it contains newlines
lines = text.split('\n')
# Get text dimensions for centering if x not specified
bbox = self.draw.textbbox((0, 0), text, font=font)
text_width = bbox[2] - bbox[0]
# Calculate total height of all lines
line_heights = []
line_widths = []
total_height = 0
padding = 2 # Add padding between lines
edge_padding = 2 # Minimum padding from display edges
# Center text horizontally if x not specified
if x is None:
x = (self.matrix.width - text_width) // 2
for line in lines:
bbox = self.draw.textbbox((0, 0), line, font=font)
line_width = bbox[2] - bbox[0]
line_height = bbox[3] - bbox[1]
line_heights.append(line_height)
line_widths.append(line_width)
total_height += line_height
# Add padding between lines
if len(lines) > 1:
total_height += padding * (len(lines) - 1)
# Calculate starting Y position to center all lines vertically
# Center text vertically if y not specified
if y is None:
y = max(edge_padding, (self.matrix.height - total_height) // 2)
text_height = bbox[3] - bbox[1]
y = (self.matrix.height - text_height) // 2
# Draw each line
current_y = y
for i, line in enumerate(lines):
if x is None:
# Center this line horizontally
line_x = (self.matrix.width - line_widths[i]) // 2
else:
line_x = x
# Ensure x coordinate stays within bounds
line_x = max(edge_padding, min(line_x, self.matrix.width - line_widths[i] - edge_padding))
# Ensure y coordinate stays within bounds
current_y = max(edge_padding, min(current_y, self.matrix.height - line_heights[i] - edge_padding))
# Draw the text (removed logging to reduce spam)
self.draw.text((line_x, current_y), line, font=font, fill=color)
# Calculate next line position
current_y += line_heights[i] + padding
# Draw text with slight glow effect for better visibility
# Draw shadow/glow
shadow_offset = 1
shadow_color = tuple(max(0, int(c * 0.3)) for c in color)
self.draw.text((x + shadow_offset, y + shadow_offset), text, font=font, fill=shadow_color)
# Update the display using double buffering
self.update_display()
# Draw main text
self.draw.text((x, y), text, font=font, fill=color)
def draw_scrolling_text(self, text: str, scroll_position: int, force_clear: bool = False) -> None:
"""Draw scrolling text on the display."""