Theme System
GUI Image Studio includes a comprehensive theme system that adapts images for different visual contexts and GUI frameworks. This guide covers theme usage, customization, and integration patterns.
Overview of Theme System
The theme system provides:
Built-in themes for common use cases
Framework-specific optimization for tkinter and customtkinter
Automatic color adaptation based on theme context
Dynamic theme switching in applications
Custom theme creation for specific needs
Theme-aware image caching for performance
Built-in Themes
Default Themes
GUI Image Studio includes three primary themes:
from gui_image_studio import get_image
# Default theme (neutral, works everywhere)
default_image = get_image(
"icon.png",
framework="tkinter",
theme="default",
size=(64, 64)
)
# Light theme (optimized for light backgrounds)
light_image = get_image(
"icon.png",
framework="tkinter",
theme="light",
size=(64, 64)
)
# Dark theme (optimized for dark backgrounds)
dark_image = get_image(
"icon.png",
framework="customtkinter",
theme="dark",
size=(64, 64)
)
Theme Characteristics
Default Theme: - Neutral color balance - Works on any background - No automatic adjustments - Best for general-purpose use
Light Theme: - Optimized for light backgrounds - Slightly darker borders/shadows - Enhanced contrast for visibility - Ideal for traditional desktop applications
Dark Theme: - Optimized for dark backgrounds - Lighter edges and highlights - Reduced harsh contrasts - Perfect for modern dark UIs
Framework Integration
Tkinter Theme Integration
import tkinter as tk
from gui_image_studio import get_image
class ThemedTkinterApp:
def __init__(self, root):
self.root = root
self.root.title("Themed Tkinter App")
# Set background color
self.root.configure(bg='white') # Light theme
# Load theme-appropriate images
self.setup_images()
self.setup_ui()
def setup_images(self):
"""Load images with light theme."""
self.home_icon = get_image(
"home_icon",
framework="tkinter",
theme="light",
size=(32, 32)
)
self.save_icon = get_image(
"save_icon",
framework="tkinter",
theme="light",
size=(32, 32)
)
self.background = get_image(
"app_background",
framework="tkinter",
theme="light",
size=(800, 600)
)
def setup_ui(self):
# Background
bg_label = tk.Label(self.root, image=self.background)
bg_label.place(x=0, y=0)
# Toolbar with themed icons
toolbar = tk.Frame(self.root, bg='white')
toolbar.pack(side=tk.TOP, fill=tk.X)
home_btn = tk.Button(
toolbar,
image=self.home_icon,
text="Home",
compound=tk.LEFT,
bg='white'
)
home_btn.pack(side=tk.LEFT, padx=5, pady=5)
save_btn = tk.Button(
toolbar,
image=self.save_icon,
text="Save",
compound=tk.LEFT,
bg='white'
)
save_btn.pack(side=tk.LEFT, padx=5, pady=5)
# Usage
if __name__ == "__main__":
root = tk.Tk()
app = ThemedTkinterApp(root)
root.mainloop()
CustomTkinter Theme Integration
import customtkinter as ctk
from gui_image_studio import get_image
class ThemedCustomTkinterApp:
def __init__(self):
# Set CustomTkinter appearance
ctk.set_appearance_mode("dark") # "light", "dark", "system"
ctk.set_default_color_theme("blue")
self.root = ctk.CTk()
self.root.title("Themed CustomTkinter App")
self.root.geometry("800x600")
self.current_theme = "dark"
self.setup_images()
self.setup_ui()
def setup_images(self):
"""Load images with current theme."""
self.logo = get_image(
"app_logo",
framework="customtkinter",
theme=self.current_theme,
size=(100, 100)
)
self.toolbar_icons = {
'new': get_image(
"new_icon",
framework="customtkinter",
theme=self.current_theme,
size=(24, 24)
),
'open': get_image(
"open_icon",
framework="customtkinter",
theme=self.current_theme,
size=(24, 24)
),
'save': get_image(
"save_icon",
framework="customtkinter",
theme=self.current_theme,
size=(24, 24)
)
}
def setup_ui(self):
# Main container
main_frame = ctk.CTkFrame(self.root)
main_frame.pack(fill="both", expand=True, padx=20, pady=20)
# Header with logo
header = ctk.CTkFrame(main_frame)
header.pack(fill="x", pady=(0, 20))
logo_label = ctk.CTkLabel(
header,
image=self.logo,
text=""
)
logo_label.pack(side="left", padx=20, pady=20)
# Theme toggle button
theme_btn = ctk.CTkButton(
header,
text="Toggle Theme",
command=self.toggle_theme
)
theme_btn.pack(side="right", padx=20, pady=20)
# Toolbar
toolbar = ctk.CTkFrame(main_frame)
toolbar.pack(fill="x", pady=(0, 20))
# Toolbar buttons with icons
self.toolbar_buttons = {}
for name, icon in self.toolbar_icons.items():
btn = ctk.CTkButton(
toolbar,
image=icon,
text=name.capitalize(),
width=100,
command=lambda n=name: self.toolbar_action(n)
)
btn.pack(side="left", padx=10, pady=10)
self.toolbar_buttons[name] = btn
def toggle_theme(self):
"""Toggle between light and dark themes."""
if self.current_theme == "dark":
self.current_theme = "light"
ctk.set_appearance_mode("light")
else:
self.current_theme = "dark"
ctk.set_appearance_mode("dark")
# Reload images with new theme
self.setup_images()
self.update_ui_images()
def update_ui_images(self):
"""Update UI elements with new themed images."""
# Update logo
logo_label = self.root.winfo_children()[0].winfo_children()[0].winfo_children()[0]
logo_label.configure(image=self.logo)
# Update toolbar icons
for name, button in self.toolbar_buttons.items():
button.configure(image=self.toolbar_icons[name])
def toolbar_action(self, action):
print(f"Toolbar action: {action}")
def run(self):
self.root.mainloop()
# Usage
if __name__ == "__main__":
app = ThemedCustomTkinterApp()
app.run()
Dynamic Theme Switching
Theme Manager Class
class ThemeManager:
def __init__(self, framework="tkinter"):
self.framework = framework
self.current_theme = "default"
self.image_cache = {}
self.theme_callbacks = []
def set_theme(self, theme_name):
"""Change the current theme."""
if theme_name != self.current_theme:
old_theme = self.current_theme
self.current_theme = theme_name
# Clear cache to force reload with new theme
self.image_cache.clear()
# Notify callbacks
for callback in self.theme_callbacks:
callback(old_theme, theme_name)
def get_current_theme(self):
"""Get the current theme name."""
return self.current_theme
def load_image(self, image_name, **kwargs):
"""Load image with current theme."""
# Create cache key
cache_key = f"{image_name}_{self.current_theme}_{hash(str(sorted(kwargs.items())))}"
if cache_key not in self.image_cache:
self.image_cache[cache_key] = get_image(
image_name,
framework=self.framework,
theme=self.current_theme,
**kwargs
)
return self.image_cache[cache_key]
def register_theme_callback(self, callback):
"""Register a callback for theme changes."""
self.theme_callbacks.append(callback)
def unregister_theme_callback(self, callback):
"""Unregister a theme change callback."""
if callback in self.theme_callbacks:
self.theme_callbacks.remove(callback)
def preload_images(self, image_list, **kwargs):
"""Preload images for all themes."""
themes = ["default", "light", "dark"]
for theme in themes:
old_theme = self.current_theme
self.current_theme = theme
for image_name in image_list:
self.load_image(image_name, **kwargs)
self.current_theme = old_theme
# Usage example
class ThemeAwareApplication:
def __init__(self, root):
self.root = root
self.theme_manager = ThemeManager("customtkinter")
# Register for theme changes
self.theme_manager.register_theme_callback(self.on_theme_changed)
self.setup_ui()
def setup_ui(self):
# Load images using theme manager
self.icon = self.theme_manager.load_image("app_icon", size=(64, 64))
self.background = self.theme_manager.load_image("background", size=(800, 600))
# Create UI elements
self.icon_label = tk.Label(self.root, image=self.icon)
self.icon_label.pack(pady=20)
# Theme selection buttons
theme_frame = tk.Frame(self.root)
theme_frame.pack(pady=10)
for theme in ["default", "light", "dark"]:
btn = tk.Button(
theme_frame,
text=theme.capitalize(),
command=lambda t=theme: self.change_theme(t)
)
btn.pack(side=tk.LEFT, padx=5)
def change_theme(self, theme_name):
"""Change application theme."""
self.theme_manager.set_theme(theme_name)
def on_theme_changed(self, old_theme, new_theme):
"""Handle theme change."""
print(f"Theme changed from {old_theme} to {new_theme}")
# Reload images
self.icon = self.theme_manager.load_image("app_icon", size=(64, 64))
self.background = self.theme_manager.load_image("background", size=(800, 600))
# Update UI
self.icon_label.configure(image=self.icon)
Custom Theme Creation
Creating Custom Themes
While GUI Image Studio doesn’t support custom theme definitions directly, you can create theme-like behavior by applying consistent transformations:
class CustomThemeProcessor:
def __init__(self, framework="tkinter"):
self.framework = framework
self.custom_themes = {
'corporate': {
'tint_color': (0, 100, 200),
'tint_intensity': 0.1,
'contrast': 1.1,
'saturation': 0.9
},
'warm': {
'tint_color': (255, 200, 150),
'tint_intensity': 0.15,
'contrast': 1.05,
'saturation': 1.1
},
'cool': {
'tint_color': (150, 200, 255),
'tint_intensity': 0.12,
'contrast': 1.08,
'saturation': 0.95
},
'high_contrast': {
'contrast': 1.5,
'saturation': 1.2,
'tint_intensity': 0.0
},
'vintage': {
'tint_color': (210, 180, 140),
'tint_intensity': 0.3,
'contrast': 1.2,
'saturation': 0.8,
'grayscale': False
}
}
def load_themed_image(self, image_name, custom_theme, **kwargs):
"""Load image with custom theme applied."""
if custom_theme not in self.custom_themes:
raise ValueError(f"Unknown custom theme: {custom_theme}")
theme_params = self.custom_themes[custom_theme].copy()
# Merge with any additional parameters
theme_params.update(kwargs)
return get_image(
image_name,
framework=self.framework,
**theme_params
)
def create_theme_set(self, image_name, themes=None, **base_kwargs):
"""Create a set of images with different custom themes."""
if themes is None:
themes = list(self.custom_themes.keys())
theme_set = {}
for theme_name in themes:
theme_set[theme_name] = self.load_themed_image(
image_name,
theme_name,
**base_kwargs
)
return theme_set
# Usage
def create_custom_themed_icons():
processor = CustomThemeProcessor("customtkinter")
# Create icon set with custom themes
icon_set = processor.create_theme_set(
"main_icon",
themes=['corporate', 'warm', 'cool'],
size=(64, 64)
)
# Use different themed versions
corporate_icon = icon_set['corporate']
warm_icon = icon_set['warm']
cool_icon = icon_set['cool']
return icon_set
Theme-Aware Components
Themed Image Gallery
class ThemedImageGallery:
def __init__(self, root, image_list, theme_manager=None):
self.root = root
self.image_list = image_list
self.theme_manager = theme_manager or ThemeManager()
self.image_labels = []
self.setup_ui()
# Register for theme changes
self.theme_manager.register_theme_callback(self.on_theme_changed)
def setup_ui(self):
# Gallery frame
self.gallery_frame = tk.Frame(self.root)
self.gallery_frame.pack(fill="both", expand=True, padx=20, pady=20)
# Load and display images
self.load_gallery_images()
# Theme controls
controls = tk.Frame(self.root)
controls.pack(pady=10)
tk.Label(controls, text="Gallery Theme:").pack(side=tk.LEFT)
for theme in ["default", "light", "dark"]:
btn = tk.Button(
controls,
text=theme.capitalize(),
command=lambda t=theme: self.change_theme(t)
)
btn.pack(side=tk.LEFT, padx=5)
def load_gallery_images(self):
"""Load all gallery images with current theme."""
# Clear existing images
for label in self.image_labels:
label.destroy()
self.image_labels.clear()
# Load images in grid
row, col = 0, 0
max_cols = 4
for image_name in self.image_list:
image = self.theme_manager.load_image(
image_name,
size=(150, 150)
)
label = tk.Label(
self.gallery_frame,
image=image,
relief=tk.RAISED,
borderwidth=2
)
label.grid(row=row, column=col, padx=5, pady=5)
label.image = image # Keep reference
self.image_labels.append(label)
col += 1
if col >= max_cols:
col = 0
row += 1
def change_theme(self, theme_name):
"""Change gallery theme."""
self.theme_manager.set_theme(theme_name)
def on_theme_changed(self, old_theme, new_theme):
"""Handle theme change."""
print(f"Gallery theme changed to {new_theme}")
self.load_gallery_images()
Performance Considerations
Theme Caching Strategies
class OptimizedThemeManager:
def __init__(self, framework="tkinter", cache_size=200):
self.framework = framework
self.current_theme = "default"
self.cache = {}
self.cache_order = []
self.max_cache_size = cache_size
self.theme_callbacks = []
def load_image(self, image_name, **kwargs):
"""Load image with optimized caching."""
cache_key = self._create_cache_key(image_name, **kwargs)
# Check cache
if cache_key in self.cache:
# Move to end (LRU)
self.cache_order.remove(cache_key)
self.cache_order.append(cache_key)
return self.cache[cache_key]
# Load image
image = get_image(
image_name,
framework=self.framework,
theme=self.current_theme,
**kwargs
)
# Add to cache
self._add_to_cache(cache_key, image)
return image
def _create_cache_key(self, image_name, **kwargs):
"""Create a unique cache key."""
key_parts = [image_name, self.current_theme]
key_parts.extend([f"{k}={v}" for k, v in sorted(kwargs.items())])
return "|".join(key_parts)
def _add_to_cache(self, key, image):
"""Add image to cache with size management."""
self.cache[key] = image
self.cache_order.append(key)
# Maintain cache size
while len(self.cache) > self.max_cache_size:
oldest_key = self.cache_order.pop(0)
del self.cache[oldest_key]
def clear_theme_cache(self, theme_name=None):
"""Clear cache for specific theme or all themes."""
if theme_name is None:
self.cache.clear()
self.cache_order.clear()
else:
# Remove entries for specific theme
keys_to_remove = [
key for key in self.cache.keys()
if f"|{theme_name}|" in key
]
for key in keys_to_remove:
del self.cache[key]
if key in self.cache_order:
self.cache_order.remove(key)
def get_cache_stats(self):
"""Get cache statistics."""
return {
'size': len(self.cache),
'max_size': self.max_cache_size,
'hit_rate': getattr(self, '_hit_count', 0) / getattr(self, '_request_count', 1)
}
Preloading Strategies
def preload_themed_images(theme_manager, image_list, themes=None):
"""Preload images for multiple themes."""
if themes is None:
themes = ["default", "light", "dark"]
current_theme = theme_manager.get_current_theme()
print(f"Preloading {len(image_list)} images for {len(themes)} themes...")
for theme in themes:
theme_manager.set_theme(theme)
for image_name in image_list:
# Load common sizes
for size in [(16, 16), (24, 24), (32, 32), (64, 64)]:
theme_manager.load_image(image_name, size=size)
# Restore original theme
theme_manager.set_theme(current_theme)
print("Preloading complete")
Best Practices
Theme Design Guidelines
Consistency: Use consistent theme application across your application
Accessibility: Ensure themes provide adequate contrast
Performance: Cache themed images appropriately
User Choice: Allow users to select their preferred theme
# Good: Consistent theme usage
class ConsistentThemedApp:
def __init__(self):
self.theme_manager = ThemeManager("customtkinter")
# Load all images through theme manager
self.icons = {
'home': self.theme_manager.load_image("home", size=(32, 32)),
'save': self.theme_manager.load_image("save", size=(32, 32)),
'open': self.theme_manager.load_image("open", size=(32, 32))
}
Theme Testing
def test_theme_compatibility(image_list, themes=None):
"""Test image compatibility across themes."""
if themes is None:
themes = ["default", "light", "dark"]
theme_manager = ThemeManager("tkinter")
results = {}
for theme in themes:
theme_manager.set_theme(theme)
theme_results = []
for image_name in image_list:
try:
image = theme_manager.load_image(image_name, size=(64, 64))
theme_results.append({'image': image_name, 'status': 'success'})
except Exception as e:
theme_results.append({'image': image_name, 'status': 'error', 'error': str(e)})
results[theme] = theme_results
return results
Integration Examples
Complete Themed Application
class CompleteThemedApplication:
def __init__(self):
# Initialize CustomTkinter
ctk.set_appearance_mode("system")
self.root = ctk.CTk()
self.root.title("Complete Themed Application")
self.root.geometry("1000x700")
# Theme management
self.theme_manager = ThemeManager("customtkinter")
self.theme_manager.register_theme_callback(self.on_theme_changed)
# Detect system theme
self.detect_system_theme()
self.setup_ui()
self.preload_images()
def detect_system_theme(self):
"""Detect and set system theme."""
appearance = ctk.get_appearance_mode()
if appearance == "Dark":
self.theme_manager.set_theme("dark")
else:
self.theme_manager.set_theme("light")
def setup_ui(self):
# Main layout
self.setup_header()
self.setup_sidebar()
self.setup_main_content()
self.setup_status_bar()
def setup_header(self):
"""Setup application header."""
header = ctk.CTkFrame(self.root, height=80)
header.pack(fill="x", padx=10, pady=(10, 0))
header.pack_propagate(False)
# Logo
logo = self.theme_manager.load_image("app_logo", size=(60, 60))
logo_label = ctk.CTkLabel(header, image=logo, text="")
logo_label.pack(side="left", padx=20, pady=10)
# Title
title = ctk.CTkLabel(
header,
text="Themed Application",
font=ctk.CTkFont(size=24, weight="bold")
)
title.pack(side="left", padx=20)
# Theme toggle
theme_btn = ctk.CTkButton(
header,
text="Toggle Theme",
command=self.toggle_theme,
width=120
)
theme_btn.pack(side="right", padx=20, pady=20)
def setup_sidebar(self):
"""Setup navigation sidebar."""
self.sidebar = ctk.CTkFrame(self.root, width=200)
self.sidebar.pack(side="left", fill="y", padx=(10, 0), pady=10)
self.sidebar.pack_propagate(False)
# Navigation buttons
nav_items = [
("home", "Home"),
("documents", "Documents"),
("settings", "Settings"),
("help", "Help")
]
self.nav_buttons = {}
for icon_name, text in nav_items:
icon = self.theme_manager.load_image(icon_name, size=(24, 24))
btn = ctk.CTkButton(
self.sidebar,
image=icon,
text=text,
anchor="w",
height=40,
command=lambda t=text: self.navigate_to(t)
)
btn.pack(fill="x", padx=10, pady=5)
self.nav_buttons[icon_name] = btn
def setup_main_content(self):
"""Setup main content area."""
self.main_frame = ctk.CTkFrame(self.root)
self.main_frame.pack(side="right", fill="both", expand=True, padx=10, pady=10)
# Content will be loaded dynamically
self.load_home_content()
def setup_status_bar(self):
"""Setup status bar."""
self.status_bar = ctk.CTkFrame(self.root, height=30)
self.status_bar.pack(side="bottom", fill="x", padx=10, pady=(0, 10))
self.status_bar.pack_propagate(False)
self.status_label = ctk.CTkLabel(
self.status_bar,
text=f"Theme: {self.theme_manager.get_current_theme().capitalize()}",
font=ctk.CTkFont(size=12)
)
self.status_label.pack(side="left", padx=10, pady=5)
def load_home_content(self):
"""Load home page content."""
# Clear existing content
for widget in self.main_frame.winfo_children():
widget.destroy()
# Welcome message
welcome = ctk.CTkLabel(
self.main_frame,
text="Welcome to the Themed Application",
font=ctk.CTkFont(size=20, weight="bold")
)
welcome.pack(pady=30)
# Feature showcase
features_frame = ctk.CTkFrame(self.main_frame)
features_frame.pack(fill="both", expand=True, padx=20, pady=20)
# Load feature images
feature_images = []
for i in range(6):
img = self.theme_manager.load_image(f"feature_{i+1}", size=(100, 100))
feature_images.append(img)
# Display in grid
for i, img in enumerate(feature_images):
row, col = divmod(i, 3)
feature_label = ctk.CTkLabel(features_frame, image=img, text="")
feature_label.grid(row=row, column=col, padx=20, pady=20)
def toggle_theme(self):
"""Toggle between light and dark themes."""
current = self.theme_manager.get_current_theme()
new_theme = "light" if current == "dark" else "dark"
# Update CustomTkinter appearance
ctk.set_appearance_mode(new_theme)
# Update theme manager
self.theme_manager.set_theme(new_theme)
def on_theme_changed(self, old_theme, new_theme):
"""Handle theme change."""
print(f"Application theme changed from {old_theme} to {new_theme}")
# Update status bar
self.status_label.configure(text=f"Theme: {new_theme.capitalize()}")
# Reload current content
self.load_home_content()
# Update navigation icons
nav_items = [
("home", "Home"),
("documents", "Documents"),
("settings", "Settings"),
("help", "Help")
]
for icon_name, text in nav_items:
if icon_name in self.nav_buttons:
new_icon = self.theme_manager.load_image(icon_name, size=(24, 24))
self.nav_buttons[icon_name].configure(image=new_icon)
def navigate_to(self, page):
"""Navigate to different pages."""
print(f"Navigating to: {page}")
# Implementation would load different content based on page
def preload_images(self):
"""Preload commonly used images."""
common_images = [
"app_logo", "home", "documents", "settings", "help"
] + [f"feature_{i+1}" for i in range(6)]
# Preload for both themes
current_theme = self.theme_manager.get_current_theme()
for theme in ["light", "dark"]:
self.theme_manager.set_theme(theme)
for image_name in common_images:
try:
self.theme_manager.load_image(image_name, size=(24, 24))
self.theme_manager.load_image(image_name, size=(60, 60))
self.theme_manager.load_image(image_name, size=(100, 100))
except:
pass # Skip missing images
# Restore original theme
self.theme_manager.set_theme(current_theme)
def run(self):
self.root.mainloop()
# Usage
if __name__ == "__main__":
app = CompleteThemedApplication()
app.run()
Next Steps
Now that you understand the theme system:
Learn Custom Filters: custom_filters
Explore Performance Optimization: performance_optimization
Try Advanced Examples: Examples
Build Themed Applications: gui_development