# 前言 由于之前订阅的游戏CFS中国足球模拟器出了DEMO版,下载尝试过后觉得很有意思。但是由于没有版权所以作者所作的基础资源使得玩惯了FM的笔者很难受,所以笔者就想着自己做MOD,再用内置编辑器的过程中笔者发现并不好用,这可能是作者没有把精力放在这的原因,毕竟是个人开发者,所以笔者就想着自己做一个球队编辑器,于是乎CFS俱乐部编辑器就诞生了。 # 介绍 ## 所用语言 由于笔者的水平问题编辑器是由Python来进行编写的,不要问为什么不用其他语言,因为不会 ## 内容 1. 编辑球队信息https://github.com/blankzsh/CFS-ClubEditor/releases/tag/CFS 2. 支持替换球队Logo 3. 支持编辑俱乐部职员名字与属性 4. 支持搜索俱乐部 ## 预览   ## 代码 代码块 ```python import os import sys import tkinter as tk from tkinter import ttk, messagebox, filedialog import sqlite3 import json from PIL import Image, ImageTk class TeamDatabaseViewer(tk.Tk): def __init__(self): super().__init__() self.title("CFS球队编辑器 BY.卡尔纳斯") self.geometry("800x600") self.minsize(750, 550) # self.iconbitmap("favicon.ico") # 图标路径 # 初始化数据 self.fields = [ "ID", "TeamName", "TeamWealth", "TeamFoundYear", "TeamLocation", "SupporterCount", "StadiumName", "Nickname", "BelongingLeague" ] self.field_labels = { "ID": "编号", "BelongingLeague": "联赛ID", "TeamName": "球队名称", "TeamWealth": "球队财富(万)", "TeamFoundYear": "成立年份", "TeamLocation": "所在地区", "SupporterCount": "支持者数量", "StadiumName": "主场名称", "Nickname": "球队昵称", } self.team_records = [] self.displayed_team_records = [] self.staff_records = [] self.conn = None self.cursor = None self.current_team_id = None self.current_search = "" self.db_directory = "" self.logo_image = None self.leagues = {} # 存储联赛名称的字典 self.temp_data = {} # 用于存储未保存的修改 # 创建界面 self.create_widgets() def create_widgets(self): """创建界面组件""" self.main_frame = ttk.Frame(self) self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 控制面板 control_frame = ttk.Frame(self.main_frame) control_frame.pack(fill=tk.X, pady=5) ttk.Button(control_frame, text="加载数据库", command=self.load_database).pack(side=tk.LEFT, padx=5) ttk.Button(control_frame, text="保存球队修改", command=self.save_team_changes).pack(side=tk.LEFT, padx=5) # 搜索功能 self.search_var = tk.StringVar() search_entry = ttk.Entry(control_frame, textvariable=self.search_var, width=25) search_entry.pack(side=tk.LEFT, padx=10) search_entry.bind("", lambda event: self.search()) ttk.Button(control_frame, text="搜索", command=self.search).pack(side=tk.LEFT) # 主内容区 content_frame = ttk.Frame(self.main_frame) content_frame.pack(fill=tk.BOTH, expand=True) # 球队列表(添加滚动条) list_frame = ttk.Frame(content_frame, width=200) list_frame.pack(side=tk.LEFT, fill=tk.Y, padx=5) scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL) self.listbox = tk.Listbox(list_frame, width=25, yscrollcommand=scrollbar.set) scrollbar.config(command=self.listbox.yview) self.listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.listbox.bind('<>', self.on_select) # 详细信息面板 detail_frame = ttk.Frame(content_frame) detail_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5) # Logo显示区域 self.logo_frame = ttk.Frame(detail_frame) self.logo_frame.pack(fill=tk.X, pady=5) self.logo_label = ttk.Label(self.logo_frame) self.logo_label.pack() # 球队信息字段 self.entries = {} for field in self.fields[:-1]: # 不显示 LeagueID row = ttk.Frame(detail_frame) row.pack(fill=tk.X, pady=2) ttk.Label(row, text=self.field_labels[field], width=12).pack(side=tk.LEFT) entry = ttk.Entry(row) entry.pack(fill=tk.X, expand=True) self.entries[field] = entry # 联赛名称显示 league_row = ttk.Frame(detail_frame) league_row.pack(fill=tk.X, pady=2) ttk.Label(league_row, text="所在联赛:", width=12).pack(side=tk.LEFT) self.league_label = ttk.Label(league_row, text="") self.league_label.pack(side=tk.LEFT) # 员工信息表格 staff_frame = ttk.Labelframe(detail_frame, text="员工信息") staff_frame.pack(fill=tk.X, pady=10) self.staff_tree = ttk.Treeview(staff_frame, columns=("ID", "姓名", "能力值", "知名度"), show="headings") for col in ("ID", "姓名", "能力值", "知名度"): self.staff_tree.heading(col, text=col) self.staff_tree.column(col, width=80, anchor=tk.CENTER) self.staff_tree.pack(fill=tk.X) self.staff_tree.bind("", self.edit_staff) # 状态栏 self.status_var = tk.StringVar() ttk.Label(self.main_frame, textvariable=self.status_var, relief=tk.SUNKEN).pack(fill=tk.X) def load_database(self): """加载数据库文件""" try: path = filedialog.askopenfilename(filetypes=[("SQLite 数据库", "*.db"), ("所有文件", "*.*")]) if not path: return self.db_directory = os.path.dirname(path) if self.conn: self.conn.close() self.conn = sqlite3.connect(path) self.cursor = self.conn.cursor() # 加载联赛信息 self.cursor.execute("SELECT ID, LeagueName FROM League") leagues = self.cursor.fetchall() self.leagues = {l[0]: l[1] for l in leagues} self.refresh_team_data() self.refresh_staff_data() self.status_var.set(f"已加载数据库:{path}") messagebox.showinfo("成功", "数据库加载成功!") except Exception as e: messagebox.showerror("错误", f"数据库加载失败:{str(e)}") def refresh_team_data(self): """刷新球队数据""" query = """ SELECT T.ID, T.TeamName, T.TeamWealth, T.TeamFoundYear, T.TeamLocation, T.SupporterCount, T.StadiumName, T.Nickname, T.BelongingLeague FROM Teams T """ self.cursor.execute(query) self.team_records = self.cursor.fetchall() self.apply_search_filter() self.refresh_list() def refresh_staff_data(self): """刷新员工数据""" self.cursor.execute("SELECT ID, Name, AbilityJSON, Fame, EmployedTeamID FROM Staff") self.staff_records = self.cursor.fetchall() def on_select(self, event): """处理列表选择事件""" try: idx = self.listbox.curselection()[0] record = self.displayed_team_records[idx] self.current_team_id = record[0] self.update_logo(self.current_team_id) # 如果当前球队有临时数据,则加载临时数据 if self.current_team_id in self.temp_data: data = self.temp_data[self.current_team_id] for field in self.fields[:-2]: # 不保存 LeagueID self.entries[field].delete(0, tk.END) self.entries[field].insert(0, str(data.get(field, ""))) else: # 否则加载数据库中的数据 for field in self.fields[:-1]: # 遍历所有字段(除BelongingLeague) if field == "BelongingLeague": # 跳过联赛ID字段 continue idx_in_record = self.fields.index(field) self.entries[field].delete(0, tk.END) self.entries[field].insert(0, str(record[idx_in_record])) league_name = self.leagues.get(record[-1], "未知联赛") self.league_label.config(text=league_name) self.update_staff(self.current_team_id) # 正确传递球队ID except IndexError: pass def update_logo(self, team_id): """更新球队Logo显示""" if self.logo_image: self.logo_image = None if team_id: logo_path = os.path.join(self.db_directory, f"L{team_id}.png") if os.path.exists(logo_path): try: img = Image.open(logo_path) img = img.resize((128, 128), Image.Resampling.LANCZOS) self.logo_image = ImageTk.PhotoImage(img) self.logo_label.config(image=self.logo_image) # 绑定点击事件 self.logo_label.bind("", lambda event: self.replace_logo(team_id)) return except Exception as e: print(f"加载Logo失败:{str(e)}") self.logo_label.config(image='') def replace_logo(self, team_id): """替换Logo""" if not team_id: messagebox.showwarning("警告", "请先选择一个球队") return # 弹出文件选择框 file_path = filedialog.askopenfilename( filetypes=[("图片文件", "*.png;*.jpg;*.jpeg;*.bmp;*.gif")] ) if not file_path: return # 检查文件格式并转换为PNG try: img = Image.open(file_path) logo_path = os.path.join(self.db_directory, f"L{team_id}.png") img.save(logo_path, "PNG") self.update_logo(team_id) messagebox.showinfo("成功", "Logo已替换") except Exception as e: messagebox.showerror("错误", f"替换Logo失败:{str(e)}") def save_team_changes(self): """保存球队信息修改""" if not self.conn: messagebox.showwarning("警告", "请先加载数据库") return try: if not self.current_team_id: messagebox.showwarning("警告", "请选择要修改的记录") return # 获取当前修改的数据 data = {} for field in self.fields[:-2]: # 不保存 LeagueID value = self.entries[field].get() if field in ["TeamWealth", "SupporterCount", "TeamFoundYear"]: try: data[field] = self.validate_number(value, self.field_labels[field]) except ValueError: return # 如果验证失败,直接返回 else: data[field] = value # 提示用户确认保存 confirm = messagebox.askyesno("确认保存", "您确定要保存对球队数据的修改吗?") if not confirm: return # 执行保存操作 self.cursor.execute(f""" UPDATE Teams SET {','.join([f"{field}=?" for field in self.fields[:-2]])} WHERE ID = ? """, [data[field] for field in self.fields[:-2]] + [self.current_team_id]) self.conn.commit() # 清空临时数据 self.temp_data.pop(self.current_team_id, None) # 刷新球队数据 self.refresh_team_data() # 重新选择当前球队 self.select_current_team() messagebox.showinfo("成功", "球队数据已保存") except Exception as e: messagebox.showerror("错误", f"保存失败:{str(e)}") def search(self): """执行搜索""" self.current_search = self.search_var.get() self.apply_search_filter() self.refresh_list() def apply_search_filter(self): """应用当前搜索条件""" filtered = [] for record in self.team_records: record_str = ''.join(map(str, record)) if self.current_search.lower() in record_str.lower(): filtered.append(record) self.displayed_team_records = filtered def refresh_list(self): """刷新列表显示""" self.listbox.delete(0, tk.END) for record in self.displayed_team_records: display_str = f"{record[1]} ({record[0]})" if record[-1] in self.leagues: display_str += f" - {self.leagues[record[-1]]}" self.listbox.insert(tk.END, display_str) def select_current_team(self): """重新选择当前球队""" if self.current_team_id: self.listbox.selection_clear(0, tk.END) for i, record in enumerate(self.displayed_team_records): if record[0] == self.current_team_id: self.listbox.selection_set(i) self.listbox.see(i) break def update_staff(self, team_id): """更新员工信息显示""" for item in self.staff_tree.get_children(): self.staff_tree.delete(item) # 强制转为字符串比较(根据实际数据库类型调整) staff = [s for s in self.staff_records if str(s[4]) == str(team_id)] # 显示所有员工(移除切片) for s in staff: # 原代码为 staff[:2] try: ability = json.loads(s[2]).get('rawAbility', 0) except (json.JSONDecodeError, AttributeError): ability = 0 self.staff_tree.insert('', 'end', values=( s[0], s[1], ability, s[3] )) def edit_staff(self, event): """编辑员工信息""" selected = self.staff_tree.selection() if not selected: return item = self.staff_tree.item(selected) staff_id = item['values'][0] staff_record = next((s for s in self.staff_records if str(s[0]) == staff_id), None) if not staff_record: return (s_id, s_name, s_ability_json, s_fame, s_team_id) = staff_record edit_win = tk.Toplevel(self) edit_win.title("编辑员工信息") edit_win.geometry("300x200") fields = ["姓名", "能力值", "知名度"] entries = {} for i, (name, data) in enumerate(zip(fields, [s_name, s_fame, s_fame])): row = ttk.Frame(edit_win) row.pack(fill=tk.X, padx=5, pady=5) ttk.Label(row, text=f"{name}:").pack(side=tk.LEFT, fill=tk.Y) entry = ttk.Entry(row) entry.insert(0, str(data)) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True) entries[name] = entry def save_changes(): try: new_name = entries["姓名"].get() new_ability = entries["能力值"].get() new_fame = entries["知名度"].get() ability_data = json.dumps({"rawAbility": int(new_ability)}) self.cursor.execute(""" UPDATE Staff SET Name = ?, Fame = ?, AbilityJSON = ? WHERE ID = ? """, (new_name, new_fame, ability_data, s_id)) self.conn.commit() self.refresh_staff_data() self.update_staff(s_team_id) edit_win.destroy() messagebox.showinfo("成功", "员工信息已更新") except ValueError: messagebox.showerror("错误", "请输入有效的数字") except Exception as e: messagebox.showerror("错误", f"更新失败:{str(e)}") ttk.Button(edit_win, text="保存", command=save_changes).pack(fill=tk.X, padx=5, pady=10) def validate_number(self, value, field_name): """验证数字输入""" try: return float(value) if '.' in value else int(value) except ValueError: messagebox.showerror("输入错误", f"{field_name} 必须为数字") raise def __del__(self): """关闭数据库连接""" if self.conn: self.conn.close() if __name__ == "__main__": app = TeamDatabaseViewer() app.mainloop() ``` ## 概述 这个工具仅仅只用了425行代码,并且用的全都是python基础库,所以可以放心用。至于打包则是用了pyinstaller。 下载链接https://github.com/blankzsh/CFS-ClubEditor/releases/tag/CFS Loading... # 前言 由于之前订阅的游戏CFS中国足球模拟器出了DEMO版,下载尝试过后觉得很有意思。但是由于没有版权所以作者所作的基础资源使得玩惯了FM的笔者很难受,所以笔者就想着自己做MOD,再用内置编辑器的过程中笔者发现并不好用,这可能是作者没有把精力放在这的原因,毕竟是个人开发者,所以笔者就想着自己做一个球队编辑器,于是乎CFS俱乐部编辑器就诞生了。 # 介绍 ## 所用语言 由于笔者的水平问题编辑器是由Python来进行编写的,不要问为什么不用其他语言,因为不会 ## 内容 1. 编辑球队信息https://github.com/blankzsh/CFS-ClubEditor/releases/tag/CFS 2. 支持替换球队Logo 3. 支持编辑俱乐部职员名字与属性 4. 支持搜索俱乐部 ## 预览   ## 代码 代码块 ```python import os import sys import tkinter as tk from tkinter import ttk, messagebox, filedialog import sqlite3 import json from PIL import Image, ImageTk class TeamDatabaseViewer(tk.Tk): def __init__(self): super().__init__() self.title("CFS球队编辑器 BY.卡尔纳斯") self.geometry("800x600") self.minsize(750, 550) # self.iconbitmap("favicon.ico") # 图标路径 # 初始化数据 self.fields = [ "ID", "TeamName", "TeamWealth", "TeamFoundYear", "TeamLocation", "SupporterCount", "StadiumName", "Nickname", "BelongingLeague" ] self.field_labels = { "ID": "编号", "BelongingLeague": "联赛ID", "TeamName": "球队名称", "TeamWealth": "球队财富(万)", "TeamFoundYear": "成立年份", "TeamLocation": "所在地区", "SupporterCount": "支持者数量", "StadiumName": "主场名称", "Nickname": "球队昵称", } self.team_records = [] self.displayed_team_records = [] self.staff_records = [] self.conn = None self.cursor = None self.current_team_id = None self.current_search = "" self.db_directory = "" self.logo_image = None self.leagues = {} # 存储联赛名称的字典 self.temp_data = {} # 用于存储未保存的修改 # 创建界面 self.create_widgets() def create_widgets(self): """创建界面组件""" self.main_frame = ttk.Frame(self) self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 控制面板 control_frame = ttk.Frame(self.main_frame) control_frame.pack(fill=tk.X, pady=5) ttk.Button(control_frame, text="加载数据库", command=self.load_database).pack(side=tk.LEFT, padx=5) ttk.Button(control_frame, text="保存球队修改", command=self.save_team_changes).pack(side=tk.LEFT, padx=5) # 搜索功能 self.search_var = tk.StringVar() search_entry = ttk.Entry(control_frame, textvariable=self.search_var, width=25) search_entry.pack(side=tk.LEFT, padx=10) search_entry.bind("<Return>", lambda event: self.search()) ttk.Button(control_frame, text="搜索", command=self.search).pack(side=tk.LEFT) # 主内容区 content_frame = ttk.Frame(self.main_frame) content_frame.pack(fill=tk.BOTH, expand=True) # 球队列表(添加滚动条) list_frame = ttk.Frame(content_frame, width=200) list_frame.pack(side=tk.LEFT, fill=tk.Y, padx=5) scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL) self.listbox = tk.Listbox(list_frame, width=25, yscrollcommand=scrollbar.set) scrollbar.config(command=self.listbox.yview) self.listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.listbox.bind('<<ListboxSelect>>', self.on_select) # 详细信息面板 detail_frame = ttk.Frame(content_frame) detail_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5) # Logo显示区域 self.logo_frame = ttk.Frame(detail_frame) self.logo_frame.pack(fill=tk.X, pady=5) self.logo_label = ttk.Label(self.logo_frame) self.logo_label.pack() # 球队信息字段 self.entries = {} for field in self.fields[:-1]: # 不显示 LeagueID row = ttk.Frame(detail_frame) row.pack(fill=tk.X, pady=2) ttk.Label(row, text=self.field_labels[field], width=12).pack(side=tk.LEFT) entry = ttk.Entry(row) entry.pack(fill=tk.X, expand=True) self.entries[field] = entry # 联赛名称显示 league_row = ttk.Frame(detail_frame) league_row.pack(fill=tk.X, pady=2) ttk.Label(league_row, text="所在联赛:", width=12).pack(side=tk.LEFT) self.league_label = ttk.Label(league_row, text="") self.league_label.pack(side=tk.LEFT) # 员工信息表格 staff_frame = ttk.Labelframe(detail_frame, text="员工信息") staff_frame.pack(fill=tk.X, pady=10) self.staff_tree = ttk.Treeview(staff_frame, columns=("ID", "姓名", "能力值", "知名度"), show="headings") for col in ("ID", "姓名", "能力值", "知名度"): self.staff_tree.heading(col, text=col) self.staff_tree.column(col, width=80, anchor=tk.CENTER) self.staff_tree.pack(fill=tk.X) self.staff_tree.bind("<Double-1>", self.edit_staff) # 状态栏 self.status_var = tk.StringVar() ttk.Label(self.main_frame, textvariable=self.status_var, relief=tk.SUNKEN).pack(fill=tk.X) def load_database(self): """加载数据库文件""" try: path = filedialog.askopenfilename(filetypes=[("SQLite 数据库", "*.db"), ("所有文件", "*.*")]) if not path: return self.db_directory = os.path.dirname(path) if self.conn: self.conn.close() self.conn = sqlite3.connect(path) self.cursor = self.conn.cursor() # 加载联赛信息 self.cursor.execute("SELECT ID, LeagueName FROM League") leagues = self.cursor.fetchall() self.leagues = {l[0]: l[1] for l in leagues} self.refresh_team_data() self.refresh_staff_data() self.status_var.set(f"已加载数据库:{path}") messagebox.showinfo("成功", "数据库加载成功!") except Exception as e: messagebox.showerror("错误", f"数据库加载失败:{str(e)}") def refresh_team_data(self): """刷新球队数据""" query = """ SELECT T.ID, T.TeamName, T.TeamWealth, T.TeamFoundYear, T.TeamLocation, T.SupporterCount, T.StadiumName, T.Nickname, T.BelongingLeague FROM Teams T """ self.cursor.execute(query) self.team_records = self.cursor.fetchall() self.apply_search_filter() self.refresh_list() def refresh_staff_data(self): """刷新员工数据""" self.cursor.execute("SELECT ID, Name, AbilityJSON, Fame, EmployedTeamID FROM Staff") self.staff_records = self.cursor.fetchall() def on_select(self, event): """处理列表选择事件""" try: idx = self.listbox.curselection()[0] record = self.displayed_team_records[idx] self.current_team_id = record[0] self.update_logo(self.current_team_id) # 如果当前球队有临时数据,则加载临时数据 if self.current_team_id in self.temp_data: data = self.temp_data[self.current_team_id] for field in self.fields[:-2]: # 不保存 LeagueID self.entries[field].delete(0, tk.END) self.entries[field].insert(0, str(data.get(field, ""))) else: # 否则加载数据库中的数据 for field in self.fields[:-1]: # 遍历所有字段(除BelongingLeague) if field == "BelongingLeague": # 跳过联赛ID字段 continue idx_in_record = self.fields.index(field) self.entries[field].delete(0, tk.END) self.entries[field].insert(0, str(record[idx_in_record])) league_name = self.leagues.get(record[-1], "未知联赛") self.league_label.config(text=league_name) self.update_staff(self.current_team_id) # 正确传递球队ID except IndexError: pass def update_logo(self, team_id): """更新球队Logo显示""" if self.logo_image: self.logo_image = None if team_id: logo_path = os.path.join(self.db_directory, f"L{team_id}.png") if os.path.exists(logo_path): try: img = Image.open(logo_path) img = img.resize((128, 128), Image.Resampling.LANCZOS) self.logo_image = ImageTk.PhotoImage(img) self.logo_label.config(image=self.logo_image) # 绑定点击事件 self.logo_label.bind("<Button-1>", lambda event: self.replace_logo(team_id)) return except Exception as e: print(f"加载Logo失败:{str(e)}") self.logo_label.config(image='') def replace_logo(self, team_id): """替换Logo""" if not team_id: messagebox.showwarning("警告", "请先选择一个球队") return # 弹出文件选择框 file_path = filedialog.askopenfilename( filetypes=[("图片文件", "*.png;*.jpg;*.jpeg;*.bmp;*.gif")] ) if not file_path: return # 检查文件格式并转换为PNG try: img = Image.open(file_path) logo_path = os.path.join(self.db_directory, f"L{team_id}.png") img.save(logo_path, "PNG") self.update_logo(team_id) messagebox.showinfo("成功", "Logo已替换") except Exception as e: messagebox.showerror("错误", f"替换Logo失败:{str(e)}") def save_team_changes(self): """保存球队信息修改""" if not self.conn: messagebox.showwarning("警告", "请先加载数据库") return try: if not self.current_team_id: messagebox.showwarning("警告", "请选择要修改的记录") return # 获取当前修改的数据 data = {} for field in self.fields[:-2]: # 不保存 LeagueID value = self.entries[field].get() if field in ["TeamWealth", "SupporterCount", "TeamFoundYear"]: try: data[field] = self.validate_number(value, self.field_labels[field]) except ValueError: return # 如果验证失败,直接返回 else: data[field] = value # 提示用户确认保存 confirm = messagebox.askyesno("确认保存", "您确定要保存对球队数据的修改吗?") if not confirm: return # 执行保存操作 self.cursor.execute(f""" UPDATE Teams SET {','.join([f"{field}=?" for field in self.fields[:-2]])} WHERE ID = ? """, [data[field] for field in self.fields[:-2]] + [self.current_team_id]) self.conn.commit() # 清空临时数据 self.temp_data.pop(self.current_team_id, None) # 刷新球队数据 self.refresh_team_data() # 重新选择当前球队 self.select_current_team() messagebox.showinfo("成功", "球队数据已保存") except Exception as e: messagebox.showerror("错误", f"保存失败:{str(e)}") def search(self): """执行搜索""" self.current_search = self.search_var.get() self.apply_search_filter() self.refresh_list() def apply_search_filter(self): """应用当前搜索条件""" filtered = [] for record in self.team_records: record_str = ''.join(map(str, record)) if self.current_search.lower() in record_str.lower(): filtered.append(record) self.displayed_team_records = filtered def refresh_list(self): """刷新列表显示""" self.listbox.delete(0, tk.END) for record in self.displayed_team_records: display_str = f"{record[1]} ({record[0]})" if record[-1] in self.leagues: display_str += f" - {self.leagues[record[-1]]}" self.listbox.insert(tk.END, display_str) def select_current_team(self): """重新选择当前球队""" if self.current_team_id: self.listbox.selection_clear(0, tk.END) for i, record in enumerate(self.displayed_team_records): if record[0] == self.current_team_id: self.listbox.selection_set(i) self.listbox.see(i) break def update_staff(self, team_id): """更新员工信息显示""" for item in self.staff_tree.get_children(): self.staff_tree.delete(item) # 强制转为字符串比较(根据实际数据库类型调整) staff = [s for s in self.staff_records if str(s[4]) == str(team_id)] # 显示所有员工(移除切片) for s in staff: # 原代码为 staff[:2] try: ability = json.loads(s[2]).get('rawAbility', 0) except (json.JSONDecodeError, AttributeError): ability = 0 self.staff_tree.insert('', 'end', values=( s[0], s[1], ability, s[3] )) def edit_staff(self, event): """编辑员工信息""" selected = self.staff_tree.selection() if not selected: return item = self.staff_tree.item(selected) staff_id = item['values'][0] staff_record = next((s for s in self.staff_records if str(s[0]) == staff_id), None) if not staff_record: return (s_id, s_name, s_ability_json, s_fame, s_team_id) = staff_record edit_win = tk.Toplevel(self) edit_win.title("编辑员工信息") edit_win.geometry("300x200") fields = ["姓名", "能力值", "知名度"] entries = {} for i, (name, data) in enumerate(zip(fields, [s_name, s_fame, s_fame])): row = ttk.Frame(edit_win) row.pack(fill=tk.X, padx=5, pady=5) ttk.Label(row, text=f"{name}:").pack(side=tk.LEFT, fill=tk.Y) entry = ttk.Entry(row) entry.insert(0, str(data)) entry.pack(side=tk.RIGHT, fill=tk.X, expand=True) entries[name] = entry def save_changes(): try: new_name = entries["姓名"].get() new_ability = entries["能力值"].get() new_fame = entries["知名度"].get() ability_data = json.dumps({"rawAbility": int(new_ability)}) self.cursor.execute(""" UPDATE Staff SET Name = ?, Fame = ?, AbilityJSON = ? WHERE ID = ? """, (new_name, new_fame, ability_data, s_id)) self.conn.commit() self.refresh_staff_data() self.update_staff(s_team_id) edit_win.destroy() messagebox.showinfo("成功", "员工信息已更新") except ValueError: messagebox.showerror("错误", "请输入有效的数字") except Exception as e: messagebox.showerror("错误", f"更新失败:{str(e)}") ttk.Button(edit_win, text="保存", command=save_changes).pack(fill=tk.X, padx=5, pady=10) def validate_number(self, value, field_name): """验证数字输入""" try: return float(value) if '.' in value else int(value) except ValueError: messagebox.showerror("输入错误", f"{field_name} 必须为数字") raise def __del__(self): """关闭数据库连接""" if self.conn: self.conn.close() if __name__ == "__main__": app = TeamDatabaseViewer() app.mainloop() ``` ## 概述 这个工具仅仅只用了425行代码,并且用的全都是python基础库,所以可以放心用。至于打包则是用了pyinstaller。 下载链接https://github.com/blankzsh/CFS-ClubEditor/releases/tag/CFS 最后修改:2025 年 03 月 02 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 3 如果觉得我的文章对你有用,请随意赞赏