|
|
# word_main_window.py
|
|
|
import sys
|
|
|
import os
|
|
|
from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
|
|
QTextEdit, QLabel, QSplitter, QFrame, QMenuBar,
|
|
|
QAction, QFileDialog, QMessageBox, QApplication)
|
|
|
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QRect
|
|
|
from PyQt5.QtGui import QFont, QPalette, QColor, QIcon, QPixmap, QTextCharFormat
|
|
|
|
|
|
from ui.word_style_ui import (WordRibbon, WordStatusBar, WordTextEdit,
|
|
|
WordStyleToolBar)
|
|
|
from services.network_service import NetworkService
|
|
|
from typing_logic import TypingLogic
|
|
|
from file_parser import FileParser
|
|
|
|
|
|
class WeatherFetchThread(QThread):
|
|
|
weather_fetched = pyqtSignal(dict)
|
|
|
|
|
|
def __init__(self):
|
|
|
super().__init__()
|
|
|
self.network_service = NetworkService()
|
|
|
|
|
|
def run(self):
|
|
|
try:
|
|
|
weather_data = self.network_service.get_weather()
|
|
|
self.weather_fetched.emit(weather_data)
|
|
|
except Exception as e:
|
|
|
self.weather_fetched.emit({'error': str(e)})
|
|
|
|
|
|
class QuoteFetchThread(QThread):
|
|
|
quote_fetched = pyqtSignal(dict)
|
|
|
|
|
|
def __init__(self):
|
|
|
super().__init__()
|
|
|
self.network_service = NetworkService()
|
|
|
|
|
|
def run(self):
|
|
|
try:
|
|
|
quote_data = self.network_service.get_daily_quote()
|
|
|
self.quote_fetched.emit(quote_data)
|
|
|
except Exception as e:
|
|
|
self.quote_fetched.emit({'error': str(e)})
|
|
|
|
|
|
class WordStyleMainWindow(QMainWindow):
|
|
|
def __init__(self):
|
|
|
super().__init__()
|
|
|
self.current_file_path = None
|
|
|
self.is_modified = False
|
|
|
self.typing_logic = None
|
|
|
self.is_loading_file = False # 添加文件加载标志
|
|
|
self.imported_content = "" # 存储导入的完整内容
|
|
|
self.displayed_chars = 0 # 已显示的字符数
|
|
|
self.setup_ui()
|
|
|
self.network_service = NetworkService()
|
|
|
|
|
|
# 设置窗口属性
|
|
|
self.setWindowTitle("文档1 - MagicWord")
|
|
|
self.setGeometry(100, 100, 1200, 800)
|
|
|
|
|
|
# 设置应用程序图标
|
|
|
self.set_window_icon()
|
|
|
|
|
|
# 初始化UI
|
|
|
self.setup_ui()
|
|
|
|
|
|
# 初始化网络服务
|
|
|
self.init_network_services()
|
|
|
|
|
|
# 初始化打字逻辑
|
|
|
self.init_typing_logic()
|
|
|
|
|
|
# 连接信号和槽
|
|
|
self.connect_signals()
|
|
|
|
|
|
def set_window_icon(self):
|
|
|
"""设置窗口图标"""
|
|
|
# 创建简单的Word风格图标
|
|
|
icon = QIcon()
|
|
|
pixmap = QPixmap(32, 32)
|
|
|
pixmap.fill(QColor("#2B579A"))
|
|
|
icon.addPixmap(pixmap)
|
|
|
self.setWindowIcon(icon)
|
|
|
|
|
|
def setup_ui(self):
|
|
|
"""设置Word风格的UI界面"""
|
|
|
|
|
|
# 创建菜单栏
|
|
|
self.create_menu_bar()
|
|
|
|
|
|
# 创建快速访问工具栏
|
|
|
self.quick_toolbar = WordStyleToolBar()
|
|
|
self.addToolBar(Qt.TopToolBarArea, self.quick_toolbar)
|
|
|
|
|
|
# 创建Ribbon功能区
|
|
|
self.ribbon = WordRibbon()
|
|
|
|
|
|
# 创建中心部件
|
|
|
central_widget = QWidget()
|
|
|
self.setCentralWidget(central_widget)
|
|
|
|
|
|
# 主布局
|
|
|
main_layout = QVBoxLayout()
|
|
|
main_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
main_layout.setSpacing(0)
|
|
|
|
|
|
# 添加Ribbon
|
|
|
main_layout.addWidget(self.ribbon)
|
|
|
|
|
|
# 创建文档编辑区域
|
|
|
self.create_document_area(main_layout)
|
|
|
|
|
|
# 创建状态栏
|
|
|
self.status_bar = WordStatusBar()
|
|
|
self.setStatusBar(self.status_bar)
|
|
|
|
|
|
central_widget.setLayout(main_layout)
|
|
|
|
|
|
# 设置样式
|
|
|
self.setStyleSheet("""
|
|
|
QMainWindow {
|
|
|
background-color: #f3f2f1;
|
|
|
}
|
|
|
""")
|
|
|
|
|
|
def create_menu_bar(self):
|
|
|
"""创建菜单栏"""
|
|
|
menubar = self.menuBar()
|
|
|
menubar.setStyleSheet("""
|
|
|
QMenuBar {
|
|
|
background-color: #f3f2f1;
|
|
|
border-bottom: 1px solid #d0d0d0;
|
|
|
font-size: 12px;
|
|
|
color: #333333;
|
|
|
}
|
|
|
QMenuBar::item:selected {
|
|
|
background-color: #e1e1e1;
|
|
|
}
|
|
|
""")
|
|
|
|
|
|
# 文件菜单
|
|
|
file_menu = menubar.addMenu('文件(F)')
|
|
|
file_menu.setStyleSheet("""
|
|
|
QMenu {
|
|
|
background-color: #ffffff;
|
|
|
border: 1px solid #d0d0d0;
|
|
|
font-size: 12px;
|
|
|
}
|
|
|
QMenu::item:selected {
|
|
|
background-color: #e1e1e1;
|
|
|
}
|
|
|
""")
|
|
|
|
|
|
# 新建
|
|
|
new_action = QAction('新建(N)', self)
|
|
|
new_action.setShortcut('Ctrl+N')
|
|
|
new_action.triggered.connect(self.new_document)
|
|
|
file_menu.addAction(new_action)
|
|
|
|
|
|
# 打开
|
|
|
open_action = QAction('打开(O)...', self)
|
|
|
open_action.setShortcut('Ctrl+O')
|
|
|
open_action.triggered.connect(self.open_file)
|
|
|
file_menu.addAction(open_action)
|
|
|
|
|
|
# 保存
|
|
|
save_action = QAction('保存(S)', self)
|
|
|
save_action.setShortcut('Ctrl+S')
|
|
|
save_action.triggered.connect(self.save_file)
|
|
|
file_menu.addAction(save_action)
|
|
|
|
|
|
# 另存为
|
|
|
save_as_action = QAction('另存为(A)...', self)
|
|
|
save_as_action.triggered.connect(self.save_as_file)
|
|
|
file_menu.addAction(save_as_action)
|
|
|
|
|
|
file_menu.addSeparator()
|
|
|
|
|
|
# 退出
|
|
|
exit_action = QAction('退出(X)', self)
|
|
|
exit_action.triggered.connect(self.close)
|
|
|
file_menu.addAction(exit_action)
|
|
|
|
|
|
# 编辑菜单
|
|
|
edit_menu = menubar.addMenu('编辑(E)')
|
|
|
|
|
|
# 撤销
|
|
|
undo_action = QAction('撤销(U)', self)
|
|
|
undo_action.setShortcut('Ctrl+Z')
|
|
|
undo_action.triggered.connect(self.undo)
|
|
|
edit_menu.addAction(undo_action)
|
|
|
|
|
|
# 重做
|
|
|
redo_action = QAction('重做(R)', self)
|
|
|
redo_action.setShortcut('Ctrl+Y')
|
|
|
redo_action.triggered.connect(self.redo)
|
|
|
edit_menu.addAction(redo_action)
|
|
|
|
|
|
edit_menu.addSeparator()
|
|
|
|
|
|
# 剪切
|
|
|
cut_action = QAction('剪切(T)', self)
|
|
|
cut_action.setShortcut('Ctrl+X')
|
|
|
cut_action.triggered.connect(self.cut)
|
|
|
edit_menu.addAction(cut_action)
|
|
|
|
|
|
# 复制
|
|
|
copy_action = QAction('复制(C)', self)
|
|
|
copy_action.setShortcut('Ctrl+C')
|
|
|
copy_action.triggered.connect(self.copy)
|
|
|
edit_menu.addAction(copy_action)
|
|
|
|
|
|
# 粘贴
|
|
|
paste_action = QAction('粘贴(P)', self)
|
|
|
paste_action.setShortcut('Ctrl+V')
|
|
|
paste_action.triggered.connect(self.paste)
|
|
|
edit_menu.addAction(paste_action)
|
|
|
|
|
|
# 视图菜单
|
|
|
view_menu = menubar.addMenu('视图(V)')
|
|
|
|
|
|
# 阅读视图
|
|
|
read_view_action = QAction('阅读视图', self)
|
|
|
read_view_action.triggered.connect(self.toggle_reading_view)
|
|
|
view_menu.addAction(read_view_action)
|
|
|
|
|
|
# 打印布局
|
|
|
print_layout_action = QAction('打印布局', self)
|
|
|
print_layout_action.setChecked(True)
|
|
|
print_layout_action.triggered.connect(self.toggle_print_layout)
|
|
|
view_menu.addAction(print_layout_action)
|
|
|
|
|
|
# 帮助菜单
|
|
|
help_menu = menubar.addMenu('帮助(H)')
|
|
|
|
|
|
# 关于
|
|
|
about_action = QAction('关于 MagicWord', self)
|
|
|
about_action.triggered.connect(self.show_about)
|
|
|
help_menu.addAction(about_action)
|
|
|
|
|
|
def create_document_area(self, main_layout):
|
|
|
"""创建文档编辑区域"""
|
|
|
|
|
|
# 创建滚动区域
|
|
|
from PyQt5.QtWidgets import QScrollArea
|
|
|
|
|
|
scroll_area = QScrollArea()
|
|
|
scroll_area.setWidgetResizable(True)
|
|
|
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
|
scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
|
scroll_area.setStyleSheet("""
|
|
|
QScrollArea {
|
|
|
background-color: #e1e1e1;
|
|
|
border: none;
|
|
|
}
|
|
|
QScrollArea QWidget {
|
|
|
background-color: #e1e1e1;
|
|
|
}
|
|
|
""")
|
|
|
|
|
|
# 创建文档容器
|
|
|
document_container = QWidget()
|
|
|
document_layout = QVBoxLayout()
|
|
|
document_layout.setContentsMargins(50, 50, 50, 50)
|
|
|
|
|
|
# 创建文本编辑区域(使用Word风格的文本编辑器)
|
|
|
self.text_edit = WordTextEdit()
|
|
|
self.text_edit.setMinimumHeight(600)
|
|
|
self.text_edit.setStyleSheet("""
|
|
|
QTextEdit {
|
|
|
background-color: #ffffff;
|
|
|
border: 1px solid #d0d0d0;
|
|
|
border-radius: 0px;
|
|
|
font-family: 'Calibri', 'Microsoft YaHei', '微软雅黑', sans-serif;
|
|
|
font-size: 12pt;
|
|
|
color: #000000;
|
|
|
padding: 40px;
|
|
|
line-height: 1.5;
|
|
|
}
|
|
|
""")
|
|
|
|
|
|
# 设置默认文档内容
|
|
|
self.text_edit.setPlainText("在此输入您的内容...")
|
|
|
|
|
|
document_layout.addWidget(self.text_edit)
|
|
|
document_container.setLayout(document_layout)
|
|
|
|
|
|
scroll_area.setWidget(document_container)
|
|
|
main_layout.addWidget(scroll_area)
|
|
|
|
|
|
def init_network_services(self):
|
|
|
"""初始化网络服务"""
|
|
|
# 获取天气信息
|
|
|
self.weather_thread = WeatherFetchThread()
|
|
|
self.weather_thread.weather_fetched.connect(self.update_weather_display)
|
|
|
self.weather_thread.start()
|
|
|
|
|
|
# 获取每日名言
|
|
|
self.quote_thread = QuoteFetchThread()
|
|
|
self.quote_thread.quote_fetched.connect(self.update_quote_display)
|
|
|
self.quote_thread.start()
|
|
|
|
|
|
def init_typing_logic(self):
|
|
|
"""初始化打字逻辑"""
|
|
|
# 使用默认内容初始化打字逻辑
|
|
|
default_content = "欢迎使用MagicWord隐私学习软件!\n\n这是一个仿Microsoft Word界面的学习工具。"
|
|
|
self.typing_logic = TypingLogic(default_content)
|
|
|
self.typing_logic.reset()
|
|
|
|
|
|
def connect_signals(self):
|
|
|
"""连接信号和槽"""
|
|
|
# 文本变化信号
|
|
|
self.text_edit.textChanged.connect(self.on_text_changed)
|
|
|
|
|
|
# Ribbon按钮信号
|
|
|
if hasattr(self.ribbon, 'tabs'):
|
|
|
for tab_name, tab_btn in self.ribbon.tabs.items():
|
|
|
tab_btn.clicked.connect(lambda checked, name=tab_name: self.on_tab_changed(name))
|
|
|
|
|
|
def on_text_changed(self):
|
|
|
"""文本变化处理 - 逐步显示模式"""
|
|
|
# 如果正在加载文件,跳过处理
|
|
|
if self.is_loading_file:
|
|
|
return
|
|
|
|
|
|
# 如果有导入的内容,实现逐步显示
|
|
|
if self.imported_content and self.typing_logic:
|
|
|
current_text = self.text_edit.toPlainText()
|
|
|
|
|
|
# 计算应该显示的字符数(用户输入多少个字符就显示多少个)
|
|
|
input_length = len(current_text)
|
|
|
|
|
|
# 如果用户输入长度超过了导入内容长度,限制在导入内容长度内
|
|
|
if input_length > len(self.imported_content):
|
|
|
input_length = len(self.imported_content)
|
|
|
|
|
|
# 如果显示的字符数需要更新
|
|
|
if input_length != self.displayed_chars:
|
|
|
self.displayed_chars = input_length
|
|
|
|
|
|
# 获取应该显示的文本部分
|
|
|
display_text = self.imported_content[:self.displayed_chars]
|
|
|
|
|
|
# 临时禁用文本变化信号,避免递归
|
|
|
self.text_edit.textChanged.disconnect(self.on_text_changed)
|
|
|
|
|
|
# 更新文本编辑器内容
|
|
|
self.text_edit.setPlainText(display_text)
|
|
|
|
|
|
# 重新连接文本变化信号
|
|
|
self.text_edit.textChanged.connect(self.on_text_changed)
|
|
|
|
|
|
# 将光标移动到末尾
|
|
|
cursor = self.text_edit.textCursor()
|
|
|
cursor.movePosition(cursor.End)
|
|
|
self.text_edit.setTextCursor(cursor)
|
|
|
|
|
|
# 更新打字逻辑(只检查已显示的部分)
|
|
|
if display_text:
|
|
|
result = self.typing_logic.check_input(display_text)
|
|
|
self.typing_logic.update_position(display_text)
|
|
|
|
|
|
# 错误处理
|
|
|
if not result['correct'] and display_text:
|
|
|
expected_char = result.get('expected', '')
|
|
|
self.status_bar.showMessage(f"输入错误,期望字符: '{expected_char}'", 2000)
|
|
|
self.highlight_next_char(result['position'], expected_char)
|
|
|
|
|
|
# 更新统计信息
|
|
|
stats = self.typing_logic.get_statistics()
|
|
|
self.update_status_bar(stats)
|
|
|
|
|
|
# 检查是否完成
|
|
|
if self.displayed_chars >= len(self.imported_content):
|
|
|
self.on_lesson_complete()
|
|
|
return
|
|
|
|
|
|
# 更新状态栏显示进度
|
|
|
progress_percentage = (self.displayed_chars / len(self.imported_content)) * 100
|
|
|
self.status_bar.showMessage(f"逐步显示进度: {progress_percentage:.1f}% ({self.displayed_chars}/{len(self.imported_content)})", 2000)
|
|
|
|
|
|
# 标记文档为已修改
|
|
|
if not self.is_modified:
|
|
|
self.is_modified = True
|
|
|
self.update_window_title()
|
|
|
|
|
|
def on_tab_changed(self, tab_name):
|
|
|
"""标签切换处理"""
|
|
|
# 更新标签状态
|
|
|
for name, btn in self.ribbon.tabs.items():
|
|
|
btn.setChecked(name == tab_name)
|
|
|
|
|
|
# 这里可以根据不同标签切换不同的功能区内容
|
|
|
print(f"切换到标签: {tab_name}")
|
|
|
|
|
|
def update_weather_display(self, weather_data):
|
|
|
"""更新天气显示"""
|
|
|
if 'error' in weather_data:
|
|
|
self.status_bar.showMessage(f"天气信息获取失败: {weather_data['error']}", 3000)
|
|
|
else:
|
|
|
temp = weather_data.get('temperature', 'N/A')
|
|
|
desc = weather_data.get('description', 'N/A')
|
|
|
self.status_bar.showMessage(f"天气: {desc}, {temp}°C", 5000)
|
|
|
|
|
|
def update_quote_display(self, quote_data):
|
|
|
"""更新名言显示"""
|
|
|
if 'error' not in quote_data:
|
|
|
content = quote_data.get('content', '获取名言失败')
|
|
|
author = quote_data.get('author', '未知')
|
|
|
self.status_bar.showMessage(f"每日名言: {content} - {author}", 10000)
|
|
|
|
|
|
def update_status_bar(self, stats):
|
|
|
"""更新状态栏统计信息"""
|
|
|
if stats:
|
|
|
# 获取打字统计信息
|
|
|
total_chars = stats.get('total_chars', 0)
|
|
|
typed_chars = stats.get('typed_chars', 0)
|
|
|
error_count = stats.get('error_count', 0)
|
|
|
accuracy_rate = stats.get('accuracy_rate', 0)
|
|
|
|
|
|
# 计算进度百分比
|
|
|
progress_text = ""
|
|
|
if total_chars > 0:
|
|
|
progress_percentage = (typed_chars / total_chars) * 100
|
|
|
progress_text = f"进度: {progress_percentage:.1f}%"
|
|
|
|
|
|
# 计算准确率
|
|
|
accuracy_text = f"准确率: {accuracy_rate:.1%}"
|
|
|
|
|
|
# 显示统计信息
|
|
|
status_text = f"{progress_text} | {accuracy_text} | 已输入: {typed_chars}/{total_chars} | 错误: {error_count}"
|
|
|
self.status_bar.showMessage(status_text, 0) # 0表示不自动消失
|
|
|
|
|
|
# 更新字数统计标签(如果存在)
|
|
|
if hasattr(self.status_bar, 'words_label'):
|
|
|
self.status_bar.words_label.setText(f"总字数: {total_chars}")
|
|
|
|
|
|
# 更新进度标签(如果存在)
|
|
|
if hasattr(self.status_bar, 'progress_label'):
|
|
|
self.status_bar.progress_label.setText(f"进度: {progress_percentage:.1f}%" if total_chars > 0 else "进度: 0%")
|
|
|
|
|
|
def update_window_title(self):
|
|
|
"""更新窗口标题"""
|
|
|
file_name = "文档1"
|
|
|
if self.current_file_path:
|
|
|
file_name = os.path.basename(self.current_file_path)
|
|
|
|
|
|
modified = "*" if self.is_modified else ""
|
|
|
self.setWindowTitle(f"{file_name}{modified} - MagicWord")
|
|
|
|
|
|
def new_document(self):
|
|
|
"""新建文档"""
|
|
|
self.text_edit.clear()
|
|
|
self.current_file_path = None
|
|
|
self.is_modified = False
|
|
|
self.update_window_title()
|
|
|
|
|
|
if self.typing_logic:
|
|
|
self.typing_logic.reset()
|
|
|
|
|
|
def open_file(self):
|
|
|
"""打开文件并设置为打字学习内容 - 逐步显示模式"""
|
|
|
file_path, _ = QFileDialog.getOpenFileName(
|
|
|
self, "打开文件", "",
|
|
|
"文档文件 (*.docx *.txt *.pdf);;所有文件 (*.*)"
|
|
|
)
|
|
|
|
|
|
if file_path:
|
|
|
try:
|
|
|
# 解析文件
|
|
|
parser = FileParser()
|
|
|
content = parser.parse_file(file_path)
|
|
|
|
|
|
if content:
|
|
|
# 设置文件加载标志
|
|
|
self.is_loading_file = True
|
|
|
|
|
|
# 存储完整内容但不立即显示
|
|
|
self.imported_content = content
|
|
|
self.displayed_chars = 0
|
|
|
|
|
|
# 设置学习内容到打字逻辑
|
|
|
if self.typing_logic:
|
|
|
self.typing_logic.reset(content) # 重置打字状态并设置新内容
|
|
|
|
|
|
# 清空文本编辑器,准备逐步显示
|
|
|
self.text_edit.clear()
|
|
|
|
|
|
# 清除文件加载标志
|
|
|
self.is_loading_file = False
|
|
|
|
|
|
# 设置当前文件路径
|
|
|
self.current_file_path = file_path
|
|
|
self.is_modified = False
|
|
|
self.update_window_title()
|
|
|
|
|
|
# 更新状态栏
|
|
|
self.status_bar.showMessage(f"已打开学习文件: {os.path.basename(file_path)},开始打字逐步显示学习内容!", 5000)
|
|
|
|
|
|
# 更新字数统计
|
|
|
if hasattr(self.status_bar, 'words_label'):
|
|
|
self.status_bar.words_label.setText(f"总字数: {len(content)}")
|
|
|
|
|
|
else:
|
|
|
QMessageBox.warning(self, "警告", "无法读取文件内容或文件为空")
|
|
|
|
|
|
except Exception as e:
|
|
|
# 确保在异常情况下也清除标志
|
|
|
self.is_loading_file = False
|
|
|
QMessageBox.critical(self, "错误", f"打开文件失败: {str(e)}")
|
|
|
print(f"文件打开错误详情: {e}") # 调试信息
|
|
|
|
|
|
def save_file(self):
|
|
|
"""保存文件"""
|
|
|
if self.current_file_path:
|
|
|
try:
|
|
|
with open(self.current_file_path, 'w', encoding='utf-8') as f:
|
|
|
f.write(self.text_edit.toPlainText())
|
|
|
|
|
|
self.is_modified = False
|
|
|
self.update_window_title()
|
|
|
self.status_bar.showMessage("文件已保存", 3000)
|
|
|
|
|
|
except Exception as e:
|
|
|
QMessageBox.critical(self, "错误", f"保存文件失败: {str(e)}")
|
|
|
else:
|
|
|
self.save_as_file()
|
|
|
|
|
|
def save_as_file(self):
|
|
|
"""另存为"""
|
|
|
file_path, _ = QFileDialog.getSaveFileName(
|
|
|
self, "另存为", "", "文本文档 (*.txt);;所有文件 (*.*)"
|
|
|
)
|
|
|
|
|
|
if file_path:
|
|
|
try:
|
|
|
with open(file_path, 'w', encoding='utf-8') as f:
|
|
|
f.write(self.text_edit.toPlainText())
|
|
|
|
|
|
self.current_file_path = file_path
|
|
|
self.is_modified = False
|
|
|
self.update_window_title()
|
|
|
self.status_bar.showMessage(f"已保存: {os.path.basename(file_path)}", 3000)
|
|
|
|
|
|
except Exception as e:
|
|
|
QMessageBox.critical(self, "错误", f"保存文件失败: {str(e)}")
|
|
|
|
|
|
def undo(self):
|
|
|
"""撤销"""
|
|
|
self.text_edit.undo()
|
|
|
|
|
|
def redo(self):
|
|
|
"""重做"""
|
|
|
self.text_edit.redo()
|
|
|
|
|
|
def cut(self):
|
|
|
"""剪切"""
|
|
|
self.text_edit.cut()
|
|
|
|
|
|
def copy(self):
|
|
|
"""复制"""
|
|
|
self.text_edit.copy()
|
|
|
|
|
|
def paste(self):
|
|
|
"""粘贴"""
|
|
|
self.text_edit.paste()
|
|
|
|
|
|
def toggle_reading_view(self):
|
|
|
"""切换阅读视图"""
|
|
|
# 这里可以实现阅读视图的逻辑
|
|
|
self.status_bar.showMessage("阅读视图功能开发中...", 3000)
|
|
|
|
|
|
def toggle_print_layout(self):
|
|
|
"""切换打印布局"""
|
|
|
# 这里可以实现打印布局的逻辑
|
|
|
self.status_bar.showMessage("打印布局功能开发中...", 3000)
|
|
|
|
|
|
def show_about(self):
|
|
|
"""显示关于对话框"""
|
|
|
QMessageBox.about(
|
|
|
self, "关于 MagicWord",
|
|
|
"MagicWord - 隐私学习软件\n\n"
|
|
|
"版本: 2.0\n"
|
|
|
"基于 Microsoft Word 界面设计\n\n"
|
|
|
"功能特色:\n"
|
|
|
"• 仿Word界面设计\n"
|
|
|
"• 隐私学习模式\n"
|
|
|
"• 多格式文档支持\n"
|
|
|
"• 实时进度跟踪\n"
|
|
|
"• 天气和名言显示"
|
|
|
)
|
|
|
|
|
|
def highlight_next_char(self, position, expected_char):
|
|
|
"""高亮显示下一个期望字符"""
|
|
|
if position < len(self.typing_logic.learning_content):
|
|
|
# 获取当前光标位置
|
|
|
cursor = self.text_edit.textCursor()
|
|
|
cursor.setPosition(position)
|
|
|
|
|
|
# 选择下一个字符
|
|
|
cursor.movePosition(cursor.Right, cursor.KeepAnchor, 1)
|
|
|
|
|
|
# 设置高亮格式
|
|
|
format = QTextCharFormat()
|
|
|
format.setBackground(QColor(255, 255, 0, 128)) # 黄色半透明背景
|
|
|
format.setForeground(QColor(255, 0, 0)) # 红色文字
|
|
|
format.setFontWeight(QFont.Bold)
|
|
|
|
|
|
# 应用格式
|
|
|
cursor.setCharFormat(format)
|
|
|
|
|
|
def on_lesson_complete(self):
|
|
|
"""课程完成处理"""
|
|
|
stats = self.typing_logic.get_statistics()
|
|
|
QMessageBox.information(
|
|
|
self, "恭喜",
|
|
|
"恭喜完成本课程学习!\n\n"
|
|
|
f"准确率: {stats['accuracy_rate']*100:.1f}%\n"
|
|
|
f"总字符数: {stats['total_chars']}\n"
|
|
|
f"错误次数: {stats['error_count']}"
|
|
|
)
|
|
|
|
|
|
# 重置状态
|
|
|
if self.typing_logic:
|
|
|
self.typing_logic.reset()
|
|
|
|
|
|
def closeEvent(self, event):
|
|
|
"""关闭事件处理"""
|
|
|
if self.is_modified:
|
|
|
reply = QMessageBox.question(
|
|
|
self, "确认退出",
|
|
|
"文档已修改,是否保存更改?",
|
|
|
QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel
|
|
|
)
|
|
|
|
|
|
if reply == QMessageBox.Save:
|
|
|
self.save_file()
|
|
|
event.accept()
|
|
|
elif reply == QMessageBox.Discard:
|
|
|
event.accept()
|
|
|
else:
|
|
|
event.ignore()
|
|
|
else:
|
|
|
event.accept()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
app = QApplication(sys.argv)
|
|
|
|
|
|
# 设置应用程序样式
|
|
|
app.setStyle('Windows')
|
|
|
|
|
|
# 创建并显示主窗口
|
|
|
window = WordStyleMainWindow()
|
|
|
window.show()
|
|
|
|
|
|
sys.exit(app.exec_()) |