From c76674752d87963c42b6b17cfffdad7e925f111b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BF=8A=E6=BA=90?= <997724925@qq.com> Date: Sun, 12 Oct 2025 19:32:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.py | 34 ++++++-- src/main_window.py | 189 +++++++++++++++++++++++++++++++++++++++----- src/typing_logic.py | 93 +++++++++++++++++++--- 3 files changed, 280 insertions(+), 36 deletions(-) diff --git a/src/main.py b/src/main.py index 40e96f8..177583a 100644 --- a/src/main.py +++ b/src/main.py @@ -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() \ No newline at end of file diff --git a/src/main_window.py b/src/main_window.py index 2459aa2..918e808 100644 --- a/src/main_window.py +++ b/src/main_window.py @@ -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 \ No newline at end of file + # 创建文本格式 + 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) \ No newline at end of file diff --git a/src/typing_logic.py b/src/typing_logic.py index 498e123..ce3e659 100644 --- a/src/typing_logic.py +++ b/src/typing_logic.py @@ -6,8 +6,11 @@ class TypingLogic: - 初始化当前索引为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 \ No newline at end of file + 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) # 确保准确率不为负数 \ No newline at end of file