331 lines
11 KiB
Python
331 lines
11 KiB
Python
"""
|
|
Main Window for Umamusume Support Card Manager
|
|
Tabbed interface for card browsing, effects, deck builder, and hints
|
|
"""
|
|
|
|
import tkinter as tk
|
|
from tkinter import ttk
|
|
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):
|
|
self.root = tk.Tk()
|
|
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):
|
|
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 all styles using centralized theme
|
|
configure_styles(self.root)
|
|
|
|
# Create main container
|
|
main_container = ttk.Frame(self.root)
|
|
main_container.pack(fill=tk.BOTH, expand=True)
|
|
|
|
# State
|
|
self.last_selected_levels = {} # card_id -> level
|
|
|
|
# Header with stats
|
|
self.create_header(main_container)
|
|
|
|
# Status bar - Create BEFORE notebook to anchor it to bottom
|
|
self.create_status_bar(main_container)
|
|
|
|
# Tabbed notebook
|
|
self.notebook = ttk.Notebook(main_container)
|
|
self.notebook.pack(fill=tk.BOTH, expand=True, padx=15, pady=8)
|
|
|
|
# Create tabs
|
|
self.create_tabs()
|
|
|
|
def create_header(self, parent):
|
|
"""Create header with database statistics and update button"""
|
|
# Header container with subtle bottom border effect
|
|
header_outer = tk.Frame(parent, bg=BG_DARK)
|
|
header_outer.pack(fill=tk.X)
|
|
|
|
header_frame = tk.Frame(header_outer, bg=BG_DARK)
|
|
header_frame.pack(fill=tk.X, padx=20, pady=15)
|
|
|
|
# Left side: Title and version
|
|
title_frame = tk.Frame(header_frame, bg=BG_DARK)
|
|
title_frame.pack(side=tk.LEFT)
|
|
|
|
# App icon and title
|
|
title_label = tk.Label(
|
|
title_frame,
|
|
text="🏇 Umamusume Support Card Manager",
|
|
font=FONT_TITLE,
|
|
bg=BG_DARK,
|
|
fg=ACCENT_PRIMARY
|
|
)
|
|
title_label.pack(side=tk.LEFT)
|
|
|
|
# Version badge
|
|
version_frame = tk.Frame(title_frame, bg=ACCENT_SECONDARY, padx=8, pady=2)
|
|
version_frame.pack(side=tk.LEFT, padx=12)
|
|
version_label = tk.Label(
|
|
version_frame,
|
|
text=f"v{VERSION}",
|
|
font=FONT_SMALL,
|
|
bg=ACCENT_SECONDARY,
|
|
fg=TEXT_PRIMARY
|
|
)
|
|
version_label.pack()
|
|
|
|
# Right side: Update button and stats
|
|
right_frame = tk.Frame(header_frame, bg=BG_DARK)
|
|
right_frame.pack(side=tk.RIGHT)
|
|
|
|
# Update button with modern styling
|
|
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 with card-like appearance
|
|
stats_frame = tk.Frame(right_frame, bg=BG_MEDIUM, padx=15, pady=8)
|
|
stats_frame.pack(side=tk.RIGHT)
|
|
|
|
stats = get_database_stats()
|
|
owned = get_owned_count()
|
|
|
|
# Build stats text with better formatting
|
|
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)
|
|
|
|
self.stats_label = tk.Label(
|
|
stats_frame,
|
|
text=stats_text,
|
|
font=FONT_SMALL,
|
|
bg=BG_MEDIUM,
|
|
fg=TEXT_SECONDARY
|
|
)
|
|
self.stats_label.pack()
|
|
|
|
# Subtle separator line
|
|
separator = tk.Frame(header_outer, bg=BG_LIGHT, height=1)
|
|
separator.pack(fill=tk.X, padx=15)
|
|
|
|
def create_tabs(self):
|
|
"""Create all tab frames"""
|
|
# Card List Tab
|
|
self.card_frame = CardListFrame(self.notebook,
|
|
on_card_selected_callback=self.on_card_selected,
|
|
on_stats_updated_callback=self.refresh_stats)
|
|
self.notebook.add(self.card_frame, text=" 📋 Card List ")
|
|
|
|
# Effects Tab
|
|
self.effects_frame = EffectsFrame(self.notebook)
|
|
self.notebook.add(self.effects_frame, text=" 📊 Effects ")
|
|
|
|
# Deck Builder Tab
|
|
self.deck_frame = DeckBuilderFrame(self.notebook)
|
|
self.notebook.add(self.deck_frame, text=" 🎴 Deck Builder ")
|
|
|
|
# Skill Search Tab
|
|
self.hints_frame = SkillSearchFrame(self.notebook)
|
|
self.notebook.add(self.hints_frame, text=" 🔍 Skill Search ")
|
|
|
|
# Deck Skills Tab
|
|
self.deck_skills_frame = DeckSkillsFrame(self.notebook)
|
|
self.notebook.add(self.deck_skills_frame, text=" 📜 Deck Skills ")
|
|
|
|
def create_status_bar(self, parent):
|
|
"""Create status bar at bottom"""
|
|
status_outer = tk.Frame(parent, bg=BG_MEDIUM)
|
|
status_outer.pack(fill=tk.X, side=tk.BOTTOM)
|
|
|
|
status_frame = tk.Frame(status_outer, bg=BG_MEDIUM)
|
|
status_frame.pack(fill=tk.X, padx=15, pady=8)
|
|
|
|
self.status_label = tk.Label(
|
|
status_frame,
|
|
text="✓ Ready",
|
|
font=FONT_SMALL,
|
|
bg=BG_MEDIUM,
|
|
fg=TEXT_MUTED
|
|
)
|
|
self.status_label.pack(side=tk.LEFT)
|
|
|
|
tk.Label(
|
|
status_frame,
|
|
text="Data from gametora.com",
|
|
font=FONT_SMALL,
|
|
bg=BG_MEDIUM,
|
|
fg=TEXT_MUTED
|
|
).pack(side=tk.RIGHT)
|
|
|
|
# Diagnostics Button
|
|
diag_btn = tk.Button(
|
|
status_frame,
|
|
text="🔍 Diagnostics",
|
|
font=FONT_SMALL,
|
|
bg=BG_MEDIUM,
|
|
fg=ACCENT_TERTIARY,
|
|
bd=0,
|
|
activebackground=BG_LIGHT,
|
|
activeforeground=TEXT_PRIMARY,
|
|
cursor='hand2',
|
|
command=self.show_diagnostics
|
|
)
|
|
diag_btn.pack(side=tk.RIGHT, padx=15)
|
|
|
|
tk.Label(
|
|
status_frame,
|
|
text="Made by Kiyreload │ ",
|
|
font=FONT_SMALL,
|
|
bg=BG_MEDIUM,
|
|
fg=ACCENT_TERTIARY
|
|
).pack(side=tk.RIGHT)
|
|
|
|
def on_card_selected(self, card_id, card_name, level=None):
|
|
"""Handle card selection from card list"""
|
|
# Store level if provided
|
|
if level is not None:
|
|
self.last_selected_levels[card_id] = level
|
|
self.selected_card_id = card_id # Update selected_card_id
|
|
|
|
# Update other tabs with selected card
|
|
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.config(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)
|
|
|
|
self.stats_label.config(text=stats_text)
|
|
|
|
def show_update_dialog(self):
|
|
"""Show the update dialog"""
|
|
show_update_dialog(self.root)
|
|
|
|
def show_diagnostics(self):
|
|
"""Show diagnostics information for debugging"""
|
|
diag_win = tk.Toplevel(self.root)
|
|
diag_win.title("System Diagnostics")
|
|
diag_win.geometry("700x500")
|
|
diag_win.configure(bg=BG_DARK)
|
|
|
|
from db.db_queries import DB_PATH
|
|
import platform
|
|
|
|
# Info text
|
|
info = [
|
|
f"--- Application Info ---",
|
|
f"Version: {VERSION}",
|
|
f"Frozen (EXE): {getattr(sys, 'frozen', False)}",
|
|
f"Python: {sys.version}",
|
|
f"Platform: {platform.platform()}",
|
|
"",
|
|
f"--- Database ---",
|
|
f"DB Path: {DB_PATH}",
|
|
f"DB Exists: {os.path.exists(DB_PATH)}",
|
|
"",
|
|
f"--- Search Paths ---",
|
|
f"Executable: {sys.executable}",
|
|
f"Script Root: {os.path.dirname(os.path.abspath(__file__))}",
|
|
f"MEIPASS (Temp): {getattr(sys, '_MEIPASS', 'N/A')}",
|
|
"",
|
|
f"--- Image Check ---"
|
|
]
|
|
|
|
# Check some images
|
|
img_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'images')
|
|
info.append(f"Images Dir (Source): {img_dir}")
|
|
info.append(f"Exists: {os.path.exists(img_dir)}")
|
|
|
|
if os.path.exists(img_dir):
|
|
files = os.listdir(img_dir)
|
|
info.append(f"Files found: {len(files)}")
|
|
if len(files) > 0:
|
|
info.append(f"Sample: {files[0]}")
|
|
|
|
content = "\n".join(info)
|
|
|
|
# Display area
|
|
text_frame = tk.Frame(diag_win, bg=BG_DARK, padx=20, pady=20)
|
|
text_frame.pack(fill=tk.BOTH, expand=True)
|
|
|
|
from gui.theme import create_styled_text
|
|
text_area = create_styled_text(text_frame)
|
|
text_area.pack(fill=tk.BOTH, expand=True)
|
|
text_area.insert(tk.END, content)
|
|
text_area.config(state=tk.DISABLED)
|
|
|
|
# Close button
|
|
btn_frame = tk.Frame(diag_win, bg=BG_DARK, pady=15)
|
|
btn_frame.pack(fill=tk.X)
|
|
create_styled_button(btn_frame, text="Close", command=diag_win.destroy).pack()
|
|
|
|
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()
|