247 lines
8.2 KiB
Python
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()
|