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()