合并至main分支 #3

Merged
hnu202326010223 merged 2 commits from develop into main 7 months ago

@ -1,2 +0,0 @@
# UIGenerator

@ -0,0 +1 @@
Subproject commit d74fecb6996af48dc7c536cfe9f66c195de04bdf

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,409 @@
('C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\build\\中小学数学学习软件\\PYZ-00.pyz',
[('__future__',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\__future__.py',
'PYMODULE'),
('_colorize',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\_colorize.py',
'PYMODULE'),
('_compat_pickle',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\_compat_pickle.py',
'PYMODULE'),
('_compression',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\_compression.py',
'PYMODULE'),
('_opcode_metadata',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\_opcode_metadata.py',
'PYMODULE'),
('_py_abc',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\_py_abc.py',
'PYMODULE'),
('_pydatetime',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\_pydatetime.py',
'PYMODULE'),
('_pydecimal',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\_pydecimal.py',
'PYMODULE'),
('_strptime',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\_strptime.py',
'PYMODULE'),
('_threading_local',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\_threading_local.py',
'PYMODULE'),
('argparse',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\argparse.py',
'PYMODULE'),
('ast',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\ast.py',
'PYMODULE'),
('base64',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\base64.py',
'PYMODULE'),
('bisect',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\bisect.py',
'PYMODULE'),
('bz2',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\bz2.py',
'PYMODULE'),
('calendar',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\calendar.py',
'PYMODULE'),
('config',
'C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\src\\config.py',
'PYMODULE'),
('contextlib',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\contextlib.py',
'PYMODULE'),
('contextvars',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\contextvars.py',
'PYMODULE'),
('controller',
'C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\src\\controller\\__init__.py',
'PYMODULE'),
('controller.app_controller',
'C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\src\\controller\\app_controller.py',
'PYMODULE'),
('copy',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\copy.py',
'PYMODULE'),
('csv',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\csv.py',
'PYMODULE'),
('dataclasses',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\dataclasses.py',
'PYMODULE'),
('datetime',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\datetime.py',
'PYMODULE'),
('decimal',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\decimal.py',
'PYMODULE'),
('dis',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\dis.py',
'PYMODULE'),
('email',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\__init__.py',
'PYMODULE'),
('email._encoded_words',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\_encoded_words.py',
'PYMODULE'),
('email._header_value_parser',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\_header_value_parser.py',
'PYMODULE'),
('email._parseaddr',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\_parseaddr.py',
'PYMODULE'),
('email._policybase',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\_policybase.py',
'PYMODULE'),
('email.base64mime',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\base64mime.py',
'PYMODULE'),
('email.charset',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\charset.py',
'PYMODULE'),
('email.contentmanager',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\contentmanager.py',
'PYMODULE'),
('email.encoders',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\encoders.py',
'PYMODULE'),
('email.errors',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\errors.py',
'PYMODULE'),
('email.feedparser',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\feedparser.py',
'PYMODULE'),
('email.generator',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\generator.py',
'PYMODULE'),
('email.header',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\header.py',
'PYMODULE'),
('email.headerregistry',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\headerregistry.py',
'PYMODULE'),
('email.iterators',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\iterators.py',
'PYMODULE'),
('email.message',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\message.py',
'PYMODULE'),
('email.mime',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\mime\\__init__.py',
'PYMODULE'),
('email.mime.base',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\mime\\base.py',
'PYMODULE'),
('email.mime.nonmultipart',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\mime\\nonmultipart.py',
'PYMODULE'),
('email.mime.text',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\mime\\text.py',
'PYMODULE'),
('email.parser',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\parser.py',
'PYMODULE'),
('email.policy',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\policy.py',
'PYMODULE'),
('email.quoprimime',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\quoprimime.py',
'PYMODULE'),
('email.utils',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\email\\utils.py',
'PYMODULE'),
('fnmatch',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\fnmatch.py',
'PYMODULE'),
('fractions',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\fractions.py',
'PYMODULE'),
('getopt',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\getopt.py',
'PYMODULE'),
('gettext',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\gettext.py',
'PYMODULE'),
('glob',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\glob.py',
'PYMODULE'),
('gzip',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\gzip.py',
'PYMODULE'),
('hashlib',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\hashlib.py',
'PYMODULE'),
('hmac',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\hmac.py',
'PYMODULE'),
('importlib',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\__init__.py',
'PYMODULE'),
('importlib._abc',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\_abc.py',
'PYMODULE'),
('importlib._bootstrap',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\_bootstrap.py',
'PYMODULE'),
('importlib._bootstrap_external',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\_bootstrap_external.py',
'PYMODULE'),
('importlib.abc',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\abc.py',
'PYMODULE'),
('importlib.machinery',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\machinery.py',
'PYMODULE'),
('importlib.metadata',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\metadata\\__init__.py',
'PYMODULE'),
('importlib.metadata._adapters',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\metadata\\_adapters.py',
'PYMODULE'),
('importlib.metadata._collections',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\metadata\\_collections.py',
'PYMODULE'),
('importlib.metadata._functools',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\metadata\\_functools.py',
'PYMODULE'),
('importlib.metadata._itertools',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\metadata\\_itertools.py',
'PYMODULE'),
('importlib.metadata._meta',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\metadata\\_meta.py',
'PYMODULE'),
('importlib.metadata._text',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\metadata\\_text.py',
'PYMODULE'),
('importlib.readers',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\readers.py',
'PYMODULE'),
('importlib.resources',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\resources\\__init__.py',
'PYMODULE'),
('importlib.resources._adapters',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\resources\\_adapters.py',
'PYMODULE'),
('importlib.resources._common',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\resources\\_common.py',
'PYMODULE'),
('importlib.resources._functional',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\resources\\_functional.py',
'PYMODULE'),
('importlib.resources._itertools',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\resources\\_itertools.py',
'PYMODULE'),
('importlib.resources.abc',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\resources\\abc.py',
'PYMODULE'),
('importlib.resources.readers',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\resources\\readers.py',
'PYMODULE'),
('importlib.util',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\importlib\\util.py',
'PYMODULE'),
('inspect',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\inspect.py',
'PYMODULE'),
('ipaddress',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\ipaddress.py',
'PYMODULE'),
('json',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\json\\__init__.py',
'PYMODULE'),
('json.decoder',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\json\\decoder.py',
'PYMODULE'),
('json.encoder',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\json\\encoder.py',
'PYMODULE'),
('json.scanner',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\json\\scanner.py',
'PYMODULE'),
('logging',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\logging\\__init__.py',
'PYMODULE'),
('lzma',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\lzma.py',
'PYMODULE'),
('model',
'C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\src\\model\\__init__.py',
'PYMODULE'),
('model.auth_service',
'C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\src\\model\\auth_service.py',
'PYMODULE'),
('model.question_generators',
'C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\src\\model\\question_generators.py',
'PYMODULE'),
('numbers',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\numbers.py',
'PYMODULE'),
('opcode',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\opcode.py',
'PYMODULE'),
('pathlib',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\pathlib\\__init__.py',
'PYMODULE'),
('pathlib._abc',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\pathlib\\_abc.py',
'PYMODULE'),
('pathlib._local',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\pathlib\\_local.py',
'PYMODULE'),
('pickle',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\pickle.py',
'PYMODULE'),
('pprint',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\pprint.py',
'PYMODULE'),
('py_compile',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\py_compile.py',
'PYMODULE'),
('quopri',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\quopri.py',
'PYMODULE'),
('random',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\random.py',
'PYMODULE'),
('selectors',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\selectors.py',
'PYMODULE'),
('shutil',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\shutil.py',
'PYMODULE'),
('signal',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\signal.py',
'PYMODULE'),
('smtplib',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\smtplib.py',
'PYMODULE'),
('socket',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\socket.py',
'PYMODULE'),
('ssl',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\ssl.py',
'PYMODULE'),
('statistics',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\statistics.py',
'PYMODULE'),
('string',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\string.py',
'PYMODULE'),
('stringprep',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\stringprep.py',
'PYMODULE'),
('subprocess',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\subprocess.py',
'PYMODULE'),
('tarfile',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\tarfile.py',
'PYMODULE'),
('tempfile',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\tempfile.py',
'PYMODULE'),
('textwrap',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\textwrap.py',
'PYMODULE'),
('threading',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\threading.py',
'PYMODULE'),
('tkinter',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\tkinter\\__init__.py',
'PYMODULE'),
('tkinter.commondialog',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\tkinter\\commondialog.py',
'PYMODULE'),
('tkinter.constants',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\tkinter\\constants.py',
'PYMODULE'),
('tkinter.messagebox',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\tkinter\\messagebox.py',
'PYMODULE'),
('tkinter.ttk',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\tkinter\\ttk.py',
'PYMODULE'),
('token',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\token.py',
'PYMODULE'),
('tokenize',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\tokenize.py',
'PYMODULE'),
('tracemalloc',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\tracemalloc.py',
'PYMODULE'),
('typing',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\typing.py',
'PYMODULE'),
('urllib',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\urllib\\__init__.py',
'PYMODULE'),
('urllib.parse',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\urllib\\parse.py',
'PYMODULE'),
('view',
'C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\src\\view\\__init__.py',
'PYMODULE'),
('view.login_view',
'C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\src\\view\\login_view.py',
'PYMODULE'),
('view.main_view',
'C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\src\\view\\main_view.py',
'PYMODULE'),
('view.password_view',
'C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\src\\view\\password_view.py',
'PYMODULE'),
('view.quiz_view',
'C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\src\\view\\quiz_view.py',
'PYMODULE'),
('view.register_view',
'C:\\Users\\哈俊元\\Desktop\\2班_袁明霜涛_哈俊元_结对项目\\src\\view\\register_view.py',
'PYMODULE'),
('zipfile',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\zipfile\\__init__.py',
'PYMODULE'),
('zipfile._path',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\zipfile\\_path\\__init__.py',
'PYMODULE'),
('zipfile._path.glob',
'C:\\Users\\哈俊元\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\zipfile\\_path\\glob.py',
'PYMODULE')])

@ -0,0 +1,25 @@
This file lists modules PyInstaller was not able to find. This does not
necessarily mean this module is required for running your program. Python and
Python 3rd-party packages include a lot of conditional or optional modules. For
example the module 'ntpath' only exists on Windows, whereas the module
'posixpath' only exists on Posix systems.
Types if import:
* top-level: imported at the top-level - look at these first
* conditional: imported within an if-statement
* delayed: imported within a function
* optional: imported within a try-except-statement
IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for
tracking down the missing module yourself. Thanks!
missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib._local (optional), subprocess (delayed, conditional, optional)
missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib._local (optional), subprocess (delayed, conditional, optional)
missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional)
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional)
missing module named 'collections.abc' - imported by traceback (top-level), typing (top-level), inspect (top-level), logging (top-level), importlib.resources.readers (top-level), selectors (top-level), tracemalloc (top-level)
missing module named posix - imported by posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional), os (conditional, optional)
missing module named resource - imported by posix (top-level)
missing module named _posixsubprocess - imported by subprocess (conditional)
missing module named fcntl - imported by subprocess (optional)

8
dist/users.json vendored

@ -0,0 +1,8 @@
{
"3154420541@qq.com": {
"username": "哈俊元",
"password": "LZLhjy123",
"level": "primary",
"register_time": "2025-10-11 14:06:12"
}
}

Binary file not shown.

@ -0,0 +1,200 @@
# 软2_[袁明霜涛]_[哈俊元]_结对项目
> 中小学数学学习软件 - 结对编程项目
## 项目简介
本项目是一个面向中小学学生的数学学习桌面应用程序,提供个性化的数学题目练习和评估功能。系统根据学生所在学段(小学、初中、高中)生成相应难度的数学题目,通过图形化界面提供友好的学习体验。
## 功能特性
### 用户管理
- ✅ **用户注册**:通过邮箱验证码完成注册
- ✅ **密码设置**6-10位必须包含大小写字母和数字
- ✅ **用户登录**:安全的身份验证机制
- ✅ **密码修改**:支持原密码验证和验证码重置两种方式
### 题目生成
- ✅ **小学题目**:加减乘除四则运算
- ✅ **初中题目**:平方、开根运算
- ✅ **高中题目**:三角函数计算
- ✅ **智能防重复**:确保同一试卷无重复题目
### 学习流程
- ✅ **难度选择**:小学/初中/高中三级难度
- ✅ **题目数量**用户自定义题目数量10-30题
- ✅ **选择题形式**每题4个选项单选作答
- ✅ **实时评分**:提交后立即显示得分情况
- ✅ **学习延续**:支持连续练习或退出选择
## 技术栈
- **编程语言**Python 3.x
- **GUI框架**Tkinter内置Python GUI库
- **数据存储**JSON文件无需数据库
- **邮件服务**SMTP协议QQ邮箱
- **架构模式**MVC模型-视图-控制器)
## 项目结构
软1_XXX_XXX_结对项目/
├── src/ # 源代码目录
│ ├── main.py # 程序入口
│ ├── config.py # 配置文件
│ ├── controller/ # 控制器层
│ │ ├── init.py
│ │ └── app_controller.py # 应用控制器
│ ├── model/ # 模型层
│ │ ├── init.py
│ │ ├── auth_service.py # 认证服务
│ │ └── question_generators.py # 题目生成器
│ └── view/ # 视图层
│ ├── init.py
│ ├── login_view.py # 登录界面
│ ├── register_view.py # 注册界面
│ ├── main_view.py # 主界面
│ ├── password_view.py # 密码修改界面
│ └── quiz_view.py # 答题界面
├── doc/ # 项目文档目录
│ ├── 项目说明文档.md # 本文档
│ └── 用户手册.md # 详细使用说明
├── users.json # 用户数据文件(运行时生成)
└── README.md # 项目说明
## 安装与运行
### 环境要求
- Python 3.7 或更高版本
- 网络连接(用于邮箱验证码发送)
### 运行步骤
1. 克隆项目到本地
2. 进入项目src目录
```bash
cd src
运行主程序:
python main.py
按照界面提示进行注册和登录
预设测试账号
系统支持新用户注册,也可使用以下测试账号:
邮箱:任意有效邮箱(接收验证码)
密码符合规范的密码Abc123
分支管理
本项目遵循Git分支管理规范
main分支稳定版本存放经过测试的代码
develop分支开发主线集成最新功能
个人分支每位开发者的功能分支zhangsan_branch
代码提交规则
源代码:必须通过个人分支 + Pull Request
文档直接推送到develop分支
开发规范
代码规范
遵循PEP 8 Python编码规范
使用类型注解提高代码可读性
模块化设计,高内聚低耦合
提交信息规范
feat: 新功能
fix: 修复bug
docs: 文档更新
style: 代码格式调整
refactor: 代码重构
功能模块详解
1. 用户认证模块 (auth_service.py)
邮箱格式验证
密码强度校验
验证码发送与验证
用户数据持久化JSON文件
2. 题目生成模块 (question_generators.py)
小学题目2-5个数字的加减乘除运算确保结果非负且除法整除
初中题目平方运算1-20、开根运算完全平方数
高中题目三角函数sin/cos/tan特殊角度计算
选项生成智能生成4个合理选项包含正确答案
3. 界面模块 (view/)
响应式图形界面设计
实时输入验证
友好的用户交互反馈
密码可见性切换功能
数据存储
项目使用JSON文件存储用户数据文件结构如下
{
"user@example.com": {
"username": "张三",
"password": "加密密码",
"level": "primary",
"register_time": "2024-01-01 10:00:00"
}
}
配置说明
在 config.py 中配置以下参数:
邮箱服务配置SMTP服务器、端口、授权码
用户数据文件路径
难度等级定义
测试用例
功能测试
用户注册流程测试
登录验证测试
密码修改测试
题目生成测试(各学段)
答题评分测试
边界测试
密码格式边界测试
题目数量边界测试
邮箱格式验证测试
已知限制
邮箱服务依赖QQ邮箱SMTP服务需配置正确的授权码
题目数量建议10-30题过多可能影响性能
网络要求:发送验证码需要网络连接
平台兼容主要支持Windows其他平台可能需调整
开发团队
班级软2
组长:[袁明霜涛]202326010223
组员:[哈俊元]202326010232

@ -0,0 +1,18 @@
import PyInstaller.__main__
import os
# 打包配置
PyInstaller.__main__.run([
'src/main.py', # 主程序入口
'--name=中小学数学学习软件', # 可执行文件名称
'--onefile', # 打包成单个exe文件
'--windowed', # 不显示命令行窗口GUI程序
'--icon=assets/icon.ico', # 程序图标(可选)
'--add-data=src/model;model', # 添加数据文件
'--add-data=src/view;view', # 添加视图文件
'--add-data=src/controller;controller', # 添加控制器文件
'--hidden-import=tkinter', # 隐藏导入的模块
'--hidden-import=email',
'--hidden-import=smtplib',
'--clean', # 清理临时文件
])

@ -0,0 +1,6 @@
"""配置文件"""
class Config:
EMAIL_ADDRESS = "3312923370@qq.com" # 发送验证码的邮箱(无需修改)
EMAIL_AUTH_CODE = "iuyrclkpsfzpdahc" # 最新邮箱授权码(已更新)
USER_DATA_FILE = "users.json" # 用户数据保存文件
DIFFICULTY_LEVELS = ['primary', 'junior', 'senior'] # 难度等级

@ -0,0 +1,267 @@
"""应用控制器"""
import tkinter as tk
from tkinter import messagebox
from model.auth_service import AuthService
from model.question_generators import PrimaryQuestionGenerator, JuniorQuestionGenerator, SeniorQuestionGenerator
from view.login_view import LoginView
from view.register_view import RegisterView
from view.main_view import MainView
from view.password_view import PasswordView
from view.quiz_view import QuizView
from config import Config
class AppController:
def __init__(self):
self.auth_service = AuthService()
# 初始化题目生成器(根据难度切换)
self.question_generators = {
'primary': PrimaryQuestionGenerator(),
'junior': JuniorQuestionGenerator(),
'senior': SeniorQuestionGenerator()
}
# 当前用户状态
self.current_email = None # 当前登录用户邮箱
self.current_username = None # 当前登录用户用户名
self.current_level = 'primary' # 当前难度等级
self.current_questions = [] # 当前答题的题目列表
self.user_answers = [] # 用户答案列表
self.current_question_index = 0 # 当前答题索引
# 初始化视图
self.root = tk.Tk()
self.root.title("中小学数学学习软件")
self.root.geometry("600x450")
self.root.resizable(False, False)
self.login_view = LoginView(self.root, self)
self.register_view = RegisterView(self.root, self)
self.main_view = MainView(self.root, self)
self.password_view = PasswordView(self.root, self)
self.quiz_view = QuizView(self.root, self)
# 显示登录界面
self.show_login_view()
def run(self):
"""运行应用"""
self.root.mainloop()
# 视图导航方法
def show_login_view(self):
"""显示登录视图"""
self.login_view.setup_ui()
def show_register_view(self):
"""显示注册视图"""
self.register_view.setup_ui()
def show_main_view(self):
"""显示主视图"""
self.main_view.setup_ui()
def show_change_pwd_option_view(self):
"""显示修改密码选项视图"""
self.password_view.show_change_pwd_option_frame()
def show_standard_change_frame(self):
"""显示标准修改密码视图"""
self.password_view.show_standard_change_frame()
def show_code_change_frame(self):
"""显示验证码修改密码视图"""
self.password_view.show_code_change_frame()
# 业务逻辑处理方法
def handle_login(self, email, password):
"""处理登录逻辑"""
if not email or not password:
messagebox.showerror("错误", "邮箱或密码不能为空")
return
success, message = self.auth_service.login(email, password)
if success:
# 保存当前用户信息
self.current_email = email
self.current_username = self.auth_service.users[email]['username']
self.current_level = self.auth_service.users[email]['level']
messagebox.showinfo("成功", f"欢迎回来,{self.current_username}!")
self.show_main_view()
else:
messagebox.showerror("错误", message)
def handle_send_code(self, email):
"""处理发送验证码"""
if not email:
messagebox.showerror("错误", "请输入邮箱")
return
success, message = self.auth_service.send_verification_code(email)
if success:
messagebox.showinfo("成功", message)
else:
messagebox.showerror("错误", message)
def handle_register(self, username, email, code, password, confirm_pwd):
"""处理注册逻辑"""
# 后端二次校验(避免前端绕过验证)
if not username:
messagebox.showerror("错误", "用户名不能为空")
return
if not email:
messagebox.showerror("错误", "邮箱不能为空")
return
if not code:
messagebox.showerror("错误", "验证码不能为空")
return
if not password or not confirm_pwd:
messagebox.showerror("错误", "密码不能为空")
return
if password != confirm_pwd:
messagebox.showerror("错误", "两次密码不一致")
return
# 调用注册服务
success, message = self.auth_service.register_user(email, code, username, password)
if success:
messagebox.showinfo("成功", message)
self.show_login_view()
else:
messagebox.showerror("错误", message)
def handle_start_quiz(self, question_count, level):
"""开始答题"""
try:
question_count = int(question_count)
if question_count <= 0:
raise ValueError
except ValueError:
messagebox.showerror("错误", "请输入有效的题目数量(正整数)")
return
self.current_level = level
generator = self.question_generators[self.current_level]
self.current_questions = []
try:
for _ in range(question_count):
question, answer = generator.generate_question()
options = generator.generate_options(answer)
self.current_questions.append({
'question': question,
'answer': answer,
'options': options
})
except Exception as e:
messagebox.showerror("错误", f"生成题目失败:{str(e)}")
return
self.user_answers = [None] * question_count
self.current_question_index = 0
self.quiz_view.show_quiz_frame()
def prev_question(self):
"""切换到上一题"""
if self.current_question_index > 0:
self.save_current_answer()
self.current_question_index -= 1
self.quiz_view.show_quiz_frame()
def next_question(self):
"""切换到下一题"""
if self.current_question_index < len(self.current_questions) - 1:
self.save_current_answer()
self.current_question_index += 1
self.quiz_view.show_quiz_frame()
def save_current_answer(self):
"""保存当前题目的用户答案"""
selected_option = self.quiz_view.get_current_answer()
self.user_answers[self.current_question_index] = selected_option
def submit_quiz(self):
"""提交答案并计算得分"""
self.save_current_answer()
# 计算得分
correct_count = 0
total_answered = 0
for i, question in enumerate(self.current_questions):
user_answer = self.user_answers[i]
# 跳过未答题目
if user_answer is None:
continue
total_answered += 1
try:
# 转换为浮点数比较(允许微小误差)
user_answer_float = float(user_answer)
correct_answer_float = float(question['answer'])
if abs(user_answer_float - correct_answer_float) < 0.001:
correct_count += 1
except (ValueError, TypeError):
# 答案转换失败(如输入非数字),视为答错
continue
# 显示结果
if total_answered == 0:
messagebox.showinfo("答题结果", "你未答任何题目得分0分")
else:
score = (correct_count / total_answered) * 100
messagebox.showinfo("答题结果",
f"答题完成!\n"
f"总题数:{len(self.current_questions)}\n"
f"已答:{total_answered}\n"
f"答对:{correct_count}\n"
f"答错:{total_answered - correct_count}\n"
f"得分:{score:.2f}")
self.show_main_view()
def handle_change_password_standard(self, old_password, new_password, confirm_new_pwd):
"""处理通过原密码修改密码"""
if not old_password or not new_password or not confirm_new_pwd:
messagebox.showerror("错误", "请输入所有密码字段")
return
if new_password != confirm_new_pwd:
messagebox.showerror("错误", "两次新密码不一致")
return
success, message = self.auth_service.change_password(self.current_email, old_password, new_password)
if success:
messagebox.showinfo("成功", message)
self.show_main_view()
else:
messagebox.showerror("错误", message)
def handle_send_pwd_code(self):
"""处理发送密码重置验证码"""
try:
success, message = self.auth_service.send_password_reset_code(self.current_email)
if success:
messagebox.showinfo("成功", message)
else:
messagebox.showerror("错误", message)
except Exception as e:
messagebox.showerror("错误", f"发送失败:{str(e)}")
def handle_change_password_with_code(self, code, new_password, confirm_new_pwd):
"""处理通过验证码修改密码"""
if not code:
messagebox.showerror("错误", "请输入验证码")
return
if not new_password or not confirm_new_pwd:
messagebox.showerror("错误", "请输入新密码")
return
if new_password != confirm_new_pwd:
messagebox.showerror("错误", "两次新密码不一致")
return
success, message = self.auth_service.reset_password_with_code(self.current_email, code, new_password)
if success:
messagebox.showinfo("成功", message)
self.show_main_view()
else:
messagebox.showerror("错误", message)

@ -0,0 +1,6 @@
"""程序入口"""
from controller.app_controller import AppController
if __name__ == "__main__":
app = AppController()
app.run()

@ -0,0 +1,165 @@
"""认证服务模型"""
import os
import random
import re
import smtplib
import json
from datetime import datetime
from email.mime.text import MIMEText
from email.header import Header
from config import Config
class AuthService:
def __init__(self):
self.users = {} # 存储用户数据key=邮箱value=字典username、password等
self.verification_codes = {} # 存储验证码key=邮箱value=验证码
self.load_users()
def load_users(self):
"""从JSON文件加载用户数据"""
if os.path.exists(Config.USER_DATA_FILE):
with open(Config.USER_DATA_FILE, 'r', encoding='utf-8') as f:
self.users = json.load(f)
def save_users(self):
"""将用户数据保存到JSON文件"""
with open(Config.USER_DATA_FILE, 'w', encoding='utf-8') as f:
json.dump(self.users, f, ensure_ascii=False, indent=2)
def is_valid_email(self, email):
"""验证邮箱格式是否正确"""
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_password(self, password):
"""验证密码强度6-10位包含大小写字母和数字"""
if 6 <= len(password) <= 10:
has_upper = any(c.isupper() for c in password)
has_lower = any(c.islower() for c in password)
has_digit = any(c.isdigit() for c in password)
return has_upper and has_lower and has_digit
return False
def is_username_exists(self, username):
"""检查用户名是否已存在"""
for user_data in self.users.values():
if user_data.get('username') == username:
return True
return False
def send_verification_code(self, email):
"""发送验证码到邮箱"""
if not self.is_valid_email(email):
return False, "邮箱格式不正确"
if email in self.users:
return False, "该邮箱已注册"
# 生成6位验证码
code = str(random.randint(100000, 999999))
self.verification_codes[email] = code # 保存验证码有效期10分钟可后续添加过期逻辑
try:
# 配置邮件内容
msg = MIMEText(f"您的注册验证码是:{code}有效期10分钟。", 'plain', 'utf-8')
msg['From'] = 'Math Learning Software <3312923370@qq.com>' # 英文显示名称
msg['To'] = Header(email, 'utf-8')
msg['Subject'] = Header("注册验证码", 'utf-8')
# 发送邮件QQ邮箱SMTP SSL
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
server.login(Config.EMAIL_ADDRESS, Config.EMAIL_AUTH_CODE)
server.sendmail(Config.EMAIL_ADDRESS, [email], msg.as_string())
server.quit()
return True, "验证码已发送至您的邮箱"
except Exception as e:
return False, f"发送失败:{str(e)}"
def register_user(self, email, code, username, password):
"""注册用户"""
# 验证验证码
if email not in self.verification_codes:
return False, "请先获取验证码"
if self.verification_codes[email] != code:
return False, "验证码不正确"
# 验证用户名
if not username:
return False, "用户名不能为空"
if self.is_username_exists(username):
return False, "用户名已存在"
# 验证密码(这里可以省略,因为前端已实时验证,但后端仍需校验)
if not self.is_valid_password(password):
return False, "密码需6-10位包含大小写字母和数字"
# 保存用户数据
self.users[email] = {
'username': username,
'password': password,
'level': 'primary', # 默认小学难度
'register_time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
self.save_users()
del self.verification_codes[email] # 清除验证码
return True, "注册成功"
def login(self, email, password):
"""用户登录"""
if email not in self.users:
return False, "用户不存在"
if self.users[email]['password'] != password:
return False, "密码不正确"
return True, "登录成功"
def change_password(self, email, old_password, new_password):
"""修改密码"""
if email not in self.users:
return False, "用户不存在"
if self.users[email]['password'] != old_password:
return False, "原密码不正确"
if not self.is_valid_password(new_password):
return False, "新密码需6-10位包含大小写字母和数字"
self.users[email]['password'] = new_password
self.save_users()
return True, "密码修改成功"
def send_password_reset_code(self, email):
"""发送密码重置验证码"""
if not self.is_valid_email(email):
return False, "邮箱格式不正确"
if email not in self.users:
return False, "该邮箱未注册"
# 生成6位验证码
code = str(random.randint(100000, 999999))
self.verification_codes[f"pwd_reset_{email}"] = code # 保存验证码
try:
# 配置邮件内容
msg = MIMEText(f"您的密码重置验证码是:{code}有效期10分钟。", 'plain', 'utf-8')
msg['From'] = 'Math Learning Software <3312923370@qq.com>'
msg['To'] = Header(email, 'utf-8')
msg['Subject'] = Header("密码重置验证码", 'utf-8')
# 发送邮件
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
server.login(Config.EMAIL_ADDRESS, Config.EMAIL_AUTH_CODE)
server.sendmail(Config.EMAIL_ADDRESS, [email], msg.as_string())
server.quit()
return True, "验证码已发送至您的邮箱"
except Exception as e:
return False, f"发送失败:{str(e)}"
def reset_password_with_code(self, email, code, new_password):
"""通过验证码重置密码"""
# 验证验证码
stored_code = self.verification_codes.get(f"pwd_reset_{email}")
if stored_code != code:
return False, "验证码不正确"
# 验证密码强度(前端已实时验证,后端仍需校验)
if not self.is_valid_password(new_password):
return False, "新密码需6-10位包含大小写字母和数字"
# 更新密码
self.users[email]['password'] = new_password
self.save_users()
# 清除验证码
if f"pwd_reset_{email}" in self.verification_codes:
del self.verification_codes[f"pwd_reset_{email}"]
return True, "密码重置成功"

@ -0,0 +1,384 @@
"""题目生成器模型 - 改进版"""
import random
import math
import re
from abc import ABC, abstractmethod
class QuestionGenerator(ABC):
"""题目生成器抽象基类"""
def __init__(self):
self.generated_questions = set() # 记录已生成的题目
@abstractmethod
def generate_question(self) -> (str, float):
"""生成题目和答案"""
pass
@abstractmethod
def generate_options(self, answer) -> list:
"""生成选择题选项4个选项包含正确答案"""
pass
@abstractmethod
def validate_question(self, question: str) -> bool:
"""验证题目是否符合难度要求"""
pass
class PrimaryQuestionGenerator(QuestionGenerator):
"""小学题目生成器 - 只实现加减乘除"""
def generate_question(self) -> (str, float):
"""生成小学题目:加减乘除运算"""
operators = ['+', '-', '×', '÷']
num_count = random.randint(2, 5) # 2-5个数字运算
numbers = [str(random.randint(1, 100)) for _ in range(num_count)]
question = numbers[0]
answer = float(numbers[0])
# 构建表达式并计算答案
for i in range(num_count - 1):
op = random.choice(operators)
next_num_str = numbers[i+1]
next_num = float(next_num_str)
# 处理减法和除法特殊情况
if op == '-':
question, next_num_str = self._handle_subtraction(question, next_num_str, answer)
next_num = float(next_num_str)
elif op == '÷' and next_num == 0:
next_num_str = str(random.randint(1, 100))
next_num = float(next_num_str)
# 随机添加括号增加复杂度
if random.choice([True, False]) and i < num_count - 2:
question = f"({question}) {op} {next_num_str}"
else:
question = f"{question} {op} {next_num_str}"
# 计算当前答案
if op == '+':
answer += next_num
elif op == '-':
answer -= next_num
elif op == '×':
answer *= next_num
elif op == '÷':
answer = round(answer / next_num, 2) # 保留两位小数
question_str = f"{question} ="
# 检查是否重复
max_attempts = 50
for attempt in range(max_attempts):
if question_str not in self.generated_questions:
self.generated_questions.add(question_str)
return question_str, answer
# 如果重复,重新生成
return self.generate_question()
# 如果尝试多次仍然重复,清空记录重新开始
self.generated_questions.clear()
return self.generate_question()
def _handle_subtraction(self, current_expr, next_num_str, current_answer):
"""处理减法,确保结果不为负数"""
next_num = float(next_num_str)
if current_answer < next_num:
# 交换数字确保结果非负
return next_num_str, str(int(current_answer))
return current_expr, next_num_str
def generate_options(self, answer) -> list:
"""生成小学题目选项"""
options = [answer]
# 根据答案大小调整选项范围
if answer <= 10:
variation_range = (1, 3)
elif answer <= 50:
variation_range = (2, 10)
else:
variation_range = (5, 20)
while len(options) < 4:
variation_type = random.choice(['add', 'subtract', 'multiply', 'divide'])
if variation_type == 'add':
variation = answer + random.randint(*variation_range)
elif variation_type == 'subtract':
variation = max(1, answer - random.randint(*variation_range))
elif variation_type == 'multiply':
variation = answer * random.randint(2, 3)
else: # divide
variation = max(1, round(answer / random.randint(2, 3), 2))
# 确保选项不重复且合理
if variation not in options and variation > 0:
options.append(variation)
random.shuffle(options)
return options
def validate_question(self, question: str) -> bool:
"""验证题目是否符合小学难度"""
# 小学题目不应包含平方、开根、三角函数
invalid_ops = ['²', '', 'sin', 'cos', 'tan']
return not any(op in question for op in invalid_ops)
class JuniorQuestionGenerator(QuestionGenerator):
"""初中题目生成器 - 只实现平方和开根"""
def generate_question(self) -> (str, float):
"""生成初中题目:平方和开根运算"""
question_types = ['square', 'sqrt']
max_attempts = 50
for attempt in range(max_attempts):
if random.random() < 0.7: # 70%概率生成复杂题目
question, answer = self._generate_complex_question()
else: # 30%概率生成简单题目
question, answer = self._generate_simple_question()
# 检查是否重复
if question not in self.generated_questions:
self.generated_questions.add(question)
return question, answer
# 如果尝试多次仍然重复,清空记录重新开始
self.generated_questions.clear()
return self.generate_question()
def _generate_complex_question(self) -> (str, float):
"""生成复杂的初中题目(包含基础运算)"""
operators = ['+', '-', '×', '÷']
num_count = random.randint(1, 3)
numbers = [str(random.randint(1, 100)) for _ in range(num_count)]
if num_count == 1:
base_question = numbers[0]
answer = float(numbers[0])
else:
base_question = numbers[0]
answer = float(numbers[0])
for i in range(1, num_count):
op = random.choice(operators)
next_num = float(numbers[i])
base_question = f"{base_question} {op} {numbers[i]}"
if op == '+':
answer += next_num
elif op == '-':
answer = max(0, answer - next_num) # 确保非负
elif op == '×':
answer *= next_num
elif op == '÷':
answer = round(answer / next_num, 2) if next_num != 0 else answer
# 添加平方或开根运算
if random.choice([True, False]):
# 平方运算
squared_num = random.randint(2, 15)
operator = random.choice(operators)
if operator == '+':
answer = squared_num ** 2 + answer
elif operator == '-':
answer = max(0, squared_num ** 2 - answer)
elif operator == '×':
answer = squared_num ** 2 * answer
elif operator == '÷':
answer = round((squared_num ** 2) / answer, 2) if answer != 0 else squared_num ** 2
question = f"{squared_num}² {operator} {base_question} ="
else:
# 开根运算
sqrt_num = random.randint(1, 15) ** 2 # 确保是完全平方数
operator = random.choice(operators)
sqrt_value = math.sqrt(sqrt_num)
if operator == '+':
answer = sqrt_value + answer
elif operator == '-':
answer = max(0, sqrt_value - answer)
elif operator == '×':
answer = sqrt_value * answer
elif operator == '÷':
answer = round(sqrt_value / answer, 2) if answer != 0 else sqrt_value
question = f"{sqrt_num} {operator} {base_question} ="
return question, round(answer, 2)
def _generate_simple_question(self) -> (str, float):
"""生成简单的初中题目"""
if random.choice([True, False]):
# 简单平方
num = random.randint(2, 20)
return f"{num}² =", num ** 2
else:
# 简单开根
num = random.randint(1, 15)
return f"{num * num} =", num
def generate_options(self, answer) -> list:
"""生成初中题目选项"""
options = [answer]
while len(options) < 4:
variation = random.choice([
answer + random.randint(1, 5),
max(0.1, answer - random.randint(1, 5)),
round(answer * random.uniform(1.1, 1.5), 2),
round(answer / random.uniform(1.1, 1.5), 2) if answer != 0 else random.randint(1, 5)
])
if variation not in options:
options.append(round(variation, 2))
random.shuffle(options)
return options
def validate_question(self, question: str) -> bool:
"""验证题目是否符合初中难度"""
# 初中题目应包含平方或开根,但不包含三角函数
return ('²' in question or '' in question) and \
not any(func in question for func in ["sin", "cos", "tan"])
class SeniorQuestionGenerator(QuestionGenerator):
"""高中题目生成器 - 只实现三角函数"""
def generate_question(self) -> (str, float):
"""生成高中题目:三角函数运算"""
max_attempts = 50
for attempt in range(max_attempts):
if random.random() < 0.7: # 70%概率生成复杂题目
question, answer = self._generate_complex_question()
else: # 30%概率生成简单题目
question, answer = self._generate_simple_question()
# 检查是否重复
if question not in self.generated_questions:
self.generated_questions.add(question)
return question, answer
# 如果尝试多次仍然重复,清空记录重新开始
self.generated_questions.clear()
return self.generate_question()
def _generate_complex_question(self) -> (str, float):
"""生成复杂的高中题目(包含基础运算)"""
operators = ['+', '-', '×', '÷']
num_count = random.randint(1, 3)
numbers = [str(random.randint(1, 100)) for _ in range(num_count)]
if num_count == 1:
base_question = numbers[0]
answer = float(numbers[0])
else:
base_question = numbers[0]
answer = float(numbers[0])
for i in range(1, num_count):
op = random.choice(operators)
next_num = float(numbers[i])
base_question = f"{base_question} {op} {numbers[i]}"
if op == '+':
answer += next_num
elif op == '-':
answer -= next_num
elif op == '×':
answer *= next_num
elif op == '÷':
answer = round(answer / next_num, 2) if next_num != 0 else answer
# 添加三角函数运算
trig_funcs = ["sin", "cos", "tan"]
angles = [0, 30, 45, 60, 90, 120, 135, 150, 180, 210, 225, 240, 270, 300, 315, 330]
trig_func = random.choice(trig_funcs)
angle = random.choice(angles)
operator = random.choice(operators)
# 计算三角函数值
trig_value = self._calculate_trig_value(trig_func, angle)
# 计算最终答案
if operator == '+':
final_answer = trig_value + answer
elif operator == '-':
final_answer = trig_value - answer
elif operator == '×':
final_answer = trig_value * answer
elif operator == '÷':
final_answer = round(trig_value / answer, 2) if answer != 0 else trig_value
question = f"{trig_func}({angle}°) {operator} {base_question} ="
return question, round(final_answer, 2)
def _generate_simple_question(self) -> (str, float):
"""生成简单的高中题目"""
trig_funcs = ["sin", "cos", "tan"]
angles = [0, 30, 45, 60, 90, 120, 135, 150, 180, 210, 225, 240, 270, 300, 315, 330]
trig_func = random.choice(trig_funcs)
angle = random.choice(angles)
trig_value = self._calculate_trig_value(trig_func, angle)
return f"{trig_func}({angle}°) =", round(trig_value, 2)
def _calculate_trig_value(self, func: str, angle: int) -> float:
"""计算三角函数值"""
# 转换为弧度
rad = math.radians(angle)
if func == "sin":
value = math.sin(rad)
elif func == "cos":
value = math.cos(rad)
elif func == "tan":
# 对于tan函数避免90°和270°等不合法角度
if angle % 180 == 90:
# 如果随机到不合法角度,重新选择
return self._calculate_trig_value(func, random.choice([0, 30, 45, 60, 120, 135, 150, 180, 210, 225, 240, 300, 315, 330]))
value = math.tan(rad)
return round(value, 2)
def generate_options(self, answer) -> list:
"""生成高中题目选项"""
options = [answer]
while len(options) < 4:
if isinstance(answer, float) and abs(answer) < 10:
# 对于较小的浮点数答案
variation = random.choice([
round(answer + random.uniform(0.1, 0.5), 2),
round(answer - random.uniform(0.1, 0.5), 2),
round(answer * random.uniform(1.1, 1.5), 2),
round(answer / random.uniform(1.1, 1.5), 2) if answer != 0 else round(random.uniform(0.1, 0.5), 2)
])
else:
# 对于较大的答案或整数答案
variation = random.choice([
answer + random.randint(1, 5),
answer - random.randint(1, 5),
round(answer * random.uniform(1.1, 1.3), 2),
round(answer / random.uniform(1.1, 1.3), 2) if answer != 0 else random.randint(1, 5)
])
if variation not in options:
options.append(round(variation, 2))
random.shuffle(options)
return options
def validate_question(self, question: str) -> bool:
"""验证题目是否符合高中难度"""
# 高中题目应包含三角函数,但不包含平方或开根
return any(func in question for func in ["sin", "cos", "tan"]) and \
not ('²' in question or '' in question)

@ -0,0 +1,51 @@
"""登录视图"""
import tkinter as tk
from tkinter import ttk
class LoginView:
def __init__(self, root, controller):
self.root = root
self.controller = controller
self.frame = None
self.setup_ui()
def setup_ui(self):
"""显示登录界面"""
self.clear_window()
self.frame = ttk.Frame(self.root, padding="20")
self.frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 邮箱输入
ttk.Label(self.frame, text="邮箱:").grid(row=0, column=0, sticky=tk.W, pady=5)
self.email_entry = ttk.Entry(self.frame, width=30)
self.email_entry.grid(row=0, column=1, pady=5)
# 密码输入
ttk.Label(self.frame, text="密码:").grid(row=1, column=0, sticky=tk.W, pady=5)
self.password_entry = ttk.Entry(self.frame, width=30, show='*')
self.password_entry.grid(row=1, column=1, pady=5)
# 显示密码按钮
self.show_pwd_btn = ttk.Button(self.frame, text="👁️", command=self.toggle_pwd_visibility)
self.show_pwd_btn.grid(row=1, column=2, pady=5, padx=5)
# 登录按钮
ttk.Button(self.frame, text="登录", command=self.login).grid(row=2, column=0, columnspan=2, pady=10)
# 注册按钮
ttk.Button(self.frame, text="注册", command=self.controller.show_register_view).grid(row=3, column=0, columnspan=2, pady=5)
# 退出按钮
ttk.Button(self.frame, text="退出", command=self.root.quit).grid(row=4, column=0, columnspan=2, pady=5)
def toggle_pwd_visibility(self):
"""切换密码输入框的可见性"""
if self.password_entry.cget('show') == '*':
self.password_entry.config(show='')
else:
self.password_entry.config(show='*')
def login(self):
"""处理登录逻辑"""
email = self.email_entry.get().strip()
password = self.password_entry.get().strip()
self.controller.handle_login(email, password)
def clear_window(self):
"""清空窗口所有组件"""
for widget in self.root.winfo_children():
widget.destroy()

@ -0,0 +1,49 @@
"""主界面视图"""
import tkinter as tk
from tkinter import ttk
from config import Config
class MainView:
def __init__(self, root, controller):
self.root = root
self.controller = controller
self.frame = None
self.setup_ui()
def setup_ui(self):
"""显示主界面(登录后)"""
self.clear_window()
self.frame = ttk.Frame(self.root, padding="20")
self.frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 欢迎信息(显示用户名)
ttk.Label(self.frame, text=f"欢迎, {self.controller.current_username}", font=('微软雅黑', 12, 'bold')).grid(row=0, column=0, columnspan=2, pady=10)
# 难度选择
ttk.Label(self.frame, text="选择难度:").grid(row=1, column=0, sticky=tk.W, pady=5)
self.level_var = tk.StringVar(value=self.controller.current_level)
self.level_combo = ttk.Combobox(self.frame, textvariable=self.level_var, values=Config.DIFFICULTY_LEVELS, state='readonly')
self.level_combo.grid(row=1, column=1, pady=5)
# 题目数量输入
ttk.Label(self.frame, text="题目数量:").grid(row=2, column=0, sticky=tk.W, pady=5)
self.question_count_entry = ttk.Entry(self.frame, width=10)
self.question_count_entry.insert(0, "10") # 默认10题
self.question_count_entry.grid(row=2, column=1, pady=5)
# 开始答题按钮
self.start_quiz_btn = ttk.Button(self.frame, text="开始答题", command=self.start_quiz)
self.start_quiz_btn.grid(row=3, column=0, columnspan=2, pady=10)
# 修改密码按钮
self.change_pwd_btn = ttk.Button(self.frame, text="修改密码", command=self.controller.show_change_pwd_option_view)
self.change_pwd_btn.grid(row=4, column=0, columnspan=2, pady=5)
# 退出登录按钮
self.logout_btn = ttk.Button(self.frame, text="退出登录", command=self.controller.show_login_view)
self.logout_btn.grid(row=5, column=0, columnspan=2, pady=5)
def start_quiz(self):
"""开始答题"""
question_count = self.question_count_entry.get().strip()
level = self.level_var.get()
self.controller.handle_start_quiz(question_count, level)
def clear_window(self):
"""清空窗口所有组件"""
for widget in self.root.winfo_children():
widget.destroy()

@ -0,0 +1,250 @@
"""密码修改视图"""
import tkinter as tk
from tkinter import ttk
class PasswordView:
def __init__(self, root, controller):
self.root = root
self.controller = controller
self.frame = None
def show_change_pwd_option_frame(self):
"""显示修改密码方式选择界面"""
self.clear_window()
self.frame = ttk.Frame(self.root, padding="20")
self.frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 显示用户名和邮箱
ttk.Label(self.frame, text=f"当前用户: {self.controller.current_username}", font=('微软雅黑', 12)).grid(row=0, column=0, columnspan=2, pady=10)
ttk.Label(self.frame, text=f"邮箱: {self.controller.current_email}", font=('微软雅黑', 10)).grid(row=1, column=0, columnspan=2, pady=5)
# 修改密码方式选择
ttk.Label(self.frame, text="选择修改密码方式:", font=('微软雅黑', 10)).grid(row=2, column=0, columnspan=2, pady=10)
# 通过原密码修改按钮
ttk.Button(self.frame, text="通过原密码修改", command=self.controller.show_standard_change_frame,
width=20).grid(row=3, column=0, columnspan=2, pady=5)
# 通过验证码修改按钮
ttk.Button(self.frame, text="通过验证码修改(忘记密码)", command=self.controller.show_code_change_frame,
width=20).grid(row=4, column=0, columnspan=2, pady=5)
# 返回主界面按钮
ttk.Button(self.frame, text="返回主界面", command=self.controller.show_main_view).grid(row=5, column=0, columnspan=2, pady=10)
def show_standard_change_frame(self):
"""显示通过原密码修改密码界面"""
self.clear_window()
self.frame = ttk.Frame(self.root, padding="20")
self.frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 标题
ttk.Label(self.frame, text="通过原密码修改密码", font=('微软雅黑', 12, 'bold')).grid(row=0, column=0, columnspan=2, pady=10)
# 原密码输入
ttk.Label(self.frame, text="原密码:").grid(row=1, column=0, sticky=tk.W, pady=5)
self.old_pwd_entry = ttk.Entry(self.frame, width=30, show='*')
self.old_pwd_entry.grid(row=1, column=1, pady=5)
# 新密码输入(含实时验证)
ttk.Label(self.frame, text="新密码:").grid(row=2, column=0, sticky=tk.W, pady=5)
self.new_pwd_entry = ttk.Entry(self.frame, width=30, show='*')
self.new_pwd_entry.grid(row=2, column=1, pady=5)
# 新密码提示标签
self.new_pwd_hint_label = ttk.Label(self.frame, text="", foreground="red")
self.new_pwd_hint_label.grid(row=2, column=2, sticky=tk.W, pady=5, padx=5)
# 显示新密码按钮
ttk.Button(self.frame, text="👁️", command=lambda: self.toggle_pwd_visibility(self.new_pwd_entry)).grid(row=2, column=3, pady=5, padx=5)
# 绑定新密码输入事件(实时验证)
self.new_pwd_entry.bind('<KeyRelease>', self.validate_new_password)
# 确认新密码输入(含实时验证)
ttk.Label(self.frame, text="确认新密码:").grid(row=3, column=0, sticky=tk.W, pady=5)
self.confirm_new_pwd_entry = ttk.Entry(self.frame, width=30, show='*')
self.confirm_new_pwd_entry.grid(row=3, column=1, pady=5)
# 确认新密码提示标签
self.confirm_new_pwd_hint_label = ttk.Label(self.frame, text="", foreground="red")
self.confirm_new_pwd_hint_label.grid(row=3, column=2, sticky=tk.W, pady=5, padx=5)
# 显示确认新密码按钮
ttk.Button(self.frame, text="👁️", command=lambda: self.toggle_pwd_visibility(self.confirm_new_pwd_entry)).grid(row=3, column=3, pady=5, padx=5)
# 绑定确认新密码输入事件(实时验证)
self.confirm_new_pwd_entry.bind('<KeyRelease>', self.validate_confirm_new_password)
# 修改密码按钮(初始置灰,需验证通过才激活)
self.change_pwd_standard_btn = ttk.Button(self.frame, text="修改密码", command=self.change_password_standard, state=tk.DISABLED)
self.change_pwd_standard_btn.grid(row=4, column=0, columnspan=2, pady=10)
# 返回按钮
ttk.Button(self.frame, text="返回", command=self.controller.show_change_pwd_option_view).grid(row=5, column=0, columnspan=2, pady=5)
def toggle_pwd_visibility(self, entry):
"""切换密码输入框的可见性"""
if entry.cget('show') == '*':
entry.config(show='')
else:
entry.config(show='*')
def validate_new_password(self, event):
"""实时验证新密码格式(同注册密码规则)"""
new_password = self.new_pwd_entry.get().strip()
if not new_password:
self.new_pwd_hint_label.config(text="", foreground="red")
self.change_pwd_standard_btn.config(state=tk.DISABLED)
return
# 检查长度和字符类型
if not (6 <= len(new_password) <= 10):
self.new_pwd_hint_label.config(text="长度需6-10位", foreground="red")
self.change_pwd_standard_btn.config(state=tk.DISABLED)
return
has_upper = any(c.isupper() for c in new_password)
has_lower = any(c.islower() for c in new_password)
has_digit = any(c.isdigit() for c in new_password)
if has_upper and has_lower and has_digit:
self.new_pwd_hint_label.config(text="格式正确✅", foreground="green")
# 触发确认新密码校验
self.validate_confirm_new_password(None)
else:
self.new_pwd_hint_label.config(text="需包含大小写和数字", foreground="red")
self.change_pwd_standard_btn.config(state=tk.DISABLED)
def validate_confirm_new_password(self, event):
"""实时验证确认新密码是否与新密码一致"""
new_password = self.new_pwd_entry.get().strip()
confirm_new_pwd = self.confirm_new_pwd_entry.get().strip()
if not confirm_new_pwd:
self.confirm_new_pwd_hint_label.config(text="", foreground="red")
self.change_pwd_standard_btn.config(state=tk.DISABLED)
return
if confirm_new_pwd == new_password:
self.confirm_new_pwd_hint_label.config(text="一致✅", foreground="green")
# 新密码格式正确且确认一致,激活修改按钮
if self.new_pwd_hint_label.cget("text") == "格式正确✅":
self.change_pwd_standard_btn.config(state=tk.NORMAL)
else:
self.confirm_new_pwd_hint_label.config(text="不一致❌", foreground="red")
self.change_pwd_standard_btn.config(state=tk.DISABLED)
def change_password_standard(self):
"""处理通过原密码修改密码逻辑"""
old_password = self.old_pwd_entry.get().strip()
new_password = self.new_pwd_entry.get().strip()
confirm_new_pwd = self.confirm_new_pwd_entry.get().strip()
self.controller.handle_change_password_standard(old_password, new_password, confirm_new_pwd)
def show_code_change_frame(self):
"""显示通过验证码修改密码界面"""
self.clear_window()
self.frame = ttk.Frame(self.root, padding="20")
self.frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 标题
ttk.Label(self.frame, text="通过验证码修改密码", font=('微软雅黑', 12, 'bold')).grid(row=0, column=0, columnspan=2, pady=10)
# 邮箱显示
ttk.Label(self.frame, text="邮箱:").grid(row=1, column=0, sticky=tk.W, pady=5)
email_label = ttk.Label(self.frame, text=self.controller.current_email)
email_label.grid(row=1, column=1, sticky=tk.W, pady=5)
# 获取验证码按钮
self.get_pwd_code_btn = ttk.Button(self.frame, text="获取验证码", command=self.send_pwd_code)
self.get_pwd_code_btn.grid(row=1, column=2, pady=5, padx=5)
# 验证码输入
ttk.Label(self.frame, text="验证码:").grid(row=2, column=0, sticky=tk.W, pady=5)
self.pwd_code_entry = ttk.Entry(self.frame, width=30)
self.pwd_code_entry.grid(row=2, column=1, pady=5)
# 新密码输入(含实时验证)
ttk.Label(self.frame, text="新密码:").grid(row=3, column=0, sticky=tk.W, pady=5)
self.new_pwd_code_entry = ttk.Entry(self.frame, width=30, show='*')
self.new_pwd_code_entry.grid(row=3, column=1, pady=5)
# 新密码提示标签
self.new_pwd_code_hint_label = ttk.Label(self.frame, text="", foreground="red")
self.new_pwd_code_hint_label.grid(row=3, column=2, sticky=tk.W, pady=5, padx=5)
# 显示新密码按钮
ttk.Button(self.frame, text="👁️", command=lambda: self.toggle_pwd_visibility(self.new_pwd_code_entry)).grid(row=3, column=3, pady=5, padx=5)
# 绑定新密码输入事件(实时验证)
self.new_pwd_code_entry.bind('<KeyRelease>', self.validate_new_pwd_code)
# 确认新密码输入(含实时验证)
ttk.Label(self.frame, text="确认新密码:").grid(row=4, column=0, sticky=tk.W, pady=5)
self.confirm_new_pwd_code_entry = ttk.Entry(self.frame, width=30, show='*')
self.confirm_new_pwd_code_entry.grid(row=4, column=1, pady=5)
# 确认新密码提示标签
self.confirm_new_pwd_code_hint_label = ttk.Label(self.frame, text="", foreground="red")
self.confirm_new_pwd_code_hint_label.grid(row=4, column=2, sticky=tk.W, pady=5, padx=5)
# 显示确认新密码按钮
ttk.Button(self.frame, text="👁️", command=lambda: self.toggle_pwd_visibility(self.confirm_new_pwd_code_entry)).grid(row=4, column=3, pady=5, padx=5)
# 绑定确认新密码输入事件(实时验证)
self.confirm_new_pwd_code_entry.bind('<KeyRelease>', self.validate_confirm_new_pwd_code)
# 修改密码按钮(初始置灰,需验证通过才激活)
self.change_pwd_code_btn = ttk.Button(self.frame, text="修改密码", command=self.change_password_with_code, state=tk.DISABLED)
self.change_pwd_code_btn.grid(row=5, column=0, columnspan=2, pady=10)
# 返回按钮
ttk.Button(self.frame, text="返回", command=self.controller.show_change_pwd_option_view).grid(row=6, column=0, columnspan=2, pady=5)
def validate_new_pwd_code(self, event):
"""实时验证验证码修改中的新密码格式"""
new_password = self.new_pwd_code_entry.get().strip()
if not new_password:
self.new_pwd_code_hint_label.config(text="", foreground="red")
self.change_pwd_code_btn.config(state=tk.DISABLED)
return
# 检查长度和字符类型
if not (6 <= len(new_password) <= 10):
self.new_pwd_code_hint_label.config(text="长度需6-10位", foreground="red")
self.change_pwd_code_btn.config(state=tk.DISABLED)
return
has_upper = any(c.isupper() for c in new_password)
has_lower = any(c.islower() for c in new_password)
has_digit = any(c.isdigit() for c in new_password)
if has_upper and has_lower and has_digit:
self.new_pwd_code_hint_label.config(text="格式正确✅", foreground="green")
# 触发确认新密码校验
self.validate_confirm_new_pwd_code(None)
else:
self.new_pwd_code_hint_label.config(text="需包含大小写和数字", foreground="red")
self.change_pwd_code_btn.config(state=tk.DISABLED)
def validate_confirm_new_pwd_code(self, event):
"""实时验证验证码修改中的确认新密码"""
new_password = self.new_pwd_code_entry.get().strip()
confirm_new_pwd = self.confirm_new_pwd_code_entry.get().strip()
if not confirm_new_pwd:
self.confirm_new_pwd_code_hint_label.config(text="", foreground="red")
self.change_pwd_code_btn.config(state=tk.DISABLED)
return
if confirm_new_pwd == new_password:
self.confirm_new_pwd_code_hint_label.config(text="一致✅", foreground="green")
# 新密码格式正确且确认一致,激活修改按钮
if self.new_pwd_code_hint_label.cget("text") == "格式正确✅":
self.change_pwd_code_btn.config(state=tk.NORMAL)
else:
self.confirm_new_pwd_code_hint_label.config(text="不一致❌", foreground="red")
self.change_pwd_code_btn.config(state=tk.DISABLED)
def send_pwd_code(self):
"""发送修改密码验证码"""
self.controller.handle_send_pwd_code()
def change_password_with_code(self):
"""处理通过验证码修改密码逻辑"""
code = self.pwd_code_entry.get().strip()
new_password = self.new_pwd_code_entry.get().strip()
confirm_new_pwd = self.confirm_new_pwd_code_entry.get().strip()
self.controller.handle_change_password_with_code(code, new_password, confirm_new_pwd)
def clear_window(self):
"""清空窗口所有组件"""
for widget in self.root.winfo_children():
widget.destroy()

@ -0,0 +1,63 @@
"""答题视图"""
import tkinter as tk
from tkinter import ttk
class QuizView:
def __init__(self, root, controller):
self.root = root
self.controller = controller
self.frame = None
self.option_var = None
def show_quiz_frame(self):
"""显示答题界面"""
self.clear_window()
self.frame = ttk.Frame(self.root, padding="20")
self.frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 题目导航
ttk.Label(self.frame, text=f"题目 {self.controller.current_question_index + 1}/{len(self.controller.current_questions)}",
font=('微软雅黑', 10, 'bold')).grid(row=0, column=0, columnspan=2, pady=5)
# 题目内容
current_question = self.controller.current_questions[self.controller.current_question_index]
ttk.Label(self.frame, text=current_question['question'],
font=('微软雅黑', 12)).grid(row=1, column=0, columnspan=2, pady=10)
# 选项按钮单选A/B/C/D格式
self.option_var = tk.StringVar(value=None)
option_labels = ['A', 'B', 'C', 'D']
for i, option in enumerate(current_question['options']):
ttk.Radiobutton(
self.frame,
text=f"{option_labels[i]}: {option}",
variable=self.option_var,
value=str(option)
).grid(row=2 + i, column=0, sticky=tk.W, pady=5, padx=20)
# 恢复之前的选择(如果有)
if self.controller.user_answers[self.controller.current_question_index] is not None:
self.option_var.set(self.controller.user_answers[self.controller.current_question_index])
# 导航按钮
btn_frame = ttk.Frame(self.frame)
btn_frame.grid(row=6, column=0, columnspan=2, pady=15)
ttk.Button(btn_frame, text="上一题", command=self.controller.prev_question).grid(row=0, column=0, padx=10)
ttk.Button(btn_frame, text="下一题", command=self.controller.next_question).grid(row=0, column=1, padx=10)
# 提交按钮(最后一题显示)
if self.controller.current_question_index == len(self.controller.current_questions) - 1:
ttk.Button(self.frame, text="提交答案", command=self.controller.submit_quiz, width=15).grid(row=7, column=0, columnspan=2, pady=10)
# 返回主界面按钮
ttk.Button(self.frame, text="返回主界面", command=self.controller.show_main_view).grid(row=8, column=0, columnspan=2, pady=5)
def get_current_answer(self):
"""获取当前题目的用户答案"""
return self.option_var.get() if self.option_var else None
def clear_window(self):
"""清空窗口所有组件"""
for widget in self.root.winfo_children():
widget.destroy()

@ -0,0 +1,142 @@
"""注册视图"""
import tkinter as tk
from tkinter import ttk
class RegisterView:
def __init__(self, root, controller):
self.root = root
self.controller = controller
self.frame = None
self.setup_ui()
def setup_ui(self):
"""显示注册界面(含密码实时验证)"""
self.clear_window()
self.frame = ttk.Frame(self.root, padding="20")
self.frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 用户名输入
ttk.Label(self.frame, text="用户名:").grid(row=0, column=0, sticky=tk.W, pady=5)
self.reg_username_entry = ttk.Entry(self.frame, width=30)
self.reg_username_entry.grid(row=0, column=1, pady=5)
# 邮箱输入
ttk.Label(self.frame, text="邮箱:").grid(row=1, column=0, sticky=tk.W, pady=5)
self.reg_email_entry = ttk.Entry(self.frame, width=30)
self.reg_email_entry.grid(row=1, column=1, pady=5)
self.get_code_btn = ttk.Button(self.frame, text="获取验证码", command=self.send_code)
self.get_code_btn.grid(row=1, column=2, pady=5, padx=5)
# 验证码输入
ttk.Label(self.frame, text="验证码:").grid(row=2, column=0, sticky=tk.W, pady=5)
self.code_entry = ttk.Entry(self.frame, width=30)
self.code_entry.grid(row=2, column=1, pady=5)
# 密码输入(含实时验证)
ttk.Label(self.frame, text="密码:").grid(row=3, column=0, sticky=tk.W, pady=5)
self.reg_password_entry = ttk.Entry(self.frame, width=30, show='*')
self.reg_password_entry.grid(row=3, column=1, pady=5)
# 密码格式提示标签
self.pwd_hint_label = ttk.Label(self.frame, text="", foreground="red")
self.pwd_hint_label.grid(row=3, column=2, sticky=tk.W, pady=5, padx=5)
# 显示密码按钮
self.show_reg_pwd_btn = ttk.Button(self.frame, text="👁️", command=lambda: self.toggle_pwd_visibility(self.reg_password_entry))
self.show_reg_pwd_btn.grid(row=3, column=3, pady=5, padx=5)
# 绑定密码输入事件(实时验证)
self.reg_password_entry.bind('<KeyRelease>', self.validate_password)
# 确认密码输入(含实时验证)
ttk.Label(self.frame, text="确认密码:").grid(row=4, column=0, sticky=tk.W, pady=5)
self.confirm_pwd_entry = ttk.Entry(self.frame, width=30, show='*')
self.confirm_pwd_entry.grid(row=4, column=1, pady=5)
# 确认密码提示标签
self.confirm_pwd_hint_label = ttk.Label(self.frame, text="", foreground="red")
self.confirm_pwd_hint_label.grid(row=4, column=2, sticky=tk.W, pady=5, padx=5)
# 显示确认密码按钮
self.show_confirm_pwd_btn = ttk.Button(self.frame, text="👁️", command=lambda: self.toggle_pwd_visibility(self.confirm_pwd_entry))
self.show_confirm_pwd_btn.grid(row=4, column=3, pady=5, padx=5)
# 绑定确认密码输入事件(实时验证)
self.confirm_pwd_entry.bind('<KeyRelease>', self.validate_confirm_password)
# 注册按钮(初始置灰,需验证通过才激活)
self.register_btn = ttk.Button(self.frame, text="注册", command=self.register, state=tk.DISABLED)
self.register_btn.grid(row=5, column=0, columnspan=2, pady=10)
# 返回登录按钮
self.back_login_btn = ttk.Button(self.frame, text="返回登录", command=self.controller.show_login_view)
self.back_login_btn.grid(row=6, column=0, columnspan=2, pady=5)
def toggle_pwd_visibility(self, entry):
"""切换密码输入框的可见性"""
if entry.cget('show') == '*':
entry.config(show='')
else:
entry.config(show='*')
def validate_password(self, event):
"""实时验证密码格式长度6-10位包含大小写字母和数字"""
password = self.reg_password_entry.get().strip()
if not password:
self.pwd_hint_label.config(text="", foreground="red")
self.register_btn.config(state=tk.DISABLED)
return
# 1. 检查长度6-10位
if not (6 <= len(password) <= 10):
self.pwd_hint_label.config(text="长度需6-10位", foreground="red")
self.register_btn.config(state=tk.DISABLED)
return
# 2. 检查是否包含大小写字母和数字
has_upper = any(c.isupper() for c in password) # 大写字母
has_lower = any(c.islower() for c in password) # 小写字母
has_digit = any(c.isdigit() for c in password) # 数字
if has_upper and has_lower and has_digit:
self.pwd_hint_label.config(text="格式正确✅", foreground="green")
# 密码格式正确后,检查确认密码是否一致
self.validate_confirm_password(None) # 触发确认密码校验
else:
self.pwd_hint_label.config(text="需包含大小写和数字", foreground="red")
self.register_btn.config(state=tk.DISABLED)
def validate_confirm_password(self, event):
"""实时验证确认密码是否与原密码一致"""
password = self.reg_password_entry.get().strip()
confirm_pwd = self.confirm_pwd_entry.get().strip()
if not confirm_pwd:
self.confirm_pwd_hint_label.config(text="", foreground="red")
self.register_btn.config(state=tk.DISABLED)
return
# 检查确认密码是否与原密码一致
if confirm_pwd == password:
self.confirm_pwd_hint_label.config(text="一致✅", foreground="green")
# 密码格式正确且确认一致,激活注册按钮
if self.pwd_hint_label.cget("text") == "格式正确✅":
self.register_btn.config(state=tk.NORMAL)
else:
self.confirm_pwd_hint_label.config(text="不一致❌", foreground="red")
self.register_btn.config(state=tk.DISABLED)
def send_code(self):
"""发送注册验证码"""
email = self.reg_email_entry.get().strip()
self.controller.handle_send_code(email)
def register(self):
"""处理注册逻辑(前端已实时验证,后端仍需校验)"""
# 获取输入值
username = self.reg_username_entry.get().strip()
email = self.reg_email_entry.get().strip()
code = self.code_entry.get().strip()
password = self.reg_password_entry.get().strip()
confirm_pwd = self.confirm_pwd_entry.get().strip()
self.controller.handle_register(username, email, code, password, confirm_pwd)
def clear_window(self):
"""清空窗口所有组件"""
for widget in self.root.winfo_children():
widget.destroy()

@ -0,0 +1,38 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['src\\main.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='中小学数学学习软件',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
Loading…
Cancel
Save