|
|
|
|
@ -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)
|