|
|
|
|
@ -0,0 +1,178 @@
|
|
|
|
|
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()
|