From b048bba44bb387303f9d38fefe91cc29b377d169 Mon Sep 17 00:00:00 2001 From: hnu202326010111 <1426688201@qq.com> Date: Thu, 9 Oct 2025 22:00:38 +0800 Subject: [PATCH 1/5] Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..4703173 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# student_system + -- 2.34.1 From bfaab857661413023f1004c2b3503fc79a6ab04a Mon Sep 17 00:00:00 2001 From: Amnesiac1745 <1426688201@qq.com> Date: Fri, 10 Oct 2025 20:05:35 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__pycache__/main_app.cpython-311.pyc | Bin 0 -> 38564 bytes src/__pycache__/question_bank.cpython-311.pyc | Bin 0 -> 2076 bytes .../question_generator.cpython-311.pyc | Bin 0 -> 17506 bytes src/__pycache__/quiz.cpython-311.pyc | Bin 0 -> 4055 bytes src/__pycache__/user_manager.cpython-311.pyc | Bin 0 -> 17301 bytes src/main.py | 23 + src/main_app.py | 632 ++++++++++++++++++ src/question_bank.py | 45 ++ src/question_generator.py | 390 +++++++++++ src/quiz.py | 91 +++ src/user_manager.py | 381 +++++++++++ src/users.json | 8 + 12 files changed, 1570 insertions(+) create mode 100644 src/__pycache__/main_app.cpython-311.pyc create mode 100644 src/__pycache__/question_bank.cpython-311.pyc create mode 100644 src/__pycache__/question_generator.cpython-311.pyc create mode 100644 src/__pycache__/quiz.cpython-311.pyc create mode 100644 src/__pycache__/user_manager.cpython-311.pyc create mode 100644 src/main.py create mode 100644 src/main_app.py create mode 100644 src/question_bank.py create mode 100644 src/question_generator.py create mode 100644 src/quiz.py create mode 100644 src/user_manager.py create mode 100644 src/users.json diff --git a/src/__pycache__/main_app.cpython-311.pyc b/src/__pycache__/main_app.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..badc7e1011cbabda901dba0ca663f99f2d2deaac GIT binary patch literal 38564 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 literal 0 HcmV?d00001 diff --git a/src/__pycache__/question_bank.cpython-311.pyc b/src/__pycache__/question_bank.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd940b3b21e8b2caaa0f33a662d99bedceac5b03 GIT binary patch literal 2076 zcmah~Uu;uV7(e%KZ))pC){Ce;2{Q16f z&iChcZf|VbCkm8|Q)?L&scuI)W5_1S!H4i_k52EeSdm@of}vq@RVct9t#4asOg@deRuryQ9^9Vro-G3HKF3Lp2WoWMpRK^ zYO|v4XmoI>X*y#0W1(Ypu<|l=@<>HN27+Z3Cdeu52nzBFrwR(Mii)5*RH?&WdqPyi z5gvkaKu{8K*N=HUoH&{812dN�s*eutOEV8c zOAl@@&i-ut@4D3A(()i6;1}&Hr|6||2 zN4|YUUvJ6Rn>{-3ZhP$RdgSi9@?O!sqvYOEFrOzaTME(^{2Uy7Jm8}DEfJ2ZvP@jE z98W6g811`dNK`nss&UA&l8neQ_Rv^4O~-_bCUi`BiYcM7AK)f3H#HN2>Hn1pZs=(5 z1Dv&gHI@}5@ESmp1h}L&cs)YLG*?5=+&OGEcl&?cS~_&h?EfcN=h7n~BK#t>MAT2G zqKQGvMV`)h>eZ9&`dYp>e*G-ZyJ5vzG=EEntjxTEcY=amcI`8O8pkk9O=1}yq5+T) h^LznqYkua@slxv~HE}!BQA5q}C!PE2UlgW;{{RBhSIz(c literal 0 HcmV?d00001 diff --git a/src/__pycache__/question_generator.cpython-311.pyc b/src/__pycache__/question_generator.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b40930b338f06c394a6280c077853fdb69d0c7a2 GIT binary patch literal 17506 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 literal 0 HcmV?d00001 diff --git a/src/__pycache__/quiz.cpython-311.pyc b/src/__pycache__/quiz.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fb1b35156f7b97e7b86cb220f4bd9c37d91fed7 GIT binary patch literal 4055 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 0: + tk.Button(button_frame, text="上一题", command=self.previous_question).pack(side="left", padx=10) + + tk.Button(button_frame, text="提交答案", command=self.submit_answer).pack(side="left", padx=10) + + if self.current_quiz.current_question_index < len(self.current_quiz.questions) - 1: + tk.Button(button_frame, text="下一题", command=self.next_question).pack(side="left", padx=10) + + tk.Button(self.quiz_frame, text="返回主菜单", command=self.show_main_menu_frame, width=15).pack(pady=10) + + self.show_frame(self.quiz_frame) + + def generate_options(self, correct_answer) -> List[float]: + """ + 为题目生成选项 + + @param correct_answer: 正确答案 + @return: 选项列表 + """ + # 生成4个选项,其中一个是正确答案 + options = {correct_answer} + + # 添加一些干扰项 + if isinstance(correct_answer, int): + while len(options) < 4: + offset = random.randint(-10, 10) + if offset != 0: + options.add(correct_answer + offset) + else: + # 浮点数情况 + while len(options) < 4: + offset = random.uniform(-10, 10) + if abs(offset) > 0.1: # 避免太接近正确答案 + options.add(round(correct_answer + offset, 2)) + + # 如果选项不足4个,补充一些随机数 + while len(options) < 4: + options.add(round(random.uniform(correct_answer - 20, correct_answer + 20), 2)) + + options_list = list(options) + random.shuffle(options_list) + return options_list[:4] + + def submit_answer(self): + """ + 提交答案 + """ + if not self.current_quiz: + return + + answer_str = self.answer_var.get() + if not answer_str: + messagebox.showerror("错误", "请选择一个答案") + return + + try: + answer = float(answer_str) + self.current_quiz.answer_question(answer) + + # 如果是最后一题,显示结果 + if self.current_quiz.is_finished(): + self.show_result_frame() + else: + messagebox.showinfo("提示", "答案已提交") + except ValueError: + messagebox.showerror("错误", "请选择有效答案") + + def next_question(self): + """ + 下一题 + """ + if not self.current_quiz: + return + + # 保存当前答案(如果有选择) + answer_str = self.answer_var.get() + if answer_str: + try: + answer = float(answer_str) + self.current_quiz.answer_question(answer) + except ValueError: + pass # 如果答案无效,保持为None + + if self.current_quiz.next_question(): + self.show_quiz_frame() + else: + # 已经是最后一题 + if self.current_quiz.answers[self.current_quiz.current_question_index] is not None: + # 如果最后一题已答题,显示结果 + self.show_result_frame() + else: + messagebox.showinfo("提示", "已经是最后一题") + + def previous_question(self): + """ + 上一题 + """ + if not self.current_quiz: + return + + # 保存当前答案(如果有选择) + answer_str = self.answer_var.get() + if answer_str: + try: + answer = float(answer_str) + self.current_quiz.answer_question(answer) + except ValueError: + pass # 如果答案无效,保持为None + + if self.current_quiz.previous_question(): + self.show_quiz_frame() + + def show_result_frame(self): + """ + 显示结果界面 + """ + if not self.current_quiz: + return + + # 清除之前的内容 + for widget in self.result_frame.winfo_children(): + widget.destroy() + + # 计算得分 + score = self.current_quiz.calculate_score() + + # 创建结果界面 + tk.Label(self.result_frame, text="测验结果", font=("Arial", 18, "bold")).pack(pady=20) + + tk.Label(self.result_frame, text=f"您的得分: {score:.1f}%", font=("Arial", 16)).pack(pady=10) + + # 根据得分显示评语 + if score >= 90: + comment = "优秀! 继续保持!" + elif score >= 80: + comment = "良好! 还可以做得更好!" + elif score >= 60: + comment = "及格了,需要继续努力!" + else: + comment = "需要加强练习哦!" + + tk.Label(self.result_frame, text=comment, font=("Arial", 14)).pack(pady=10) + + # 显示答题详情 + correct_count = sum(1 for q, a in zip(self.current_quiz.questions, self.current_quiz.answers) + if a is not None and abs(q.answer - a) < 1e-6) + total_count = len(self.current_quiz.questions) + tk.Label(self.result_frame, text=f"答对: {correct_count}/{total_count}", font=("Arial", 12)).pack(pady=5) + + # 按钮 + button_frame = tk.Frame(self.result_frame) + button_frame.pack(pady=20) + + level_names = {"elementary": "小学", "middle": "初中", "high": "高中"} + tk.Button(button_frame, text=f"继续{level_names[self.current_level]}题目", + command=lambda: self.show_quiz_setup_frame(self.current_level), width=20).pack(side="left", padx=10) + + tk.Button(button_frame, text="返回主菜单", command=self.show_main_menu_frame, width=15).pack(side="left", padx=10) + + self.show_frame(self.result_frame) + + def show_change_password_frame(self): + """ + 显示修改密码界面 + """ + # 清除之前的内容 + for widget in self.change_password_frame.winfo_children(): + widget.destroy() + + # 创建修改密码界面 + tk.Label(self.change_password_frame, text="修改密码", font=("Arial", 18, "bold")).pack(pady=20) + + tk.Label(self.change_password_frame, text="原密码:").pack(pady=5) + self.old_password_entry = tk.Entry(self.change_password_frame, width=30, show="*") + self.old_password_entry.pack(pady=5) + + tk.Label(self.change_password_frame, text="新密码:", fg="blue").pack(pady=(10, 5)) + tk.Label(self.change_password_frame, text="(6-10位,必须包含大小写字母和数字)", fg="gray").pack(pady=5) + self.new_password_entry = tk.Entry(self.change_password_frame, width=30, show="*") + self.new_password_entry.pack(pady=5) + + tk.Label(self.change_password_frame, text="确认新密码:").pack(pady=5) + self.confirm_new_password_entry = tk.Entry(self.change_password_frame, width=30, show="*") + self.confirm_new_password_entry.pack(pady=5) + + tk.Button(self.change_password_frame, text="修改密码", command=self.change_password, width=20).pack(pady=10) + tk.Button(self.change_password_frame, text="返回主菜单", command=self.show_main_menu_frame, width=20).pack(pady=5) + + self.show_frame(self.change_password_frame) + + def change_password(self): + """ + 修改密码 + """ + old_password = self.old_password_entry.get() + new_password = self.new_password_entry.get() + confirm_new_password = self.confirm_new_password_entry.get() + + if not old_password or not new_password or not confirm_new_password: + messagebox.showerror("错误", "请填写所有字段") + return + + if new_password != confirm_new_password: + messagebox.showerror("错误", "新密码和确认密码不一致") + return + + if self.user_manager.change_password(old_password, new_password): + messagebox.showinfo("成功", "密码修改成功") + self.show_main_menu_frame() + # 错误信息在user_manager.change_password中已经显示 + + def logout(self): + """ + 退出登录 + """ + self.user_manager.logout() + self.show_login_frame() + + def show_delete_account_frame(self): + """ + 显示注销账户界面 + """ + # 清除之前的内容 + for widget in self.delete_account_frame.winfo_children(): + widget.destroy() + + # 创建注销账户界面 + tk.Label(self.delete_account_frame, text="注销账户", font=("Arial", 18, "bold"), fg="red").pack(pady=20) + + tk.Label(self.delete_account_frame, text="警告:此操作将永久删除您的账户和所有数据!", fg="red").pack(pady=5) + tk.Label(self.delete_account_frame, text="请确认您的邮箱和密码:", fg="red").pack(pady=5) + + tk.Label(self.delete_account_frame, text="邮箱:").pack(pady=10) + self.delete_email_entry = tk.Entry(self.delete_account_frame, width=30) + self.delete_email_entry.pack(pady=5) + + tk.Label(self.delete_account_frame, text="密码:").pack(pady=5) + self.delete_password_entry = tk.Entry(self.delete_account_frame, width=30, show="*") + self.delete_password_entry.pack(pady=5) + + # 按钮框架 + button_frame = tk.Frame(self.delete_account_frame) + button_frame.pack(pady=20) + + tk.Button(button_frame, text="确认注销", command=self.confirm_delete_account, + width=15, fg="red").pack(side="left", padx=10) + tk.Button(button_frame, text="取消", command=lambda: self.root.after(100, self.show_login_frame), + width=15).pack(side="left", padx=10) + + self.show_frame(self.delete_account_frame) + + def confirm_delete_account(self): + """ + 确认并执行账户删除操作 + """ + email = self.delete_email_entry.get().strip() + password = self.delete_password_entry.get() + + if not email or not password: + messagebox.showerror("错误", "请输入邮箱和密码") + return + + # 确认操作 + if messagebox.askyesno("确认注销", "确定要注销账户吗?此操作无法撤销!"): + if self.user_manager.delete_account(email, password): + messagebox.showinfo("成功", "账户已注销,您可以使用该邮箱重新注册") + self.root.after(100, self.show_login_frame) # 延迟执行以避免界面冲突 + # 错误信息在user_manager.delete_account中已经显示 + + def delete_account(self): + """ + 注销账户(从主菜单调用的旧方法,保持兼容性) + """ + self.show_delete_account_frame() diff --git a/src/question_bank.py b/src/question_bank.py new file mode 100644 index 0000000..20925a9 --- /dev/null +++ b/src/question_bank.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +题库模块 +""" + +from typing import List, Dict +from question_generator import Expression, ElementaryQuestionGenerator, MiddleQuestionGenerator, HighQuestionGenerator + + +class QuestionBank: + """ + 题库类,管理不同难度的题目生成器 + """ + + def __init__(self): + """ + 初始化题库 + """ + self.generators = { + "elementary": ElementaryQuestionGenerator(), + "middle": MiddleQuestionGenerator(), + "high": HighQuestionGenerator() + } + + def generate_questions(self, level: str, count: int) -> List[Expression]: + """ + 生成指定数量和难度的题目 + + @param level: 题目难度(elementary, middle, high) + @param count: 题目数量 + @return: 题目列表 + """ + if level not in self.generators: + raise ValueError("无效的题目难度") + + generator = self.generators[level] + questions = [] + + for _ in range(count): + question = generator.generate_question() + questions.append(question) + + return questions \ No newline at end of file diff --git a/src/question_generator.py b/src/question_generator.py new file mode 100644 index 0000000..a9f0f04 --- /dev/null +++ b/src/question_generator.py @@ -0,0 +1,390 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +题目生成器模块 +""" + +import random +import math +import re +from abc import ABC, abstractmethod +from typing import List, Set, Optional + + +class Expression: + """ + 数学表达式类 + """ + + def __init__(self, expression_str: str, answer: float): + """ + 初始化表达式 + + @param expression_str: 表达式字符串 + @param answer: 表达式答案 + """ + self.expression_str = expression_str + self.answer = answer + + def __str__(self) -> str: + """ + 返回表达式的字符串表示 + + @return: 表达式字符串 + """ + return self.expression_str + + def __eq__(self, other) -> bool: + """ + 比较两个表达式是否相等 + + @param other: 另一个表达式 + @return: 是否相等 + """ + if isinstance(other, Expression): + return self.expression_str == other.expression_str + return False + + def __hash__(self) -> int: + """ + 计算表达式的哈希值 + + @return: 哈希值 + """ + return hash(self.expression_str) + + +class QuestionGenerator(ABC): + """ + 题目生成器抽象基类 + """ + + def __init__(self): + """ + 初始化题目生成器 + """ + self.generated_questions: Set[str] = set() + self.load_existing_questions() + + def load_existing_questions(self) -> None: + """ + 加载已存在的题目用于查重 + """ + # 在实际应用中,可以从文件或其他存储中加载已存在的题目 + pass + + @abstractmethod + def generate_question(self) -> Expression: + """ + 生成题目(抽象方法) + + @return: 数学表达式 + """ + pass + + +class ElementaryQuestionGenerator(QuestionGenerator): + """ + 小学题目生成器(+, -, *, /, 括号) + """ + + def generate_question(self) -> Expression: + """ + 生成小学题目 + + @return: 数学表达式 + """ + max_attempts = 100 + for _ in range(max_attempts): + # 随机生成操作数数量(2-5个) + num_operands = random.randint(2, 5) + operands = [random.randint(1, 50) for _ in range(num_operands)] + operators = [random.choice(['+', '-', '*']) if random.random() < 0.8 else '/' + for _ in range(num_operands - 1)] # 减少减法和除法概率以避免负数 + + # 随机添加括号 + expression_parts = [] + for i in range(num_operands): + expression_parts.append(str(operands[i])) + if i < len(operators): + expression_parts.append(operators[i]) + + # 随机添加括号 + if num_operands >= 3 and random.random() < 0.3: + # 在随机位置添加括号 + open_pos = random.randint(0, len(expression_parts) - 3) + # 确保括号内至少有两个操作数 + close_pos = min(open_pos + 2 + random.randint(1, 2) * 2, len(expression_parts)) + + # 确保括号位置是操作数位置 + if open_pos % 2 == 0 and close_pos % 2 == 0: + expression_parts.insert(open_pos, '(') + expression_parts.insert(close_pos + 1, ')') + + expression_str = ''.join(expression_parts) + + # 验证表达式是否有效 + try: + # 替换除法符号以便计算 + eval_expr = expression_str.replace('/', '/') + result = eval(eval_expr) + + # 确保结果是非负数且是合理的(整数或有限小数) + if isinstance(result, (int, float)) and result >= 0 and abs(result) < 1000: + # 格式化结果,保留合适的小数位数 + if isinstance(result, float) and result.is_integer(): + result = int(result) + + expr = Expression(expression_str, result) + # 检查是否已生成过相同题目 + if str(expr) not in self.generated_questions: + self.generated_questions.add(str(expr)) + return expr + except: + continue + + # 如果无法生成有效题目,返回默认题目 + expr = Expression("1+1", 2) + self.generated_questions.add(str(expr)) + return expr + + +class MiddleQuestionGenerator(QuestionGenerator): + """ + 初中题目生成器(包含平方或开根号) + """ + + def generate_question(self) -> Expression: + """ + 生成初中题目 + + @return: 数学表达式 + """ + max_attempts = 100 + for _ in range(max_attempts): + expression_parts = [] + + # 确保至少有一个平方或开根号 + has_square_or_sqrt = True # 确保至少有一个平方或开根号 + + # 生成操作数数量(2-5个) + num_operands = random.randint(2, 5) + + # 标记是否已添加特殊操作 + special_added = False + + for i in range(num_operands): + # 确保至少添加一个平方或开根号 + if not special_added and i == num_operands - 1: + # 如果还没添加特殊操作,强制在最后一个操作数添加 + choice = random.choice([0, 1]) # 提高开根号概率 + if choice == 0: + # 添加平方 + base = random.randint(1, 12) + expression_parts.append(f"{base}²") + else: + # 添加开根号 + square = random.randint(1, 12) + value = square * square + expression_parts.append(f"√{value}") + special_added = True + else: + rand_val = random.random() + # 修改这里的概率,增加开根号和平方的出现频率 + if not special_added and rand_val < 0.6: # 提高特殊操作概率从0.4到0.6 + # 添加特殊操作(平方或开根号) + choice = random.choice([0, 1]) if random.random() < 0.6 else 1 # 提高开根号概率 + if choice == 0: + # 添加平方 + base = random.randint(1, 12) + expression_parts.append(f"{base}²") + else: + # 添加开根号 + square = random.randint(1, 12) + value = square * square + expression_parts.append(f"√{value}") + special_added = True + else: + # 普通操作数 + expression_parts.append(str(random.randint(1, 50))) + + # 添加运算符(除了最后一个操作数) + if i < num_operands - 1: + expression_parts.append(random.choice(['+', '-', '*', '/'])) + + expression_str = ''.join(expression_parts) + + # 处理可能的语法问题 + expression_str = self.fix_expression_syntax(expression_str) + + # 计算结果 + try: + # 替换表达式中的函数以便计算 + eval_expr = expression_str.replace('²', '**2').replace('√', 'math.sqrt') + result = eval(eval_expr) + + # 确保结果是合理的 + if isinstance(result, (int, float)) and abs(result) < 1000 and not math.isnan(result): + # 格式化结果 + if isinstance(result, float) and abs(result - round(result)) < 1e-10: + result = int(round(result)) + + expr = Expression(expression_str, result) + # 检查是否已生成过相同题目 + if str(expr) not in self.generated_questions: + self.generated_questions.add(str(expr)) + return expr + except: + continue + + # 如果无法生成有效题目,返回默认题目 + expr = Expression("√4+2²", 6) + self.generated_questions.add(str(expr)) + return expr + + def fix_expression_syntax(self, expression: str) -> str: + """ + 修复表达式语法问题 + + @param expression: 原始表达式 + @return: 修复后的表达式 + """ + # 确保函数调用之间有运算符 + expression = re.sub(r'(\d)([√])', r'\1*\2', expression) + expression = re.sub(r'(²)([√])', r'\1*\2', expression) + expression = re.sub(r'(\))(\d)', r'\1*\2', expression) + expression = re.sub(r'(\d)(\()', r'\1*\2', expression) + # 修复根号表达式显示,确保根号符号正确显示 + expression = re.sub(r'√(\d+)', r'√\1', expression) + return expression + + +class HighQuestionGenerator(QuestionGenerator): + """ + 高中题目生成器(包含三角函数) + """ + + def generate_question(self) -> Expression: + """ + 生成高中题目 + + @return: 数学表达式 + """ + max_attempts = 100 + for _ in range(max_attempts): + expression_parts = [] + + # 确保至少有一个三角函数 + has_trig_function = random.random() < 0.8 + + # 生成操作数数量(2-4个) + num_operands = random.randint(2, 4) + + for i in range(num_operands): + if has_trig_function and i == 0: + # 第一个操作数有较高概率是三角函数 + if random.random() < 0.33: + angle = random.choice([0, 30, 45, 60, 90]) + expression_parts.append(f"sin({angle}°)") + elif random.random() < 0.5: + angle = random.choice([0, 30, 45, 60, 90]) + expression_parts.append(f"cos({angle}°)") + else: + angle = random.choice([0, 30, 45, 60, 90]) + expression_parts.append(f"tan({angle}°)") + else: + # 其他操作数 + rand_val = random.random() + if rand_val < 0.1 and has_trig_function: + # 添加三角函数 + angle = random.choice([0, 30, 45, 60, 90]) + expression_parts.append(f"sin({angle}°)") + elif rand_val < 0.2 and has_trig_function: + angle = random.choice([0, 30, 45, 60, 90]) + expression_parts.append(f"cos({angle}°)") + elif rand_val < 0.3 and has_trig_function: + angle = random.choice([0, 30, 45, 60, 90]) + expression_parts.append(f"tan({angle}°)") + elif rand_val < 0.55: + # 普通数字 + expression_parts.append(str(random.randint(1, 20))) + elif rand_val < 0.7: + # 平方 + base = random.randint(1, 10) + expression_parts.append(f"{base}²") + else: + # 开根号 + square = random.randint(1, 10) + value = square * square + expression_parts.append(f"√{value}") + + # 添加运算符(除了最后一个操作数) + if i < num_operands - 1: + expression_parts.append(random.choice(['+', '-', '*', '/'])) + + expression_str = ''.join(expression_parts) + + # 处理可能的语法问题 + expression_str = self.fix_expression_syntax(expression_str) + + # 计算结果 + try: + # 替换表达式中的函数以便计算 + eval_expr = expression_str.replace('²', '**2').replace('√', 'math.sqrt') + # 修复:确保正确的替换顺序,先替换角度制三角函数 + eval_expr = re.sub(r'sin\((\d+)°\)', r'math.sin(math.radians(\1))', eval_expr) + eval_expr = re.sub(r'cos\((\d+)°\)', r'math.cos(math.radians(\1))', eval_expr) + eval_expr = re.sub(r'tan\((\d+)°\)', r'math.tan(math.radians(\1))', eval_expr) + + result = eval(eval_expr) + + # 确保结果是合理的 + if isinstance(result, (int, float)) and abs(result) < 1000 and not math.isnan(result) and not math.isinf(result): + # 格式化结果 + if isinstance(result, float) and abs(result - round(result, 10)) < 1e-10: + result = int(round(result)) + # 特殊处理常见的三角函数值,使其更加准确 + elif isinstance(result, float): + # 对于常见的三角函数值进行舍入处理 + if abs(result - 0.5) < 1e-10: # sin(30°) = 0.5 + result = 0.5 + elif abs(result - 0.7071067811865476) < 1e-10: # sin(45°) = cos(45°) ≈ 0.707 + result = round(result, 10) + elif abs(result - 0.8660254037844386) < 1e-10: # sin(60°) ≈ 0.866 + result = round(result, 10) + elif abs(result - 0.5773502691896257) < 1e-10: # tan(30°) ≈ 0.577 + result = round(result, 10) + elif abs(result - 1.7320508075688772) < 1e-10: # tan(60°) ≈ 1.732 + result = round(result, 10) + elif abs(result - 1.0) < 1e-10: # tan(45°) = 1, sin(90°) = 1 + result = 1.0 + + expr = Expression(expression_str, result) + # 检查是否已生成过相同题目 + if str(expr) not in self.generated_questions: + self.generated_questions.add(str(expr)) + return expr + except: + continue + + # 如果无法生成有效题目,返回默认题目 + expr = Expression("sin(30°)", 0.5) + self.generated_questions.add(str(expr)) + return expr + + def fix_expression_syntax(self, expression: str) -> str: + """ + 修复表达式语法问题 + + @param expression: 原始表达式 + @return: 修复后的表达式 + """ + # 确保函数调用之间有运算符 + expression = re.sub(r'(\d)([sincostan√])', r'\1*\2', expression) + expression = re.sub(r'(²)([sincostan√])', r'\1*\2', expression) + expression = re.sub(r'(sqrt\(\d+\))(\d)', r'\1*\2', expression) + expression = re.sub(r'(\))(\d)', r'\1*\2', expression) + expression = re.sub(r'(\d)(\()', r'\1*\2', expression) + expression = re.sub(r'°\)(\d)', r'°)*\1', expression) + return expression \ No newline at end of file diff --git a/src/quiz.py b/src/quiz.py new file mode 100644 index 0000000..80ae9a1 --- /dev/null +++ b/src/quiz.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +测验模块 +""" + +from typing import List, Optional +from question_generator import Expression + + +class Quiz: + """ + 测验类,管理一次答题过程 + """ + + def __init__(self, questions: List[Expression]): + """ + 初始化测验 + + @param questions: 题目列表 + """ + self.questions = questions + self.answers: List[Optional[float]] = [None] * len(questions) + self.current_question_index = 0 + self.score = 0 + + def answer_question(self, answer: float) -> None: + """ + 回答当前题目 + + @param answer: 用户答案 + """ + if 0 <= self.current_question_index < len(self.questions): + self.answers[self.current_question_index] = answer + + def next_question(self) -> bool: + """ + 跳转到下一题 + + @return: 是否有下一题 + """ + if self.current_question_index < len(self.questions) - 1: + self.current_question_index += 1 + return True + return False + + def previous_question(self) -> bool: + """ + 返回上一题 + + @return: 是否有上一题 + """ + if self.current_question_index > 0: + self.current_question_index -= 1 + return True + return False + + def get_current_question(self) -> Optional[Expression]: + """ + 获取当前题目 + + @return: 当前题目 + """ + if 0 <= self.current_question_index < len(self.questions): + return self.questions[self.current_question_index] + return None + + def calculate_score(self) -> float: + """ + 计算得分 + + @return: 得分(百分比) + """ + correct_count = 0 + for i, (question, answer) in enumerate(zip(self.questions, self.answers)): + if answer is not None: + # 允许一定的浮点数误差 + if abs(question.answer - answer) < 1e-6: + correct_count += 1 + + self.score = (correct_count / len(self.questions)) * 100 if self.questions else 0 + return self.score + + def is_finished(self) -> bool: + """ + 检查测验是否已完成 + + @return: 是否已完成 + """ + return self.current_question_index == len(self.questions) - 1 and self.answers[self.current_question_index] is not None \ No newline at end of file diff --git a/src/user_manager.py b/src/user_manager.py new file mode 100644 index 0000000..a214d7e --- /dev/null +++ b/src/user_manager.py @@ -0,0 +1,381 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +用户管理模块 +""" + +import json +import os +import re +import random +import hashlib +import smtplib +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart +from typing import Dict, Optional +from tkinter import messagebox + + +class User: + """ + 用户类,存储用户信息 + """ + + def __init__(self, email: str, username: str = "", password_hash: str = "", registration_code: str = ""): + """ + 初始化用户对象 + + @param email: 用户邮箱 + @param username: 用户名 + @param password_hash: 密码哈希值 + @param registration_code: 注册码 + """ + self.email = email + self.username = username + self.password_hash = password_hash + self.registration_code = registration_code + self.is_registered = bool(password_hash) + + +class UserManager: + """ + 用户管理类,负责处理用户注册、登录和密码管理 + """ + + def __init__(self, data_file: str = "users.json"): + """ + 初始化用户管理器 + + @param data_file: 存储用户数据的文件路径 + """ + self.data_file = data_file + self.users: Dict[str, User] = {} + self.current_user: Optional[User] = None + # 邮件配置 + self.smtp_server = "smtp.qq.com" # 可根据需要修改SMTP服务器 + self.smtp_port = 465 + self.sender_email = "3257534544@qq.com" # 需要替换为实际邮箱 + self.sender_password = "pmfyurbkwfpkdbed" # 需要替换为实际应用密码 + self.load_users() + + def load_users(self) -> None: + """ + 从文件加载用户数据 + """ + if os.path.exists(self.data_file): + try: + with open(self.data_file, 'r', encoding='utf-8') as f: + data = json.load(f) + for email, user_data in data.items(): + user = User( + email=email, + username=user_data.get('username', ''), + password_hash=user_data.get('password_hash', ''), + registration_code=user_data.get('registration_code', '') + ) + user.is_registered = user_data.get('is_registered', False) + self.users[email] = user + except (json.JSONDecodeError, FileNotFoundError): + self.users = {} + + def save_users(self) -> None: + """ + 保存用户数据到文件 + """ + data = {} + for email, user in self.users.items(): + data[email] = { + 'username': user.username, + 'password_hash': user.password_hash, + 'registration_code': user.registration_code, + 'is_registered': user.is_registered + } + + try: + with open(self.data_file, 'w', encoding='utf-8') as f: + json.dump(data, f, ensure_ascii=False, indent=2) + except IOError as e: + messagebox.showerror("错误", f"保存用户数据失败: {str(e)}") + + def is_valid_email(self, email: str) -> bool: + """ + 验证邮箱格式 + + @param email: 邮箱地址 + @return: 邮箱格式是否有效 + """ + pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' + return re.match(pattern, email) is not None + + def is_valid_username(self, username: str) -> bool: + """ + 验证用户名是否符合要求 + + @param username: 用户名 + @return: 用户名是否有效 + """ + # 用户名长度应在3-20个字符之间 + if not (3 <= len(username) <= 20): + return False + # 用户名只能包含字母、数字和下划线 + pattern = r'^[a-zA-Z0-9_]+$' + return re.match(pattern, username) is not None + + def generate_registration_code(self) -> str: + """ + 生成注册码 + + @return: 6位数字注册码 + """ + return str(random.randint(100000, 999999)) + + def hash_password(self, password: str) -> str: + """ + 对密码进行哈希处理 + + @param password: 原始密码 + @return: 哈希后的密码 + """ + return hashlib.sha256(password.encode('utf-8')).hexdigest() + + def is_valid_password(self, password: str) -> bool: + """ + 验证密码是否符合要求 + + @param password: 密码 + @return: 密码是否有效 + """ + if not (6 <= len(password) <= 10): + return False + + has_lower = any(c.islower() for c in password) + has_upper = any(c.isupper() for c in password) + has_digit = any(c.isdigit() for c in password) + + return has_lower and has_upper and has_digit + + def register_user(self, email: str, username: str) -> bool: + """ + 注册新用户(发送注册码) + + @param email: 用户邮箱 + @param username: 用户名 + @return: 是否成功发送注册码 + """ + if not self.is_valid_email(email): + messagebox.showerror("错误", "邮箱格式不正确") + return False + + if not self.is_valid_username(username): + messagebox.showerror("错误", "用户名应为3-20位,只能包含字母、数字和下划线") + return False + + # 检查邮箱是否已被注册 + if email in self.users and self.users[email].is_registered: + messagebox.showerror("错误", "该邮箱已注册") + return False + + # 检查用户名是否已被使用 + for user in self.users.values(): + if user.username == username and user.is_registered: + messagebox.showerror("错误", "该用户名已存在") + return False + + registration_code = self.generate_registration_code() + user = User(email=email, username=username, registration_code=registration_code) + self.users[email] = user + + # 尝试发送邮件 + if self.send_registration_code_via_email(email, registration_code): + self.save_users() + messagebox.showinfo("成功", "注册码已发送到您的邮箱,请查收") + return True + else: + messagebox.showerror("错误", "无法发送注册码到邮箱,请检查网络连接或邮箱地址") + # 即使邮件发送失败,也保存用户信息以便重试 + self.save_users() + return False + + def send_registration_code_via_email(self, email: str, code: str) -> bool: + """ + 通过电子邮件发送注册码 + + @param email: 接收邮箱 + @param code: 注册码 + @return: 是否发送成功 + """ + try: + # 创建邮件内容 + message = MIMEMultipart() + message["From"] = self.sender_email + message["To"] = email + message["Subject"] = "数学练习系统注册码" + + body = f""" + 您好! + + 欢迎使用数学练习系统! + + 您的注册码是: {code} + + 请在注册界面输入此注册码完成注册。 + + 如果您没有请求此注册码,请忽略此邮件。 + + 祝学习愉快! + 数学练习系统团队 + """ + + message.attach(MIMEText(body, "plain", "utf-8")) + + # 使用SMTP_SSL连接QQ邮箱服务器 + server = smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) + server.login(self.sender_email, self.sender_password) + text = message.as_string() + server.sendmail(self.sender_email, email, text) + server.quit() + + return True + except Exception as e: + print(f"邮件发送失败: {str(e)}") + return False + + def verify_registration_code(self, email: str, code: str) -> bool: + """ + 验证注册码 + + @param email: 用户邮箱 + @param code: 用户输入的注册码 + @return: 注册码是否正确 + """ + if email not in self.users: + messagebox.showerror("错误", "请先获取注册码") + return False + + user = self.users[email] + if user.registration_code != code: + messagebox.showerror("错误", "注册码不正确") + return False + + return True + + def set_password(self, email: str, password: str) -> bool: + """ + 设置用户密码 + + @param email: 用户邮箱 + @param password: 用户密码 + @return: 是否设置成功 + """ + if not self.is_valid_password(password): + messagebox.showerror("错误", "密码必须为6-10位,且包含大小写字母和数字") + return False + + if email not in self.users: + messagebox.showerror("错误", "用户不存在") + return False + + user = self.users[email] + user.password_hash = self.hash_password(password) + user.is_registered = True + self.save_users() + return True + + def login(self, username: str, password: str) -> bool: + """ + 用户登录 + + @param username: 用户名 + @param password: 用户密码 + @return: 是否登录成功 + """ + # 根据用户名查找用户 + user = None + for u in self.users.values(): + if u.username == username: + user = u + break + + if user is None: + messagebox.showerror("错误", "用户不存在") + return False + + if not user.is_registered: + messagebox.showerror("错误", "请先完成注册") + return False + + if user.password_hash != self.hash_password(password): + messagebox.showerror("错误", "密码不正确") + return False + + self.current_user = user + return True + + def change_password(self, old_password: str, new_password: str) -> bool: + """ + 修改用户密码 + + @param old_password: 原密码 + @param new_password: 新密码 + @return: 是否修改成功 + """ + if not self.current_user: + messagebox.showerror("错误", "用户未登录") + return False + + if self.current_user.password_hash != self.hash_password(old_password): + messagebox.showerror("错误", "原密码不正确") + return False + + if not self.is_valid_password(new_password): + messagebox.showerror("错误", "新密码必须为6-10位,且包含大小写字母和数字") + return False + + self.current_user.password_hash = self.hash_password(new_password) + self.save_users() + return True + + def logout(self) -> None: + """ + 用户登出 + """ + self.current_user = None + + def delete_account(self, email: str, password: str) -> bool: + """ + 注销账户 + + @param email: 用户邮箱 + @param password: 用户密码 + @return: 是否注销成功 + """ + if not self.is_valid_email(email): + messagebox.showerror("错误", "邮箱格式不正确") + return False + + # 检查用户是否存在 + if email not in self.users: + messagebox.showerror("错误", "该邮箱未注册") + return False + + user = self.users[email] + + # 检查用户是否已完成注册 + if not user.is_registered: + messagebox.showerror("错误", "该账户未完成注册") + return False + + # 验证密码 + if user.password_hash != self.hash_password(password): + messagebox.showerror("错误", "密码不正确") + return False + + # 删除用户 + del self.users[email] + # 如果当前用户是被删除的用户,则登出 + if self.current_user and self.current_user.email == email: + self.current_user = None + + self.save_users() + return True diff --git a/src/users.json b/src/users.json new file mode 100644 index 0000000..ffcea7e --- /dev/null +++ b/src/users.json @@ -0,0 +1,8 @@ +{ + "1426688201@qq.com": { + "username": "111", + "password_hash": "c4318372f98f4c46ed3a32c16ee4d7a76c832886d887631c0294b3314f34edf1", + "registration_code": "825880", + "is_registered": true + } +} \ No newline at end of file -- 2.34.1 From 39f8b9ad59e0110e045b57091eb6e9995e6c2e98 Mon Sep 17 00:00:00 2001 From: Amnesiac1745 <1426688201@qq.com> Date: Fri, 10 Oct 2025 21:07:13 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__pycache__/main_app.cpython-311.pyc | Bin 38564 -> 38384 bytes .../question_generator.cpython-311.pyc | Bin 17506 -> 18700 bytes src/main_app.py | 9 ++--- src/question_generator.py | 32 ++++++++++++++---- src/users.json | 9 +---- 5 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/__pycache__/main_app.cpython-311.pyc b/src/__pycache__/main_app.cpython-311.pyc index badc7e1011cbabda901dba0ca663f99f2d2deaac..f8332c659d2b957abb39e7b3b6a993853e92eca2 100644 GIT binary patch delta 889 zcmZ`&U2IH26u#%~-rc>sd)-_8zg@Z|+wKasXthn%Uz(EIG^*10DN%HTMCrC@WGfV{ z6l?i3h?h!*CR!@_xKz1nhJTc(J5_=)tMU(vs4$29h zK1&9-fQJ`(F8Kr1J8QP^3V)l-Jp=Eb2jEj`Qi8$`%fy^tE*3T1%3a~q= z3(eHbzB?}%se_$$RT=1d=ZM=(Pw~n7g6^`%+3$?>oZZQ-G0{(CEci@g91}64=pGRp zE}Bd9kvSI4Fq54vE0HD2WaTRs+Ng+yEBnmUz^XTv8wptBcd*Bsj~VD1TjL)T=>f~$ zzRyhGSl8|{A_MzcHY5W{+8tw zgx*@CajESSQg2P@PDS<<7gn|=qM$ep-gI5kD+Es(8*6ouB@mOh%rgm&wmPGW`#8~- zMox6K&6Ah%Bh;60=zP^CO1%w{JRe3*REY-&VLb;IGb`~vIUjK$50#)WluNTv7n;d$ zF$pgv4YfHI(^dngvS2tg6z{PGR4zG9Lf(pV{$G@(W9Y5Y+$x delta 1128 zcmZ`&ZA@EL7(VZ9Z_91(-My}bF+Rtz#m)jFDljN8GGb&iM5Iol>$K1oX)u#pj5@2d z;hQm-eVJ_Whh4VJ=wO+i5RD`5ho3);X|>7N;@2-TKQe1(;u4K|?rq?gzIo5{p7);j zJm)!Y&dpxn&aH6L8SCD~;`uX8*BP3{9|2YG^g=PIE^t;ey4nCr<4L)avKd5cE z3xK12Y8k*)8jqL+$fMIyx5l&@{{o;1$H%tFN7C%t!asL72g@ zsh~JbLJ-(Ko)@q$7ooq&iDlDv;|xiPtjl&RZ!YDFdx)!q4oy3tyq~ps)0j$PW8+o_ zgOi}^t#~K^GbGH!3>yy+;l7vk7l2fT`D z=P`^`HR7@3E_fJMkKbq5Gs2!=wH;x;Kn&|Io*hAwR@SisF-pRsmTHDK?<_P04D8wh z9++P*R26nTm!xiGLG`f$^{@sutY4@yYyD8~0DNr033iMxRGIoTeb@2g!qHLBx@^nH zJFd#LU&^(Y_)E{H<-HksZ$ko0C8 zO=+b$qco>BJZm-xo-EOTV+=Zt-mQME{~N}R&;5@zm^|?@j9}=MpP01n#N)Ea@5z!) zD4~}V^I&B>GM@wVE;^wYUtD|!O7WM)LfDD?$>p}70In?gZ~RUF{`>!|>gxUlE1OE4 diff --git a/src/__pycache__/question_generator.cpython-311.pyc b/src/__pycache__/question_generator.cpython-311.pyc index b40930b338f06c394a6280c077853fdb69d0c7a2..d0555e05bb0b0a8c1520ec70a7704fc886d3f4ee 100644 GIT binary patch delta 2575 zcmb`IZA?>F7{~9q*LH%m^p=<2wioFOE4@;X0z(R-qu^$Q;vC{cV5M@4pa^(cre<}x z88haHAv=39do@eu%=U$?OSb4fFio7BS!}yI>&T2uKWrvjR-#MxWy#LD)U31`zj)LB z&biNdp68x(&+|X`#w3_}4X8g>sj?}2eZt%ttZe>F{aX%Lhvp4-hVDHVHmm9wb`heYxDm5Id7B#16iux4) z7cZPj-_0(y9a0C)cgHCzqz>hzjpv4P`)NRRP}B6(*H#KpU8MKT0j7)E4^m@AEz)WG zY)efdotdtK67s@_v%Y>#2t4&6n=P;GXp*Z7Y&Na4hIuvC~B(@|$IkWT&lrwy@@C{(g zSLx^ZRdK#5##hZ$f5k-kwGzKJ&bP$)mf4Pr)+paD@$Kk1XJK;KaunuXX_1Usz`Oq>_F7+k?fums;kA02$%dJ z=lTM)LvOcT<18m1J8h0~4vBM2s;=hpk>z|qg_nQ9*izA|rZ1`K);x4Vzr(stQFO9C zLE+}UKm~^H>3<$UzZwMK53ewuTBe)^P#H9$36lX`F3tkC(a*)D;5q`P+qgC+%$tD< zjX8B_*vz66PMxgNQo77B8NuJ&stIw z5@UKoQI|R;*8``NnIWXbRi#Hin!np97Zu!umjK-atr+9WF>Xx0T~k(d?(E+hKZy2|z;G1ANKSI8bf zD%lg(l?XJ*M@_a3iUo?4>8rMn9$uNYm)d(snbx1)7v-E1=bTi{^R_r&5#uXP2WDEL zyi4L;alR?WH_cjSgHgUs;@cw2^*AVEt(oyfxjKofTTxXLGz)|#$P(=fPE(`A#j}l7>frdbK@RJ_FdlWLdi_E=XQ6QvEP#JuIMqTbo zT=cx59X(mLm!@;DPD&X?@9yw?*;yq+LxA3|Dg}R{3snNRiV~RT!-nd!G!E*k-Hb+^ z9Ag&+bj#K)$)qOBG@E zk#3qSwb&-Bve~8sc)1Pu$hh~X#V0G$7rx?J!2ml}WGTvO+{qZ(^6*IG8St<~+2|#L zY?xu9T#dxl5J8s4`PDIg^$at!HOkjZe0`jE$N0r>6z`FE&;Nq#k8<@ASHDD%myDYV zJ8S8mYU$1f1w0sjd3^;aejcxTJRif}BMSCzyczwYfuL^$ADz7d3?b?9O64K)zFt7t zO}uV`bYH><&=@AuT^%bK#L6Sm8N}36_YkWWpDs(cRO{& zUC_W{5-K0ra?Kn@C*3Y3IT7GdblqK){W|XA{h|UrNZEX5q8R|3^4^SCsu$#;JE;zB r_)GwX1a)slR<2%}H;zNlXUIWK)DMUywU@GCgFAe8T+)pVo_! delta 1624 zcmai!e@t6d6vyvh9z?SrYGR?B63F;pjr!N`@>gN7%KU^Z3#m)VrSxg+sVoD^MEHUw(wPVaQ^x#9=8;%w#T;`egkx*UMltOcUpTnL|8C-pZS?o%1~fpJ^V zcGH+Wkr6fUZNITMI$Mcwl?bhpv_;WDz1IPH`$l)IwC=6iB;mt<0=n&2h2M3&Mhf+) z9rC+t*05_aJ1o@Lz~R!TEj<7Zo(Mb5k)htN`1Is6%vc+*;ckj&?p8|P7Gt|GV2BR&boYe^ zy26o2PlWA(8%5siy|}Iu(ZvxpbHLG{44aBQX=l(gCStqNS}FFcRXtv;2-FPcN<&hT zU~@!edRcSsD#4tk=3LHh%HLciT&of`*Qu|1{-%}@+*06hIvJ{hq417)p}sg5?uFJ1 ze-gO-@M#BnSs}uN&@#hlA>JVHWpkc$7rozM9Ft@Phv0(u5gLXA_AOBFJ1E>shmU*; z^{YSmW_1!}mtqe1&}qwzxbACziZT_>KdR6(FwZ#&=c^PoP<2A!t^cmNO);igFC{#w z)=P=n_nB}`M4X@#P| z7qkRXK5UP+I6Sz2pohiKF^ph{C<6a{*aLFH?!mEOV_GKVKW|3Tq|~WNsZ;$cm?6?4 zE~OIiG(0QjnhVwA4Ks9^nvOOGtxIb9*8YUt9uTevgq>7kBdXHmqx2B$4&~9q@O;Rx zdjU^y1U?AKmKV`{nd9GLgP%iwo@{D9mOqLa4@hhbeTX#pqB-AnAf+%IlVk;Vi}1DA zZ^3-U#2mO*yVP2# str: """ @@ -364,14 +375,21 @@ class HighQuestionGenerator(QuestionGenerator): # 检查是否已生成过相同题目 if str(expr) not in self.generated_questions: self.generated_questions.add(str(expr)) - return expr + # 将表达式中的乘号和除号替换为更易读的形式 + readable_expr_str = expression_str.replace('*', '×').replace('/', '÷') + readable_expr = Expression(readable_expr_str, result) + return readable_expr except: continue # 如果无法生成有效题目,返回默认题目 expr = Expression("sin(30°)", 0.5) self.generated_questions.add(str(expr)) - return expr + readable_expr = Expression("sin(30°)", 0.5) + # 将表达式中的乘号和除号替换为更易读的形式 + readable_expr_str = "sin(30°)".replace('*', '×').replace('/', '÷') + readable_expr = Expression(readable_expr_str, 0.5) + return readable_expr def fix_expression_syntax(self, expression: str) -> str: """ diff --git a/src/users.json b/src/users.json index ffcea7e..9e26dfe 100644 --- a/src/users.json +++ b/src/users.json @@ -1,8 +1 @@ -{ - "1426688201@qq.com": { - "username": "111", - "password_hash": "c4318372f98f4c46ed3a32c16ee4d7a76c832886d887631c0294b3314f34edf1", - "registration_code": "825880", - "is_registered": true - } -} \ No newline at end of file +{} \ No newline at end of file -- 2.34.1 From 3ce07fcfce54bf4a96d9b1c4f0283634f37525e7 Mon Sep 17 00:00:00 2001 From: Amnesiac1745 <1426688201@qq.com> Date: Sat, 11 Oct 2025 19:01:56 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E7=BE=8E=E5=8C=96=E7=95=8C=E9=9D=A2?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__pycache__/main_app.cpython-311.pyc | Bin 38384 -> 49470 bytes src/main_app.py | 356 ++++++++++++++++------- src/users.json | 9 +- 3 files changed, 257 insertions(+), 108 deletions(-) diff --git a/src/__pycache__/main_app.cpython-311.pyc b/src/__pycache__/main_app.cpython-311.pyc index f8332c659d2b957abb39e7b3b6a993853e92eca2..e3f5ecfef21a85b6e050a7a848d52bacf3006e9f 100644 GIT binary patch literal 49470 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

tevF9`U5}r?aQDMOrK*9azpp$k{uFLc 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 EUnRAAI{*Lx diff --git a/src/main_app.py b/src/main_app.py index 1146541..99591b2 100644 --- a/src/main_app.py +++ b/src/main_app.py @@ -29,23 +29,27 @@ class MathQuizApp: """ self.root = root self.root.title("数学练习系统") - self.root.geometry("600x500") - self.root.resizable(False, False) + self.root.geometry("700x600") + self.root.resizable(True, True) + self.root.configure(bg="#f0f0f0") # 初始化系统组件 self.user_manager = UserManager() self.question_bank = QuestionBank() + # 设置样式 + self.setup_styles() + # 创建不同的界面框架 - self.login_frame = tk.Frame(self.root) - self.register_frame = tk.Frame(self.root) - self.set_password_frame = tk.Frame(self.root) - self.main_menu_frame = tk.Frame(self.root) - self.quiz_setup_frame = tk.Frame(self.root) - self.quiz_frame = tk.Frame(self.root) - self.result_frame = tk.Frame(self.root) - self.change_password_frame = tk.Frame(self.root) - self.delete_account_frame = tk.Frame(self.root) # 添加删除账户框架 + self.login_frame = tk.Frame(self.root, bg="#f0f0f0") + self.register_frame = tk.Frame(self.root, bg="#f0f0f0") + self.set_password_frame = tk.Frame(self.root, bg="#f0f0f0") + self.main_menu_frame = tk.Frame(self.root, bg="#f0f0f0") + self.quiz_setup_frame = tk.Frame(self.root, bg="#f0f0f0") + self.quiz_frame = tk.Frame(self.root, bg="#f0f0f0") + self.result_frame = tk.Frame(self.root, bg="#f0f0f0") + self.change_password_frame = tk.Frame(self.root, bg="#f0f0f0") + self.delete_account_frame = tk.Frame(self.root, bg="#f0f0f0") # 添加删除账户框架 # 初始化应用状态 self.current_quiz: Optional[Quiz] = None @@ -56,20 +60,39 @@ class MathQuizApp: self.create_widgets() self.show_login_frame() + def setup_styles(self): + """ + 设置界面样式 + """ + self.title_font = ("Arial", 20, "bold") + self.header_font = ("Arial", 16, "bold") + self.normal_font = ("Arial", 12) + self.button_font = ("Arial", 11, "bold") + + # 定义颜色方案 + self.primary_color = "#4a6fa5" + self.secondary_color = "#6b8cbc" + self.accent_color = "#ff6b6b" + self.success_color = "#4caf50" + self.warning_color = "#ffc107" + self.danger_color = "#f44336" + self.light_bg = "#f8f9fa" + self.dark_text = "#333333" + def create_widgets(self): """ 创建界面组件 """ # 创建不同的界面框架 - self.login_frame = tk.Frame(self.root) - self.register_frame = tk.Frame(self.root) - self.set_password_frame = tk.Frame(self.root) - self.main_menu_frame = tk.Frame(self.root) - self.quiz_setup_frame = tk.Frame(self.root) - self.quiz_frame = tk.Frame(self.root) - self.result_frame = tk.Frame(self.root) - self.change_password_frame = tk.Frame(self.root) - self.delete_account_frame = tk.Frame(self.root) # 添加删除账户框架 + self.login_frame = tk.Frame(self.root, bg="#f0f0f0") + self.register_frame = tk.Frame(self.root, bg="#f0f0f0") + self.set_password_frame = tk.Frame(self.root, bg="#f0f0f0") + self.main_menu_frame = tk.Frame(self.root, bg="#f0f0f0") + self.quiz_setup_frame = tk.Frame(self.root, bg="#f0f0f0") + self.quiz_frame = tk.Frame(self.root, bg="#f0f0f0") + self.result_frame = tk.Frame(self.root, bg="#f0f0f0") + self.change_password_frame = tk.Frame(self.root, bg="#f0f0f0") + self.delete_account_frame = tk.Frame(self.root, bg="#f0f0f0") # 添加删除账户框架 def show_frame(self, frame: tk.Frame): """ @@ -95,19 +118,33 @@ class MathQuizApp: widget.destroy() # 创建登录界面 - tk.Label(self.login_frame, text="用户登录", font=("Arial", 18, "bold")).pack(pady=20) + main_frame = tk.Frame(self.login_frame, bg="#ffffff", relief=tk.RAISED, bd=2) + main_frame.pack(pady=50, padx=50, fill="both", expand=True) + + title_frame = tk.Frame(main_frame, bg=self.primary_color) + title_frame.pack(fill="x", pady=(0, 30)) + tk.Label(title_frame, text="用户登录", font=self.title_font, bg=self.primary_color, fg="white").pack(pady=20) - tk.Label(self.login_frame, text="用户名:").pack(pady=5) - self.login_email_entry = tk.Entry(self.login_frame, width=30) + form_frame = tk.Frame(main_frame, bg="#ffffff") + form_frame.pack(pady=10) + + tk.Label(form_frame, text="用户名:", font=self.normal_font, bg="#ffffff").pack(pady=5, anchor="w") + self.login_email_entry = tk.Entry(form_frame, width=30, font=self.normal_font, relief=tk.FLAT, bd=5, bg="#f0f0f0") self.login_email_entry.pack(pady=5) - tk.Label(self.login_frame, text="密码:").pack(pady=5) - self.login_password_entry = tk.Entry(self.login_frame, width=30, show="*") + tk.Label(form_frame, text="密码:", font=self.normal_font, bg="#ffffff").pack(pady=5, anchor="w") + self.login_password_entry = tk.Entry(form_frame, width=30, font=self.normal_font, show="*", relief=tk.FLAT, bd=5, bg="#f0f0f0") self.login_password_entry.pack(pady=5) - tk.Button(self.login_frame, text="登录", command=self.login, width=20).pack(pady=10) - tk.Button(self.login_frame, text="注册新用户", command=self.show_register_frame, width=20).pack(pady=5) - tk.Button(self.login_frame, text="注销账户", command=self.show_delete_account_frame, width=20).pack(pady=5) + button_frame = tk.Frame(main_frame, bg="#ffffff") + button_frame.pack(pady=20) + + tk.Button(button_frame, text="登录", command=self.login, width=20, bg=self.primary_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=10) + tk.Button(button_frame, text="注册新用户", command=self.show_register_frame, width=20, bg=self.secondary_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=5) + tk.Button(button_frame, text="注销账户", command=self.show_delete_account_frame, width=20, bg=self.danger_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=5) self.show_frame(self.login_frame) @@ -136,24 +173,38 @@ class MathQuizApp: widget.destroy() # 创建注册界面 - tk.Label(self.register_frame, text="用户注册", font=("Arial", 18, "bold")).pack(pady=20) + main_frame = tk.Frame(self.register_frame, bg="#ffffff", relief=tk.RAISED, bd=2) + main_frame.pack(pady=30, padx=50, fill="both", expand=True) + + title_frame = tk.Frame(main_frame, bg=self.primary_color) + title_frame.pack(fill="x", pady=(0, 20)) + tk.Label(title_frame, text="用户注册", font=self.title_font, bg=self.primary_color, fg="white").pack(pady=20) + + form_frame = tk.Frame(main_frame, bg="#ffffff") + form_frame.pack(pady=10) - tk.Label(self.register_frame, text="用户名:").pack(pady=5) - self.register_username_entry = tk.Entry(self.register_frame, width=30) + tk.Label(form_frame, text="用户名:", font=self.normal_font, bg="#ffffff").pack(pady=5, anchor="w") + self.register_username_entry = tk.Entry(form_frame, width=30, font=self.normal_font, relief=tk.FLAT, bd=5, bg="#f0f0f0") self.register_username_entry.pack(pady=5) - tk.Label(self.register_frame, text="邮箱:").pack(pady=5) - self.register_email_entry = tk.Entry(self.register_frame, width=30) + tk.Label(form_frame, text="邮箱:", font=self.normal_font, bg="#ffffff").pack(pady=5, anchor="w") + self.register_email_entry = tk.Entry(form_frame, width=30, font=self.normal_font, relief=tk.FLAT, bd=5, bg="#f0f0f0") self.register_email_entry.pack(pady=5) - tk.Button(self.register_frame, text="获取注册码", command=self.send_registration_code, width=20).pack(pady=10) + tk.Button(form_frame, text="获取注册码", command=self.send_registration_code, width=20, bg=self.primary_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=10) - tk.Label(self.register_frame, text="注册码:").pack(pady=5) - self.registration_code_entry = tk.Entry(self.register_frame, width=30) + tk.Label(form_frame, text="注册码:", font=self.normal_font, bg="#ffffff").pack(pady=5, anchor="w") + self.registration_code_entry = tk.Entry(form_frame, width=30, font=self.normal_font, relief=tk.FLAT, bd=5, bg="#f0f0f0") self.registration_code_entry.pack(pady=5) - tk.Button(self.register_frame, text="验证注册码", command=self.verify_registration_code, width=20).pack(pady=10) - tk.Button(self.register_frame, text="返回登录", command=self.show_login_frame, width=20).pack(pady=5) + button_frame = tk.Frame(main_frame, bg="#ffffff") + button_frame.pack(pady=20) + + tk.Button(button_frame, text="验证注册码", command=self.verify_registration_code, width=20, bg=self.success_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=10) + tk.Button(button_frame, text="返回登录", command=self.show_login_frame, width=20, bg="#cccccc", fg=self.dark_text, + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=5) self.show_frame(self.register_frame) @@ -197,21 +248,34 @@ class MathQuizApp: widget.destroy() # 创建设置密码界面 - tk.Label(self.set_password_frame, text="设置密码", font=("Arial", 18, "bold")).pack(pady=20) + main_frame = tk.Frame(self.set_password_frame, bg="#ffffff", relief=tk.RAISED, bd=2) + main_frame.pack(pady=30, padx=50, fill="both", expand=True) - tk.Label(self.set_password_frame, text="邮箱: " + email).pack(pady=5) + title_frame = tk.Frame(main_frame, bg=self.primary_color) + title_frame.pack(fill="x", pady=(0, 20)) + tk.Label(title_frame, text="设置密码", font=self.title_font, bg=self.primary_color, fg="white").pack(pady=20) - tk.Label(self.set_password_frame, text="密码:", fg="blue").pack(pady=(10, 5)) - tk.Label(self.set_password_frame, text="(6-10位,必须包含大小写字母和数字)", fg="gray").pack(pady=5) - self.set_password_entry = tk.Entry(self.set_password_frame, width=30, show="*") + form_frame = tk.Frame(main_frame, bg="#ffffff") + form_frame.pack(pady=10) + + tk.Label(form_frame, text="邮箱: " + email, font=self.normal_font, bg="#ffffff").pack(pady=5, anchor="w") + + tk.Label(form_frame, text="密码:", font=self.normal_font, bg="#ffffff", fg=self.primary_color).pack(pady=(10, 5), anchor="w") + tk.Label(form_frame, text="(6-10位,必须包含大小写字母和数字)", font=("Arial", 10), bg="#ffffff", fg="gray").pack(pady=5, anchor="w") + self.set_password_entry = tk.Entry(form_frame, width=30, font=self.normal_font, show="*", relief=tk.FLAT, bd=5, bg="#f0f0f0") self.set_password_entry.pack(pady=5) - tk.Label(self.set_password_frame, text="确认密码:").pack(pady=5) - self.confirm_password_entry = tk.Entry(self.set_password_frame, width=30, show="*") + tk.Label(form_frame, text="确认密码:", font=self.normal_font, bg="#ffffff").pack(pady=5, anchor="w") + self.confirm_password_entry = tk.Entry(form_frame, width=30, font=self.normal_font, show="*", relief=tk.FLAT, bd=5, bg="#f0f0f0") self.confirm_password_entry.pack(pady=5) - tk.Button(self.set_password_frame, text="设置密码", command=lambda: self.set_password(email), width=20).pack(pady=10) - tk.Button(self.set_password_frame, text="返回登录", command=self.show_login_frame, width=20).pack(pady=5) + button_frame = tk.Frame(main_frame, bg="#ffffff") + button_frame.pack(pady=20) + + tk.Button(button_frame, text="设置密码", command=lambda: self.set_password(email), width=20, bg=self.success_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=10) + tk.Button(button_frame, text="返回登录", command=self.show_login_frame, width=20, bg="#cccccc", fg=self.dark_text, + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=5) self.show_frame(self.set_password_frame) @@ -245,23 +309,30 @@ class MathQuizApp: widget.destroy() # 创建主菜单界面 - tk.Label(self.main_menu_frame, text="主菜单", font=("Arial", 18, "bold")).pack(pady=20) + main_frame = tk.Frame(self.main_menu_frame, bg="#ffffff", relief=tk.RAISED, bd=2) + main_frame.pack(pady=30, padx=50, fill="both", expand=True) + + title_frame = tk.Frame(main_frame, bg=self.primary_color) + title_frame.pack(fill="x", pady=(0, 20)) + tk.Label(title_frame, text="主菜单", font=self.title_font, bg=self.primary_color, fg="white").pack(pady=20) user_email = self.user_manager.current_user.email if self.user_manager.current_user else "未知用户" - tk.Label(self.main_menu_frame, text=f"欢迎, {user_email}!", font=("Arial", 12)).pack(pady=10) + tk.Label(main_frame, text=f"欢迎, {user_email}!", font=self.header_font, bg="#ffffff").pack(pady=10) - tk.Button(self.main_menu_frame, text="小学题目", command=lambda: self.show_quiz_setup_frame("elementary"), - width=20, height=2).pack(pady=10) - tk.Button(self.main_menu_frame, text="初中题目", command=lambda: self.show_quiz_setup_frame("middle"), - width=20, height=2).pack(pady=10) - tk.Button(self.main_menu_frame, text="高中题目", command=lambda: self.show_quiz_setup_frame("high"), - width=20, height=2).pack(pady=10) + button_frame = tk.Frame(main_frame, bg="#ffffff") + button_frame.pack(pady=20) + + tk.Button(button_frame, text="小学题目", command=lambda: self.show_quiz_setup_frame("elementary"), + width=20, height=2, bg="#4CAF50", fg="white", font=self.button_font, relief=tk.FLAT, bd=0).pack(pady=10) + tk.Button(button_frame, text="初中题目", command=lambda: self.show_quiz_setup_frame("middle"), + width=20, height=2, bg="#2196F3", fg="white", font=self.button_font, relief=tk.FLAT, bd=0).pack(pady=10) + tk.Button(button_frame, text="高中题目", command=lambda: self.show_quiz_setup_frame("high"), + width=20, height=2, bg="#FF9800", fg="white", font=self.button_font, relief=tk.FLAT, bd=0).pack(pady=10) - tk.Button(self.main_menu_frame, text="修改密码", command=self.show_change_password_frame, - width=20).pack(pady=10) - tk.Button(self.main_menu_frame, text="退出登录", command=self.logout, width=20).pack(pady=5) - tk.Button(self.main_menu_frame, text="注销账户", command=self.delete_account, - width=20, fg="red").pack(pady=5) + tk.Button(button_frame, text="修改密码", command=self.show_change_password_frame, + width=20, bg=self.secondary_color, fg="white", font=self.button_font, relief=tk.FLAT, bd=0, pady=5).pack(pady=10) + tk.Button(button_frame, text="退出登录", command=self.logout, width=20, bg=self.danger_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, pady=5).pack(pady=5) self.show_frame(self.main_menu_frame) @@ -278,16 +349,31 @@ class MathQuizApp: widget.destroy() # 创建测验设置界面 + main_frame = tk.Frame(self.quiz_setup_frame, bg="#ffffff", relief=tk.RAISED, bd=2) + main_frame.pack(pady=30, padx=50, fill="both", expand=True) + level_names = {"elementary": "小学", "middle": "初中", "high": "高中"} - tk.Label(self.quiz_setup_frame, text=f"{level_names[level]}数学题目", font=("Arial", 18, "bold")).pack(pady=20) + level_colors = {"elementary": "#4CAF50", "middle": "#2196F3", "high": "#FF9800"} + + title_frame = tk.Frame(main_frame, bg=level_colors[level]) + title_frame.pack(fill="x", pady=(0, 20)) + tk.Label(title_frame, text=f"{level_names[level]}数学题目", font=self.title_font, bg=level_colors[level], fg="white").pack(pady=20) - tk.Label(self.quiz_setup_frame, text="请输入题目数量 (1-20):").pack(pady=10) - self.question_count_entry = tk.Entry(self.quiz_setup_frame, width=20) + form_frame = tk.Frame(main_frame, bg="#ffffff") + form_frame.pack(pady=20) + + tk.Label(form_frame, text="请输入题目数量 (1-20):", font=self.normal_font, bg="#ffffff").pack(pady=10) + self.question_count_entry = tk.Entry(form_frame, width=20, font=self.normal_font, justify="center", relief=tk.FLAT, bd=5, bg="#f0f0f0") self.question_count_entry.pack(pady=5) self.question_count_entry.insert(0, "10") # 默认10题 - tk.Button(self.quiz_setup_frame, text="开始答题", command=self.start_quiz, width=20).pack(pady=10) - tk.Button(self.quiz_setup_frame, text="返回主菜单", command=self.show_main_menu_frame, width=20).pack(pady=5) + button_frame = tk.Frame(main_frame, bg="#ffffff") + button_frame.pack(pady=20) + + tk.Button(button_frame, text="开始答题", command=self.start_quiz, width=20, bg=self.success_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=10) + tk.Button(button_frame, text="返回主菜单", command=self.show_main_menu_frame, width=20, bg="#cccccc", fg=self.dark_text, + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=5) self.show_frame(self.quiz_setup_frame) @@ -330,42 +416,57 @@ class MathQuizApp: if not current_question: return + main_frame = tk.Frame(self.quiz_frame, bg="#ffffff", relief=tk.RAISED, bd=2) + main_frame.pack(pady=20, padx=30, fill="both", expand=True) + # 显示题目进度 progress = f"题目 {self.current_quiz.current_question_index + 1}/{len(self.current_quiz.questions)}" - tk.Label(self.quiz_frame, text=progress, font=("Arial", 12)).pack(pady=10) + progress_frame = tk.Frame(main_frame, bg=self.primary_color) + progress_frame.pack(fill="x", pady=(0, 20)) + tk.Label(progress_frame, text=progress, font=self.header_font, bg=self.primary_color, fg="white").pack(pady=10) # 显示题目 + question_frame = tk.Frame(main_frame, bg="#ffffff") + question_frame.pack(pady=10) + + tk.Label(question_frame, text="题目:", font=self.header_font, bg="#ffffff").pack(pady=(10, 5)) question_text = str(current_question) - tk.Label(self.quiz_frame, text="题目:", font=("Arial", 14, "bold")).pack(pady=(10, 5)) - tk.Label(self.quiz_frame, text=question_text, font=("Arial", 16), wraplength=500).pack(pady=10) + tk.Label(question_frame, text=question_text, font=("Arial", 16, "bold"), bg="#ffffff", wraplength=500).pack(pady=10) # 计算选项 options = self.generate_options(current_question.answer) # 显示选项 - tk.Label(self.quiz_frame, text="请选择答案:", font=("Arial", 12)).pack(pady=(20, 10)) + tk.Label(main_frame, text="请选择答案:", font=self.normal_font, bg="#ffffff").pack(pady=(20, 10)) self.answer_var = tk.StringVar() - for i, option in enumerate(options): - tk.Radiobutton(self.quiz_frame, text=f"{['A', 'B', 'C', 'D'][i]}. {option}", - variable=self.answer_var, value=str(option), font=("Arial", 12)).pack(anchor="w", padx=50, pady=5) + self.answer_var.set(" ") # 明确设置初始值为空字符串,确保不选中任何选项 + + options_frame = tk.Frame(main_frame, bg="#ffffff") + options_frame.pack(pady=10) - # 默认不选择任何选项 - self.answer_var.set("") + for i, option in enumerate(options): + tk.Radiobutton(options_frame, text=f"{['A', 'B', 'C', 'D'][i]}. {option}", + variable=self.answer_var, value=str(option), font=self.normal_font, + bg="#ffffff", selectcolor="#e0e0e0", activebackground="#f0f0f0").pack(anchor="w", padx=50, pady=5) # 按钮框架 - button_frame = tk.Frame(self.quiz_frame) + button_frame = tk.Frame(main_frame, bg="#ffffff") button_frame.pack(pady=20) if self.current_quiz.current_question_index > 0: - tk.Button(button_frame, text="上一题", command=self.previous_question).pack(side="left", padx=10) + tk.Button(button_frame, text="上一题", command=self.previous_question, bg="#cccccc", fg=self.dark_text, + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(side="left", padx=10) - tk.Button(button_frame, text="提交答案", command=self.submit_answer).pack(side="left", padx=10) + tk.Button(button_frame, text="提交答案", command=self.submit_answer, bg=self.success_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(side="left", padx=10) if self.current_quiz.current_question_index < len(self.current_quiz.questions) - 1: - tk.Button(button_frame, text="下一题", command=self.next_question).pack(side="left", padx=10) + tk.Button(button_frame, text="下一题", command=self.next_question, bg=self.primary_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(side="left", padx=10) - tk.Button(self.quiz_frame, text="返回主菜单", command=self.show_main_menu_frame, width=15).pack(pady=10) + tk.Button(main_frame, text="返回主菜单", command=self.show_main_menu_frame, width=15, bg=self.danger_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=10) self.show_frame(self.quiz_frame) @@ -484,37 +585,54 @@ class MathQuizApp: score = self.current_quiz.calculate_score() # 创建结果界面 - tk.Label(self.result_frame, text="测验结果", font=("Arial", 18, "bold")).pack(pady=20) + main_frame = tk.Frame(self.result_frame, bg="#ffffff", relief=tk.RAISED, bd=2) + main_frame.pack(pady=30, padx=50, fill="both", expand=True) + + title_frame = tk.Frame(main_frame, bg=self.primary_color) + title_frame.pack(fill="x", pady=(0, 20)) + tk.Label(title_frame, text="测验结果", font=self.title_font, bg=self.primary_color, fg="white").pack(pady=20) + + result_frame = tk.Frame(main_frame, bg="#ffffff") + result_frame.pack(pady=20) - tk.Label(self.result_frame, text=f"您的得分: {score:.1f}%", font=("Arial", 16)).pack(pady=10) + # 根据得分显示不同颜色的分数 + score_color = self.danger_color if score < 60 else self.warning_color if score < 80 else self.success_color + + tk.Label(result_frame, text=f"您的得分: {score:.1f}%", font=("Arial", 20, "bold"), fg=score_color, bg="#ffffff").pack(pady=10) # 根据得分显示评语 if score >= 90: comment = "优秀! 继续保持!" + comment_color = self.success_color elif score >= 80: comment = "良好! 还可以做得更好!" + comment_color = self.success_color elif score >= 60: comment = "及格了,需要继续努力!" + comment_color = self.warning_color else: comment = "需要加强练习哦!" + comment_color = self.danger_color - tk.Label(self.result_frame, text=comment, font=("Arial", 14)).pack(pady=10) + tk.Label(result_frame, text=comment, font=self.header_font, fg=comment_color, bg="#ffffff").pack(pady=10) # 显示答题详情 correct_count = sum(1 for q, a in zip(self.current_quiz.questions, self.current_quiz.answers) if a is not None and abs(q.answer - a) < 1e-6) total_count = len(self.current_quiz.questions) - tk.Label(self.result_frame, text=f"答对: {correct_count}/{total_count}", font=("Arial", 12)).pack(pady=5) + tk.Label(result_frame, text=f"答对: {correct_count}/{total_count}", font=self.normal_font, bg="#ffffff").pack(pady=5) # 按钮 - button_frame = tk.Frame(self.result_frame) - button_frame.pack(pady=20) + button_frame = tk.Frame(main_frame, bg="#ffffff") + button_frame.pack(pady=30) level_names = {"elementary": "小学", "middle": "初中", "high": "高中"} tk.Button(button_frame, text=f"继续{level_names[self.current_level]}题目", - command=lambda: self.show_quiz_setup_frame(self.current_level), width=20).pack(side="left", padx=10) + command=lambda: self.show_quiz_setup_frame(self.current_level), width=20, bg=self.primary_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(side="left", padx=10) - tk.Button(button_frame, text="返回主菜单", command=self.show_main_menu_frame, width=15).pack(side="left", padx=10) + tk.Button(button_frame, text="返回主菜单", command=self.show_main_menu_frame, width=15, bg="#cccccc", fg=self.dark_text, + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(side="left", padx=10) self.show_frame(self.result_frame) @@ -527,23 +645,36 @@ class MathQuizApp: widget.destroy() # 创建修改密码界面 - tk.Label(self.change_password_frame, text="修改密码", font=("Arial", 18, "bold")).pack(pady=20) + main_frame = tk.Frame(self.change_password_frame, bg="#ffffff", relief=tk.RAISED, bd=2) + main_frame.pack(pady=30, padx=50, fill="both", expand=True) + + title_frame = tk.Frame(main_frame, bg=self.primary_color) + title_frame.pack(fill="x", pady=(0, 20)) + tk.Label(title_frame, text="修改密码", font=self.title_font, bg=self.primary_color, fg="white").pack(pady=20) - tk.Label(self.change_password_frame, text="原密码:").pack(pady=5) - self.old_password_entry = tk.Entry(self.change_password_frame, width=30, show="*") + form_frame = tk.Frame(main_frame, bg="#ffffff") + form_frame.pack(pady=10) + + tk.Label(form_frame, text="原密码:", font=self.normal_font, bg="#ffffff").pack(pady=5, anchor="w") + self.old_password_entry = tk.Entry(form_frame, width=30, font=self.normal_font, show="*", relief=tk.FLAT, bd=5, bg="#f0f0f0") self.old_password_entry.pack(pady=5) - tk.Label(self.change_password_frame, text="新密码:", fg="blue").pack(pady=(10, 5)) - tk.Label(self.change_password_frame, text="(6-10位,必须包含大小写字母和数字)", fg="gray").pack(pady=5) - self.new_password_entry = tk.Entry(self.change_password_frame, width=30, show="*") + tk.Label(form_frame, text="新密码:", font=self.normal_font, bg="#ffffff", fg=self.primary_color).pack(pady=(10, 5), anchor="w") + tk.Label(form_frame, text="(6-10位,必须包含大小写字母和数字)", font=("Arial", 10), bg="#ffffff", fg="gray").pack(pady=5, anchor="w") + self.new_password_entry = tk.Entry(form_frame, width=30, font=self.normal_font, show="*", relief=tk.FLAT, bd=5, bg="#f0f0f0") self.new_password_entry.pack(pady=5) - tk.Label(self.change_password_frame, text="确认新密码:").pack(pady=5) - self.confirm_new_password_entry = tk.Entry(self.change_password_frame, width=30, show="*") + tk.Label(form_frame, text="确认新密码:", font=self.normal_font, bg="#ffffff").pack(pady=5, anchor="w") + self.confirm_new_password_entry = tk.Entry(form_frame, width=30, font=self.normal_font, show="*", relief=tk.FLAT, bd=5, bg="#f0f0f0") self.confirm_new_password_entry.pack(pady=5) - tk.Button(self.change_password_frame, text="修改密码", command=self.change_password, width=20).pack(pady=10) - tk.Button(self.change_password_frame, text="返回主菜单", command=self.show_main_menu_frame, width=20).pack(pady=5) + button_frame = tk.Frame(main_frame, bg="#ffffff") + button_frame.pack(pady=20) + + tk.Button(button_frame, text="修改密码", command=self.change_password, width=20, bg=self.success_color, fg="white", + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=10) + tk.Button(button_frame, text="返回主菜单", command=self.show_main_menu_frame, width=20, bg="#cccccc", fg=self.dark_text, + font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(pady=5) self.show_frame(self.change_password_frame) @@ -584,27 +715,38 @@ class MathQuizApp: widget.destroy() # 创建注销账户界面 - tk.Label(self.delete_account_frame, text="注销账户", font=("Arial", 18, "bold"), fg="red").pack(pady=20) + main_frame = tk.Frame(self.delete_account_frame, bg="#ffffff", relief=tk.RAISED, bd=2) + main_frame.pack(pady=30, padx=50, fill="both", expand=True) + + title_frame = tk.Frame(main_frame, bg=self.danger_color) + title_frame.pack(fill="x", pady=(0, 20)) + tk.Label(title_frame, text="注销账户", font=self.title_font, bg=self.danger_color, fg="white").pack(pady=20) + + warning_frame = tk.Frame(main_frame, bg="#ffffff") + warning_frame.pack(pady=10) + + tk.Label(warning_frame, text="警告:此操作将永久删除您的账户和所有数据!", font=self.normal_font, fg=self.danger_color, bg="#ffffff").pack(pady=5) + tk.Label(warning_frame, text="请确认您的邮箱和密码:", font=self.normal_font, fg=self.danger_color, bg="#ffffff").pack(pady=5) - tk.Label(self.delete_account_frame, text="警告:此操作将永久删除您的账户和所有数据!", fg="red").pack(pady=5) - tk.Label(self.delete_account_frame, text="请确认您的邮箱和密码:", fg="red").pack(pady=5) + form_frame = tk.Frame(main_frame, bg="#ffffff") + form_frame.pack(pady=10) - tk.Label(self.delete_account_frame, text="邮箱:").pack(pady=10) - self.delete_email_entry = tk.Entry(self.delete_account_frame, width=30) + tk.Label(form_frame, text="邮箱:", font=self.normal_font, bg="#ffffff").pack(pady=10, anchor="w") + self.delete_email_entry = tk.Entry(form_frame, width=30, font=self.normal_font, relief=tk.FLAT, bd=5, bg="#f0f0f0") self.delete_email_entry.pack(pady=5) - tk.Label(self.delete_account_frame, text="密码:").pack(pady=5) - self.delete_password_entry = tk.Entry(self.delete_account_frame, width=30, show="*") + tk.Label(form_frame, text="密码:", font=self.normal_font, bg="#ffffff").pack(pady=5, anchor="w") + self.delete_password_entry = tk.Entry(form_frame, width=30, font=self.normal_font, show="*", relief=tk.FLAT, bd=5, bg="#f0f0f0") self.delete_password_entry.pack(pady=5) # 按钮框架 - button_frame = tk.Frame(self.delete_account_frame) + button_frame = tk.Frame(main_frame, bg="#ffffff") button_frame.pack(pady=20) tk.Button(button_frame, text="确认注销", command=self.confirm_delete_account, - width=15, fg="red").pack(side="left", padx=10) + width=15, bg=self.danger_color, fg="white", font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(side="left", padx=10) tk.Button(button_frame, text="取消", command=self.show_login_frame, - width=15).pack(side="left", padx=10) + width=15, bg="#cccccc", fg=self.dark_text, font=self.button_font, relief=tk.FLAT, bd=0, padx=10, pady=5).pack(side="left", padx=10) self.show_frame(self.delete_account_frame) diff --git a/src/users.json b/src/users.json index 9e26dfe..0e0979a 100644 --- a/src/users.json +++ b/src/users.json @@ -1 +1,8 @@ -{} \ No newline at end of file +{ + "1426688201@qq.com": { + "username": "111", + "password_hash": "c4318372f98f4c46ed3a32c16ee4d7a76c832886d887631c0294b3314f34edf1", + "registration_code": "939598", + "is_registered": true + } +} \ No newline at end of file -- 2.34.1 From a33bc412fc3fd13133de06a42d98c867a139d898 Mon Sep 17 00:00:00 2001 From: hnu202326010111 <1426688201@qq.com> Date: Sat, 11 Oct 2025 20:48:04 +0800 Subject: [PATCH 5/5] Delete 'README.md' --- README.md | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index 4703173..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# student_system - -- 2.34.1