Files
UmaCardApplication/gui/main_window.py

247 lines
8.2 KiB
Python

"""
Main Window for Umamusume Support Card Manager
Tabbed interface for card browsing, effects, deck builder, and hints
"""
import tkinter as tk
import customtkinter as ctk
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from db.db_queries import get_database_stats, get_owned_count
from gui.card_view import CardListFrame
from gui.effects_view import EffectsFrame
from gui.hints_skills_view import SkillSearchFrame
from gui.deck_skills_view import DeckSkillsFrame
from gui.deck_builder import DeckBuilderFrame
from gui.update_dialog import show_update_dialog
from gui.theme import (
configure_styles, create_styled_button,
BG_DARK, BG_MEDIUM, BG_LIGHT, BG_HIGHLIGHT,
ACCENT_PRIMARY, ACCENT_SECONDARY, ACCENT_TERTIARY,
TEXT_PRIMARY, TEXT_SECONDARY, TEXT_MUTED,
FONT_TITLE, FONT_HEADER, FONT_BODY, FONT_SMALL
)
from utils import resolve_image_path
from version import VERSION
class MainWindow:
"""Main application window with tabbed interface"""
def __init__(self):
# Initialize CTk root
self.root = ctk.CTk()
self.root.title("Umamusume Support Card Manager")
self.root.geometry("1400x850")
self.root.minsize(1350, 800)
# Set icon
try:
icon_path = resolve_image_path("1_Special Week.png")
if icon_path and os.path.exists(icon_path):
# ctk uses iconbitmap for windows usually, but iconphoto works too
icon_img = tk.PhotoImage(file=icon_path)
self.root.iconphoto(True, icon_img)
except Exception as e:
print(f"Failed to set icon: {e}")
# Configure styles for legacy widgets
configure_styles(self.root)
# Create main container
# Note: CTk already has a main frame in a way, but we'll use a container for padding
main_container = ctk.CTkFrame(self.root, fg_color="transparent")
main_container.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# State
self.last_selected_levels = {} # card_id -> level
# Header with stats
self.create_header(main_container)
# Status bar
self.create_status_bar(main_container)
# Tabbed notebook -> CTkTabview
self.tabview = ctk.CTkTabview(main_container)
self.tabview.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Create tabs
self.create_tabs()
def create_header(self, parent):
"""Create header with database statistics and update button"""
# Header container
header_frame = ctk.CTkFrame(parent, fg_color="transparent")
header_frame.pack(fill=tk.X, padx=10, pady=(0, 10))
# Left side: Title and version
title_frame = ctk.CTkFrame(header_frame, fg_color="transparent")
title_frame.pack(side=tk.LEFT)
# App icon and title
title_label = ctk.CTkLabel(
title_frame,
text="🏇 Umamusume Support Card Manager",
font=FONT_TITLE,
text_color=ACCENT_PRIMARY
)
title_label.pack(side=tk.LEFT, padx=(0, 10))
# Version badge
version_label = ctk.CTkLabel(
title_frame,
text=f"v{VERSION}",
font=FONT_SMALL,
fg_color=ACCENT_SECONDARY,
text_color=TEXT_PRIMARY,
corner_radius=6,
height=24,
width=60
)
version_label.pack(side=tk.LEFT)
# Right side: Update button and stats
right_frame = ctk.CTkFrame(header_frame, fg_color="transparent")
right_frame.pack(side=tk.RIGHT)
# Update button
self.update_button = create_styled_button(
right_frame,
text="🔄 Check for Updates",
command=self.show_update_dialog,
style_type='default'
)
self.update_button.pack(side=tk.RIGHT, padx=(15, 0))
# Stats panel
self.stats_label = ctk.CTkLabel(
right_frame,
text="Loading stats...",
font=FONT_SMALL,
fg_color=BG_MEDIUM,
text_color=TEXT_SECONDARY,
corner_radius=8,
padx=15,
pady=5
)
self.stats_label.pack(side=tk.RIGHT)
# Initial stats load
self.refresh_stats()
def create_tabs(self):
"""Create all tab frames"""
# Add tabs
tab_cards = self.tabview.add(" 📋 Card List ")
tab_effects = self.tabview.add(" 📊 Search Effects ")
tab_deck = self.tabview.add(" 🎴 Deck Builder ")
tab_search = self.tabview.add(" 🔍 Skill Search ")
tab_skills = self.tabview.add(" 📜 Deck Skills ")
# Card List Tab
# Note: CardListFrame and others inherit from ttk.Frame/tk.Frame.
# We need to make sure they can be packed into a CTkFrame (the tab).
self.card_frame = CardListFrame(tab_cards,
on_card_selected_callback=self.on_card_selected,
on_stats_updated_callback=self.refresh_stats)
self.card_frame.pack(fill=tk.BOTH, expand=True)
# Effects Tab
self.effects_frame = EffectsFrame(tab_effects)
self.effects_frame.pack(fill=tk.BOTH, expand=True)
# Deck Builder Tab
self.deck_frame = DeckBuilderFrame(tab_deck)
self.deck_frame.pack(fill=tk.BOTH, expand=True)
# Skill Search Tab
self.hints_frame = SkillSearchFrame(tab_search)
self.hints_frame.pack(fill=tk.BOTH, expand=True)
# Deck Skills Tab
self.deck_skills_frame = DeckSkillsFrame(tab_skills)
self.deck_skills_frame.pack(fill=tk.BOTH, expand=True)
def create_status_bar(self, parent):
"""Create status bar at bottom"""
# Using pack side=BOTTOM relative to the main container
status_frame = ctk.CTkFrame(parent, height=30, fg_color="transparent")
status_frame.pack(fill=tk.X, side=tk.BOTTOM, pady=(10, 0))
self.status_label = ctk.CTkLabel(
status_frame,
text="✓ Ready",
font=FONT_SMALL,
text_color=TEXT_MUTED
)
self.status_label.pack(side=tk.LEFT, padx=10)
ctk.CTkLabel(
status_frame,
text="Data from gametora.com",
font=FONT_SMALL,
text_color=TEXT_MUTED
).pack(side=tk.RIGHT)
ctk.CTkLabel(
status_frame,
text="VibeCoded by Kiyreload │ ",
font=FONT_SMALL,
text_color=ACCENT_TERTIARY
).pack(side=tk.RIGHT)
def on_card_selected(self, card_id, card_name, level=None):
"""Handle card selection from card list"""
if level is not None:
self.last_selected_levels[card_id] = level
self.selected_card_id = card_id
# Update other tabs
if hasattr(self, 'effects_frame'):
self.effects_frame.set_card(card_id)
if hasattr(self, 'deck_skills_frame'):
self.deck_skills_frame.set_card(card_id)
self.status_label.configure(text=f"📌 Selected: {card_name}")
def refresh_stats(self):
"""Refresh the statistics display"""
stats = get_database_stats()
owned = get_owned_count()
stats_parts = [
f"📊 {stats.get('total_cards', 0)} Cards",
f"{owned} Owned",
f"🏆 {stats.get('by_rarity', {}).get('SSR', 0)} SSR",
f"{stats.get('by_rarity', {}).get('SR', 0)} SR",
f"{stats.get('by_rarity', {}).get('R', 0)} R"
]
stats_text = "".join(stats_parts)
if hasattr(self, 'stats_label'):
self.stats_label.configure(text=stats_text)
def show_update_dialog(self):
"""Show the update dialog"""
show_update_dialog(self.root)
def run(self):
"""
Start the GUI application and display the main window.
"""
self.root.mainloop()
def main():
"""Entry point for GUI"""
app = MainWindow()
app.run()
if __name__ == "__main__":
main()