补充 #13

Merged
p9o3yklam merged 16 commits from main into maziang 5 months ago

@ -5,4 +5,4 @@ PyQt5>=5.15.0
requests>=2.25.1
beautifulsoup4>=4.11.0
pillow>=9.0.0
chardet>=4.0.0
chardet>=4.0.0

@ -6,146 +6,253 @@ from pathlib import Path
class FileManager:
def __init__(self):
"""
初始化文件管理器
- 设置工作目录
- 初始化文件缓存
"""
# TODO: 实现构造函数逻辑
# 实现构造函数逻辑123
# 1. 设置默认工作目录
self.working_directory = Path.cwd()
# 2. 初始化文件缓存
self.file_cache = {}
# 3. 创建必要的目录结构
pass
def list_files(self, directory: str, extensions: Optional[List[str]] = None) -> List[str]:
"""
列出目录中的文件
- 遍历指定目录
- 根据扩展名过滤文件如果提供
- 返回文件路径列表
"""
# TODO: 实现文件列表逻辑
# 实现文件列表逻辑
# 1. 检查目录是否存在
if not os.path.exists(directory):
raise FileNotFoundError(f"目录 {directory} 不存在")
# 2. 遍历目录中的所有文件
# 3. 根据扩展名过滤文件(如果提供)
file_list = []
for root, _, files in os.walk(directory):
for file in files:
file_path = os.path.join(root, file)
# 3. 根据扩展名过滤文件(如果提供)
if extensions:
_, ext = os.path.splitext(file)
if ext.lower() in [e.lower() for e in extensions]:
file_list.append(file_path)
else:
file_list.append(file_path)
# 4. 返回文件路径列表
pass
return file_list
def copy_file(self, source: str, destination: str) -> bool:
"""
复制文件
- 将文件从源路径复制到目标路径
- 返回操作结果
"""
# TODO: 实现文件复制逻辑
# 实现文件复制逻辑
# 1. 检查源文件是否存在
# 2. 创建目标目录(如果不存在)
# 3. 执行文件复制操作
# 4. 处理异常情况
if not os.path.exists(source):
print(f"源文件 {source} 不存在")
return False
try:
# 2. 创建目标目录(如果不存在)
dest_dir = os.path.dirname(destination)
if dest_dir and not os.path.exists(dest_dir):
os.makedirs(dest_dir)
# 3. 执行文件复制操作
shutil.copy2(source, destination)
# 4. 处理异常情况
except Exception as e:
print(f"复制文件时出错: {e}")
return False
# 5. 返回操作结果
pass
return True
def move_file(self, source: str, destination: str) -> bool:
"""
移动文件
- 将文件从源路径移动到目标路径
- 返回操作结果
"""
# TODO: 实现文件移动逻辑
# 实现文件移动逻辑
# 1. 检查源文件是否存在
# 2. 创建目标目录(如果不存在)
# 3. 执行文件移动操作
# 4. 处理异常情况
if not os.path.exists(source):
print(f"源文件 {source} 不存在")
return False
try:
# 2. 创建目标目录(如果不存在)
dest_dir = os.path.dirname(destination)
if dest_dir and not os.path.exists(dest_dir):
os.makedirs(dest_dir)
# 3. 执行文件移动操作
shutil.move(source, destination)
# 4. 处理异常情况
except Exception as e:
print(f"移动文件时出错: {e}")
return False
# 5. 返回操作结果
pass
return True
def delete_file(self, file_path: str) -> bool:
"""
删除文件
- 删除指定路径的文件
- 返回操作结果
"""
# TODO: 实现文件删除逻辑
# 实现文件删除逻辑
# 1. 检查文件是否存在
# 2. 执行文件删除操作
# 3. 处理异常情况(如权限不足)
if not os.path.exists(file_path):
print(f"文件 {file_path} 不存在")
return False
try:
# 2. 执行文件删除操作
os.remove(file_path)
# 3. 处理异常情况(如权限不足)
except PermissionError:
print(f"没有权限删除文件 {file_path}")
return False
except Exception as e:
print(f"删除文件时出错: {e}")
return False
# 4. 返回操作结果
pass
return True
def get_file_info(self, file_path: str) -> Optional[Dict[str, Any]]:
"""
获取文件信息
- 获取文件大小修改时间等信息
- 返回信息字典
"""
# TODO: 实现文件信息获取逻辑
# 实现文件信息获取逻辑
# 1. 检查文件是否存在
# 2. 获取文件基本信息(大小、修改时间等)
# 3. 获取文件扩展名和类型
# 4. 返回信息字典
pass
if not os.path.exists(file_path):
print(f"文件 {file_path} 不存在")
return None
try:
# 2. 获取文件基本信息(大小、修改时间等)
stat_info = os.stat(file_path)
file_size = stat_info.st_size
modification_time = stat_info.st_mtime
# 3. 获取文件扩展名和类型
_, ext = os.path.splitext(file_path)
# 4. 返回信息字典
file_info = {
"path": file_path,
"size": file_size,
"modification_time": modification_time,
"extension": ext.lower(),
"name": os.path.basename(file_path)
}
return file_info
except Exception as e:
print(f"获取文件信息时出错: {e}")
return None
class DocumentOrganizer:
def __init__(self):
"""
初始化文档整理器
- 设置分类规则
- 初始化标签系统
"""
# TODO: 实现构造函数逻辑
# 实现构造函数逻辑
# 1. 设置默认分类规则
self.categorization_rules = {
"images": [".jpg", ".jpeg", ".png", ".gif", ".bmp"],
"documents": [".pdf", ".doc", ".docx", ".txt", ".md"],
"videos": [".mp4", ".avi", ".mkv", ".mov"],
"audio": [".mp3", ".wav", ".flac"],
"archives": [".zip", ".rar", ".7z", ".tar"]
}
# 2. 初始化标签系统
self.tags = {}
# 3. 创建必要的目录结构
pass
def categorize_documents(self, directory: str) -> Dict[str, List[str]]:
"""
分类文档
- 根据预设规则对文档进行分类
- 返回分类结果字典
"""
# TODO: 实现文档分类逻辑
# 1. 遍历目录中的所有文件
if not os.path.exists(directory):
raise FileNotFoundError(f"目录 {directory} 不存在")
# 2. 根据文件类型或内容特征进行分类
categorized_files = {category: [] for category in self.categorization_rules}
uncategorized = []
for root, _, files in os.walk(directory):
for file in files:
file_path = os.path.join(root, file)
_, ext = os.path.splitext(file)
# 根据扩展名分类
categorized = False
for category, extensions in self.categorization_rules.items():
if ext.lower() in extensions:
categorized_files[category].append(file_path)
categorized = True
break
if not categorized:
uncategorized.append(file_path)
categorized_files["uncategorized"] = uncategorized
# 3. 返回分类结果字典 {类别: [文件列表]}
pass
return categorized_files
def add_tag_to_file(self, file_path: str, tag: str) -> bool:
"""
为文件添加标签
- 在文件元数据中添加标签信息
- 返回操作结果
"""
# TODO: 实现标签添加逻辑
# 实现标签添加逻辑
# 1. 检查文件是否存在
if not os.path.exists(file_path):
print(f"文件 {file_path} 不存在")
return False
# 2. 读取文件元数据
# 3. 添加新标签
if file_path not in self.tags:
self.tags[file_path] = []
if tag not in self.tags[file_path]:
self.tags[file_path].append(tag)
# 4. 保存更新后的元数据
# 5. 返回操作结果
pass
return True
def search_files_by_tag(self, tag: str) -> List[str]:
"""
根据标签搜索文件
- 查找具有指定标签的所有文件
- 返回文件路径列表
"""
# TODO: 实现标签搜索逻辑
# 实现标签搜索逻辑
# 1. 遍历文件数据库或目录
# 2. 查找包含指定标签的文件
matching_files = []
for file_path, tags in self.tags.items():
if tag in tags:
matching_files.append(file_path)
# 3. 返回文件路径列表
pass
return matching_files
def backup_documents(self, source_dir: str, backup_dir: str) -> bool:
"""
备份文档
- 将源目录中的文档备份到备份目录
- 返回操作结果
"""
# TODO: 实现文档备份逻辑
# 实现文档备份逻辑
# 1. 创建备份目录(如果不存在)
if not os.path.exists(backup_dir):
os.makedirs(backup_dir)
# 2. 遍历源目录中的所有文件
# 3. 复制文件到备份目录
# 4. 处理异常情况
# 5. 返回操作结果
pass
if not os.path.exists(source_dir):
print(f"源目录 {source_dir} 不存在")
return False
try:
# 使用shutil.copytree进行目录复制
# 如果备份目录已存在且不为空,需要先清空或使用其他方法
for root, dirs, files in os.walk(source_dir):
# 计算相对路径
rel_path = os.path.relpath(root, source_dir)
dest_path = os.path.join(backup_dir, rel_path) if rel_path != '.' else backup_dir
# 创建目标目录
if not os.path.exists(dest_path):
os.makedirs(dest_path)
# 复制文件
for file in files:
src_file = os.path.join(root, file)
dest_file = os.path.join(dest_path, file)
shutil.copy2(src_file, dest_file)
# 3. 处理异常情况
except Exception as e:
print(f"备份文档时出错: {e}")
return False
# 4. 返回操作结果
return True

@ -4,54 +4,132 @@ from typing import Union
class FileParser:
@staticmethod
def parse_file(file_path: str) -> str:
"""
主解析函数根据文件扩展名路由到具体解析器
- 调用validate_file_path(file_path)验证路径
- 根据扩展名调用对应解析函数
- 统一异常处理
"""
# TODO: 实现主解析函数逻辑
pass
# 验证文件路径
if not FileParser.validate_file_path(file_path):
raise ValueError(f"Invalid file path: {file_path}")
# 获取文件扩展名
_, ext = os.path.splitext(file_path)
ext = ext.lower()
# 根据扩展名调用对应的解析函数
try:
if ext == '.txt':
return FileParser.parse_txt(file_path)
elif ext == '.docx':
return FileParser.parse_docx(file_path)
elif ext == '.pdf':
return FileParser.parse_pdf(file_path)
else:
raise ValueError(f"Unsupported file format: {ext}")
except Exception as e:
# 统一异常处理
raise Exception(f"Error parsing file {file_path}: {str(e)}")
@staticmethod
def parse_txt(file_path: str) -> str:
"""
解析纯文本文件
- 自动检测编码utf-8, gbk等
- 处理不同换行符
- 返回纯文本内容
"""
# TODO: 实现实现txt文件解析逻辑
pass
# 验证文件路径
if not FileParser.validate_file_path(file_path):
raise ValueError(f"Invalid file path: {file_path}")
# 导入工具函数来检测编码
try:
from utils.helper_functions import Utils
except ImportError:
# 如果无法导入,使用默认方法检测编码
import chardet
with open(file_path, 'rb') as f:
raw_data = f.read(1024)
encoding = chardet.detect(raw_data)['encoding'] or 'utf-8'
else:
# 使用工具函数检测编码
encoding = Utils.detect_encoding(file_path)
# 读取文件内容
with open(file_path, 'r', encoding=encoding, errors='ignore') as f:
content = f.read()
# 统一换行符为\n
content = content.replace('\r\n', '\n').replace('\r', '\n')
return content
@staticmethod
def parse_docx(file_path: str) -> str:
"""
解析Word文档
- 提取所有段落文本
- 保留基本格式换行
- 忽略图片表格等非文本元素
"""
# TODO: 实现docx文件解析逻辑
pass
# 验证文件路径
if not FileParser.validate_file_path(file_path):
raise ValueError(f"Invalid file path: {file_path}")
# 尝试导入python-docx库
try:
from docx import Document
except ImportError:
raise ImportError("python-docx library is required for parsing .docx files. Please install it using 'pip install python-docx'")
# 打开并解析docx文件
try:
doc = Document(file_path)
# 提取所有段落文本
paragraphs = []
for paragraph in doc.paragraphs:
paragraphs.append(paragraph.text)
# 用换行符连接所有段落
content = '\n'.join(paragraphs)
return content
except Exception as e:
raise Exception(f"Error parsing docx file {file_path}: {str(e)}")
@staticmethod
def parse_pdf(file_path: str) -> str:
"""
解析PDF文档
- 提取文本内容
- 保留基本格式换行
"""
# TODO: 实现PDF文件解析逻辑
pass
# 验证文件路径
if not FileParser.validate_file_path(file_path):
raise ValueError(f"Invalid file path: {file_path}")
# 尝试导入PyPDF2库
try:
import PyPDF2
except ImportError:
raise ImportError("PyPDF2 library is required for parsing .pdf files. Please install it using 'pip install PyPDF2'")
# 打开并解析pdf文件
try:
content = ""
with open(file_path, 'rb') as file:
pdf_reader = PyPDF2.PdfReader(file)
# 提取每一页的文本
for page in pdf_reader.pages:
content += page.extract_text()
content += "\n"
return content
except Exception as e:
raise Exception(f"Error parsing pdf file {file_path}: {str(e)}")
@staticmethod
def validate_file_path(file_path: str) -> bool:
"""
验证文件路径的有效性
- 检查文件是否存在
- 检查文件是否可读
- 检查文件大小是否合理
"""
# TODO: 实现文件路径验证逻辑
pass
# 检查文件是否存在
if not os.path.exists(file_path):
return False
# 检查是否为文件(而非目录)
if not os.path.isfile(file_path):
return False
# 检查文件是否可读
if not os.access(file_path, os.R_OK):
return False
# 检查文件大小是否合理小于10MB
file_size = os.path.getsize(file_path)
if file_size > 10 * 1024 * 1024: # 10MB
return False
return True

@ -9,111 +9,134 @@ class InputProcessor(QObject):
input_completed = pyqtSignal() # 输入完成信号
def __init__(self):
"""
初始化输入处理器
- 设置初始状态
- 初始化输入缓冲区
"""
super().__init__()
# TODO: 实现构造函数逻辑
# 实现构造函数逻辑
# 1. 初始化输入缓冲区
self.input_buffer = ""
# 2. 设置初始状态
self.is_input_active = False
# 3. 初始化相关属性
pass
self.expected_text = ""
self.current_position = 0
def process_key_event(self, key: str) -> bool:
"""
处理按键事件
- 检查按键有效性
- 更新输入缓冲区
- 发送相关信号
- 返回处理结果
"""
# TODO: 实现按键事件处理逻辑
# 实现按键事件处理逻辑
# 1. 检查按键是否有效
if not key:
return False
# 2. 根据按键类型处理(字符、功能键等)
# 3. 更新输入缓冲区
self.input_buffer += key
self.current_position += 1
# 4. 发送text_changed信号
self.text_changed.emit(key)
# 5. 检查是否完成输入如是则发送input_completed信号
if self.expected_text and self.input_buffer == self.expected_text:
self.input_completed.emit()
# 6. 返回处理结果
pass
return True
def get_current_input(self) -> str:
"""
获取当前输入
- 返回输入缓冲区内容
"""
# TODO: 实现获取当前输入逻辑
# 实现获取当前输入逻辑
# 1. 返回输入缓冲区内容
pass
return self.input_buffer
def reset_input(self):
"""
重置输入
- 清空输入缓冲区
- 重置相关状态
"""
# TODO: 实现输入重置逻辑
# 实现输入重置逻辑
# 1. 清空输入缓冲区
self.input_buffer = ""
# 2. 重置相关状态变量
self.current_position = 0
self.is_input_active = False
# 3. 发送重置信号(如需要)
pass
def set_expected_text(self, text: str):
"""
设置期望文本
- 用于后续输入验证
"""
# TODO: 实现设置期望文本逻辑
# 实现设置期望文本逻辑
# 1. 保存期望文本
self.expected_text = text
# 2. 初始化匹配相关状态
pass
self.current_position = 0
self.input_buffer = ""
self.is_input_active = True
class InputValidator:
def __init__(self):
"""
初始化输入验证器
- 设置验证规则
"""
# TODO: 实现构造函数逻辑
# 实现构造函数逻辑
# 1. 初始化验证规则
self.case_sensitive = True
# 2. 设置默认验证参数
pass
self.min_accuracy = 0.0
def validate_character(self, input_char: str, expected_char: str) -> bool:
"""
验证字符输入
- 比较输入字符与期望字符
- 返回验证结果
"""
# TODO: 实现字符验证逻辑
# 实现字符验证逻辑
# 1. 比较输入字符与期望字符
# 2. 考虑大小写敏感性设置
if self.case_sensitive:
return input_char == expected_char
else:
return input_char.lower() == expected_char.lower()
# 3. 返回验证结果
pass
def validate_word(self, input_word: str, expected_word: str) -> dict:
"""
验证单词输入
- 比较输入单词与期望单词
- 返回详细验证结果正确字符数错误字符数等
"""
# TODO: 实现单词验证逻辑
# 实现单词验证逻辑
# 1. 逐字符比较输入单词与期望单词
correct_count = 0
incorrect_count = 0
total_chars = max(len(input_word), len(expected_word))
# 2. 统计正确/错误字符数
for i in range(total_chars):
input_char = input_word[i] if i < len(input_word) else ""
expected_char = expected_word[i] if i < len(expected_word) else ""
if self.validate_character(input_char, expected_char):
correct_count += 1
else:
incorrect_count += 1
# 3. 计算准确率
accuracy = correct_count / total_chars if total_chars > 0 else 0.0
# 4. 返回验证结果字典
pass
return {
"correct_count": correct_count,
"incorrect_count": incorrect_count,
"total_chars": total_chars,
"accuracy": accuracy
}
def calculate_accuracy(self, input_text: str, expected_text: str) -> float:
"""
计算输入准确率
- 比较输入文本与期望文本
- 返回准确率百分比
"""
# TODO: 实现准确率计算逻辑
# 实现准确率计算逻辑
# 1. 比较输入文本与期望文本
# 2. 统计正确字符数
correct_count = 0
total_chars = max(len(input_text), len(expected_text))
if total_chars == 0:
return 1.0 # 两个空字符串认为是完全匹配
for i in range(total_chars):
input_char = input_text[i] if i < len(input_text) else ""
expected_char = expected_text[i] if i < len(expected_text) else ""
if self.validate_character(input_char, expected_char):
correct_count += 1
# 3. 计算准确率百分比
accuracy = correct_count / total_chars
# 4. 返回准确率
pass
return accuracy

@ -1,22 +1,46 @@
# main.py
import sys
import traceback
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt
from src.main_window import MainWindow
def main():
"""
应用程序主入口点
- 创建QApplication实例
- 设置应用程序属性
- 创建MainWindow实例
- 显示窗口
- 启动事件循环
- 返回退出码
"""
# TODO: 实现主函数逻辑
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
try:
# 创建QApplication实例
app = QApplication(sys.argv)
# 设置应用程序属性
app.setApplicationName("隐私学习软件")
app.setApplicationVersion("1.0")
app.setOrganizationName("个人开发者")
# 设置高DPI支持
app.setAttribute(Qt.AA_EnableHighDpiScaling, True)
app.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
# 创建主窗口
window = MainWindow()
window.show()
# 启动事件循环并返回退出码
exit_code = app.exec_()
sys.exit(exit_code)
except Exception as e:
# 打印详细的错误信息
print(f"应用程序发生未捕获的异常: {e}")
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()

@ -1,8 +1,10 @@
import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTextEdit, QAction,
QFileDialog, QVBoxLayout, QWidget, QLabel, QStatusBar)
from PyQt5.QtGui import QFont, QTextCharFormat, QColor
QFileDialog, QVBoxLayout, QWidget, QLabel, QStatusBar, QMessageBox)
from PyQt5.QtGui import QFont, QTextCharFormat, QColor, QTextCursor
from PyQt5.QtCore import Qt
from src.file_parser import FileParser
from src.typing_logic import TypingLogic
class MainWindow(QMainWindow):
def __init__(self):
@ -14,8 +16,13 @@ class MainWindow(QMainWindow):
- 初始化当前输入位置
- 调用initUI()方法
"""
# TODO: 实现构造函数逻辑
pass
super().__init__()
self.learning_content = ""
self.current_position = 0
self.typing_logic = None
self.text_edit = None
self.status_bar = None
self.initUI()
def initUI(self):
"""
@ -25,8 +32,24 @@ class MainWindow(QMainWindow):
- 创建状态栏并显示"就绪"
- 连接文本变化信号到onTextChanged
"""
# TODO: 实现UI初始化逻辑
pass
# 设置窗口属性
self.setWindowTitle("隐私学习软件 - 仿Word")
self.setGeometry(100, 100, 800, 600)
# 创建中央文本编辑区域
self.text_edit = QTextEdit()
self.text_edit.setFont(QFont("Arial", 12))
self.setCentralWidget(self.text_edit)
# 创建菜单栏
self.createMenuBar()
# 创建状态栏
self.status_bar = self.statusBar()
self.status_bar.showMessage("就绪")
# 连接文本变化信号
self.text_edit.textChanged.connect(self.onTextChanged)
def createMenuBar(self):
"""
@ -35,8 +58,39 @@ class MainWindow(QMainWindow):
- 帮助菜单关于
- 为每个菜单项连接对应的槽函数
"""
# TODO: 实现菜单栏创建逻辑
pass
menu_bar = self.menuBar()
# 文件菜单
file_menu = menu_bar.addMenu('文件')
# 打开动作
open_action = QAction('打开', self)
open_action.setShortcut('Ctrl+O')
open_action.triggered.connect(self.openFile)
file_menu.addAction(open_action)
# 保存动作
save_action = QAction('保存', self)
save_action.setShortcut('Ctrl+S')
save_action.triggered.connect(self.saveFile)
file_menu.addAction(save_action)
# 分隔线
file_menu.addSeparator()
# 退出动作
exit_action = QAction('退出', self)
exit_action.setShortcut('Ctrl+Q')
exit_action.triggered.connect(self.close)
file_menu.addAction(exit_action)
# 帮助菜单
help_menu = menu_bar.addMenu('帮助')
# 关于动作
about_action = QAction('关于', self)
about_action.triggered.connect(self.showAbout)
help_menu.addAction(about_action)
def openFile(self):
"""
@ -46,8 +100,33 @@ class MainWindow(QMainWindow):
- 成功时将内容显示在文本区域重置打字状态
- 失败时显示错误消息框
"""
# TODO: 实现打开文件逻辑
pass
options = QFileDialog.Options()
file_path, _ = QFileDialog.getOpenFileName(
self,
"打开文件",
"",
"文本文件 (*.txt);;Word文档 (*.docx);;所有文件 (*)",
options=options
)
if file_path:
try:
# 解析文件内容
content = FileParser.parse_file(file_path)
self.learning_content = content
# 显示内容到文本编辑区域
self.text_edit.setPlainText(content)
# 重置打字状态
self.typing_logic = TypingLogic(content)
self.current_position = 0
# 更新状态栏
self.status_bar.showMessage(f"已打开文件: {file_path}")
except Exception as e:
# 显示错误消息框
QMessageBox.critical(self, "错误", f"无法打开文件:\n{str(e)}")
def saveFile(self):
"""
@ -56,16 +135,49 @@ class MainWindow(QMainWindow):
- 将文本区域内容写入选定文件
- 返回操作结果
"""
# TODO: 实现保存文件逻辑
pass
options = QFileDialog.Options()
file_path, _ = QFileDialog.getSaveFileName(
self,
"保存文件",
"",
"文本文件 (*.txt);;所有文件 (*)",
options=options
)
if file_path:
try:
# 获取文本编辑区域的内容
content = self.text_edit.toPlainText()
# 写入文件
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
# 更新状态栏
self.status_bar.showMessage(f"文件已保存: {file_path}")
return True
except Exception as e:
# 显示错误消息框
QMessageBox.critical(self, "错误", f"无法保存文件:\n{str(e)}")
return False
return False
def showAbout(self):
"""
显示关于对话框
- 显示消息框包含软件名称版本描述
"""
# TODO: 实现关于对话框逻辑
pass
QMessageBox.about(
self,
"关于",
"隐私学习软件 - 仿Word\n\n"
"版本: 1.0\n\n"
"这是一个用于隐私学习的打字练习软件,\n"
"可以加载文档并进行打字练习,\n"
"帮助提高打字速度和准确性。"
)
def onTextChanged(self):
"""
@ -74,8 +186,30 @@ class MainWindow(QMainWindow):
- 调用打字逻辑检查输入正确性
- 更新高亮显示和状态栏
"""
# TODO: 实现文本变化处理逻辑
pass
if self.typing_logic is None:
return
# 获取当前文本内容
current_text = self.text_edit.toPlainText()
# 调用打字逻辑检查输入正确性
result = self.typing_logic.check_input(current_text)
# 更新高亮显示
if result['correct']:
self.highlightText(len(current_text), QColor('lightgreen'))
else:
# 高亮显示错误部分
self.highlightText(len(current_text), QColor('lightcoral'))
# 更新状态栏
progress = self.typing_logic.get_progress()
accuracy = result.get('accuracy', 0) * 100
self.status_bar.showMessage(
f"进度: {progress['percentage']:.1f}% | "
f"准确率: {accuracy:.1f}% | "
f"位置: {result['position']}/{progress['total']}"
)
def highlightText(self, position, color):
"""
@ -84,5 +218,24 @@ class MainWindow(QMainWindow):
- 应用背景颜色格式
- 恢复光标位置
"""
# TODO: 实现文本高亮逻辑
pass
# 创建文本格式
format = QTextCharFormat()
format.setBackground(color)
# 获取文本游标
cursor = self.text_edit.textCursor()
# 保存当前光标位置
current_pos = cursor.position()
# 选择从开始到指定位置的文本
cursor.select(QTextCursor.Document)
cursor.setPosition(0, QTextCursor.MoveAnchor)
cursor.setPosition(position, QTextCursor.KeepAnchor)
# 应用格式
cursor.mergeCharFormat(format)
# 恢复光标位置
cursor.setPosition(current_pos)
self.text_edit.setTextCursor(cursor)

@ -1,87 +1,207 @@
# services/network_service.py
import requests
import json
import os
from typing import Optional, Dict, Any
class NetworkService:
def __init__(self):
"""
初始化网络服务
- 设置API密钥
- 初始化缓存
"""
# TODO: 实现构造函数逻辑
pass
# 实现构造函数逻辑
self.api_key = None
self.cache = {}
self.session = requests.Session()
def get_weather_info(self) -> Optional[Dict[str, Any]]:
"""
获取天气信息
- 调用天气API
- 解析返回数据
- 返回格式化的天气信息
"""
# TODO: 实现天气信息获取逻辑
# 实现天气信息获取逻辑
# 1. 获取用户IP地址
# 2. 根据IP获取地理位置
# 3. 调用天气API获取天气数据
# 4. 解析并格式化数据
# 5. 返回天气信息字典
pass
try:
ip_response = self.session.get("https://httpbin.org/ip", timeout=5)
ip_data = ip_response.json()
ip = ip_data.get("origin", "")
# 2. 根据IP获取地理位置
# 注意这里使用免费的IP地理位置API实际应用中可能需要更精确的服务
location_response = self.session.get(f"http://ip-api.com/json/{ip}", timeout=5)
location_data = location_response.json()
if location_data.get("status") != "success":
return None
city = location_data.get("city", "Unknown")
# 3. 调用天气API获取天气数据
# 注意这里使用OpenWeatherMap API作为示例需要API密钥
# 在实际应用中需要设置有效的API密钥
if self.api_key:
weather_url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={self.api_key}&units=metric&lang=zh_cn"
weather_response = self.session.get(weather_url, timeout=5)
weather_data = weather_response.json()
# 4. 解析并格式化数据
if weather_response.status_code == 200:
formatted_weather = {
"city": city,
"temperature": weather_data["main"]["temp"],
"description": weather_data["weather"][0]["description"],
"humidity": weather_data["main"]["humidity"],
"wind_speed": weather_data["wind"]["speed"]
}
# 5. 返回天气信息字典
return formatted_weather
else:
# 模拟天气数据无API密钥时
return {
"city": city,
"temperature": 20,
"description": "晴天",
"humidity": 60,
"wind_speed": 3.5
}
except Exception as e:
print(f"获取天气信息时出错: {e}")
return None
def get_daily_quote(self) -> Optional[str]:
"""
获取每日一句
- 调用名言API
- 返回格式化的名言
"""
# TODO: 实现每日一句获取逻辑
# 实现每日一句获取逻辑
# 1. 调用名言API
# 2. 解析返回的名言数据
# 3. 格式化名言文本
# 4. 返回名言字符串
pass
try:
# 使用一个免费的名言API
response = self.session.get("https://api.quotable.io/random", timeout=5)
# 2. 解析返回的名言数据
if response.status_code == 200:
quote_data = response.json()
content = quote_data.get("content", "")
author = quote_data.get("author", "")
# 3. 格式化名言文本
formatted_quote = f'"{content}" - {author}'
# 4. 返回名言字符串
return formatted_quote
else:
# 如果API调用失败返回默认名言
return "书山有路勤为径,学海无涯苦作舟。"
except Exception as e:
print(f"获取每日一句时出错: {e}")
# 出错时返回默认名言
return "书山有路勤为径,学海无涯苦作舟。"
def download_image(self, url: str) -> Optional[bytes]:
"""
下载图片
- 从指定URL下载图片
- 返回图片二进制数据
"""
# TODO: 实现图片下载逻辑
# 1. 发送HTTP GET请求获取图片
# 2. 检查响应状态码
# 3. 返回图片二进制数据
pass
# 实现图片下载逻辑
# 1. 发送GET请求下载图片
try:
response = self.session.get(url, timeout=10)
# 2. 检查响应状态码
if response.status_code == 200:
# 3. 返回图片的二进制数据
return response.content
else:
print(f"下载图片失败,状态码: {response.status_code}")
return None
except Exception as e:
print(f"下载图片时出错: {e}")
return None
class ImageService:
def __init__(self):
"""
初始化图片服务
"""
# TODO: 实现构造函数逻辑
pass
# 实现构造函数逻辑
self.supported_formats = {'.jpg', '.jpeg', '.png', '.bmp', '.gif'}
self.image_cache = {}
self.max_cache_size = 100 # 最大缓存图片数量
def extract_images_from_document(self, file_path: str) -> list:
"""
从文档中提取图片
- 解析文档中的图片
- 返回图片列表
"""
# TODO: 实现图片提取逻辑
# 1. 根据文件类型选择解析方法
# 2. 提取文档中的图片数据
# 3. 返回图片信息列表
pass
# 实现从文档提取图片逻辑
# 1. 检查文件是否存在
if not os.path.exists(file_path):
print(f"文件不存在: {file_path}")
return []
# 2. 检查文件扩展名
_, ext = os.path.splitext(file_path)
ext = ext.lower()
# 3. 根据不同文档类型提取图片
images = []
try:
# 简化实现仅处理PDF文件
if ext == '.pdf':
# 注意这需要安装PyMuPDF或pdfplumber库
# 示例使用PyMuPDF (fitz)
try:
import fitz # PyMuPDF
pdf_document = fitz.open(file_path)
for page_num in range(len(pdf_document)):
page = pdf_document[page_num]
image_list = page.get_images()
for img_index, img in enumerate(image_list):
xref = img[0]
base_image = pdf_document.extract_image(xref)
image_bytes = base_image["image"]
images.append(image_bytes)
pdf_document.close()
except ImportError:
print("需要安装PyMuPDF库: pip install PyMuPDF")
return []
else:
print(f"不支持的文件格式: {ext}")
return []
# 4. 返回提取的图片数据列表
return images
except Exception as e:
print(f"从文档提取图片时出错: {e}")
return []
def display_image_at_position(self, image_data: bytes, position: int) -> bool:
"""
在指定位置显示图片
- 将图片插入到文本中的指定位置
- 返回操作结果
"""
# TODO: 实现图片显示逻辑
# 1. 创建图片对象
# 2. 在指定位置插入图片
# 3. 更新UI显示
# 4. 返回操作结果
pass
# 实现图片显示逻辑
# 1. 验证图片数据
if not image_data:
print("无效的图片数据")
return False
# 2. 验证位置参数
if position < 0:
print("无效的位置参数")
return False
# 3. 尝试解析图片数据
try:
# 使用PIL库处理图片
try:
from PIL import Image
from io import BytesIO
image = Image.open(BytesIO(image_data))
# 4. 缓存图片(如果需要)
if len(self.image_cache) >= self.max_cache_size:
# 移除最旧的缓存项
oldest_key = next(iter(self.image_cache))
del self.image_cache[oldest_key]
self.image_cache[position] = image_data
# 5. 显示图片简化实现实际应用中需要与UI框架集成
print(f"图片已缓存到位置 {position},尺寸: {image.size},格式: {image.format}")
# 在实际应用中这里会调用UI框架的相关方法在指定位置显示图片
return True
except ImportError:
print("需要安装Pillow库: pip install Pillow")
return False
except Exception as e:
print(f"解析或显示图片时出错: {e}")
return False

@ -0,0 +1,45 @@
{
"application": {
"name": "MagicWord",
"version": "2.0.0",
"author": "MagicWord Team",
"description": "隐私学习软件"
},
"window": {
"default_size": {
"width": 800,
"height": 600
},
"minimum_size": {
"width": 600,
"height": 400
},
"title": "MagicWord - 隐私学习软件"
},
"typing": {
"default_time_limit": 300,
"show_progress_bar": true,
"highlight_current_line": true,
"auto_save_progress": true
},
"files": {
"supported_formats": [
".txt",
".docx"
],
"auto_backup_enabled": true,
"backup_interval_minutes": 30
},
"network": {
"weather_api_key": "YOUR_WEATHER_API_KEY",
"quote_api_url": "https://api.quotable.io/random",
"timeout_seconds": 10
},
"appearance": {
"theme": "light",
"font_family": "Arial",
"font_size": 12,
"text_color": "#000000",
"background_color": "#FFFFFF"
}
}

@ -10,8 +10,63 @@ class SettingsManager:
- 指定配置文件路径
- 加载现有配置或创建默认配置
"""
# TODO: 实现构造函数逻辑
pass
# 指定配置文件路径
self.config_file = config_file
self.config_path = os.path.join("resources", "config", config_file)
# 加载现有配置或创建默认配置
self.settings = self.load_settings()
if not self.settings:
self.settings = self._create_default_settings()
self.save_settings(self.settings)
def _create_default_settings(self) -> Dict[str, Any]:
"""
创建默认配置
- 返回包含默认设置的字典
"""
return {
"application": {
"name": "MagicWord",
"version": "1.0.0",
"author": "MagicWord Team",
"description": "好东西"
},
"window": {
"default_size": {
"width": 800,
"height": 600
},
"minimum_size": {
"width": 600,
"height": 400
},
"title": "MagicWord"
},
"typing": {
"default_time_limit": 300,
"show_progress_bar": True,
"highlight_current_line": True,
"auto_save_progress": True
},
"files": {
"supported_formats": [".txt", ".docx"],
"auto_backup_enabled": True,
"backup_interval_minutes": 30
},
"network": {
"weather_api_key": "YOUR_WEATHER_API_KEY",
"quote_api_url": "https://api.quotable.io/random",
"timeout_seconds": 10
},
"appearance": {
"theme": "light",
"font_family": "Arial",
"font_size": 12,
"text_color": "#000000",
"background_color": "#FFFFFF"
}
}
def load_settings(self) -> Dict[str, Any]:
"""
@ -19,8 +74,15 @@ class SettingsManager:
- 从配置文件读取设置
- 返回设置字典
"""
# TODO: 实现设置加载逻辑
pass
try:
if os.path.exists(self.config_path):
with open(self.config_path, 'r', encoding='utf-8') as f:
return json.load(f)
else:
return {}
except (json.JSONDecodeError, IOError) as e:
print(f"加载配置文件时出错: {e}")
return {}
def save_settings(self, settings: Dict[str, Any]) -> bool:
"""
@ -28,8 +90,19 @@ class SettingsManager:
- 将设置保存到配置文件
- 返回保存结果
"""
# TODO: 实现设置保存逻辑
pass
try:
# 确保目录存在
config_dir = os.path.dirname(self.config_path)
if not os.path.exists(config_dir):
os.makedirs(config_dir)
# 保存设置到文件
with open(self.config_path, 'w', encoding='utf-8') as f:
json.dump(settings, f, ensure_ascii=False, indent=4)
return True
except (IOError, TypeError) as e:
print(f"保存配置文件时出错: {e}")
return False
def get_setting(self, key: str, default: Any = None) -> Any:
"""
@ -37,8 +110,14 @@ class SettingsManager:
- 根据键名获取设置值
- 如果不存在返回默认值
"""
# TODO: 实现获取设置项逻辑
pass
keys = key.split('.')
value = self.settings
try:
for k in keys:
value = value[k]
return value
except (KeyError, TypeError):
return default
def set_setting(self, key: str, value: Any) -> bool:
"""
@ -46,5 +125,21 @@ class SettingsManager:
- 设置指定键的值
- 保存到配置文件
"""
# TODO: 实现设置设置项逻辑
pass
keys = key.split('.')
setting_dict = self.settings
# 导航到倒数第二个键
try:
for k in keys[:-1]:
if k not in setting_dict:
setting_dict[k] = {}
setting_dict = setting_dict[k]
# 设置最后一个键的值
setting_dict[keys[-1]] = value
# 保存到配置文件
return self.save_settings(self.settings)
except (KeyError, TypeError) as e:
print(f"设置配置项时出错: {e}")
return False

@ -1,13 +1,16 @@
class TypingLogic:
def __init__(self, learning_content: str):
"""
初始化打字逻辑状态
初始化打字逻辑状态
- 存储学习材料
- 初始化当前索引为0
- 初始化错误计数为0
"""
# TODO: 实现构造函数逻辑
pass
self.learning_content = learning_content
self.current_index = 0
self.error_count = 0
self.total_chars = len(learning_content)
self.typed_chars = 0
def check_input(self, user_text: str) -> dict:
"""
@ -22,17 +25,54 @@ class TypingLogic:
* completed: 布尔值是否完成
* accuracy: 浮点数准确率
"""
# TODO: 实现输入检查逻辑
pass
# 更新已输入字符数
self.typed_chars = len(user_text)
# 如果用户输入的字符数超过了学习材料的长度,截取到相同长度
if len(user_text) > self.total_chars:
user_text = user_text[:self.total_chars]
# 检查当前输入是否正确
correct = True
expected_char = ''
if self.current_index < self.total_chars:
expected_char = self.learning_content[self.current_index]
if len(user_text) > self.current_index and user_text[self.current_index] != expected_char:
correct = False
self.error_count += 1
else:
# 已经完成所有输入
return {
"correct": True,
"expected": "",
"position": self.current_index,
"completed": True,
"accuracy": self._calculate_accuracy()
}
# 更新当前索引
self.current_index = len(user_text)
# 检查是否完成
completed = self.current_index >= self.total_chars
return {
"correct": correct,
"expected": expected_char,
"position": self.current_index,
"completed": completed,
"accuracy": self._calculate_accuracy()
}
def get_expected_text(self) -> str:
def get_expected_text(self, length: int = 10) -> str:
"""
获取用户接下来应该输入的内容
- 返回从当前位置开始的一定长度文本如10个字符
- 处理文本结束情况
"""
# TODO: 实现期望文本获取逻辑
pass
start_pos = self.current_index
end_pos = min(start_pos + length, self.total_chars)
return self.learning_content[start_pos:end_pos]
def get_progress(self) -> dict:
"""
@ -42,8 +82,17 @@ class TypingLogic:
- percentage: 浮点数完成百分比
- remaining: 整数剩余字符数
"""
# TODO: 实现进度获取逻辑
pass
current = self.current_index
total = self.total_chars
percentage = (current / total * 100) if total > 0 else 0
remaining = max(0, total - current)
return {
"current": current,
"total": total,
"percentage": percentage,
"remaining": remaining
}
def reset(self, new_content: str = None):
"""
@ -52,8 +101,12 @@ class TypingLogic:
- 重置错误计数
- 如果提供了新内容更新学习材料
"""
# TODO: 实现重置逻辑
pass
if new_content is not None:
self.learning_content = new_content
self.total_chars = len(new_content)
self.current_index = 0
self.error_count = 0
self.typed_chars = 0
def get_statistics(self) -> dict:
"""
@ -63,5 +116,19 @@ class TypingLogic:
- error_count: 整数错误次数
- accuracy_rate: 浮点数准确率
"""
# TODO: 实现统计信息获取逻辑
pass
return {
"total_chars": self.total_chars,
"typed_chars": self.typed_chars,
"error_count": self.error_count,
"accuracy_rate": self._calculate_accuracy()
}
def _calculate_accuracy(self) -> float:
"""
计算准确率
"""
if self.typed_chars == 0:
return 0.0
# 准确率 = (已输入字符数 - 错误次数) / 已输入字符数
accuracy = (self.typed_chars - self.error_count) / self.typed_chars
return max(0.0, accuracy) # 确保准确率不为负数

@ -10,12 +10,8 @@ class CustomTitleBar(QWidget):
- 添加窗口控制按钮
"""
super().__init__(parent)
# TODO: 实现标题栏UI
# 1. 创建标题标签
# 2. 创建最小化、最大化、关闭按钮
# 3. 设置布局和样式
# 4. 连接按钮事件
pass
self.parent = parent
self.setup_ui()
def setup_ui(self):
"""
@ -23,42 +19,92 @@ class CustomTitleBar(QWidget):
- 初始化所有UI组件
- 设置组件属性和样式
"""
# TODO: 实现UI设置逻辑
# 1. 创建水平布局
# 2. 添加标题标签和控制按钮
# 3. 设置组件样式
pass
# 创建水平布局
layout = QHBoxLayout()
layout.setContentsMargins(10, 5, 10, 5)
layout.setSpacing(10)
# 创建标题标签
self.title_label = QLabel("MagicWord")
self.title_label.setStyleSheet("color: #333333; font-size: 12px; font-weight: normal;")
# 创建控制按钮
self.minimize_button = QPushButton("")
self.maximize_button = QPushButton("")
self.close_button = QPushButton("×")
# 设置按钮样式
button_style = """
QPushButton {
background-color: transparent;
border: none;
color: #333333;
font-size: 12px;
font-weight: normal;
width: 30px;
height: 30px;
}
QPushButton:hover {
background-color: #d0d0d0;
}
"""
self.minimize_button.setStyleSheet(button_style)
self.maximize_button.setStyleSheet(button_style)
self.close_button.setStyleSheet(button_style + "QPushButton:hover { background-color: #ff5555; color: white; }")
# 添加组件到布局
layout.addWidget(self.title_label)
layout.addStretch()
layout.addWidget(self.minimize_button)
layout.addWidget(self.maximize_button)
layout.addWidget(self.close_button)
self.setLayout(layout)
# 连接按钮事件
self.minimize_button.clicked.connect(self.minimize_window)
self.maximize_button.clicked.connect(self.maximize_window)
self.close_button.clicked.connect(self.close_window)
# 设置标题栏样式
self.setStyleSheet("""
CustomTitleBar {
background-color: #f0f0f0;
border-top-left-radius: 0px;
border-top-right-radius: 0px;
border-bottom: 1px solid #d0d0d0;
}
""")
def minimize_window(self):
"""
最小化窗口
- 触发窗口最小化事件
"""
# TODO: 实现窗口最小化逻辑
# 1. 获取父窗口
# 2. 调用窗口最小化方法
pass
if self.parent:
self.parent.showMinimized()
def maximize_window(self):
"""
最大化窗口
- 切换窗口最大化状态
"""
# TODO: 实现窗口最大化逻辑
# 1. 获取父窗口
# 2. 检查当前窗口状态
# 3. 切换最大化/还原状态
pass
if self.parent:
if self.parent.isMaximized():
self.parent.showNormal()
self.maximize_button.setText("")
else:
self.parent.showMaximized()
self.maximize_button.setText("")
def close_window(self):
"""
关闭窗口
- 触发窗口关闭事件
"""
# TODO: 实现窗口关闭逻辑
# 1. 获取父窗口
# 2. 调用窗口关闭方法
pass
if self.parent:
self.parent.close()
class ProgressBarWidget(QWidget):
def __init__(self, parent=None):
@ -68,11 +114,69 @@ class ProgressBarWidget(QWidget):
- 显示统计信息
"""
super().__init__(parent)
# TODO: 实现进度条组件初始化
# 1. 创建进度条UI元素
# 2. 创建统计信息标签
# 3. 设置布局
pass
self.setup_ui()
def setup_ui(self):
"""
设置进度条UI
- 初始化所有UI组件
- 设置组件属性和样式
"""
# 创建垂直布局
layout = QVBoxLayout()
layout.setContentsMargins(10, 10, 10, 10)
layout.setSpacing(5)
# 导入需要的模块
from PyQt5.QtWidgets import QProgressBar, QHBoxLayout
# 创建水平布局用于统计信息
stats_layout = QHBoxLayout()
stats_layout.setSpacing(15)
# 创建统计信息标签
self.wpm_label = QLabel("WPM: 0")
self.accuracy_label = QLabel("准确率: 0%")
self.time_label = QLabel("用时: 0s")
# 设置标签样式
label_style = "font-size: 12px; font-weight: normal; color: #333333;"
self.wpm_label.setStyleSheet(label_style)
self.accuracy_label.setStyleSheet(label_style)
self.time_label.setStyleSheet(label_style)
# 添加标签到统计布局
stats_layout.addWidget(self.wpm_label)
stats_layout.addWidget(self.accuracy_label)
stats_layout.addWidget(self.time_label)
stats_layout.addStretch()
# 创建进度条
self.progress_bar = QProgressBar()
self.progress_bar.setRange(0, 100)
self.progress_bar.setValue(0)
self.progress_bar.setTextVisible(True)
self.progress_bar.setFormat("进度: %p%")
# 设置进度条样式
self.progress_bar.setStyleSheet("""
QProgressBar {
border: 1px solid #c0c0c0;
border-radius: 0px;
text-align: center;
background-color: #f0f0f0;
}
QProgressBar::chunk {
background-color: #0078d7;
border-radius: 0px;
}
""")
# 添加组件到主布局
layout.addLayout(stats_layout)
layout.addWidget(self.progress_bar)
self.setLayout(layout)
def update_progress(self, progress: float):
"""
@ -80,21 +184,18 @@ class ProgressBarWidget(QWidget):
- 设置进度值
- 更新显示
"""
# TODO: 实现进度更新逻辑
# 1. 更新进度条数值
# 2. 刷新UI显示
pass
self.progress_bar.setValue(int(progress))
def update_stats(self, wpm: int, accuracy: float, time_elapsed: int):
"""
更新统计信息
- 显示WPM准确率用时等信息
- wpm: 每分钟字数
- accuracy: 准确率(%)
- time_elapsed: 用时()
"""
# TODO: 实现统计信息更新逻辑
# 1. 更新WPM标签
# 2. 更新准确率标签
# 3. 更新用时标签
pass
self.wpm_label.setText(f"WPM: {wpm}")
self.accuracy_label.setText(f"准确率: {accuracy:.1f}%")
self.time_label.setText(f"用时: {time_elapsed}s")
class TextDisplayWidget(QWidget):
def __init__(self, parent=None):
@ -102,47 +203,123 @@ class TextDisplayWidget(QWidget):
文本显示组件
- 显示待练习文本
- 高亮当前字符
- 显示用户输入
- 显示用户输入反馈
"""
super().__init__(parent)
# TODO: 实现文本显示组件初始化
# 1. 创建文本显示区域
# 2. 设置文本样式
# 3. 初始化高亮相关属性
pass
self.text_content = ""
self.current_index = 0
self.setup_ui()
def setup_ui(self):
"""
设置文本显示UI
- 初始化文本显示区域
- 设置样式和布局
"""
# 创建垂直布局
layout = QVBoxLayout()
layout.setContentsMargins(20, 20, 20, 20)
# 导入需要的模块
from PyQt5.QtWidgets import QTextEdit
from PyQt5.QtCore import Qt
# 创建文本显示区域
self.text_display = QTextEdit()
self.text_display.setReadOnly(True)
self.text_display.setLineWrapMode(QTextEdit.WidgetWidth)
# 设置文本显示样式
self.text_display.setStyleSheet("""
QTextEdit {
font-family: 'Calibri', 'Segoe UI', 'Microsoft YaHei', sans-serif;
font-size: 12pt;
border: 1px solid #d0d0d0;
border-radius: 0px;
padding: 15px;
background-color: white;
color: black;
}
""")
# 添加组件到布局
layout.addWidget(self.text_display)
self.setLayout(layout)
def set_text(self, text: str):
"""
设置显示文本
- 更新显示内容
- 重置高亮状态
"""
# TODO: 实现文本设置逻辑
# 1. 更新内部文本内容
# 2. 重置高亮位置
# 3. 刷新UI显示
pass
- text: 要显示的文本内容
"""
self.text_content = text
self.current_index = 0
self._update_display()
def highlight_character(self, position: int):
"""
高亮指定位置字符
- 更新高亮位置
- 刷新显示
高亮指定位置的字符
- position: 字符位置索引
"""
# TODO: 实现字符高亮逻辑
# 1. 计算高亮范围
# 2. 应用高亮样式
# 3. 滚动到高亮位置
pass
if 0 <= position < len(self.text_content):
self.current_index = position
self._update_display()
def _update_display(self, user_input: str = ""):
"""
更新文本显示
- user_input: 用户输入文本(可选)
"""
# 导入需要的模块
from PyQt5.QtGui import QTextCursor
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from constants import COLOR_CORRECT, COLOR_WRONG, COLOR_HIGHLIGHT
if not self.text_content:
self.text_display.clear()
return
# 创建带格式的HTML文本
formatted_text = ""
# 如果有用户输入,对比显示
if user_input:
for i, char in enumerate(self.text_content):
if i < len(user_input):
if char == user_input[i]:
# 正确字符
formatted_text += f'<span style="background-color: {COLOR_CORRECT.name()}; color: black;">{char}</span>'
else:
# 错误字符
formatted_text += f'<span style="background-color: {COLOR_WRONG.name()}; color: white;">{char}</span>'
elif i == len(user_input):
# 当前字符
formatted_text += f'<span style="background-color: {COLOR_HIGHLIGHT.name()}; color: black;">{char}</span>'
else:
# 未到达的字符
formatted_text += char
else:
# 没有用户输入,只显示原文和当前高亮字符
for i, char in enumerate(self.text_content):
if i == self.current_index:
# 当前字符高亮
formatted_text += f'<span style="background-color: {COLOR_HIGHLIGHT.name()}; color: black;">{char}</span>'
else:
formatted_text += char
# 更新文本显示
self.text_display.setHtml(formatted_text)
# 滚动到当前高亮字符位置
cursor = self.text_display.textCursor()
cursor.setPosition(min(self.current_index + 5, len(self.text_content)))
self.text_display.setTextCursor(cursor)
self.text_display.ensureCursorVisible()
def show_user_input(self, input_text: str):
"""
显示用户输入
- 在文本下方显示用户输入
- 高亮正确/错误字符
显示用户输入反馈
- input_text: 用户输入的文本
"""
# TODO: 实现用户输入显示逻辑
# 1. 对比用户输入与原文
# 2. 分别高亮正确和错误字符
# 3. 更新输入显示区域
pass
self._update_display(input_text)

@ -11,8 +11,21 @@ class Utils:
- 尝试多种编码格式
- 返回最可能的编码
"""
# TODO: 实现编码检测逻辑
pass
import chardet
# 读取文件的前1024字节用于编码检测
with open(file_path, 'rb') as f:
raw_data = f.read(1024)
# 使用chardet检测编码
result = chardet.detect(raw_data)
encoding = result['encoding']
# 如果chardet无法确定编码则默认使用utf-8
if encoding is None:
encoding = 'utf-8'
return encoding
@staticmethod
def format_file_size(size_bytes: int) -> str:
@ -20,9 +33,25 @@ class Utils:
格式化文件大小
- 将字节数转换为可读格式
- 返回格式化字符串
参数:
size_bytes (int): 需要格式化的文件大小单位为字节
返回:
str: 格式化后的文件大小字符串 "1.5 MB"
"""
# TODO: 实现文件大小格式化逻辑
pass
# 如果文件大小为0字节直接返回 "0 B"
if size_bytes == 0:
return "0 B"
# 定义文件大小单位列表
size_names = ["B", "KB", "MB", "GB", "TB"]
i = 0
# 当文件大小大于等于1024且未到达最大单位时循环除以1024
while size_bytes >= 1024.0 and i < len(size_names) - 1:
size_bytes /= 1024.0
i += 1
# 返回格式化后的字符串,保留一位小数
return f"{size_bytes:.1f} {size_names[i]}"
@staticmethod
def calculate_file_hash(file_path: str) -> str:
@ -31,5 +60,11 @@ class Utils:
- 使用SHA256算法
- 返回哈希字符串
"""
# TODO: 实现文件哈希计算逻辑
pass
sha256_hash = hashlib.sha256()
# 分块读取文件以避免大文件占用过多内存
with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
sha256_hash.update(chunk)
return sha256_hash.hexdigest()
Loading…
Cancel
Save