|
|
|
@ -0,0 +1,516 @@
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
import tkinter as tk
|
|
|
|
|
|
|
|
from tkinter import ttk, messagebox
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class StudentManagementSystemGUI:
|
|
|
|
|
|
|
|
def __init__(self, root):
|
|
|
|
|
|
|
|
self.root = root
|
|
|
|
|
|
|
|
self.root.title("学生信息管理系统")
|
|
|
|
|
|
|
|
self.root.geometry("1000x700")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 初始化数据
|
|
|
|
|
|
|
|
self.students_info = {}
|
|
|
|
|
|
|
|
self.file_path = os.path.join(os.getcwd(), 'student_data.json')
|
|
|
|
|
|
|
|
self.subject_name = {
|
|
|
|
|
|
|
|
'math': '数学',
|
|
|
|
|
|
|
|
'python': 'Python程序设计',
|
|
|
|
|
|
|
|
'english': '英语',
|
|
|
|
|
|
|
|
'physics': '物理',
|
|
|
|
|
|
|
|
'sum': '总分'
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 加载数据
|
|
|
|
|
|
|
|
self.load_data()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 创建UI
|
|
|
|
|
|
|
|
self.create_widgets()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 默认显示所有学生
|
|
|
|
|
|
|
|
self.refresh_student_list()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_data(self):
|
|
|
|
|
|
|
|
"""从文件加载学生数据"""
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
if os.path.exists(self.file_path):
|
|
|
|
|
|
|
|
with open(self.file_path, 'r', encoding='utf-8') as f:
|
|
|
|
|
|
|
|
self.students_info = json.load(f)
|
|
|
|
|
|
|
|
messagebox.showinfo("成功", "学生信息已成功加载!")
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
messagebox.showinfo("提示", "未找到数据文件,已初始化空数据库。")
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
messagebox.showerror("错误", f"加载数据时出错: {e}")
|
|
|
|
|
|
|
|
self.students_info = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def save_data(self):
|
|
|
|
|
|
|
|
"""保存学生数据到文件"""
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
with open(self.file_path, 'w', encoding='utf-8') as f:
|
|
|
|
|
|
|
|
json.dump(self.students_info, f, ensure_ascii=False, indent=4)
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
messagebox.showerror("错误", f"保存数据时出错: {e}")
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_widgets(self):
|
|
|
|
|
|
|
|
"""创建所有UI组件"""
|
|
|
|
|
|
|
|
# 顶部按钮区域
|
|
|
|
|
|
|
|
button_frame = ttk.Frame(self.root, padding="10")
|
|
|
|
|
|
|
|
button_frame.pack(fill=tk.X)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Button(button_frame, text="添加学生", command=self.show_add_dialog).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
ttk.Button(button_frame, text="修改学生", command=self.show_edit_dialog).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
ttk.Button(button_frame, text="删除学生", command=self.delete_student).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
ttk.Button(button_frame, text="刷新列表", command=self.refresh_student_list).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
ttk.Button(button_frame, text="统计信息", command=self.show_statistics).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
ttk.Button(button_frame, text="按总分排序", command=self.sort_by_total_score).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 搜索区域
|
|
|
|
|
|
|
|
search_frame = ttk.Frame(self.root, padding="10")
|
|
|
|
|
|
|
|
search_frame.pack(fill=tk.X)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(search_frame, text="搜索:").pack(side=tk.LEFT)
|
|
|
|
|
|
|
|
self.search_var = tk.StringVar()
|
|
|
|
|
|
|
|
search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=30)
|
|
|
|
|
|
|
|
search_entry.pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
|
|
search_entry.bind("<KeyRelease>", self.on_search_changed)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 学生列表
|
|
|
|
|
|
|
|
self.tree = ttk.Treeview(self.root, columns=('id', 'name', 'gender', 'class', 'total'), show='headings')
|
|
|
|
|
|
|
|
self.tree.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 设置列
|
|
|
|
|
|
|
|
self.tree.heading('id', text='学号', anchor=tk.W)
|
|
|
|
|
|
|
|
self.tree.heading('name', text='姓名', anchor=tk.W)
|
|
|
|
|
|
|
|
self.tree.heading('gender', text='性别', anchor=tk.W)
|
|
|
|
|
|
|
|
self.tree.heading('class', text='班级', anchor=tk.W)
|
|
|
|
|
|
|
|
self.tree.heading('total', text='总分', anchor=tk.W)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 设置列宽
|
|
|
|
|
|
|
|
self.tree.column('id', width=100)
|
|
|
|
|
|
|
|
self.tree.column('name', width=100)
|
|
|
|
|
|
|
|
self.tree.column('gender', width=60)
|
|
|
|
|
|
|
|
self.tree.column('class', width=100)
|
|
|
|
|
|
|
|
self.tree.column('total', width=80)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 详细信息区域
|
|
|
|
|
|
|
|
detail_frame = ttk.Frame(self.root, padding="10")
|
|
|
|
|
|
|
|
detail_frame.pack(fill=tk.X)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.detail_text = tk.Text(detail_frame, height=10, state=tk.DISABLED)
|
|
|
|
|
|
|
|
self.detail_text.pack(fill=tk.BOTH, expand=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 绑定选中事件
|
|
|
|
|
|
|
|
self.tree.bind("<<TreeviewSelect>>", self.on_student_selected)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def refresh_student_list(self, students=None):
|
|
|
|
|
|
|
|
"""刷新学生列表"""
|
|
|
|
|
|
|
|
self.tree.delete(*self.tree.get_children())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
students_to_show = students if students else self.students_info.values()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for student in students_to_show:
|
|
|
|
|
|
|
|
self.tree.insert('', tk.END, values=(
|
|
|
|
|
|
|
|
student['basic_info']['id'],
|
|
|
|
|
|
|
|
student['basic_info']['name'],
|
|
|
|
|
|
|
|
student['basic_info']['gender'],
|
|
|
|
|
|
|
|
student['basic_info']['class'],
|
|
|
|
|
|
|
|
student['score']['sum']
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def on_student_selected(self):
|
|
|
|
|
|
|
|
"""当选中学生时显示详细信息"""
|
|
|
|
|
|
|
|
selected_item = self.tree.selection()
|
|
|
|
|
|
|
|
if not selected_item:
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
student_id = self.tree.item(selected_item)['values'][0]
|
|
|
|
|
|
|
|
student = self.students_info[student_id]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.detail_text.config(state=tk.NORMAL)
|
|
|
|
|
|
|
|
self.detail_text.delete(1.0, tk.END)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 显示基本信息
|
|
|
|
|
|
|
|
self.detail_text.insert(tk.END, "=== 基本信息 ===\n")
|
|
|
|
|
|
|
|
for key, value in student['basic_info'].items():
|
|
|
|
|
|
|
|
self.detail_text.insert(tk.END, f"{key}: {value}\n")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 显示成绩
|
|
|
|
|
|
|
|
self.detail_text.insert(tk.END, "\n=== 成绩信息 ===\n")
|
|
|
|
|
|
|
|
for subject, score in student['score'].items():
|
|
|
|
|
|
|
|
if subject in self.subject_name:
|
|
|
|
|
|
|
|
self.detail_text.insert(tk.END, f"{self.subject_name[subject]}: {score}\n")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.detail_text.config(state=tk.DISABLED)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def on_search_changed(self, event):
|
|
|
|
|
|
|
|
"""搜索框内容变化时过滤列表"""
|
|
|
|
|
|
|
|
search_term = self.search_var.get().lower()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if not search_term:
|
|
|
|
|
|
|
|
self.refresh_student_list()
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
filtered_students = [
|
|
|
|
|
|
|
|
student for student in self.students_info.values()
|
|
|
|
|
|
|
|
if (search_term in student['basic_info']['id'].lower() or
|
|
|
|
|
|
|
|
search_term in student['basic_info']['name'].lower() or
|
|
|
|
|
|
|
|
search_term in student['basic_info']['class'].lower())
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.refresh_student_list(filtered_students)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def show_add_dialog(self):
|
|
|
|
|
|
|
|
"""显示添加学生对话框"""
|
|
|
|
|
|
|
|
dialog = tk.Toplevel(self.root)
|
|
|
|
|
|
|
|
dialog.title("添加学生")
|
|
|
|
|
|
|
|
dialog.geometry("400x500")
|
|
|
|
|
|
|
|
dialog.resizable(False, False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 学号
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="学号(8位数字):").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
id_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
id_entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 身份证号
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="身份证号(18位):").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
id_card_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
id_card_entry.grid(row=1, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 姓名
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="姓名:").grid(row=2, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
name_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
name_entry.grid(row=2, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 性别
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="性别:").grid(row=3, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
gender_var = tk.StringVar(value="男")
|
|
|
|
|
|
|
|
ttk.Radiobutton(dialog, text="男", variable=gender_var, value="男").grid(row=3, column=1, padx=5, pady=5,
|
|
|
|
|
|
|
|
sticky=tk.W)
|
|
|
|
|
|
|
|
ttk.Radiobutton(dialog, text="女", variable=gender_var, value="女").grid(row=3, column=1, padx=5, pady=5,
|
|
|
|
|
|
|
|
sticky=tk.E)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 出生日期
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="出生日期(YYYY-MM-DD):").grid(row=4, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
birth_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
birth_entry.grid(row=4, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 班级
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="班级:").grid(row=5, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
class_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
class_entry.grid(row=5, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 入学年份
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="入学年份:").grid(row=6, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
year_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
year_entry.grid(row=6, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 成绩
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="=== 成绩信息 ===").grid(row=7, column=0, columnspan=2, pady=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="数学:").grid(row=8, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
math_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
math_entry.grid(row=8, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="Python:").grid(row=9, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
python_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
python_entry.grid(row=9, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="英语:").grid(row=10, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
english_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
english_entry.grid(row=10, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="物理:").grid(row=11, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
physics_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
physics_entry.grid(row=11, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 按钮
|
|
|
|
|
|
|
|
button_frame = ttk.Frame(dialog)
|
|
|
|
|
|
|
|
button_frame.grid(row=12, column=0, columnspan=2, pady=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_student():
|
|
|
|
|
|
|
|
"""添加学生"""
|
|
|
|
|
|
|
|
# 验证学号
|
|
|
|
|
|
|
|
student_id = id_entry.get().strip()
|
|
|
|
|
|
|
|
if not student_id.isdigit() or len(student_id) != 8:
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "学号必须为8位数字!")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
if student_id in self.students_info:
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "该学号已存在!")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 验证身份证号
|
|
|
|
|
|
|
|
id_card = id_card_entry.get().strip().upper()
|
|
|
|
|
|
|
|
if len(id_card) != 18:
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "身份证号必须为18位!")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
if not (id_card[:-1].isdigit() and (id_card[-1].isdigit() or id_card[-1] == 'X')):
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "无效的身份证号格式!前17位必须是数字,最后一位可以是数字或X")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
if any(student['basic_info'].get('id_card') == id_card for student in self.students_info.values()):
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "该身份证号已存在!")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 验证成绩
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
math_score = float(math_entry.get())
|
|
|
|
|
|
|
|
python_score = float(python_entry.get())
|
|
|
|
|
|
|
|
english_score = float(english_entry.get())
|
|
|
|
|
|
|
|
physics_score = float(physics_entry.get())
|
|
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "成绩必须为数字!")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 创建学生信息
|
|
|
|
|
|
|
|
student = {
|
|
|
|
|
|
|
|
'basic_info': {
|
|
|
|
|
|
|
|
'id': student_id,
|
|
|
|
|
|
|
|
'id_card': id_card,
|
|
|
|
|
|
|
|
'name': name_entry.get(),
|
|
|
|
|
|
|
|
'gender': gender_var.get(),
|
|
|
|
|
|
|
|
'birth_date': birth_entry.get(),
|
|
|
|
|
|
|
|
'class': class_entry.get(),
|
|
|
|
|
|
|
|
'admission_year': year_entry.get()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
'score': {
|
|
|
|
|
|
|
|
'math': math_score,
|
|
|
|
|
|
|
|
'python': python_score,
|
|
|
|
|
|
|
|
'english': english_score,
|
|
|
|
|
|
|
|
'physics': physics_score,
|
|
|
|
|
|
|
|
'sum': math_score + python_score + english_score + physics_score
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.students_info[student_id] = student
|
|
|
|
|
|
|
|
if self.save_data():
|
|
|
|
|
|
|
|
messagebox.showinfo("成功", "学生信息添加成功!")
|
|
|
|
|
|
|
|
self.refresh_student_list()
|
|
|
|
|
|
|
|
dialog.destroy()
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "学生信息添加失败!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Button(button_frame, text="确定", command=add_student).pack(side=tk.LEFT, padx=10)
|
|
|
|
|
|
|
|
ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def show_edit_dialog(self):
|
|
|
|
|
|
|
|
"""显示编辑学生对话框"""
|
|
|
|
|
|
|
|
selected_item = self.tree.selection()
|
|
|
|
|
|
|
|
if not selected_item:
|
|
|
|
|
|
|
|
messagebox.showwarning("提示", "请先选择一个学生!")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
student_id = self.tree.item(selected_item)['values'][0]
|
|
|
|
|
|
|
|
student = self.students_info[student_id]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dialog = tk.Toplevel(self.root)
|
|
|
|
|
|
|
|
dialog.title("编辑学生信息")
|
|
|
|
|
|
|
|
dialog.geometry("400x500")
|
|
|
|
|
|
|
|
dialog.resizable(False, False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 学号(不可编辑)
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="学号:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
ttk.Label(dialog, text=student['basic_info']['id']).grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 身份证号
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="身份证号:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
id_card_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
id_card_entry.insert(0, student['basic_info'].get('id_card', ''))
|
|
|
|
|
|
|
|
id_card_entry.grid(row=1, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 姓名
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="姓名:").grid(row=2, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
name_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
name_entry.insert(0, student['basic_info']['name'])
|
|
|
|
|
|
|
|
name_entry.grid(row=2, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 性别
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="性别:").grid(row=3, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
gender_var = tk.StringVar(value=student['basic_info']['gender'])
|
|
|
|
|
|
|
|
ttk.Radiobutton(dialog, text="男", variable=gender_var, value="男").grid(row=3, column=1, padx=5, pady=5,
|
|
|
|
|
|
|
|
sticky=tk.W)
|
|
|
|
|
|
|
|
ttk.Radiobutton(dialog, text="女", variable=gender_var, value="女").grid(row=3, column=1, padx=5, pady=5,
|
|
|
|
|
|
|
|
sticky=tk.E)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 出生日期
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="出生日期:").grid(row=4, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
birth_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
birth_entry.insert(0, student['basic_info']['birth_date'])
|
|
|
|
|
|
|
|
birth_entry.grid(row=4, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 班级
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="班级:").grid(row=5, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
class_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
class_entry.insert(0, student['basic_info']['class'])
|
|
|
|
|
|
|
|
class_entry.grid(row=5, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 入学年份
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="入学年份:").grid(row=6, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
year_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
year_entry.insert(0, student['basic_info']['admission_year'])
|
|
|
|
|
|
|
|
year_entry.grid(row=6, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 成绩
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="=== 成绩信息 ===").grid(row=7, column=0, columnspan=2, pady=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="数学:").grid(row=8, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
math_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
math_entry.insert(0, student['score']['math'])
|
|
|
|
|
|
|
|
math_entry.grid(row=8, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="Python:").grid(row=9, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
python_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
python_entry.insert(0, student['score']['python'])
|
|
|
|
|
|
|
|
python_entry.grid(row=9, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="英语:").grid(row=10, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
english_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
english_entry.insert(0, student['score']['english'])
|
|
|
|
|
|
|
|
english_entry.grid(row=10, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(dialog, text="物理:").grid(row=11, column=0, padx=5, pady=5, sticky=tk.W)
|
|
|
|
|
|
|
|
physics_entry = ttk.Entry(dialog)
|
|
|
|
|
|
|
|
physics_entry.insert(0, student['score']['physics'])
|
|
|
|
|
|
|
|
physics_entry.grid(row=11, column=1, padx=5, pady=5, sticky=tk.EW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 按钮
|
|
|
|
|
|
|
|
button_frame = ttk.Frame(dialog)
|
|
|
|
|
|
|
|
button_frame.grid(row=12, column=0, columnspan=2, pady=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def update_student(student=None):
|
|
|
|
|
|
|
|
"""更新学生信息"""
|
|
|
|
|
|
|
|
# 验证身份证号
|
|
|
|
|
|
|
|
id_card = id_card_entry.get().strip().upper()
|
|
|
|
|
|
|
|
if len(id_card) != 18:
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "身份证号必须为18位!")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
if not (id_card[:-1].isdigit() and (id_card[-1].isdigit() or id_card[-1] == 'X')):
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "无效的身份证号格式!前17位必须是数字,最后一位可以是数字或X")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
if any(sid != student_id and student['basic_info'].get('id_card') == id_card
|
|
|
|
|
|
|
|
for sid, student in self.students_info.items()):
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "该身份证号已被其他学生使用!")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 验证成绩
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
math_score = float(math_entry.get())
|
|
|
|
|
|
|
|
python_score = float(python_entry.get())
|
|
|
|
|
|
|
|
english_score = float(english_entry.get())
|
|
|
|
|
|
|
|
physics_score = float(physics_entry.get())
|
|
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "成绩必须为数字!")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 更新学生信息
|
|
|
|
|
|
|
|
student['basic_info'].update({
|
|
|
|
|
|
|
|
'id_card': id_card,
|
|
|
|
|
|
|
|
'name': name_entry.get(),
|
|
|
|
|
|
|
|
'gender': gender_var.get(),
|
|
|
|
|
|
|
|
'birth_date': birth_entry.get(),
|
|
|
|
|
|
|
|
'class': class_entry.get(),
|
|
|
|
|
|
|
|
'admission_year': year_entry.get()
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
student['score'].update({
|
|
|
|
|
|
|
|
'math': math_score,
|
|
|
|
|
|
|
|
'python': python_score,
|
|
|
|
|
|
|
|
'english': english_score,
|
|
|
|
|
|
|
|
'physics': physics_score,
|
|
|
|
|
|
|
|
'sum': math_score + python_score + english_score + physics_score
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self.save_data():
|
|
|
|
|
|
|
|
messagebox.showinfo("成功", "学生信息修改成功!")
|
|
|
|
|
|
|
|
self.refresh_student_list()
|
|
|
|
|
|
|
|
dialog.destroy()
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "学生信息修改失败!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Button(button_frame, text="确定", command=update_student).pack(side=tk.LEFT, padx=10)
|
|
|
|
|
|
|
|
ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def delete_student(self):
|
|
|
|
|
|
|
|
"""删除学生"""
|
|
|
|
|
|
|
|
selected_item = self.tree.selection()
|
|
|
|
|
|
|
|
if not selected_item:
|
|
|
|
|
|
|
|
messagebox.showwarning("提示", "请先选择一个学生!")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
student_id = self.tree.item(selected_item)['values'][0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if messagebox.askyesno("确认", f"确定要删除学号为 {student_id} 的学生吗?"):
|
|
|
|
|
|
|
|
del self.students_info[student_id]
|
|
|
|
|
|
|
|
if self.save_data():
|
|
|
|
|
|
|
|
messagebox.showinfo("成功", "学生信息删除成功!")
|
|
|
|
|
|
|
|
self.refresh_student_list()
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
messagebox.showerror("错误", "学生信息删除失败!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def show_statistics(self):
|
|
|
|
|
|
|
|
"""显示统计信息"""
|
|
|
|
|
|
|
|
if not self.students_info:
|
|
|
|
|
|
|
|
messagebox.showwarning("提示", "当前没有学生信息!")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
total = len(self.students_info)
|
|
|
|
|
|
|
|
math_avg = sum(s['score']['math'] for s in self.students_info.values()) / total
|
|
|
|
|
|
|
|
python_avg = sum(s['score']['python'] for s in self.students_info.values()) / total
|
|
|
|
|
|
|
|
english_avg = sum(s['score']['english'] for s in self.students_info.values()) / total
|
|
|
|
|
|
|
|
physics_avg = sum(s['score']['physics'] for s in self.students_info.values()) / total
|
|
|
|
|
|
|
|
total_avg = sum(s['score']['sum'] for s in self.students_info.values()) / total
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stats = f"""=== 统计信息 ===
|
|
|
|
|
|
|
|
学生总数: {total}
|
|
|
|
|
|
|
|
数学平均分: {math_avg:.2f}
|
|
|
|
|
|
|
|
Python平均分: {python_avg:.2f}
|
|
|
|
|
|
|
|
英语平均分: {english_avg:.2f}
|
|
|
|
|
|
|
|
物理平均分: {physics_avg:.2f}
|
|
|
|
|
|
|
|
总分平均分: {total_avg:.2f}"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
messagebox.showinfo("统计信息", stats)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def sort_by_total_score(self):
|
|
|
|
|
|
|
|
"""按总分排序显示"""
|
|
|
|
|
|
|
|
if not self.students_info:
|
|
|
|
|
|
|
|
messagebox.showwarning("提示", "当前没有学生信息!")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sorted_students = sorted(
|
|
|
|
|
|
|
|
self.students_info.values(),
|
|
|
|
|
|
|
|
key=lambda x: x['score']['sum'],
|
|
|
|
|
|
|
|
reverse=True
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 创建一个新窗口显示排序结果
|
|
|
|
|
|
|
|
sort_window = tk.Toplevel(self.root)
|
|
|
|
|
|
|
|
sort_window.title("按总分排序")
|
|
|
|
|
|
|
|
sort_window.geometry("600x400")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 创建Treeview显示排序结果
|
|
|
|
|
|
|
|
tree = ttk.Treeview(sort_window, columns=('rank', 'id', 'name', 'total'), show='headings')
|
|
|
|
|
|
|
|
tree.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tree.heading('rank', text='排名')
|
|
|
|
|
|
|
|
tree.heading('id', text='学号')
|
|
|
|
|
|
|
|
tree.heading('name', text='姓名')
|
|
|
|
|
|
|
|
tree.heading('total', text='总分')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tree.column('rank', width=50, anchor=tk.CENTER)
|
|
|
|
|
|
|
|
tree.column('id', width=100, anchor=tk.CENTER)
|
|
|
|
|
|
|
|
tree.column('name', width=100, anchor=tk.CENTER)
|
|
|
|
|
|
|
|
tree.column('total', width=80, anchor=tk.CENTER)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for rank, student in enumerate(sorted_students, 1):
|
|
|
|
|
|
|
|
tree.insert('', tk.END, values=(
|
|
|
|
|
|
|
|
rank,
|
|
|
|
|
|
|
|
student['basic_info']['id'],
|
|
|
|
|
|
|
|
student['basic_info']['name'],
|
|
|
|
|
|
|
|
student['score']['sum']
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
|
|
root = tk.Tk()
|
|
|
|
|
|
|
|
app = StudentManagementSystemGUI(root)
|
|
|
|
|
|
|
|
root.mainloop()
|