ADD file via upload

main
pz763pajf 5 months ago
parent 42c6a28b8a
commit 438d585455

@ -0,0 +1,465 @@
import tkinter as tk
from tkinter import messagebox, ttk
import random
import sqlite3
import datetime
from points_system import PointsSystem
class AttendanceSystem:
def __init__(self):
# 创建主窗口
self.root = tk.Tk()
self.root.title("课堂点名系统 - 完整版")
self.root.geometry("800x600")
# 初始化积分系统
self.points_system = PointsSystem()
# 创建标签页
self.notebook = ttk.Notebook(self.root)
self.notebook.pack(fill='both', expand=True, padx=10, pady=10)
# 点名页面
self.create_attendance_tab()
# 积分排名页面
self.create_ranking_tab()
# 关于页面
self.create_about_tab()
# 初始加载数据
self.load_students()
def create_attendance_tab(self):
"""创建点名标签页"""
self.attendance_frame = ttk.Frame(self.notebook)
self.notebook.add(self.attendance_frame, text="课堂点名")
# 标题
title_label = tk.Label(self.attendance_frame, text="课堂随机点名系统",
font=("Arial", 18, "bold"))
title_label.pack(pady=10)
# 控制按钮框架
button_frame = tk.Frame(self.attendance_frame)
button_frame.pack(pady=10)
# 导入按钮
import_button = tk.Button(button_frame, text="导入学生名单",
font=("Arial", 12),
command=self.import_students,
bg="lightgreen", width=15)
import_button.grid(row=0, column=0, padx=5)
# 随机点名按钮
self.random_button = tk.Button(button_frame, text="随机点名",
font=("Arial", 12),
command=self.random_pick,
bg="lightblue", width=15)
self.random_button.grid(row=0, column=1, padx=5)
# 权重点名按钮
self.weighted_button = tk.Button(button_frame, text="权重点名",
font=("Arial", 12),
command=self.weighted_pick,
bg="orange", width=15)
self.weighted_button.grid(row=0, column=2, padx=5)
# 结果显示
self.result_label = tk.Label(self.attendance_frame, text="请先导入学生名单",
font=("Arial", 20, "bold"),
fg="red", height=3)
self.result_label.pack(pady=10)
# 积分操作按钮
points_frame = tk.Frame(self.attendance_frame)
points_frame.pack(pady=5)
tk.Button(points_frame, text="重复问题+0.5",
command=lambda: self.add_special_points("repeat_question"),
width=12).grid(row=0, column=0, padx=2)
tk.Button(points_frame, text="回答正确+2",
command=lambda: self.add_special_points("answer_correct"),
width=12).grid(row=0, column=1, padx=2)
tk.Button(points_frame, text="回答错误-1",
command=lambda: self.add_special_points("answer_wrong"),
width=12).grid(row=0, column=2, padx=2)
# 重置积分按钮
reset_frame = tk.Frame(self.attendance_frame)
reset_frame.pack(pady=5)
tk.Button(reset_frame, text="重置所有积分",
command=self.reset_points,
font=("Arial", 10), bg="lightcoral", width=15).pack()
# 学生名单显示
list_frame = tk.Frame(self.attendance_frame)
list_frame.pack(fill='both', expand=True, padx=10, pady=10)
tk.Label(list_frame, text="学生名单", font=("Arial", 12, "bold")).pack()
# 创建表格样式的名单显示
columns = ("学号", "姓名", "专业", "积分")
self.tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=8)
for col in columns:
self.tree.heading(col, text=col)
self.tree.column(col, width=100)
self.tree.pack(fill='both', expand=True)
def create_ranking_tab(self):
"""创建积分排名标签页"""
self.ranking_frame = ttk.Frame(self.notebook)
self.notebook.add(self.ranking_frame, text="积分排名")
# 标题
tk.Label(self.ranking_frame, text="学生积分排行榜",
font=("Arial", 16, "bold")).pack(pady=10)
# 按钮框架
button_frame = tk.Frame(self.ranking_frame)
button_frame.pack(pady=5)
# 刷新按钮
tk.Button(button_frame, text="刷新排名",
command=self.show_ranking,
font=("Arial", 12), bg="lightyellow", width=10).pack(side='left', padx=5)
# 导出按钮
tk.Button(button_frame, text="导出CSV",
command=self.export_data,
font=("Arial", 12), bg="lightgreen", width=10).pack(side='left', padx=5)
# 显示图表按钮
tk.Button(button_frame, text="显示图表",
command=self.show_chart,
font=("Arial", 12), bg="lightcoral", width=10).pack(side='left', padx=5)
# 排名显示
self.ranking_text = tk.Text(self.ranking_frame, height=15, width=60,
font=("Arial", 11))
self.ranking_text.pack(padx=10, pady=10, fill='both', expand=True)
# 初始显示排名
self.show_ranking()
def create_about_tab(self):
"""创建关于页面"""
self.about_frame = ttk.Frame(self.notebook)
self.notebook.add(self.about_frame, text="关于")
# 标题
title_label = tk.Label(self.about_frame, text="课堂点名系统",
font=("Arial", 20, "bold"))
title_label.pack(pady=20)
# 版本信息
info_text = """
开发信息
版本v1.0
开发者单人开发
开发时间2024年11月
技术栈Python + Tkinter + SQLite
系统功能
学生名单导入
随机点名
权重点名积分低优先
积分管理系统
数据可视化
CSV导出
积分规则
正常点名+1
重复问题+0.5
回答正确+2
回答错误-1
权重点名积分越低越容易被点
"""
info_label = tk.Label(self.about_frame, text=info_text,
font=("Arial", 11), justify="left")
info_label.pack(pady=10)
def import_students(self):
"""导入学生名单"""
test_students = [
("031902100", "张三", "计算机科学与技术"),
("031902101", "李四", "软件工程"),
("031902102", "王五", "计算机科学与技术"),
("031902103", "赵六", "软件工程"),
("031902104", "钱七", "计算机科学与技术"),
("031902105", "孙八", "软件工程"),
("031902106", "周九", "计算机科学与技术"),
("031902107", "吴十", "软件工程")
]
# 使用积分系统的方法来操作数据库
conn = sqlite3.connect('students.db')
cursor = conn.cursor()
# 清空现有数据
cursor.execute("DELETE FROM students")
# 插入测试数据
for student_id, name, major in test_students:
cursor.execute(
"INSERT INTO students (student_id, name, major) VALUES (?, ?, ?)",
(student_id, name, major)
)
conn.commit()
conn.close()
# 更新显示
self.load_students()
messagebox.showinfo("导入成功", f"成功导入 {len(test_students)} 名学生!")
self.show_ranking()
def load_students(self):
"""加载学生名单到表格"""
# 清空表格
for item in self.tree.get_children():
self.tree.delete(item)
# 直接从数据库读取
conn = sqlite3.connect('students.db')
cursor = conn.cursor()
cursor.execute("SELECT student_id, name, major, points FROM students")
students_data = cursor.fetchall()
conn.close()
for student_id, name, major, points in students_data:
self.tree.insert("", "end", values=(student_id, name, major, points))
# 更新结果显示
if students_data:
self.result_label.config(text="准备就绪,可以开始点名")
else:
self.result_label.config(text="请先导入学生名单")
def random_pick(self):
"""完全随机点名"""
if not self.points_system.has_students():
messagebox.showwarning("提示", "请先导入学生名单!")
return
# 直接从数据库读取
conn = sqlite3.connect('students.db')
cursor = conn.cursor()
cursor.execute("SELECT student_id, name FROM students")
students = cursor.fetchall()
conn.close()
if not students:
messagebox.showwarning("提示", "请先导入学生名单!")
return
student_id, name = random.choice(students)
self.show_pick_result(student_id, name, "random")
def weighted_pick(self):
"""权重随机点名(积分低的更容易被选)"""
if not self.points_system.has_students():
messagebox.showwarning("提示", "请先导入学生名单!")
return
result = self.points_system.get_weighted_random_student()
if not result:
messagebox.showwarning("提示", "请先导入学生名单!")
return
student_id, name, points = result
self.show_pick_result(student_id, name, "weighted")
def show_pick_result(self, student_id, name, pick_type):
"""显示点名结果"""
# 更新积分
points_added = self.points_system.add_points(student_id, "attendance")
# 显示结果
result_text = f"{name} ({student_id})"
if pick_type == "weighted":
result_text += "\n[权重模式:积分低优先]"
self.result_label.config(text=result_text)
# 刷新显示
self.load_students()
self.show_ranking()
messagebox.showinfo("点名结果",
f"被点到的同学是:{name}\n"
f"学号:{student_id}\n"
f"积分+{points_added}")
def add_special_points(self, points_type):
"""添加特殊积分"""
# 获取当前选中的学生
selection = self.tree.selection()
if not selection:
messagebox.showwarning("提示", "请先点名选择一个学生!")
return
# 获取选中学生的学号
item = self.tree.item(selection[0])
student_id = item['values'][0]
# 添加积分
points_added = self.points_system.add_points(student_id, points_type)
# 刷新显示
self.load_students()
self.show_ranking()
point_names = {
"repeat_question": "重复问题",
"answer_correct": "回答正确",
"answer_wrong": "回答错误"
}
messagebox.showinfo("积分更新",
f"{point_names[points_type]}\n"
f"积分{points_added:+}")
def reset_points(self):
"""重置积分确认"""
if not self.points_system.has_students():
messagebox.showwarning("提示", "请先导入学生名单!")
return
if messagebox.askyesno("确认", "确定要重置所有学生的积分吗?"):
self.points_system.reset_all_points()
self.load_students()
self.show_ranking()
messagebox.showinfo("成功", "所有积分已重置为0")
def show_ranking(self):
"""显示积分排名"""
ranking = self.points_system.get_ranking(10)
self.ranking_text.delete(1.0, tk.END)
if not ranking:
self.ranking_text.insert(1.0, "暂无数据,请先导入学生名单")
return
# 显示排名
header = "排名 姓名 学号 积分\n"
header += "=" * 35 + "\n"
self.ranking_text.insert(1.0, header)
for i, (name, student_id, points) in enumerate(ranking, 1):
rank_text = f"{i:2d} {name:8} {student_id} {points:3d}\n"
self.ranking_text.insert(tk.END, rank_text)
def export_data(self):
"""导出数据到CSV"""
try:
filename = self.points_system.export_to_csv()
messagebox.showinfo("导出成功", f"数据已导出到:\n{filename}")
except Exception as e:
messagebox.showerror("导出失败", f"导出时出错:{str(e)}")
def show_chart(self):
"""显示积分图表"""
try:
# 尝试使用matplotlib
import matplotlib.pyplot as plt
students_data = self.points_system.get_all_students_data()
if not students_data:
messagebox.showwarning("提示", "没有数据可显示")
return
names = [student[0] for student in students_data]
points = [student[1] for student in students_data]
# 创建图表
plt.figure(figsize=(12, 6))
# 柱状图
plt.subplot(1, 2, 1)
bars = plt.bar(names, points, color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7'])
plt.title('学生积分柱状图', fontsize=14, fontweight='bold')
plt.xlabel('学生姓名')
plt.ylabel('积分')
plt.xticks(rotation=45)
# 在柱子上显示数值
for bar, point in zip(bars, points):
plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.1,
str(point), ha='center', va='bottom')
# 饼图前5名
plt.subplot(1, 2, 2)
top_names = names[:5]
top_points = points[:5]
# 如果数据不足5个使用所有数据
if len(top_names) < 5:
top_names = names
top_points = points
colors = ['#FF9999', '#66B2FF', '#99FF99', '#FFD700', '#FFB6C1']
plt.pie(top_points, labels=top_names, autopct='%1.1f%%', colors=colors[:len(top_names)])
plt.title('积分分布饼图前5名', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
except ImportError:
# 如果matplotlib不可用显示文本图表
self.show_text_chart()
def show_text_chart(self):
"""显示文本格式的图表matplotlib不可用时"""
students_data = self.points_system.get_all_students_data()
if not students_data:
messagebox.showwarning("提示", "没有数据可显示")
return
chart_window = tk.Toplevel(self.root)
chart_window.title("积分图表")
chart_window.geometry("600x400")
tk.Label(chart_window, text="积分排行榜 - 文本图表",
font=("Arial", 16, "bold")).pack(pady=10)
# 创建文本显示区域
text_widget = tk.Text(chart_window, font=("Consolas", 10), width=80, height=20)
text_widget.pack(padx=10, pady=10, fill='both', expand=True)
# 生成文本图表
chart_text = "积分柱状图(文本版):\n"
chart_text += "=" * 50 + "\n\n"
max_points = max(student[1] for student in students_data) if students_data else 1
for name, points in students_data:
# 计算柱状图长度最大20个字符
bar_length = int((points / max_points) * 20) if max_points > 0 else 0
bar = "" * bar_length
chart_text += f"{name:6} [{points:2d}分] {bar}\n"
chart_text += f"\n导出时间:{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
chart_text += f"总学生数:{len(students_data)}"
text_widget.insert(1.0, chart_text)
text_widget.config(state='disabled') # 设为只读
def run(self):
"""启动程序"""
self.root.mainloop()
if __name__ == "__main__":
print("=== 课堂点名系统启动 ===")
app = AttendanceSystem()
app.run()
Loading…
Cancel
Save