feat: Implement initial GUI for deck, card, and skill management with CustomTkinter components.

This commit is contained in:
kiyreload27
2026-01-04 19:31:01 +00:00
parent d8e4dd909d
commit 8749e9a1d8
9 changed files with 1130 additions and 901 deletions

View File

@@ -1,12 +1,15 @@
"""
Effects Search View - Search for effects across all owned cards
Updated for CustomTkinter
"""
import tkinter as tk
from tkinter import ttk, messagebox
import customtkinter as ctk
import sys
import os
import re
from PIL import Image, ImageTk # Added missing import
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@@ -20,78 +23,93 @@ from gui.theme import (
)
from utils import resolve_image_path
class EffectsFrame(ttk.Frame):
class EffectsFrame(ctk.CTkFrame):
"""Frame for searching effects across owned cards"""
def __init__(self, parent):
super().__init__(parent)
super().__init__(parent, fg_color="transparent")
self.icon_cache = {}
self.create_widgets()
def create_widgets(self):
"""Create the effects search interface"""
# Header / Search Bar
header_frame = tk.Frame(self, bg=BG_DARK)
header_frame = ctk.CTkFrame(self, fg_color="transparent")
header_frame.pack(fill=tk.X, padx=20, pady=15)
# Search container
search_container = tk.Frame(header_frame, bg=BG_DARK)
search_container = ctk.CTkFrame(header_frame, fg_color="transparent")
search_container.pack(fill=tk.X)
tk.Label(search_container, text="🔍 Search Effect:",
font=FONT_HEADER, bg=BG_DARK, fg=TEXT_PRIMARY).pack(side=tk.LEFT, padx=(0, 10))
ctk.CTkLabel(search_container, text="🔍 Search Effect:",
font=FONT_HEADER, text_color=TEXT_PRIMARY).pack(side=tk.LEFT, padx=(0, 10))
self.search_var = tk.StringVar()
self.search_entry = create_styled_entry(search_container, textvariable=self.search_var)
self.search_entry = ctk.CTkEntry(search_container, textvariable=self.search_var, width=300)
self.search_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))
self.search_entry.bind('<Return>', lambda e: self.perform_search())
search_btn = create_styled_button(search_container, text="Search",
command=self.perform_search, style_type='primary')
command=self.perform_search, style_type='accent')
search_btn.pack(side=tk.LEFT)
# Example/Help text
help_frame = tk.Frame(header_frame, bg=BG_DARK)
help_frame = ctk.CTkFrame(header_frame, fg_color="transparent")
help_frame.pack(fill=tk.X, pady=(5, 0))
tk.Label(help_frame, text="Examples: Friendship, Motivation, Race Bonus, Skill Pt",
font=FONT_SMALL, bg=BG_DARK, fg=TEXT_MUTED).pack(side=tk.LEFT)
ctk.CTkLabel(help_frame, text="Examples: Friendship, Motivation, Race Bonus, Skill Pt",
font=FONT_SMALL, text_color=TEXT_MUTED).pack(side=tk.LEFT)
# Results Area
results_frame = ttk.LabelFrame(self, text=" Search Results (Owned Cards) ", padding=10)
results_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=(0, 20))
results_container = ctk.CTkFrame(self, fg_color="transparent")
results_container.pack(fill=tk.BOTH, expand=True, padx=20, pady=(0, 20))
# Treeview
columns = ('card', 'level', 'current_value', 'effect_name')
self.tree = ttk.Treeview(results_frame, columns=columns, show='headings', selectmode='browse')
# Label for the frame
ctk.CTkLabel(results_container, text="Search Results (Owned Cards)",
font=FONT_SUBHEADER, text_color=ACCENT_PRIMARY).pack(pady=(10, 5))
self.tree.heading('card', text='Card Name', anchor='w')
# Treeview Container
tree_frame = ctk.CTkFrame(results_container, fg_color="transparent")
tree_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10))
# Treeview - ADDING IMAGE COLUMN
# Note: Treeview column #0 is the tree column where icons live.
# We will put the image in #0 and text in #0 if possible, or name in #1
# Treeview - ADDING IMAGE COLUMN
# Use #0 for Icon only, like Card View
columns = ('card_name', 'level', 'current_value', 'effect_name')
self.tree = ttk.Treeview(tree_frame, columns=columns, show='tree headings', selectmode='browse', style="CardList.Treeview")
self.tree.heading('#0', text='Image')
self.tree.column('#0', width=100, anchor='center')
self.tree.heading('card_name', text='Card Name', anchor='w')
self.tree.heading('level', text='Level', anchor='center')
self.tree.heading('current_value', text='Value', anchor='center')
self.tree.heading('effect_name', text='Effect Name', anchor='w')
self.tree.column('card', width=250)
self.tree.column('card_name', width=200)
self.tree.column('level', width=60, anchor='center')
self.tree.column('current_value', width=80, anchor='center')
self.tree.column('effect_name', width=150)
self.tree.column('effect_name', width=200)
scrollbar = ttk.Scrollbar(results_frame, orient=tk.VERTICAL, command=self.tree.yview)
scrollbar = ttk.Scrollbar(tree_frame, orient=tk.VERTICAL, command=self.tree.yview)
# ... (rest of scrollbar setup) ...
self.tree.configure(yscrollcommand=scrollbar.set)
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# Status Label
self.status_label = tk.Label(results_frame, text="", bg=BG_MEDIUM, fg=TEXT_SECONDARY, font=FONT_SMALL)
self.status_label.pack(side=tk.BOTTOM, fill=tk.X, pady=(5, 0))
self.status_label = ctk.CTkLabel(results_container, text="", font=FONT_SMALL, text_color=TEXT_SECONDARY)
self.status_label.pack(side=tk.BOTTOM, fill=tk.X, pady=(5, 10))
def parse_value(self, value_str):
"""Parse effect value string to float for sorting"""
try:
# Extract number from string (e.g. "20%" -> 20, "+15" -> 15)
# Remove non-numeric characters except . and -
clean = re.sub(r'[^\d.-]', '', str(value_str))
return float(clean)
except:
return -999999.0 # Sort to bottom if invalid
return -999999.0
def perform_search(self):
"""Execute search and update results"""
@@ -108,7 +126,7 @@ class EffectsFrame(ttk.Frame):
results = search_owned_effects(term)
if not results:
self.status_label.config(text="No matching effects found among owned cards.")
self.status_label.configure(text="No matching effects found among owned cards.")
return
# Process and Sort
@@ -127,13 +145,36 @@ class EffectsFrame(ttk.Frame):
# Populate Tree
for item in processed_results:
r = item['data']
#Columns: card, level, current_value, effect_name
values = (r[1], f"Lv {r[5]}", r[4], r[3])
self.tree.insert('', tk.END, values=values)
self.status_label.config(text=f"Found {len(processed_results)} owned cards with matching effects.")
card_id = r[0]
image_path = r[2]
# Load Image
img = self.icon_cache.get(card_id)
if not img:
resolved_path = resolve_image_path(image_path)
if resolved_path and os.path.exists(resolved_path):
try:
pil_img = Image.open(resolved_path)
# Match CardList size
pil_img.thumbnail((78, 78), Image.Resampling.LANCZOS)
img = ImageTk.PhotoImage(pil_img)
self.icon_cache[card_id] = img
except:
pass
kv = {'image': img} if img else {}
# Insert into tree
# #0 = Image (Text '')
# Cols = Name, Level, Value, Effect
values = (r[1], f"Lv {r[5]}", r[4], r[3])
self.tree.insert('', tk.END, text='', values=values, **kv)
self.status_label.configure(text=f"Found {len(processed_results)} owned cards with matching effects.")
# Compatibility methods for main_window integration (empty as we don't need them anymore)
# Compatibility methods for main_window integration
def set_card(self, card_id):
pass