You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

179 lines
7.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from db_operate import DBHelper
from excel_operate import import_students_from_excel, export_score_to_excel
from call_logic import CallLogic
# 设置matplotlib中文显示
plt.rcParams["font.family"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False
class CallSystemUI:
def __init__(self, root):
self.root = root
self.root.title("课堂随机点名系统")
self.root.geometry("800x600")
self.call_logic = CallLogic()
self.create_widgets()
def create_widgets(self):
"""创建界面组件"""
# 顶部按钮栏
btn_frame = ttk.Frame(self.root)
btn_frame.pack(pady=10, fill=tk.X)
# 导入Excel按钮
ttk.Button(btn_frame, text="导入学生名单", command=self.import_excel).pack(side=tk.LEFT, padx=5)
# 随机点名按钮
ttk.Button(btn_frame, text="随机点名", command=self.random_call).pack(side=tk.LEFT, padx=5)
# 顺序点名按钮
ttk.Button(btn_frame, text="顺序点名", command=self.order_call).pack(side=tk.LEFT, padx=5)
# 可视化按钮
ttk.Button(btn_frame, text="积分可视化", command=self.show_visual).pack(side=tk.LEFT, padx=5)
# 导出积分按钮
ttk.Button(btn_frame, text="导出积分详单", command=self.export_excel).pack(side=tk.LEFT, padx=5)
# 点名结果展示区
self.result_frame = ttk.LabelFrame(self.root, text="点名结果")
self.result_frame.pack(pady=10, fill=tk.X, padx=20)
self.result_label = ttk.Label(self.result_frame, text="等待点名...", font=("Arial", 16))
self.result_label.pack(pady=20)
# 积分记录按钮区(点名后显示)
self.record_frame = ttk.Frame(self.root)
self.record_frame.pack(pady=10, fill=tk.X, padx=20)
self.student_id_var = tk.StringVar() # 存储选中学生的学号
# 学生名单表格
self.table_frame = ttk.Frame(self.root)
self.table_frame.pack(pady=10, fill=tk.BOTH, expand=True, padx=20)
self.tree = ttk.Treeview(self.table_frame, 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.pack(fill=tk.BOTH, expand=True)
self.refresh_table()
def import_excel(self):
"""导入Excel学生名单"""
file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx")])
if not file_path:
return
success, msg = import_students_from_excel(file_path)
messagebox.showinfo("导入结果", msg)
self.refresh_table()
def export_excel(self):
"""导出积分详单"""
file_path = filedialog.asksaveasfilename(defaultextension=".xlsx", filetypes=[("Excel文件", "*.xlsx")])
if not file_path:
return
success, msg = export_score_to_excel(file_path)
messagebox.showinfo("导出结果", msg)
def random_call(self):
"""随机点名"""
student, msg = self.call_logic.random_call()
if not student:
messagebox.showwarning("警告", msg)
return
# 展示结果
self.result_label.config(text=f"随机点名结果:\n学号:{student[0]} 姓名:{student[1]} 专业:{student[2]} 总积分:{student[3]}")
self.student_id_var.set(student[0])
# 显示积分记录按钮
self.show_record_buttons("random")
def order_call(self):
"""顺序点名"""
student, msg = self.call_logic.order_call()
if not student:
messagebox.showwarning("警告", msg)
return
self.result_label.config(text=f"顺序点名结果:\n学号:{student[0]} 姓名:{student[1]} 专业:{student[2]} 总积分:{student[3]}")
self.student_id_var.set(student[0])
# 显示积分记录按钮
self.show_record_buttons("order")
def show_record_buttons(self, call_mode):
"""显示积分记录按钮(到课/回答问题)"""
# 清空原有按钮
for widget in self.record_frame.winfo_children():
widget.destroy()
# 创建按钮
ttk.Button(self.record_frame, text="到课(+1分", command=lambda: self.record_score(call_mode, True, 0)).pack(side=tk.LEFT, padx=5)
ttk.Button(self.record_frame, text="未到课0分", command=lambda: self.record_score(call_mode, False, 0)).pack(side=tk.LEFT, padx=5)
ttk.Button(self.record_frame, text="准确重复问题(+0.5分)", command=lambda: self.record_score(call_mode, True, 0.5)).pack(side=tk.LEFT, padx=5)
ttk.Button(self.record_frame, text="重复问题错误(-1分", command=lambda: self.record_score(call_mode, True, -1)).pack(side=tk.LEFT, padx=5)
ttk.Button(self.record_frame, text="回答正确(+2分", command=lambda: self.record_score(call_mode, True, 2)).pack(side=tk.LEFT, padx=5)
def record_score(self, call_mode, is_arrived, answer_score):
"""记录积分"""
student_id = self.student_id_var.get()
if not student_id:
messagebox.showwarning("警告", "无学生信息")
return
success = self.call_logic.record_call_result(student_id, call_mode, is_arrived, answer_score)
if success:
messagebox.showinfo("成功", "积分记录成功")
self.refresh_table()
else:
messagebox.showerror("错误", "积分记录失败")
def refresh_table(self):
"""刷新学生名单表格"""
# 清空表格
for item in self.tree.get_children():
self.tree.delete(item)
# 重新加载数据
db = DBHelper()
students = db.get_all_students()
db.close()
for student in students:
self.tree.insert("", tk.END, values=(student[0], student[1], student[2], student[3], student[4]))
def show_visual(self):
"""积分可视化"""
# 创建新窗口
visual_window = tk.Toplevel(self.root)
visual_window.title("积分可视化")
visual_window.geometry("800x500")
# 获取数据
db = DBHelper()
students = db.get_all_students()
db.close()
if not students:
messagebox.showwarning("警告", "无学生数据")
return
# 取TOP10学生
students_sorted = sorted(students, key=lambda x: x[3], reverse=True)[:10]
names = [s[1] for s in students_sorted]
scores = [s[3] for s in students_sorted]
call_nums = [s[4] for s in students_sorted]
# 创建图表
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
# 柱形图:积分排名
ax1.bar(names, scores, color="skyblue")
ax1.set_title("TOP10学生积分排名")
ax1.set_xlabel("姓名")
ax1.set_ylabel("总积分")
ax1.tick_params(axis='x', rotation=45)
# 折线图:随机点名次数
ax2.plot(names, call_nums, marker="o", color="orange")
ax2.set_title("TOP10学生随机点名次数")
ax2.set_xlabel("姓名")
ax2.set_ylabel("点名次数")
ax2.tick_params(axis='x', rotation=45)
# 嵌入Tkinter窗口
canvas = FigureCanvasTkAgg(fig, master=visual_window)
canvas.draw()
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
if __name__ == "__main__":
root = tk.Tk()
app = CallSystemUI(root)
root.mainloop()