|
|
|
|
@ -1,8 +1,45 @@
|
|
|
|
|
import sys
|
|
|
|
|
import os
|
|
|
|
|
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTextEdit, QAction,
|
|
|
|
|
QFileDialog, QVBoxLayout, QWidget, QLabel, QStatusBar)
|
|
|
|
|
from PyQt5.QtGui import QFont, QTextCharFormat, QColor
|
|
|
|
|
from PyQt5.QtCore import Qt
|
|
|
|
|
QFileDialog, QVBoxLayout, QWidget, QLabel, QStatusBar, QMessageBox)
|
|
|
|
|
from PyQt5.QtGui import QFont, QTextCharFormat, QColor, QTextCursor
|
|
|
|
|
from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal
|
|
|
|
|
|
|
|
|
|
# 添加项目根目录到Python路径
|
|
|
|
|
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
|
|
|
|
|
|
|
|
|
# 导入自定义UI组件
|
|
|
|
|
from src.ui.components import CustomTitleBar, ProgressBarWidget, TextDisplayWidget, StatsDisplayWidget, QuoteDisplayWidget, WeatherDisplayWidget
|
|
|
|
|
from src.file_parser import FileParser
|
|
|
|
|
from src.typing_logic import TypingLogic
|
|
|
|
|
from src.services.network_service import NetworkService
|
|
|
|
|
|
|
|
|
|
class WeatherFetchThread(QThread):
|
|
|
|
|
"""天气信息获取线程"""
|
|
|
|
|
weather_fetched = pyqtSignal(object) # 天气信息获取成功信号
|
|
|
|
|
error_occurred = pyqtSignal(str) # 错误发生信号
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
super().__init__()
|
|
|
|
|
self.network_service = NetworkService()
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
|
try:
|
|
|
|
|
weather_info = self.network_service.get_weather_info()
|
|
|
|
|
if weather_info:
|
|
|
|
|
# 格式化天气信息
|
|
|
|
|
formatted_info = (
|
|
|
|
|
f"天气: {weather_info['city']} - "
|
|
|
|
|
f"{weather_info['description']} - "
|
|
|
|
|
f"温度: {weather_info['temperature']}°C - "
|
|
|
|
|
f"湿度: {weather_info['humidity']}% - "
|
|
|
|
|
f"风速: {weather_info['wind_speed']} m/s"
|
|
|
|
|
)
|
|
|
|
|
self.weather_fetched.emit(formatted_info)
|
|
|
|
|
else:
|
|
|
|
|
self.error_occurred.emit("无法获取天气信息")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.error_occurred.emit(f"获取天气信息时出错: {str(e)}")
|
|
|
|
|
|
|
|
|
|
class MainWindow(QMainWindow):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
@ -14,40 +51,260 @@ 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.title_bar = None
|
|
|
|
|
self.progress_bar_widget = None
|
|
|
|
|
self.text_display_widget = None
|
|
|
|
|
self.initUI()
|
|
|
|
|
|
|
|
|
|
def initUI(self):
|
|
|
|
|
"""
|
|
|
|
|
创建和布局所有UI组件
|
|
|
|
|
- 创建中央文本编辑区域QTextEdit
|
|
|
|
|
- 创建自定义标题栏
|
|
|
|
|
- 创建文本显示组件
|
|
|
|
|
- 调用createMenuBar()创建菜单
|
|
|
|
|
- 创建状态栏并显示"就绪"
|
|
|
|
|
- 连接文本变化信号到onTextChanged
|
|
|
|
|
"""
|
|
|
|
|
# TODO: 实现UI初始化逻辑
|
|
|
|
|
pass
|
|
|
|
|
# 设置窗口属性
|
|
|
|
|
self.setWindowTitle("隐私学习软件 - 仿Word")
|
|
|
|
|
self.setGeometry(100, 100, 800, 600)
|
|
|
|
|
self.setWindowFlags(Qt.FramelessWindowHint) # 移除默认标题栏
|
|
|
|
|
|
|
|
|
|
# 创建中央widget
|
|
|
|
|
central_widget = QWidget()
|
|
|
|
|
self.setCentralWidget(central_widget)
|
|
|
|
|
|
|
|
|
|
# 创建主布局
|
|
|
|
|
main_layout = QVBoxLayout()
|
|
|
|
|
main_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
main_layout.setSpacing(0)
|
|
|
|
|
central_widget.setLayout(main_layout)
|
|
|
|
|
|
|
|
|
|
# 创建自定义标题栏
|
|
|
|
|
self.title_bar = CustomTitleBar(self)
|
|
|
|
|
main_layout.addWidget(self.title_bar)
|
|
|
|
|
|
|
|
|
|
# 创建统计信息显示组件(默认隐藏)
|
|
|
|
|
self.stats_display = StatsDisplayWidget(self)
|
|
|
|
|
self.stats_display.setVisible(False) # 默认隐藏
|
|
|
|
|
main_layout.addWidget(self.stats_display)
|
|
|
|
|
|
|
|
|
|
# 创建每日一言显示组件(默认隐藏)
|
|
|
|
|
self.quote_display = QuoteDisplayWidget(self)
|
|
|
|
|
self.quote_display.setVisible(False) # 默认隐藏
|
|
|
|
|
main_layout.addWidget(self.quote_display)
|
|
|
|
|
|
|
|
|
|
# 创建天气显示组件(默认隐藏)
|
|
|
|
|
self.weather_display = WeatherDisplayWidget(self)
|
|
|
|
|
self.weather_display.setVisible(False) # 默认隐藏
|
|
|
|
|
main_layout.addWidget(self.weather_display)
|
|
|
|
|
|
|
|
|
|
# 创建文本显示组件
|
|
|
|
|
self.text_display_widget = TextDisplayWidget(self)
|
|
|
|
|
main_layout.addWidget(self.text_display_widget)
|
|
|
|
|
|
|
|
|
|
# 连接文本显示组件的文本变化信号
|
|
|
|
|
self.text_display_widget.text_display.textChanged.connect(self.onTextChanged)
|
|
|
|
|
|
|
|
|
|
# 创建菜单栏
|
|
|
|
|
self.createMenuBar()
|
|
|
|
|
|
|
|
|
|
# 创建状态栏
|
|
|
|
|
self.status_bar = self.statusBar()
|
|
|
|
|
self.status_bar.showMessage("就绪")
|
|
|
|
|
|
|
|
|
|
def createTopFunctionArea(self, main_layout):
|
|
|
|
|
"""
|
|
|
|
|
创建顶部功能区域
|
|
|
|
|
- 显示准确率、WPM等统计信息
|
|
|
|
|
- 显示每日一言功能
|
|
|
|
|
"""
|
|
|
|
|
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QPushButton
|
|
|
|
|
from PyQt5.QtCore import Qt
|
|
|
|
|
|
|
|
|
|
# 创建顶部功能区域widget
|
|
|
|
|
top_widget = QWidget()
|
|
|
|
|
top_widget.setStyleSheet("""
|
|
|
|
|
QWidget {
|
|
|
|
|
background-color: #f0f0f0;
|
|
|
|
|
border-bottom: 1px solid #d0d0d0;
|
|
|
|
|
}
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
# 创建水平布局
|
|
|
|
|
top_layout = QHBoxLayout()
|
|
|
|
|
top_layout.setContentsMargins(10, 5, 10, 5)
|
|
|
|
|
top_layout.setSpacing(15)
|
|
|
|
|
|
|
|
|
|
# 创建统计信息标签
|
|
|
|
|
self.wpm_label = QLabel("WPM: 0")
|
|
|
|
|
self.accuracy_label = QLabel("准确率: 0%")
|
|
|
|
|
self.quote_label = QLabel("每日一言: 暂无")
|
|
|
|
|
self.quote_label.setStyleSheet("QLabel { color: #666666; font-style: italic; }")
|
|
|
|
|
|
|
|
|
|
# 设置标签样式
|
|
|
|
|
label_style = "font-size: 12px; font-weight: normal; color: #333333;"
|
|
|
|
|
self.wpm_label.setStyleSheet(label_style)
|
|
|
|
|
self.accuracy_label.setStyleSheet(label_style)
|
|
|
|
|
|
|
|
|
|
# 创建每日一言刷新按钮
|
|
|
|
|
self.refresh_quote_button = QPushButton("刷新")
|
|
|
|
|
self.refresh_quote_button.setStyleSheet("""
|
|
|
|
|
QPushButton {
|
|
|
|
|
background-color: #0078d7;
|
|
|
|
|
color: white;
|
|
|
|
|
border: none;
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
QPushButton:hover {
|
|
|
|
|
background-color: #005a9e;
|
|
|
|
|
}
|
|
|
|
|
""")
|
|
|
|
|
self.refresh_quote_button.clicked.connect(self.refresh_daily_quote)
|
|
|
|
|
|
|
|
|
|
# 添加组件到布局
|
|
|
|
|
top_layout.addWidget(self.wpm_label)
|
|
|
|
|
top_layout.addWidget(self.accuracy_label)
|
|
|
|
|
top_layout.addStretch()
|
|
|
|
|
top_layout.addWidget(self.quote_label)
|
|
|
|
|
top_layout.addWidget(self.refresh_quote_button)
|
|
|
|
|
|
|
|
|
|
top_widget.setLayout(top_layout)
|
|
|
|
|
main_layout.addWidget(top_widget)
|
|
|
|
|
|
|
|
|
|
def createMenuBar(self):
|
|
|
|
|
"""
|
|
|
|
|
创建菜单栏和所有菜单项
|
|
|
|
|
- 文件菜单:打开(Ctrl+O)、保存(Ctrl+S)、退出(Ctrl+Q)
|
|
|
|
|
- 视图菜单:显示统计信息、显示每日一言
|
|
|
|
|
- 帮助菜单:关于
|
|
|
|
|
- 为每个菜单项连接对应的槽函数
|
|
|
|
|
"""
|
|
|
|
|
# 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)
|
|
|
|
|
|
|
|
|
|
# 视图菜单
|
|
|
|
|
view_menu = menu_bar.addMenu('视图')
|
|
|
|
|
|
|
|
|
|
# 显示统计信息动作
|
|
|
|
|
self.stats_action = QAction('显示统计信息', self)
|
|
|
|
|
self.stats_action.setCheckable(True)
|
|
|
|
|
self.stats_action.setChecked(True)
|
|
|
|
|
self.stats_action.triggered.connect(self.toggleStatsDisplay)
|
|
|
|
|
view_menu.addAction(self.stats_action)
|
|
|
|
|
|
|
|
|
|
# 显示每日一言动作
|
|
|
|
|
self.quote_action = QAction('显示每日一言', self)
|
|
|
|
|
self.quote_action.setCheckable(True)
|
|
|
|
|
self.quote_action.setChecked(True)
|
|
|
|
|
self.quote_action.triggered.connect(self.toggleQuoteDisplay)
|
|
|
|
|
view_menu.addAction(self.quote_action)
|
|
|
|
|
|
|
|
|
|
# 显示天气信息动作
|
|
|
|
|
self.weather_action = QAction('显示天气信息', self)
|
|
|
|
|
self.weather_action.setCheckable(True)
|
|
|
|
|
self.weather_action.setChecked(True)
|
|
|
|
|
self.weather_action.triggered.connect(self.toggleWeatherDisplay)
|
|
|
|
|
view_menu.addAction(self.weather_action)
|
|
|
|
|
|
|
|
|
|
# 帮助菜单
|
|
|
|
|
help_menu = menu_bar.addMenu('帮助')
|
|
|
|
|
|
|
|
|
|
# 关于动作
|
|
|
|
|
about_action = QAction('关于', self)
|
|
|
|
|
about_action.triggered.connect(self.showAbout)
|
|
|
|
|
help_menu.addAction(about_action)
|
|
|
|
|
|
|
|
|
|
def toggleStatsDisplay(self, checked):
|
|
|
|
|
"""
|
|
|
|
|
切换统计信息显示
|
|
|
|
|
- checked: 是否显示统计信息
|
|
|
|
|
"""
|
|
|
|
|
self.stats_display.setVisible(checked)
|
|
|
|
|
|
|
|
|
|
def toggleQuoteDisplay(self, checked):
|
|
|
|
|
"""
|
|
|
|
|
切换每日一言显示
|
|
|
|
|
- checked: 是否显示每日一言
|
|
|
|
|
"""
|
|
|
|
|
self.quote_display.setVisible(checked)
|
|
|
|
|
# 如果启用显示且quote为空,则刷新一次
|
|
|
|
|
if checked and not self.quote_display.quote_label.text():
|
|
|
|
|
self.refresh_daily_quote()
|
|
|
|
|
|
|
|
|
|
def toggleWeatherDisplay(self, checked):
|
|
|
|
|
"""切换天气信息显示"""
|
|
|
|
|
self.weather_display.setVisible(checked)
|
|
|
|
|
# 如果启用显示且天气信息为空,则刷新一次
|
|
|
|
|
if checked and not self.weather_display.weather_label.text():
|
|
|
|
|
self.refresh_weather_info()
|
|
|
|
|
|
|
|
|
|
def openFile(self):
|
|
|
|
|
"""
|
|
|
|
|
打开文件选择对话框并加载选中的文件
|
|
|
|
|
- 显示文件选择对话框,过滤条件:*.txt, *.docx
|
|
|
|
|
- 如果用户选择了文件,调用FileParser.parse_file(file_path)
|
|
|
|
|
- 成功时:将内容显示在文本区域,重置打字状态
|
|
|
|
|
- 成功时:将内容存储但不直接显示,重置打字状态
|
|
|
|
|
- 失败时:显示错误消息框
|
|
|
|
|
"""
|
|
|
|
|
# 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
|
|
|
|
|
|
|
|
|
|
# 在文本显示组件中设置内容(初始为空,通过打字逐步显示)
|
|
|
|
|
if self.text_display_widget:
|
|
|
|
|
self.text_display_widget.set_text(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,33 +313,170 @@ 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):
|
|
|
|
|
def refresh_daily_quote(self):
|
|
|
|
|
"""
|
|
|
|
|
处理文本变化事件,实现打字逻辑
|
|
|
|
|
- 获取当前文本内容
|
|
|
|
|
- 调用打字逻辑检查输入正确性
|
|
|
|
|
- 更新高亮显示和状态栏
|
|
|
|
|
刷新每日一言
|
|
|
|
|
- 从网络API获取名言
|
|
|
|
|
- 更新显示
|
|
|
|
|
"""
|
|
|
|
|
# TODO: 实现文本变化处理逻辑
|
|
|
|
|
pass
|
|
|
|
|
import requests
|
|
|
|
|
import json
|
|
|
|
|
from PyQt5.QtCore import Qt
|
|
|
|
|
from src.constants import QUOTE_API_URL
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# 发送请求获取每日一言
|
|
|
|
|
response = requests.get(QUOTE_API_URL, timeout=5)
|
|
|
|
|
if response.status_code == 200:
|
|
|
|
|
data = response.json()
|
|
|
|
|
quote_content = data.get('content', '暂无内容')
|
|
|
|
|
quote_author = data.get('author', '未知作者')
|
|
|
|
|
|
|
|
|
|
# 更新显示
|
|
|
|
|
self.quote_label.setText(f"每日一言: {quote_content} — {quote_author}")
|
|
|
|
|
|
|
|
|
|
# 同时更新统计信息显示组件中的每日一言
|
|
|
|
|
if hasattr(self, 'stats_display') and self.stats_display:
|
|
|
|
|
self.stats_display.update_quote(f"{quote_content} — {quote_author}")
|
|
|
|
|
else:
|
|
|
|
|
self.quote_label.setText("每日一言: 获取失败")
|
|
|
|
|
# 同时更新统计信息显示组件中的每日一言
|
|
|
|
|
if hasattr(self, 'stats_display') and self.stats_display:
|
|
|
|
|
self.stats_display.update_quote("获取失败")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.quote_label.setText("每日一言: 获取失败")
|
|
|
|
|
# 同时更新统计信息显示组件中的每日一言
|
|
|
|
|
if hasattr(self, 'stats_display') and self.stats_display:
|
|
|
|
|
self.stats_display.update_quote("获取失败")
|
|
|
|
|
|
|
|
|
|
def highlightText(self, position, color):
|
|
|
|
|
def onTextChanged(self):
|
|
|
|
|
"""
|
|
|
|
|
高亮显示从开始到指定位置的文本
|
|
|
|
|
- 使用QTextCursor选择文本范围
|
|
|
|
|
- 应用背景颜色格式
|
|
|
|
|
- 恢复光标位置
|
|
|
|
|
处理用户输入变化事件(打字练习)
|
|
|
|
|
- 获取文本显示组件中的文本
|
|
|
|
|
- 使用TypingLogic.check_input检查输入
|
|
|
|
|
- 根据结果更新文本显示组件
|
|
|
|
|
- 更新统计数据展示
|
|
|
|
|
"""
|
|
|
|
|
# TODO: 实现文本高亮逻辑
|
|
|
|
|
pass
|
|
|
|
|
# 防止递归调用
|
|
|
|
|
if getattr(self, '_processing_text_change', False):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if not self.typing_logic:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 设置标志防止递归
|
|
|
|
|
self._processing_text_change = True
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# 获取当前输入文本
|
|
|
|
|
current_text = self.text_display_widget.text_display.toPlainText()
|
|
|
|
|
|
|
|
|
|
# 检查输入是否正确
|
|
|
|
|
result = self.typing_logic.check_input(current_text)
|
|
|
|
|
is_correct = result["correct"]
|
|
|
|
|
expected_char = result["expected"]
|
|
|
|
|
|
|
|
|
|
# 更新文本显示组件
|
|
|
|
|
if self.text_display_widget:
|
|
|
|
|
# 显示用户输入反馈
|
|
|
|
|
self.text_display_widget.show_user_input(current_text)
|
|
|
|
|
|
|
|
|
|
# 不再高亮下一个字符,因为内容通过打字逐步显示
|
|
|
|
|
|
|
|
|
|
# 计算统计数据
|
|
|
|
|
stats = self.typing_logic.get_statistics()
|
|
|
|
|
accuracy = stats['accuracy_rate'] * 100 # 转换为百分比
|
|
|
|
|
# 可以根据需要添加更多统计数据的计算
|
|
|
|
|
wpm = 0 # 暂时设置为0,后续可以实现WPM计算
|
|
|
|
|
|
|
|
|
|
# 更新状态栏
|
|
|
|
|
self.status_bar.showMessage(f"WPM: {wpm:.1f} | 准确率: {accuracy:.1f}%")
|
|
|
|
|
|
|
|
|
|
# 更新统计信息显示组件
|
|
|
|
|
if hasattr(self, 'stats_display') and self.stats_display.isVisible():
|
|
|
|
|
self.stats_display.update_stats(int(wpm), accuracy)
|
|
|
|
|
|
|
|
|
|
# 更新每日一言显示组件(如果需要)
|
|
|
|
|
if hasattr(self, 'quote_display') and self.quote_display.isVisible() and not self.quote_display.quote_label.text():
|
|
|
|
|
self.refresh_daily_quote()
|
|
|
|
|
|
|
|
|
|
# 更新顶部功能区的统计数据(如果仍然存在)
|
|
|
|
|
if hasattr(self, 'wpm_label') and self.wpm_label:
|
|
|
|
|
self.wpm_label.setText(f"WPM: {wpm:.1f}")
|
|
|
|
|
if hasattr(self, 'accuracy_label') and self.accuracy_label:
|
|
|
|
|
self.accuracy_label.setText(f"准确率: {accuracy:.1f}%")
|
|
|
|
|
finally:
|
|
|
|
|
# 清除递归防止标志
|
|
|
|
|
self._processing_text_change = False
|
|
|
|
|
|
|
|
|
|
def refresh_daily_quote(self):
|
|
|
|
|
"""刷新每日一言"""
|
|
|
|
|
# 创建并启动获取名言的线程
|
|
|
|
|
self.quote_thread = QuoteFetchThread()
|
|
|
|
|
self.quote_thread.quote_fetched.connect(self.on_quote_fetched)
|
|
|
|
|
self.quote_thread.error_occurred.connect(self.on_quote_error)
|
|
|
|
|
self.quote_thread.start()
|
|
|
|
|
|
|
|
|
|
def refresh_weather_info(self):
|
|
|
|
|
"""刷新天气信息"""
|
|
|
|
|
# 创建并启动获取天气信息的线程
|
|
|
|
|
self.weather_thread = WeatherFetchThread()
|
|
|
|
|
self.weather_thread.weather_fetched.connect(self.on_weather_fetched)
|
|
|
|
|
self.weather_thread.error_occurred.connect(self.on_weather_error)
|
|
|
|
|
self.weather_thread.start()
|
|
|
|
|
|
|
|
|
|
def on_weather_fetched(self, weather_info):
|
|
|
|
|
"""处理天气信息获取成功"""
|
|
|
|
|
# 更新天气显示组件
|
|
|
|
|
if hasattr(self, 'weather_display') and self.weather_display:
|
|
|
|
|
self.weather_display.update_weather(weather_info)
|
|
|
|
|
|
|
|
|
|
def on_weather_error(self, error_msg):
|
|
|
|
|
"""处理天气信息获取错误"""
|
|
|
|
|
# 更新天气显示组件
|
|
|
|
|
if hasattr(self, 'weather_display') and self.weather_display:
|
|
|
|
|
self.weather_display.update_weather(error_msg)
|