DnD_Campaign_Manager / src /ui /tabs /character_portrait_tab.py
official.ghost.logic
Deploy D&D Campaign Manager v2
71b378e
"""
Character Portrait Tab for D'n'D Campaign Manager
"""
import gradio as gr
from typing import Optional, Tuple
from src.agents.character_agent import CharacterAgent
from src.ui.components.dropdown_manager import DropdownManager
class CharacterPortraitTab:
"""Generate Portrait tab for AI character portrait generation"""
def __init__(self, character_agent: CharacterAgent, dropdown_manager: DropdownManager):
self.character_agent = character_agent
self.dropdown_manager = dropdown_manager
def generate_portrait_ui(
self,
character_id: str,
style: str = "fantasy art",
quality: str = "standard",
provider: str = "auto"
) -> Tuple[Optional[str], str]:
"""
Generate character portrait
Returns:
Tuple of (image_path, status_message)
"""
try:
if not character_id.strip():
return None, "❌ Error: Please provide a character ID"
# Load character
character = self.character_agent.load_character(character_id)
if not character:
return None, f"❌ Character not found: {character_id}"
# Generate portrait
file_path, status = self.character_agent.generate_portrait(
character=character,
style=style,
quality=quality,
provider=provider
)
return file_path, status
except Exception as e:
import traceback
error_msg = f"❌ Error generating portrait:\n\n{str(e)}\n\n{traceback.format_exc()}"
return None, error_msg
def create(self) -> gr.Dropdown:
"""Create and return the Generate Portrait tab component"""
with gr.Tab("Generate Portrait"):
gr.Markdown("""
### 🎨 AI Character Portrait Generator
Generate stunning character portraits using DALL-E 3 or HuggingFace!
""")
with gr.Row():
with gr.Column():
portrait_refresh_btn = gr.Button("πŸ”„ Refresh Character List", variant="secondary")
portrait_character_dropdown = gr.Dropdown(
choices=[],
label="Select Character",
info="Choose a character to generate portrait for (type to search)",
allow_custom_value=False,
interactive=True
)
portrait_provider = gr.Radio(
choices=["auto", "openai", "huggingface"],
label="Image Provider",
value="auto",
info="Auto: Try OpenAI first, fallback to HuggingFace if needed"
)
portrait_style = gr.Dropdown(
choices=[
"fantasy art",
"digital painting",
"anime style",
"oil painting",
"watercolor",
"comic book art",
"concept art"
],
label="Art Style",
value="fantasy art",
info="Choose the artistic style"
)
portrait_quality = gr.Radio(
choices=["standard", "hd"],
label="Image Quality (OpenAI only)",
value="standard",
info="HD costs more tokens (OpenAI only)"
)
generate_portrait_btn = gr.Button(
"🎨 Generate Portrait",
variant="primary",
size="lg"
)
portrait_status = gr.Textbox(
label="Status",
lines=4
)
with gr.Column():
portrait_output = gr.Image(
label="Generated Portrait",
type="filepath",
height=512
)
gr.Markdown("""
**Providers:**
- **OpenAI DALL-E 3**: High quality, costs $0.04/image (standard) or $0.08/image (HD)
- **HuggingFace (Free!)**: Stable Diffusion XL, ~100 requests/day on free tier
- **Auto**: Tries OpenAI first, automatically falls back to HuggingFace if billing issues
Portraits are automatically saved to `data/portraits/` directory.
**Tips:**
- Use "auto" mode for seamless fallback
- OpenAI HD quality produces better results but costs 2x
- HuggingFace is free but may have a 30-60s warm-up time
- Different styles work better for different races/classes
""")
# Refresh portrait character dropdown
portrait_refresh_btn.click(
fn=self.dropdown_manager.refresh_character_dropdown,
inputs=[],
outputs=[portrait_character_dropdown]
)
# Generate portrait action - convert dropdown label to ID
def generate_portrait_from_dropdown(label, style, quality, provider):
char_id = self.dropdown_manager.get_character_id_from_label(label)
return self.generate_portrait_ui(char_id, style, quality, provider)
generate_portrait_btn.click(
fn=generate_portrait_from_dropdown,
inputs=[portrait_character_dropdown, portrait_style, portrait_quality, portrait_provider],
outputs=[portrait_output, portrait_status]
)
# Return the dropdown for auto-population
return portrait_character_dropdown