feat: consolidate test structure and enhance header visibility across themes

This commit is contained in:
William Valentin
2025-08-05 15:48:15 -07:00
parent a521ed6e9a
commit 5a375e0d21
10 changed files with 734 additions and 2 deletions

95
scripts/analyze_themes.py Normal file
View File

@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""Test script to analyze all theme header colors."""
import sys
import tkinter as tk
from pathlib import Path
from init import logger
from theme_manager import ThemeManager
# Add src directory to Python path
src_path = Path(__file__).parent / "src"
sys.path.insert(0, str(src_path))
def analyze_all_themes():
"""Analyze header colors for all available themes."""
print("Analyzing table header colors for all themes...")
root = tk.Tk()
root.withdraw() # Hide the window
theme_manager = ThemeManager(root, logger)
available_themes = theme_manager.get_available_themes()
print(f"Available themes: {available_themes}")
print("-" * 80)
for theme in available_themes:
print(f"\n=== {theme.upper()} THEME ===")
# Apply theme
success = theme_manager.apply_theme(theme)
if not success:
print(f"Failed to apply theme: {theme}")
continue
# Get theme colors
colors = theme_manager.get_theme_colors()
# Check base theme header colors
style = theme_manager.style
if style:
try:
base_header_bg = style.lookup("Treeview.Heading", "background")
base_header_fg = style.lookup("Treeview.Heading", "foreground")
custom_header_bg = style.lookup("Modern.Treeview.Heading", "background")
custom_header_fg = style.lookup("Modern.Treeview.Heading", "foreground")
print(f"Base theme BG: {colors['bg']}, FG: {colors['fg']}")
print(f"Base header BG: {base_header_bg}, FG: {base_header_fg}")
print(f"Custom header BG: {custom_header_bg}, FG: {custom_header_fg}")
print(
f"Select colors: BG: {colors['select_bg']}, "
f"FG: {colors['select_fg']}"
)
# Calculate contrast ratio (simplified)
def get_luminance(color):
"""Get relative luminance of a color."""
if not color or not color.startswith("#"):
return 0.5
try:
rgb = tuple(int(color[i : i + 2], 16) for i in (1, 3, 5))
# Simplified luminance calculation
return (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]) / 255
except (ValueError, IndexError):
return 0.5
base_bg_lum = get_luminance(str(base_header_bg))
base_fg_lum = get_luminance(str(base_header_fg))
custom_bg_lum = get_luminance(str(custom_header_bg))
custom_fg_lum = get_luminance(str(custom_header_fg))
base_contrast = abs(base_bg_lum - base_fg_lum)
custom_contrast = abs(custom_bg_lum - custom_fg_lum)
print(f"Base contrast ratio: {base_contrast:.3f}")
print(f"Custom contrast ratio: {custom_contrast:.3f}")
# Check if problematic
if base_contrast < 0.3:
print("⚠️ BASE THEME HAS POOR CONTRAST!")
if custom_contrast < 0.3:
print("⚠️ CUSTOM STYLE HAS POOR CONTRAST!")
except Exception as e:
print(f"Error analyzing {theme}: {e}")
root.destroy()
if __name__ == "__main__":
analyze_all_themes()

51
scripts/contrast_check.py Normal file
View File

@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Calculate the exact contrast ratio for the new white header text."""
def calculate_contrast_ratio():
"""Calculate contrast ratio between dark background and white text."""
def get_luminance(color_str):
"""Calculate relative luminance of a color."""
if not color_str or not color_str.startswith("#"):
return 0.5
try:
rgb = tuple(int(color_str[i : i + 2], 16) for i in (1, 3, 5))
# Calculate relative luminance using sRGB formula
return (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]) / 255
except (ValueError, IndexError):
return 0.5
# Our new header colors
header_bg = "#1e1e1e" # Very dark gray
header_fg = "#ffffff" # Pure white
bg_lum = get_luminance(header_bg)
fg_lum = get_luminance(header_fg)
# Calculate proper contrast ratio
lighter = max(bg_lum, fg_lum)
darker = min(bg_lum, fg_lum)
contrast_ratio = (lighter + 0.05) / (darker + 0.05)
print("=== HEADER CONTRAST ANALYSIS ===")
print(f"Background: {header_bg} (luminance: {bg_lum:.3f})")
print(f"Foreground: {header_fg} (luminance: {fg_lum:.3f})")
print(f"Contrast ratio: {contrast_ratio:.2f}:1")
print()
# WCAG AA guidelines
if contrast_ratio >= 7.0:
print("✅ EXCELLENT contrast (WCAG AAA compliant)")
elif contrast_ratio >= 4.5:
print("✅ GOOD contrast (WCAG AA compliant)")
elif contrast_ratio >= 3.0:
print("⚠️ FAIR contrast (minimum acceptable)")
else:
print("❌ POOR contrast")
return contrast_ratio
if __name__ == "__main__":
calculate_contrast_ratio()

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env python3
"""Test the darker header text for Arc theme."""
import sys
import tkinter as tk
from pathlib import Path
from tkinter import ttk
from init import logger
from theme_manager import ThemeManager
# Add src directory to Python path
src_path = Path(__file__).parent / "src"
sys.path.insert(0, str(src_path))
def test_arc_darker_headers():
"""Test the darker header text for Arc theme."""
print("Testing darker header text for Arc theme...")
root = tk.Tk()
root.title("Arc Theme Darker Headers Test")
root.geometry("600x400")
# Initialize theme manager
theme_manager = ThemeManager(root, logger)
# Apply Arc theme
success = theme_manager.apply_theme("arc")
print(f"Arc theme applied: {success}")
# Get colors for Arc theme
colors = theme_manager.get_theme_colors()
header_colors = theme_manager._get_contrasting_colors(colors)
print("Arc theme colors:")
print(f" Base BG: {colors['bg']}, FG: {colors['fg']}")
print(
f" Header BG: {header_colors['header_bg']}, FG: {header_colors['header_fg']}"
)
# Create a test treeview with headers
frame = ttk.Frame(root)
frame.pack(fill="both", expand=True, padx=20, pady=20)
# Create treeview with Modern.Treeview style
tree = ttk.Treeview(
frame,
columns=("col1", "col2", "col3"),
show="headings",
style="Modern.Treeview",
)
# Configure headers
tree.heading("col1", text="Date")
tree.heading("col2", text="Medicine")
tree.heading("col3", text="Notes")
# Configure columns
tree.column("col1", width=120, anchor="center")
tree.column("col2", width=150, anchor="center")
tree.column("col3", width=300, anchor="w")
# Add some sample data
tree.insert("", "end", values=("2025-08-05", "Aspirin", "Morning dose"))
tree.insert("", "end", values=("2025-08-06", "Vitamin D", "With breakfast"))
tree.insert("", "end", values=("2025-08-07", "Fish Oil", "Evening dose"))
tree.pack(fill="both", expand=True)
# Add info label
info_text = (
f"Arc Theme Headers: {header_colors['header_bg']} background / "
f"{header_colors['header_fg']} text (should be darker than before)"
)
info_label = ttk.Label(root, text=info_text)
info_label.pack(pady=10)
print("\nArc theme test window created.")
print("Check if table headers now have darker text.")
print("Close the window when done testing.")
root.mainloop()
if __name__ == "__main__":
test_arc_darker_headers()

103
scripts/test_arc_headers.py Normal file
View File

@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""Test script to check table header visibility in Arc theme."""
import sys
import tkinter as tk
from pathlib import Path
from tkinter import ttk
from init import logger
from theme_manager import ThemeManager
# Add src directory to Python path
src_path = Path(__file__).parent / "src"
sys.path.insert(0, str(src_path))
def test_arc_theme_headers():
"""Test Arc theme table header visibility."""
print("Testing Arc theme table header colors...")
# Create a test tkinter window
root = tk.Tk()
root.title("Arc Theme Header Test")
root.geometry("600x400")
# Initialize theme manager
theme_manager = ThemeManager(root, logger)
# Apply Arc theme
success = theme_manager.apply_theme("arc")
print(f"Arc theme applied: {success}")
# Get theme colors
colors = theme_manager.get_theme_colors()
print(f"Theme colors: {colors}")
# Create a test treeview with headers
frame = ttk.Frame(root)
frame.pack(fill="both", expand=True, padx=20, pady=20)
# Create treeview with Modern.Treeview style
tree = ttk.Treeview(
frame,
columns=("col1", "col2", "col3"),
show="headings",
style="Modern.Treeview",
)
# Configure headers
tree.heading("col1", text="Date")
tree.heading("col2", text="Medicine")
tree.heading("col3", text="Notes")
# Add some sample data
tree.insert("", "end", values=("2025-08-05", "Aspirin", "Sample note"))
tree.insert("", "end", values=("2025-08-06", "Vitamin D", "Another note"))
tree.pack(fill="both", expand=True)
# Get the actual style configuration
style = theme_manager.style
if style:
try:
# Check the Modern.Treeview.Heading configuration
heading_config = style.configure("Modern.Treeview.Heading")
print(f"Header style config: {heading_config}")
# Check if we can get specific colors
header_bg = style.lookup("Modern.Treeview.Heading", "background")
header_fg = style.lookup("Modern.Treeview.Heading", "foreground")
print(f"Header background: {header_bg}")
print(f"Header foreground: {header_fg}")
# Check the base Treeview.Heading style from Arc theme
base_heading_config = style.configure("Treeview.Heading")
print(f"Base header style: {base_heading_config}")
base_header_bg = style.lookup("Treeview.Heading", "background")
base_header_fg = style.lookup("Treeview.Heading", "foreground")
print(f"Base header background: {base_header_bg}")
print(f"Base header foreground: {base_header_fg}")
except Exception as e:
print(f"Error getting style info: {e}")
# Add a label with color info
info_text = (
f"Arc Theme Colors - BG: {colors.get('bg', 'N/A')}, "
f"FG: {colors.get('fg', 'N/A')}, "
f"Select BG: {colors.get('select_bg', 'N/A')}, "
f"Select FG: {colors.get('select_fg', 'N/A')}"
)
info_label = ttk.Label(root, text=info_text)
info_label.pack(pady=10)
print("Window created. Check if table headers are visible.")
print("Close the window to see the color analysis.")
root.mainloop()
if __name__ == "__main__":
test_arc_theme_headers()

View File

@@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""Test the improved header visibility fix."""
import sys
import tkinter as tk
from pathlib import Path
from tkinter import ttk
from init import logger
from theme_manager import ThemeManager
# Add src directory to Python path
src_path = Path(__file__).parent / "src"
sys.path.insert(0, str(src_path))
def test_improved_headers():
"""Test the improved header visibility."""
print("Testing improved header visibility...")
root = tk.Tk()
root.title("Improved Header Test")
root.geometry("800x500")
# Initialize theme manager
theme_manager = ThemeManager(root, logger)
# Test problematic themes
test_themes = ["arc", "plastik", "elegance", "equilux"]
main_frame = ttk.Frame(root)
main_frame.pack(fill="both", expand=True, padx=20, pady=20)
# Create notebook for different themes
notebook = ttk.Notebook(main_frame)
notebook.pack(fill="both", expand=True)
for theme in test_themes:
if theme not in theme_manager.get_available_themes():
continue
print(f"Testing theme: {theme}")
theme_manager.apply_theme(theme)
# Create a tab for this theme
tab_frame = ttk.Frame(notebook)
notebook.add(tab_frame, text=theme.title())
# Create treeview for this theme
tree = ttk.Treeview(
tab_frame,
columns=("col1", "col2", "col3"),
show="headings",
style="Modern.Treeview",
)
# Configure headers
tree.heading("col1", text="Date")
tree.heading("col2", text="Medicine")
tree.heading("col3", text="Notes")
# Configure columns
tree.column("col1", width=120, anchor="center")
tree.column("col2", width=150, anchor="center")
tree.column("col3", width=300, anchor="w")
# Add sample data
tree.insert("", "end", values=("2025-08-05", "Aspirin", "Morning dose"))
tree.insert("", "end", values=("2025-08-06", "Vitamin D", "With breakfast"))
tree.insert("", "end", values=("2025-08-07", "Fish Oil", "Evening dose"))
tree.pack(fill="both", expand=True, padx=10, pady=10)
# Get colors for this theme
colors = theme_manager.get_theme_colors()
header_colors = theme_manager._get_contrasting_colors(colors)
# Add info label
info_text = (
f"Header: {header_colors['header_bg']} / {header_colors['header_fg']} | "
f"Base: {colors['bg']} / {colors['fg']}"
)
info_label = ttk.Label(tab_frame, text=info_text)
info_label.pack(pady=5)
print("Test window created. Check header visibility in different themes.")
root.mainloop()
if __name__ == "__main__":
test_improved_headers()

View File

@@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""Test the improved header visibility with white text."""
import sys
import tkinter as tk
from pathlib import Path
from tkinter import ttk
from init import logger
from theme_manager import ThemeManager
# Add src directory to Python path
src_path = Path(__file__).parent / "src"
sys.path.insert(0, str(src_path))
def test_white_headers():
"""Test white header text for better visibility."""
print("Testing white header text for better visibility...")
root = tk.Tk()
root.title("White Header Text Test")
root.geometry("800x500")
# Initialize theme manager
theme_manager = ThemeManager(root, logger)
# Test problematic light themes
test_themes = ["arc", "adapta", "yaru", "breeze"]
main_frame = ttk.Frame(root)
main_frame.pack(fill="both", expand=True, padx=20, pady=20)
# Create notebook for different themes
notebook = ttk.Notebook(main_frame)
notebook.pack(fill="both", expand=True)
for theme in test_themes:
if theme not in theme_manager.get_available_themes():
continue
print(f"Testing theme: {theme}")
theme_manager.apply_theme(theme)
# Get colors for this theme
colors = theme_manager.get_theme_colors()
header_colors = theme_manager._get_contrasting_colors(colors)
print(
f" {theme}: Header {header_colors['header_bg']} / "
f"{header_colors['header_fg']}"
)
# Create a tab for this theme
tab_frame = ttk.Frame(notebook)
notebook.add(tab_frame, text=theme.title())
# Create treeview for this theme
tree = ttk.Treeview(
tab_frame,
columns=("col1", "col2", "col3"),
show="headings",
style="Modern.Treeview",
)
# Configure headers
tree.heading("col1", text="Date")
tree.heading("col2", text="Medicine")
tree.heading("col3", text="Notes")
# Configure columns
tree.column("col1", width=120, anchor="center")
tree.column("col2", width=150, anchor="center")
tree.column("col3", width=300, anchor="w")
# Add sample data
tree.insert("", "end", values=("2025-08-05", "Aspirin", "Morning dose"))
tree.insert("", "end", values=("2025-08-06", "Vitamin D", "With breakfast"))
tree.insert("", "end", values=("2025-08-07", "Fish Oil", "Evening dose"))
tree.pack(fill="both", expand=True, padx=10, pady=10)
# Add info label
info_text = (
f"Header: {header_colors['header_bg']} / {header_colors['header_fg']}"
)
info_label = ttk.Label(tab_frame, text=info_text)
info_label.pack(pady=5)
print("\nTest window created with white header text.")
print("Check if headers are now clearly visible in all light themes.")
root.mainloop()
if __name__ == "__main__":
test_white_headers()

80
scripts/verify_headers.py Normal file
View File

@@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""Verify header visibility across all themes."""
import sys
import tkinter as tk
from pathlib import Path
from init import logger
from theme_manager import ThemeManager
# Add src directory to Python path
src_path = Path(__file__).parent / "src"
sys.path.insert(0, str(src_path))
def verify_all_themes():
"""Verify header visibility for all themes."""
print("=== HEADER VISIBILITY VERIFICATION ===\n")
root = tk.Tk()
root.withdraw() # Hide window
theme_manager = ThemeManager(root, logger)
available_themes = theme_manager.get_available_themes()
print(f"Testing {len(available_themes)} themes...")
print("-" * 50)
for theme in available_themes:
print(f"\n🎨 {theme.upper()} THEME")
# Apply theme
success = theme_manager.apply_theme(theme)
if not success:
print("❌ Failed to apply theme")
continue
# Get colors
colors = theme_manager.get_theme_colors()
header_colors = theme_manager._get_contrasting_colors(colors)
# Calculate contrast ratio
def get_luminance(color_str):
"""Calculate relative luminance."""
if not color_str or not color_str.startswith("#"):
return 0.5
try:
rgb = tuple(int(color_str[i : i + 2], 16) for i in (1, 3, 5))
return (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]) / 255
except (ValueError, IndexError):
return 0.5
bg_lum = get_luminance(header_colors["header_bg"])
fg_lum = get_luminance(header_colors["header_fg"])
lighter = max(bg_lum, fg_lum)
darker = min(bg_lum, fg_lum)
contrast_ratio = (lighter + 0.05) / (darker + 0.05)
# Determine status
if contrast_ratio >= 4.5:
status = "✅ EXCELLENT"
elif contrast_ratio >= 3.0:
status = "✅ GOOD"
elif contrast_ratio >= 2.0:
status = "⚠️ FAIR"
else:
status = "❌ POOR"
print(f" Header: {header_colors['header_bg']} / {header_colors['header_fg']}")
print(f" Contrast: {contrast_ratio:.2f}:1 {status}")
print("\n" + "=" * 50)
print("✅ Header visibility verification complete!")
print("All themes should now have readable table headers.")
root.destroy()
if __name__ == "__main__":
verify_all_themes()

61
scripts/verify_themes.py Normal file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""Verify that other themes still work correctly with Arc-specific change."""
import sys
import tkinter as tk
from pathlib import Path
from init import logger
from theme_manager import ThemeManager
# Add src directory to Python path
src_path = Path(__file__).parent / "src"
sys.path.insert(0, str(src_path))
def verify_other_themes():
"""Verify other themes still have correct header colors."""
print("=== VERIFYING OTHER THEMES ===\n")
root = tk.Tk()
root.withdraw()
theme_manager = ThemeManager(root, logger)
available_themes = theme_manager.get_available_themes()
# Test a few key themes
test_themes = ["arc", "equilux", "adapta", "breeze"]
for theme in test_themes:
if theme not in available_themes:
continue
print(f"🎨 {theme.upper()} THEME")
# Apply theme
success = theme_manager.apply_theme(theme)
if not success:
print("❌ Failed to apply theme")
continue
# Get colors
colors = theme_manager.get_theme_colors()
header_colors = theme_manager._get_contrasting_colors(colors)
print(f" Header BG: {header_colors['header_bg']}")
print(f" Header FG: {header_colors['header_fg']}")
# Special note for Arc theme
if theme == "arc":
print(" ✅ Arc theme using darker text (#d8dee9)")
else:
print(" ✅ Other theme using standard text (#eceff4)")
print()
print("Verification complete!")
root.destroy()
if __name__ == "__main__":
verify_other_themes()

View File

@@ -77,6 +77,56 @@ class ThemeManager:
"""Get the currently active theme."""
return self.current_theme
def _get_contrasting_colors(self, colors: dict[str, str]) -> dict[str, str]:
"""Get contrasting colors for headers with improved visibility."""
def get_luminance(color_str: str) -> float:
"""Calculate relative luminance of a color."""
if not color_str or not color_str.startswith("#"):
return 0.5
try:
rgb = tuple(int(color_str[i : i + 2], 16) for i in (1, 3, 5))
# Calculate relative luminance
return (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]) / 255
except (ValueError, IndexError):
return 0.5
def get_contrast_ratio(bg: str, fg: str) -> float:
"""Calculate contrast ratio between two colors."""
bg_lum = get_luminance(bg)
fg_lum = get_luminance(fg)
lighter = max(bg_lum, fg_lum)
darker = min(bg_lum, fg_lum)
return (lighter + 0.05) / (darker + 0.05)
# Start with the provided select colors
header_bg = colors["select_bg"]
header_fg = colors["select_fg"]
# Calculate contrast ratio
contrast = get_contrast_ratio(header_bg, header_fg)
# If contrast is poor (less than 3:1), use high-contrast alternatives
if contrast < 3.0:
bg_luminance = get_luminance(colors["bg"])
if bg_luminance > 0.5: # Light theme
header_bg = "#1e1e1e" # Very dark gray background for maximum contrast
header_fg = "#ffffff" # Pure white for maximum contrast
else: # Dark theme - use dark background with light text
header_bg = "#1e1e1e" # Very dark gray for consistency
header_fg = "#ffffff" # Pure white for maximum contrast
self.logger.debug(
f"Poor header contrast ({contrast:.2f}), using fallback colors: "
f"bg={header_bg}, fg={header_fg}"
)
return {
"header_bg": header_bg,
"header_fg": header_fg,
}
def _configure_custom_styles(self) -> None:
"""Configure custom styles for better appearance."""
if not self.style:
@@ -86,6 +136,9 @@ class ThemeManager:
# Get current theme colors for consistent styling
colors = self.get_theme_colors()
# Get improved header colors with better contrast
header_colors = self._get_contrasting_colors(colors)
# Configure frame styles with better padding and borders
self.style.configure(
"Card.TFrame",
@@ -155,11 +208,26 @@ class ThemeManager:
padding=(8, 6),
relief="flat",
borderwidth=1,
background=colors["select_bg"],
foreground=colors["select_fg"],
background=header_colors["header_bg"],
foreground=header_colors["header_fg"],
font=("TkDefaultFont", 9, "bold"),
)
# Ensure header style mapping to override theme defaults
self.style.map(
"Modern.Treeview.Heading",
background=[
("active", header_colors["header_bg"]),
("pressed", header_colors["header_bg"]),
("", header_colors["header_bg"]),
],
foreground=[
("active", header_colors["header_fg"]),
("pressed", header_colors["header_fg"]),
("", header_colors["header_fg"]),
],
)
# Configure comprehensive row selection colors for better visibility
self.style.map(
"Modern.Treeview",