mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-11 21:33:00 +00:00
feat: Add local ticker icons support with external fallback - Add support for local ticker icons from assets/stocks/ticker_icons - Maintain existing external icon fetching as fallback - Improve icon loading with better error handling - Add text-based fallback for missing icons - Update image resizing to use newer PIL API
This commit is contained in:
@@ -38,7 +38,12 @@ class StockManager:
|
|||||||
self.last_fps_log_time = time.time()
|
self.last_fps_log_time = time.time()
|
||||||
self.frame_times = []
|
self.frame_times = []
|
||||||
|
|
||||||
# Try to use the assets/stocks directory from the repository
|
# Set up the ticker icons directory
|
||||||
|
self.ticker_icons_dir = os.path.join('assets', 'stocks', 'ticker_icons')
|
||||||
|
if not os.path.exists(self.ticker_icons_dir):
|
||||||
|
logger.warning(f"Ticker icons directory not found: {self.ticker_icons_dir}")
|
||||||
|
|
||||||
|
# Set up the logo directory for external logos
|
||||||
self.logo_dir = os.path.join('assets', 'stocks')
|
self.logo_dir = os.path.join('assets', 'stocks')
|
||||||
|
|
||||||
# Check if we can use the logo directory, otherwise use temporary
|
# Check if we can use the logo directory, otherwise use temporary
|
||||||
@@ -404,7 +409,7 @@ class StockManager:
|
|||||||
return None # Return None indicates save failure
|
return None # Return None indicates save failure
|
||||||
|
|
||||||
def _get_stock_logo(self, symbol: str) -> Image.Image:
|
def _get_stock_logo(self, symbol: str) -> Image.Image:
|
||||||
"""Get stock logo image, or create a text-based fallback.
|
"""Get stock logo image, first checking local ticker icons, then falling back to external sources.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
symbol: Stock symbol (e.g., 'AAPL', 'MSFT')
|
symbol: Stock symbol (e.g., 'AAPL', 'MSFT')
|
||||||
@@ -412,6 +417,25 @@ class StockManager:
|
|||||||
Returns:
|
Returns:
|
||||||
PIL Image of the logo or text-based fallback
|
PIL Image of the logo or text-based fallback
|
||||||
"""
|
"""
|
||||||
|
# First try to get the local ticker icon
|
||||||
|
try:
|
||||||
|
icon_path = os.path.join(self.ticker_icons_dir, f"{symbol}.png")
|
||||||
|
if os.path.exists(icon_path):
|
||||||
|
with Image.open(icon_path) as img:
|
||||||
|
# Convert to RGBA if not already
|
||||||
|
if img.mode != 'RGBA':
|
||||||
|
img = img.convert('RGBA')
|
||||||
|
# Resize to fit in the display
|
||||||
|
max_size = min(int(self.display_manager.matrix.width / 1.5),
|
||||||
|
int(self.display_manager.matrix.height / 1.5))
|
||||||
|
img = img.resize((max_size, max_size), Image.Resampling.LANCZOS)
|
||||||
|
return img.copy()
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Error loading local ticker icon for {symbol}: {e}")
|
||||||
|
|
||||||
|
# If local icon not found or failed to load, try external sources
|
||||||
|
logger.info(f"No local icon found for {symbol}, trying external sources")
|
||||||
|
|
||||||
# Try to get the path to a saved logo (or save it)
|
# Try to get the path to a saved logo (or save it)
|
||||||
logo_path = self._download_stock_logo(symbol)
|
logo_path = self._download_stock_logo(symbol)
|
||||||
|
|
||||||
@@ -431,7 +455,7 @@ class StockManager:
|
|||||||
# Resize to fit in the display
|
# Resize to fit in the display
|
||||||
max_size = min(int(self.display_manager.matrix.width / 1.5),
|
max_size = min(int(self.display_manager.matrix.width / 1.5),
|
||||||
int(self.display_manager.matrix.height / 1.5))
|
int(self.display_manager.matrix.height / 1.5))
|
||||||
logo = logo.resize((max_size, max_size), Image.LANCZOS)
|
logo = logo.resize((max_size, max_size), Image.Resampling.LANCZOS)
|
||||||
|
|
||||||
return logo
|
return logo
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -454,14 +478,29 @@ class StockManager:
|
|||||||
|
|
||||||
max_size = min(int(self.display_manager.matrix.width / 1.5),
|
max_size = min(int(self.display_manager.matrix.width / 1.5),
|
||||||
int(self.display_manager.matrix.height / 1.5))
|
int(self.display_manager.matrix.height / 1.5))
|
||||||
logo = logo.resize((max_size, max_size), Image.LANCZOS)
|
logo = logo.resize((max_size, max_size), Image.Resampling.LANCZOS)
|
||||||
return logo
|
return logo
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error processing in-memory logo data for {symbol}: {e}")
|
logger.error(f"Error processing in-memory logo data for {symbol}: {e}")
|
||||||
|
|
||||||
# Fallback if all attempts fail
|
# If all attempts fail, create a text-based fallback
|
||||||
logger.warning(f"Failed to obtain logo for {symbol} from disk or download.")
|
logger.warning(f"Failed to obtain logo for {symbol} from any source. Using text fallback.")
|
||||||
return None
|
fallback = Image.new('RGBA', (32, 32), (0, 0, 0, 0))
|
||||||
|
draw = ImageDraw.Draw(fallback)
|
||||||
|
try:
|
||||||
|
font = ImageFont.truetype("assets/fonts/OpenSans-Regular.ttf", 16)
|
||||||
|
except:
|
||||||
|
font = ImageFont.load_default()
|
||||||
|
|
||||||
|
# Draw the symbol text
|
||||||
|
text = symbol[:3] # Limit to first 3 characters
|
||||||
|
text_bbox = draw.textbbox((0, 0), text, font=font)
|
||||||
|
text_width = text_bbox[2] - text_bbox[0]
|
||||||
|
text_height = text_bbox[3] - text_bbox[1]
|
||||||
|
x = (32 - text_width) // 2
|
||||||
|
y = (32 - text_height) // 2
|
||||||
|
draw.text((x, y), text, font=font, fill=(255, 255, 255, 255))
|
||||||
|
return fallback
|
||||||
|
|
||||||
def _create_stock_display(self, symbol: str, price: float, change: float, change_percent: float) -> Image.Image:
|
def _create_stock_display(self, symbol: str, price: float, change: float, change_percent: float) -> Image.Image:
|
||||||
"""Create a display image for a stock with logo, symbol, price, and change.
|
"""Create a display image for a stock with logo, symbol, price, and change.
|
||||||
|
|||||||
Reference in New Issue
Block a user