PYTHON-开发运动会计分管理软件
项目概述
1. 软件功能
我们的运动会计分管理软件具有以下功能:
- 添加运动员:提供接口让用户输入运动员的基本信息,包括分组、姓名、名次。
- 管理分组:支持创建、修改和删除运动员分组,方便组织和查看。
- 得分计算:根据运动员名次自动计算得分,并在用户界面中更新。
- 查看总分:统计所有运动员的总分、平均分、最高分和最低分,并支持分组汇总。
- 数据导出:允许用户将所有运动员信息导出为 CSV 文件,便于存档和后续使用。
- 切换破纪录状态:通过右键菜单允许用户方便地切换运动员的破纪录状态,影响得分计算。
2. 技术栈
本项目所需的技术栈包括:
- Python:作为编程语言实现逻辑和数据处理。
- tkinter:构建图形用户界面,快速开发桌面应用。
- CSV模块:用于处理数据导出与导入的标准库。
3. 软件开发环境
开发此软件时,需确保您的计算机上安装了 Python(建议使用 Python 3.7 及以上版本)。同时,我们使用的 tkinter
库是 Python 的标准库,不需要单独安装。CSV模块也是Python自带的模块,您可以直接使用。
安装与基础设置
-
安装 Python:
- 前往 Python 官网 下载并安装适合您操作系统的版本。请确保勾选“Add Python to PATH”的选项。
-
创建项目文件夹:
- 在您的计算机上创建一个新的文件夹,命名为
ScoreManagementApp
,在其中创建一个文件score_app.py
,并在里面编写代码。
- 在您的计算机上创建一个新的文件夹,命名为
-
环境配置:
- 在终端中使用
pip
安装所需的第三方库(如有需要)。本项目不依赖于外部库,直接使用tkinter
和csv
标准库。
- 在终端中使用
开发步骤详解
1. 数据结构设计
通过使用数据类 (dataclass
) 来定义运动员的信息结构,使得数据的管理和访问更加方便。
from dataclasses import dataclass
@dataclass
class Athlete:
group: str
name: str
rank: int
score: int
is_record: bool = False
上述代码简洁地定义了运动员的各种属性,包括其所属分组、姓名、名次、得分和破纪录状态。使用数据类的好处是可以更直观地管理运动员信息,并提升代码的可读性。
2. 创建主应用程序类 ScoreApp
以下是实现 ScoreApp
类的基本框架和关键功能。该类负责所有界面组件的管理以及实现各项业务逻辑。
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, Menu, OptionMenu, StringVar
import csv
class ScoreApp:
def __init__(self, root):
self.root = root
self.root.title("运动会计分管理软件")
# 初始化名次和分数对应关系
self.rank_to_score = {1: 9, 2: 7, 3: 6, 4: 5, 5: 4, 6: 3, 7: 2, 8: 1}
# 可用分组
self.available_groups = {"组A"}
# 表格呈现运动员信息
self.tree = ttk.Treeview(root, columns=("分组", "姓名", "名次", "分数", "破纪录"), show='headings')
self.setup_treeview()
# 控件布局和功能定义
self.setup_controls()
# 初始化运动员列表
self.scores = []
self.group_filter_var = StringVar(root)
self.group_filter_var.set("所有分组")
self.setup_group_selection()
def setup_treeview(self):
""" 设置Treeview组件 """
self.tree.heading("分组", text="分组")
self.tree.heading("姓名", text="姓名")
self.tree.heading("名次", text="名次")
self.tree.heading("分数", text="分数")
self.tree.heading("破纪录", text="破纪录")
for col in ("分组", "姓名", "名次", "分数", "破纪录"):
self.tree.column(col, width=100)
self.tree.grid(row=0, column=0, columnspan=5, padx=10, pady=10)
def setup_controls(self):
""" 创建按钮和控制框 """
tk.Button(self.root, text="添加运动员", command=self.open_add_athlete_dialog).grid(row=1, column=0, padx=5, pady=5)
tk.Button(self.root, text="管理分组", command=self.open_group_management).grid(row=1, column=1, padx=5, pady=5)
tk.Button(self.root, text="查看总分", command=self.view_total_scores).grid(row=1, column=2, padx=5, pady=5)
tk.Button(self.root, text="导出数据", command=self.export_to_csv).grid(row=1, column=3, padx=5, pady=5)
tk.Button(self.root, text="删除选中行", command=self.delete_selected).grid(row=1, column=4, padx=5, pady=5)
tk.Button(self.root, text="设置分数", command=self.open_settings).grid(row=1, column=5, padx=5, pady=5)
def setup_group_selection(self):
""" 设置分组选择下拉框 """
# 分组选择实现略
上述代码展示了如何初始化应用程序的窗口并构建基本元素,比如设置表格(Treeview
)和按钮。设计时需要确保用户界面友好,并能方便地访问不同功能。
3. 添加运动员信息
添加运动员的对话框包括必要的输入字段,并在用户确认后将数据录入到列表中。
def open_add_athlete_dialog(self):
""" 打开添加运动员的对话框 """
add_window = tk.Toplevel(self.root)
add_window.title("添加运动员")
tk.Label(add_window, text="分组:").grid(row=0, column=0, padx=10, pady=5)
group_entry = StringVar(add_window)
group_entry.set("选择分组或自定义")
group_menu = OptionMenu(add_window, group_entry, *self.available_groups)
group_menu.grid(row=0, column=1, padx=10, pady=5)
tk.Label(add_window, text="姓名:").grid(row=1, column=0, padx=10, pady=5)
name_entry = tk.Entry(add_window)
name_entry.grid(row=1, column=1, padx=10, pady=5)
tk.Label(add_window, text="名次:").grid(row=2, column=0, padx=10, pady=5)
rank_entry = tk.Entry(add_window)
rank_entry.grid(row=2, column=1, padx=10, pady=5)
tk.Button(add_window, text="确认", command=lambda: self.add_score(group_entry.get(), name_entry.get(), rank_entry.get(), add_window)).grid(row=3, columnspan=3, pady=10)
def add_score(self, group, name, rank_str, add_window):
""" 添加运动员信息 """
if group and name and rank_str.isdigit() and int(rank_str) in self.rank_to_score:
rank = int(rank_str)
score = self.rank_to_score[rank]
athlete = Athlete(group, name, rank, score)
self.scores.append(athlete)
self.tree.insert("", "end", values=(athlete.group, athlete.name, athlete.rank, athlete.score, "✔" if athlete.is_record else "✖"))
add_window.destroy()
else:
messagebox.showwarning("输入错误", "请确保所有输入有效!")
open_add_athlete_dialog
方法负责展示添加运动员信息的输入窗口,而 add_score
方法则实现将输入的运动员信息添加到数据结构和界面表格中。输入验证确保数据的有效性,避免无效的输入占用系统资源。
4. 查看总分和数据导出
审核和导出运动员总分也是软件必不可少的功能。我们将通过一个弹出的新窗口展示统计数据,并支持将数据导出为 CSV 格式以备存档。
def view_total_scores(self):
total_window = tk.Toplevel(self.root)
total_window.title("总分")
total_label = tk.Label(total_window, text="所有运动员总分:")
total_label.pack(padx=10, pady=5)
total_score = sum(athlete.score * (2 if athlete.is_record else 1) for athlete in self.scores)
all_scores_label = tk.Label(total_window, text=f"总分: {total_score}")
all_scores_label.pack(padx=10, pady=5)
# 计算和展示平均分、最高分、最低分
average_score = total_score / len(self.scores) if self.scores else 0
highest_score = max(athlete.score for athlete in self.scores) if self.scores else 0
lowest_score = min(athlete.score for athlete in self.scores) if self.scores else 0
stats_label = tk.Label(total_window, text=f"平均分: {average_score:.2f}, 最高分: {highest_score}, 最低分: {lowest_score}")
stats_label.pack(padx=10, pady=5)
def export_to_csv(self):
filename = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV files", "*.csv")])
if filename:
with open(filename, mode='w', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
writer.writerow(["分组", "姓名", "名次", "分数", "破纪录"])
for athlete in self.scores:
writer.writerow([athlete.group, athlete.name, athlete.rank, athlete.score, athlete.is_record])
messagebox.showinfo("成功", f"数据已导出到 {filename}")
view_total_scores
方法计算所有运动员的分数,并将其展示在新窗口中,而 export_to_csv
则构建 CSV 文件并保存用户指定的位置。此功能对于后续的数据分析和管理至关重要,帮助用户轻松整理和传递信息。
5. 管理分组和删除运动员
用户需要能够动态地管理运动员组和删除不再需要的运动员记录。以下是相关的实现:
def open_group_management(self):
group_window = tk.Toplevel(self.root)
group_window.title("管理分组")
tk.Label(group_window, text="自定义分组:").grid(row=0, column=0, padx=10, pady=5)
custom_group_entry = tk.Entry(group_window)
custom_group_entry.grid(row=0, column=1, padx=10, pady=5)
tk.Button(group_window, text="添加分组", command=lambda: self.add_custom_group(custom_group_entry.get())).grid(row=0, column=2, padx=10, pady=5)
tk.Label(group_window, text="当前分组:").grid(row=1, column=0, padx=10, pady=5)
current_groups_var = StringVar(value=", ".join(sorted(self.available_groups)))
tk.Entry(group_window, textvariable=current_groups_var, state='readonly').grid(row=1, column=1, padx=10, pady=5)
tk.Label(group_window, text="删除分组:").grid(row=2, column=0, padx=10, pady=5)
delete_group_entry = tk.Entry(group_window)
delete_group_entry.grid(row=2, column=1, padx=10, pady=5)
tk.Button(group_window, text="删除分组", command=lambda: self.delete_custom_group(delete_group_entry.get())).grid(row=2, column=2, padx=10, pady=5)
def add_custom_group(self, group_name):
if group_name and group_name not in self.available_groups:
self.available_groups.add(group_name)
self.setup_group_selection()
messagebox.showinfo("成功", f"已添加自定义分组: {group_name}")
def delete_selected(self):
selected_item = self.tree.selection()
if selected_item:
for item in selected_item:
index = self.tree.index(item)
self.tree.delete(item)
del self.scores[index]
self.update_displayed_athletes()
else:
messagebox.showwarning("选择错误", "请先选择一行!")
open_group_management
方法启用分组的添加和删除,增强了对运动员的组织能力,而 delete_selected
则允许用户删除选中的运动员记录,使得整体管理变得灵活。
6. 用户交互的多样性
在软件中,右键菜单为用户提供了灵活的操作手段,比如切换运动员的破纪录状态。调用右键菜单的相关实现代码如下:
def show_popup(self, event):
try:
selected = self.tree.identify_row(event.y)
if selected:
self.tree.selection_set(selected)
self.popup_menu.post(event.x_root, event.y_root)
except Exception as e:
print(f"Error showing popup menu: {e}")
self.popup_menu = Menu(root, tearoff=0)
self.popup_menu.add_command(label="切换破纪录", command=self.toggle_record)
通过实现相应的事件绑定,我们使得软件的交互性更强,更加贴合用户的操作习惯。
7. 代码结构与模块化
在整个应用中,保持良好的代码结构和模块化设计是至关重要的。这不仅能提升代码的可读性,还能帮助团队中的其他开发者快速理解和修改代码。
可以将不同的功能区域分开为不同的模块,例如将与数据存储、数据处理、用户界面等相关的功能分别放入不同的文件中。使用 Python 的模块导入功能来连接这些组件,可以有效管理大型项目,提升维护性。
总结
本篇博客详细介绍了如何使用 Python 的 tkinter
库创建一个运动会计分管理软件。
作为示例为研究GUI和数据结构打下基础
完整源码:
import tkinter as tk
from tkinter import ttk, simpledialog, messagebox, Menu, OptionMenu, StringVar, filedialog
from dataclasses import dataclass
import csv
# 数据类: 运动员信息
@dataclass
class Athlete:
group: str
name: str
rank: int
score: int
is_record: bool = False
class ScoreApp:
def __init__(self, root):
self.root = root
self.root.title("运动会计分软件")
# 默认名次与分数对应关系
self.rank_to_score = {
1: 9,
2: 7,
3: 6,
4: 5,
5: 4,
6: 3,
7: 2,
8: 1
}
# 可用分组
self.available_groups = {"组A"} # 默认分组
# 表格呈现
self.tree = ttk.Treeview(root, columns=("分组", "姓名", "名次", "分数", "破纪录"), show='headings')
self.tree.heading("分组", text="分组")
self.tree.heading("姓名", text="姓名")
self.tree.heading("名次", text="名次")
self.tree.heading("分数", text="分数")
self.tree.heading("破纪录", text="破纪录")
self.tree.column("分组", width=100)
self.tree.column("姓名", width=200)
self.tree.column("名次", width=100)
self.tree.column("分数", width=100)
self.tree.column("破纪录", width=100)
self.tree.grid(row=0, column=0, columnspan=5, padx=10, pady=10)
# 添加按钮
self.add_button = tk.Button(root, text="添加运动员", command=self.open_add_athlete_dialog)
self.add_button.grid(row=1, column=0, padx=5, pady=5)
# 管理分组按钮
self.group_management_button = tk.Button(root, text="管理分组", command=self.open_group_management)
self.group_management_button.grid(row=1, column=1, padx=5, pady=5)
# 查看总分按钮
self.total_button = tk.Button(root, text="查看总分", command=self.view_total_scores)
self.total_button.grid(row=1, column=2, padx=5, pady=5)
# 导出按钮
self.export_button = tk.Button(root, text="导出数据", command=self.export_to_csv)
self.export_button.grid(row=1, column=3, padx=5, pady=5)
# 删除按钮
self.delete_button = tk.Button(root, text="删除选中行", command=self.delete_selected)
self.delete_button.grid(row=1, column=4, padx=5, pady=5)
# 设置按钮
self.settings_button = tk.Button(root, text="设置分数", command=self.open_settings)
self.settings_button.grid(row=1, column=5, padx=5, pady=5)
# 右键菜单
self.popup_menu = Menu(root, tearoff=0)
self.popup_menu.add_command(label="切换破纪录", command=self.toggle_record)
self.popup_menu.add_separator()
# 这里调用 open_group_management
self.popup_menu.add_command(label="设置分组", command=self.open_group_management)
# 绑定右键点击
self.tree.bind("<Button-3>", self.show_popup)
# 运动员信息列表
self.scores = []
# 分组过滤变量
self.group_filter_var = StringVar(root)
self.group_filter_var.set("所有分组")
self.setup_group_selection()
def setup_group_selection(self):
""" 设置和显示分组选择下拉框 """
groups = sorted(self.available_groups) + ["所有分组"]
group_menu = OptionMenu(self.root, self.group_filter_var, *groups, command=self.update_displayed_athletes)
group_menu.grid(row=1, column=6, padx=5, pady=5)
def update_displayed_athletes(self, *args):
""" 根据选择的分组更新显示的运动员 """
self.tree.delete(*self.tree.get_children())
filtered_scores = [athlete for athlete in self.scores if self.group_filter_var.get() in ("所有分组", athlete.group)]
for athlete in filtered_scores:
self.tree.insert("", "end", values=(athlete.group, athlete.name, athlete.rank, athlete.score, "✔" if athlete.is_record else "✖"))
def open_add_athlete_dialog(self):
""" 打开添加运动员的对话框 """
add_window = tk.Toplevel(self.root)
add_window.title("添加运动员")
# 输入字段
tk.Label(add_window, text="分组:").grid(row=0, column=0, padx=10, pady=5)
group_entry = StringVar(add_window)
group_entry.set("选择分组或自定义")
group_menu = OptionMenu(add_window, group_entry, *self.available_groups)
group_menu.grid(row=0, column=1, padx=10, pady=5)
custom_group_entry = tk.Entry(add_window) # 自定义分组输入
custom_group_entry.grid(row=0, column=2, padx=10, pady=5)
tk.Label(add_window, text="姓名:").grid(row=1, column=0, padx=10, pady=5)
name_entry = tk.Entry(add_window)
name_entry.grid(row=1, column=1, padx=10, pady=5)
tk.Label(add_window, text="名次:").grid(row=2, column=0, padx=10, pady=5)
rank_entry = tk.Entry(add_window)
rank_entry.grid(row=2, column=1, padx=10, pady=5)
# 确认按钮
tk.Button(add_window, text="确认", command=lambda: self.add_score(custom_group_entry.get(), group_entry.get(), name_entry.get(), rank_entry.get(), add_window)).grid(row=3, columnspan=3, pady=10)
def add_score(self, custom_group, group, name, rank_str, add_window):
""" 添加运动员信息 """
group_to_use = custom_group if custom_group else group
if group_to_use and name and rank_str.isdigit() and int(rank_str) in self.rank_to_score:
rank = int(rank_str)
score = self.rank_to_score[rank]
athlete = Athlete(group_to_use, name, rank, score) # 使用数据类
self.scores.append(athlete) # 添加运动员
self.tree.insert("", "end", values=(athlete.group, athlete.name, athlete.rank, athlete.score, "✔" if athlete.is_record else "✖"))
add_window.destroy() # 关闭添加窗口
self.sort_treeview() # 添加后自动排序
self.available_groups.add(group_to_use) # 自定义分组添加到集合
self.setup_group_selection() # 更新分组选择
else:
messagebox.showwarning("输入错误", "请确保所有输入有效!")
def sort_treeview(self):
""" 按照分数从高到低排序 """
sorted_scores = sorted(self.scores, key=lambda x: x.score, reverse=True)
self.tree.delete(*self.tree.get_children()) # 清空当前显示的所有行
# 重新显示排序后的运动员信息
for athlete in sorted_scores:
self.tree.insert("", "end", values=(athlete.group, athlete.name, athlete.rank, athlete.score, "✔" if athlete.is_record else "✖"))
def view_total_scores(self):
""" 查看总分窗口 """
total_window = tk.Toplevel(self.root)
total_window.title("总分")
total_label = tk.Label(total_window, text="所有运动员总分:")
total_label.pack(padx=10, pady=5)
total_score = sum(athlete.score * (2 if athlete.is_record else 1) for athlete in self.scores)
all_scores_label = tk.Label(total_window, text=f"总分: {total_score}")
all_scores_label.pack(padx=10, pady=5)
# 计算平均分、最高分、最低分
if self.scores:
average_score = total_score / len(self.scores)
highest_score = max(athlete.score for athlete in self.scores)
lowest_score = min(athlete.score for athlete in self.scores)
else:
average_score = highest_score = lowest_score = 0
stats_label = tk.Label(total_window, text=f"平均分: {average_score:.2f}, 最高分: {highest_score}, 最低分: {lowest_score}")
stats_label.pack(padx=10, pady=5)
# 各分组的总分
group_scores = {}
for athlete in self.scores:
group = athlete.group
group_score = athlete.score * (2 if athlete.is_record else 1)
group_scores[group] = group_scores.get(group, 0) + group_score
# 显示各组的总分
group_total_label = tk.Label(total_window, text="各分组总分:")
group_total_label.pack(padx=10, pady=5)
for group, group_total in group_scores.items():
group_score_label = tk.Label(total_window, text=f"{group}: {group_total}")
group_score_label.pack(padx=10, pady=5)
def export_to_csv(self):
""" 弹出文件对话框选择保存路径并导出 CSV """
filename = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV files", "*.csv")])
if filename:
with open(filename, mode='w', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
writer.writerow(["分组", "姓名", "名次", "分数", "破纪录"])
for athlete in self.scores:
writer.writerow([athlete.group, athlete.name, athlete.rank, athlete.score, athlete.is_record])
messagebox.showinfo("成功", f"数据已导出到 {filename}")
def delete_selected(self):
selected_item = self.tree.selection()
if selected_item:
for item in selected_item:
index = self.tree.index(item)
self.tree.delete(item)
# 从列表中删除相应的运动员信息
del self.scores[index]
self.update_displayed_athletes() # 更新显示
else:
messagebox.showwarning("选择错误", "请先选择一行!")
def open_settings(self):
""" 打开设置分数的窗口 """
settings_window = tk.Toplevel(self.root)
settings_window.title("设置分数")
self.rank_income_vars = {}
for rank in range(1, 9):
tk.Label(settings_window, text=f"第{rank}名分数:").grid(row=rank-1, column=0, padx=5, pady=5)
var = tk.StringVar(value=self.rank_to_score.get(rank, 0))
self.rank_income_vars[rank] = var
tk.Entry(settings_window, textvariable=var).grid(row=rank-1, column=1, padx=5, pady=5)
tk.Button(settings_window, text="确认", command=self.save_settings).grid(row=8, columnspan=2, pady=10)
def save_settings(self):
for rank, var in self.rank_income_vars.items():
try:
score = int(var.get())
self.rank_to_score[rank] = score
except ValueError:
messagebox.showwarning("输入错误", f"名次 {rank} 的分数无效,请输入数字!")
return
for index, athlete in enumerate(self.scores):
# 更新分数
if athlete.rank in self.rank_to_score:
score = self.rank_to_score[athlete.rank]
athlete.score = score # 更新分数
if index < len(self.tree.get_children()): # 确保索引有效
self.tree.item(self.tree.get_children()[index], values=(athlete.group, athlete.name, athlete.rank, score, athlete.is_record))
messagebox.showinfo("成功", "分数设置已更新!")
def toggle_record(self):
selected_item = self.tree.selection()
if selected_item:
item = selected_item[0]
index = self.tree.index(item)
athlete = self.scores[index] # 获取运动员信息
athlete.is_record = not athlete.is_record # 切换破纪录状态
# 更新分数
if athlete.is_record:
athlete.score *= 2
else:
athlete.score //= 2
# 更新树形控件
self.tree.item(item, values=(athlete.group, athlete.name, athlete.rank, athlete.score, "✔" if athlete.is_record else "✖"))
def show_popup(self, event):
try:
selected = self.tree.identify_row(event.y)
if selected:
self.tree.selection_set(selected)
self.popup_menu.post(event.x_root, event.y_root)
except Exception as e:
print(f"Error showing popup menu: {e}")
def open_group_management(self):
""" 打开分组管理窗口 """
group_window = tk.Toplevel(self.root)
group_window.title("管理分组")
tk.Label(group_window, text="自定义分组:").grid(row=0, column=0, padx=10, pady=5)
custom_group_entry = tk.Entry(group_window)
custom_group_entry.grid(row=0, column=1, padx=10, pady=5)
tk.Button(group_window, text="添加分组", command=lambda: self.add_custom_group(custom_group_entry.get())).grid(row=0, column=2, padx=10, pady=5)
tk.Label(group_window, text="当前分组:").grid(row=1, column=0, padx=10, pady=5)
current_groups_var = StringVar(value=", ".join(sorted(self.available_groups)))
tk.Entry(group_window, textvariable=current_groups_var, state='readonly').grid(row=1, column=1, padx=10, pady=5)
tk.Label(group_window, text="删除分组:").grid(row=2, column=0, padx=10, pady=5)
delete_group_entry = tk.Entry(group_window)
delete_group_entry.grid(row=2, column=1, padx=10, pady=5)
tk.Button(group_window, text="删除分组", command=lambda: self.delete_custom_group(delete_group_entry.get())).grid(row=2, column=2, padx=10, pady=5)
def add_custom_group(self, group_name):
""" 添加自定义分组 """
if group_name and group_name not in self.available_groups:
self.available_groups.add(group_name)
self.setup_group_selection() # 更新分组选择菜单
messagebox.showinfo("成功", f"已添加自定义分组: {group_name}")
else:
messagebox.showwarning("输入错误", "分组名称不能为空或已存在!")
def delete_custom_group(self, group_name):
""" 删除自定义分组 """
if group_name in self.available_groups:
self.available_groups.remove(group_name)
self.setup_group_selection() # 更新分组选择菜单
messagebox.showinfo("成功", f"已删除分组: {group_name}")
else:
messagebox.showwarning("输入错误", "该分组不存在!")
def show_help(self):
help_message = (
"本软件用于运动会计分管理...\n"
"使用说明:\n"
"1. 请依次输入运动员信息,选择分组或自定义分组。\n"
"2. 点击“添加运动员”来记录。\n"
"3. 点击“查看总分”可以查看所有运动员及各分组的总分。\n"
"4. 右键点击运动员可以切换破纪录状态。\n"
"5. 点击“导出数据”将运动员信息导出为CSV文件。"
)
messagebox.showinfo("帮助", help_message)
if __name__ == "__main__":
root = tk.Tk()
app = ScoreApp(root)
root.mainloop()
#这个示例代码用了tkinter库来做GUI,其中我采用的的是Tree组件使界面有一个类似于电子表格的管理方式
#scv库的作用是可以让统计的数据导出
#其中的tkinter库和scv库都是自带的
#现在看一下运行后的成果
#这里选用如果破纪录是翻倍分数
源码地址:https://download.csdn.net/download/HYP_Coder/90001597?spm=1001.2014.3001.5503
B站视频:嗯?别进来!_哔哩哔哩_bilibili
标签:group,计分,Python,athlete,window,管理软件,score,tk,self From: https://blog.csdn.net/HYP_Coder/article/details/143868152