1.0 #76

Merged
p9o3yklam merged 6 commits from main into maziang 4 months ago

@ -0,0 +1,309 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MagicWord macOS M系列芯片打包脚本
用于构建macOS Apple Silicon原生应用程序
"""
import os
import sys
import subprocess
import platform
import shutil
import plistlib
from datetime import datetime
def run_command(command, shell=False, cwd=None):
"""运行命令并返回结果"""
try:
result = subprocess.run(command, shell=shell, capture_output=True, text=True, encoding='utf-8', cwd=cwd)
return result.returncode, result.stdout, result.stderr
except Exception as e:
return -1, "", str(e)
def check_system():
"""检查系统是否为macOS Apple Silicon"""
if platform.system() != "Darwin":
print("错误: 此脚本仅支持macOS系统")
return False
# 检查是否为Apple Silicon
machine = platform.machine()
if machine not in ['arm64', 'aarch64']:
print(f"警告: 当前为 {machine} 架构建议Apple Silicon (arm64) 以获得最佳性能")
print(f"系统信息: macOS {platform.mac_ver()[0]}, {machine}")
return True
def clean_build_dirs():
"""清理构建目录"""
print("清理构建目录...")
dirs_to_clean = ['build', 'dist', '__pycache__', '*.egg-info']
for dir_name in dirs_to_clean:
if '*' in dir_name:
import glob
for path in glob.glob(dir_name):
if os.path.isdir(path):
shutil.rmtree(path, ignore_errors=True)
elif os.path.exists(dir_name):
if os.path.isdir(dir_name):
shutil.rmtree(dir_name, ignore_errors=True)
else:
os.remove(dir_name)
# 清理src目录下的__pycache__
for root, dirs, files in os.walk('src'):
for dir_name in dirs:
if dir_name == '__pycache__':
cache_path = os.path.join(root, dir_name)
shutil.rmtree(cache_path, ignore_errors=True)
print(f"清理缓存: {cache_path}")
def install_dependencies():
"""安装依赖"""
print("安装项目依赖...")
# 首先确保pip是最新的
run_command([sys.executable, "-m", "pip", "install", "--upgrade", "pip"])
# 安装requirements.txt中的依赖
code, stdout, stderr = run_command([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"])
if code != 0:
print(f"依赖安装失败: {stderr}")
return False
# 安装PyInstaller
print("安装PyInstaller...")
code, stdout, stderr = run_command([sys.executable, "-m", "pip", "install", "pyinstaller"])
if code != 0:
print(f"PyInstaller安装失败: {stderr}")
return False
print("依赖安装成功")
return True
def build_macos_app():
"""构建macOS应用包"""
print("构建macOS应用包...")
# PyInstaller命令 - 针对macOS优化
pyinstaller_cmd = [
"pyinstaller",
"--name", "MagicWord",
"--version", "0.3.0",
"--distpath", "dist",
"--workpath", "build",
"--specpath", ".",
# macOS特定的数据文件路径格式
"--add-data", "resources:resources",
"--add-data", "src:src",
# 隐藏导入模块
"--hidden-import", "PyQt5",
"--hidden-import", "PyQt5.QtCore",
"--hidden-import", "PyQt5.QtGui",
"--hidden-import", "PyQt5.QtWidgets",
"--hidden-import", "requests",
"--hidden-import", "beautifulsoup4",
"--hidden-import", "python-docx",
"--hidden-import", "PyPDF2",
"--hidden-import", "ebooklib",
"--hidden-import", "chardet",
"--hidden-import", "PIL",
# macOS应用包选项
"--windowed", # 无控制台窗口
"--osx-bundle-identifier", "com.magicword.app",
"--target-architecture", "arm64", # Apple Silicon
"--noconfirm",
"src/main.py"
]
print("运行PyInstaller...")
code, stdout, stderr = run_command(pyinstaller_cmd)
if code != 0:
print(f"构建失败: {stderr}")
print("尝试通用架构...")
# 尝试通用架构
pyinstaller_cmd[-2] = "--target-architecture"
pyinstaller_cmd[-1] = "universal2"
code, stdout, stderr = run_command(pyinstaller_cmd)
if code != 0:
print(f"通用架构构建也失败: {stderr}")
return False
print("macOS应用包构建成功")
return True
def create_app_bundle():
"""创建macOS应用束"""
print("创建macOS应用束...")
app_path = "dist/MagicWord.app"
if not os.path.exists(app_path):
print(f"错误: 找不到应用包 {app_path}")
return False
# 创建Info.plist文件
info_plist = {
'CFBundleName': 'MagicWord',
'CFBundleDisplayName': 'MagicWord - 隐私学习软件',
'CFBundleIdentifier': 'com.magicword.app',
'CFBundleVersion': '0.3.0',
'CFBundleShortVersionString': '0.3.0',
'CFBundleExecutable': 'MagicWord',
'CFBundlePackageType': 'APPL',
'CFBundleSignature': '????',
'LSMinimumSystemVersion': '11.0', # macOS Big Sur及更高版本
'NSHighResolutionCapable': True,
'NSHumanReadableCopyright': 'Copyright © 2024 MagicWord Team. All rights reserved.',
'CFBundleDocumentTypes': [
{
'CFBundleTypeName': 'Text Document',
'CFBundleTypeExtensions': ['txt', 'docx', 'pdf'],
'CFBundleTypeRole': 'Editor'
}
]
}
plist_path = os.path.join(app_path, "Contents", "Info.plist")
with open(plist_path, 'wb') as f:
plistlib.dump(info_plist, f)
# 复制图标文件
icon_files = [
'resources/icons/app_icon_128X128.png',
'resources/icons/app_icon_256X256.png',
'resources/icons/app_icon_32X32.png',
'resources/icons/app_icon_64X64.png'
]
resources_dir = os.path.join(app_path, "Contents", "Resources")
os.makedirs(resources_dir, exist_ok=True)
for icon_file in icon_files:
if os.path.exists(icon_file):
shutil.copy2(icon_file, resources_dir)
print(f"复制图标: {icon_file}")
print("macOS应用束创建完成")
return True
def create_dmg():
"""创建DMG安装包"""
print("创建DMG安装包...")
app_path = "dist/MagicWord.app"
if not os.path.exists(app_path):
print(f"错误: 找不到应用包 {app_path}")
return False
# 创建发布目录
release_dir = "macos_release"
if os.path.exists(release_dir):
shutil.rmtree(release_dir)
os.makedirs(release_dir)
# 复制应用到发布目录
release_app_path = os.path.join(release_dir, "MagicWord.app")
shutil.copytree(app_path, release_app_path)
# 创建应用程序链接
applications_link = os.path.join(release_dir, "Applications")
os.symlink("/Applications", applications_link)
# 创建README文件
readme_content = f"""# MagicWord 0.3.0 for macOS
## 安装说明
1. MagicWord.app 拖拽到 Applications 文件夹
2. 首次运行时如果出现安全提示请前往 系统设置 > 隐私与安全性 允许应用运行
3. 或者右键点击应用选择"打开"
## 系统要求
- macOS Big Sur (11.0) 或更高版本
- Apple Silicon (M1/M2/M3) Intel 处理器
## 功能特性
- 隐私学习通过打字练习来学习文档内容
- 支持多种文档格式TXT, DOCX, PDF
- 智能打字模式
- 美观的Word风格界面
## 版本信息
构建时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
平台: {platform.system()} {platform.machine()}
Python版本: {platform.python_version()}
## 技术支持
如有问题请查看项目文档或联系开发团队
"""
with open(os.path.join(release_dir, "README.txt"), "w") as f:
f.write(readme_content)
# 创建DMG文件如果系统支持
dmg_name = f"MagicWord-0.3.0-macOS-{platform.machine()}.dmg"
dmg_path = os.path.join("dist", dmg_name)
# 使用hdiutil创建DMG
create_dmg_cmd = [
"hdiutil", "create",
"-volname", "MagicWord",
"-srcfolder", release_dir,
"-ov",
"-format", "UDZO",
dmg_path
]
code, stdout, stderr = run_command(create_dmg_cmd)
if code == 0:
print(f"DMG创建成功: {dmg_path}")
return True
else:
print(f"DMG创建失败: {stderr}")
print("已创建应用包可手动打包DMG")
return False
def main():
"""主函数"""
print("=== MagicWord macOS打包脚本 ===")
print(f"构建时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
# 检查系统
if not check_system():
return False
# 清理构建目录
clean_build_dirs()
# 安装依赖
if not install_dependencies():
print("依赖安装失败")
return False
# 构建应用
if not build_macos_app():
print("应用构建失败")
return False
# 创建应用束
if not create_app_bundle():
print("应用束创建失败")
return False
# 创建DMG
create_dmg()
print("\n=== 构建完成 ===")
print("应用位置: dist/MagicWord.app")
print("如需安装请将应用拖拽到Applications文件夹")
print("首次运行时可能需要允许未知来源的应用")
if __name__ == "__main__":
main()

@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""
MagicWord macOS简化打包脚本
"""
import os
import sys
import subprocess
import shutil
def run_command(cmd):
"""运行命令"""
print(f"运行: {' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"错误: {result.stderr}")
return False
return True
def main():
print("=== MagicWord macOS打包 ===")
# 清理旧的构建文件
print("清理构建文件...")
for folder in ['build', 'dist', '__pycache__']:
if os.path.exists(folder):
shutil.rmtree(folder)
# 安装依赖
print("安装依赖...")
if not run_command([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"]):
return
if not run_command([sys.executable, "-m", "pip", "install", "pyinstaller"]):
return
# 构建命令 - 包含所有必要的图片和图标文件
cmd = [
sys.executable, "-m", "PyInstaller",
"--name", "MagicWord",
"--distpath", "dist",
"--workpath", "build",
# 资源文件
"--add-data", "resources:resources",
# UI图片文件
"--add-data", "src/ui/UI.png:ui",
"--add-data", "src/ui/114514.png:ui",
# 图标文件
"--add-data", "resources/icons/app_icon_32X32.png:resources/icons",
"--add-data", "resources/icons/app_icon_64X64.png:resources/icons",
"--add-data", "resources/icons/app_icon_128X128.png:resources/icons",
"--add-data", "resources/icons/app_icon_256X256.png:resources/icons",
# 隐藏导入模块
"--hidden-import", "PyQt5",
"--hidden-import", "PyQt5.QtCore",
"--hidden-import", "PyQt5.QtGui",
"--hidden-import", "PyQt5.QtWidgets",
"--hidden-import", "requests",
"--hidden-import", "beautifulsoup4",
"--hidden-import", "python-docx",
"--hidden-import", "PyPDF2",
"--hidden-import", "ebooklib",
"--hidden-import", "chardet",
"--hidden-import", "PIL",
# macOS应用选项
"--windowed",
"--osx-bundle-identifier", "com.magicword.app",
"--icon", "resources/icons/app_icon_128X128.png",
"src/main.py"
]
print("构建应用...")
if run_command(cmd):
print("✅ 构建成功!")
print(f"应用位置: {os.path.abspath('dist/MagicWord.app')}")
print("\n安装步骤:")
print("1. 将 MagicWord.app 拖拽到 Applications 文件夹")
print("2. 首次运行时,右键点击应用选择'打开'")
else:
print("❌ 构建失败!")
if __name__ == "__main__":
main()

@ -0,0 +1,106 @@
#!/bin/bash
# MagicWord macOS快速打包脚本
echo "=== MagicWord macOS打包脚本 ==="
echo "开始打包进程..."
# 检查Python版本
python_version=$(python3 --version 2>/dev/null || python --version)
echo "Python版本: $python_version"
# 安装依赖
echo "安装依赖..."
pip3 install -r requirements.txt
pip3 install pyinstaller
# 清理旧的构建文件
echo "清理旧的构建文件..."
rm -rf build dist __pycache__ *.spec
find src -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null || true
# 构建应用
echo "构建macOS应用..."
python3 -m pyinstaller \\
--name "MagicWord" \\
--distpath "dist" \\
--workpath "build" \\
--add-data "resources:resources" \\
--hidden-import "PyQt5" \\
--hidden-import "PyQt5.QtCore" \\
--hidden-import "PyQt5.QtGui" \\
--hidden-import "PyQt5.QtWidgets" \\
--hidden-import "requests" \\
--hidden-import "beautifulsoup4" \\
--hidden-import "python-docx" \\
--hidden-import "PyPDF2" \\
--hidden-import "ebooklib" \\
--hidden-import "chardet" \\
--hidden-import "PIL" \\
--windowed \\
--osx-bundle-identifier "com.magicword.app" \\
--target-architecture arm64 \\
--noconfirm \\
src/main.py
# 检查构建结果
if [ -d "dist/MagicWord.app" ]; then
echo "✅ 构建成功!"
echo "应用位置: dist/MagicWord.app"
echo ""
echo "安装步骤:"
echo "1. 将 MagicWord.app 拖拽到 Applications 文件夹"
echo "2. 首次运行时,右键点击应用选择'打开'"
echo "3. 或者在 系统设置 > 隐私与安全性 中允许应用运行"
else
echo "❌ 构建失败!"
exit 1
fi
# 可选创建DMG
read -p "是否创建DMG安装包(y/n): " create_dmg
if [[ $create_dmg =~ ^[Yy]$ ]]; then
echo "创建DMG安装包..."
# 创建发布目录
release_dir="macos_release"
rm -rf "$release_dir"
mkdir -p "$release_dir"
# 复制应用
cp -r "dist/MagicWord.app" "$release_dir/"
# 创建Applications链接
ln -s "/Applications" "$release_dir/Applications"
# 创建README
cat > "$release_dir/README.txt" << 'EOF'
MagicWord for macOS
安装说明:
1. 将 MagicWord.app 拖拽到 Applications 文件夹
2. 首次运行时,右键点击应用选择"打开"
3. 或者在 系统设置 > 隐私与安全性 中允许应用运行
系统要求:
- macOS Big Sur (11.0) 或更高版本
- Apple Silicon (M1/M2/M3) 推荐
功能特性:
- 隐私学习:通过打字练习来学习文档内容
- 支持多种文档格式TXT, DOCX, PDF
- 智能打字模式
- 美观的Word风格界面
EOF
# 创建DMG
dmg_name="MagicWord-0.3.0-macOS-arm64.dmg"
hdiutil create -volname "MagicWord" -srcfolder "$release_dir" -ov -format UDZO "$dmg_name"
if [ -f "$dmg_name" ]; then
echo "✅ DMG创建成功: $dmg_name"
else
echo "⚠️ DMG创建失败但应用包已准备就绪"
fi
fi
echo "=== 打包完成 ==="

@ -0,0 +1,434 @@
# learning_mode_window.py
import sys
import os
from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QTextEdit, QLabel, QFrame, QMenuBar,
QAction, QFileDialog, QMessageBox, QApplication,
QSplitter, QScrollArea, QStatusBar, QProgressBar)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QRect
from PyQt5.QtGui import QFont, QPalette, QColor, QIcon, QPixmap, QTextCharFormat, QTextCursor
from src.ui.components import CustomTitleBar, TextDisplayWidget
from src.typing_logic import TypingLogic
from src.file_parser import FileParser
from src.ui.theme_manager import theme_manager
class LearningModeWindow(QMainWindow):
def __init__(self, parent=None, imported_content="", current_position=0):
"""
学习模式窗口
- 顶部显示UI.png图片
- 下方显示输入字符页面
- 输入字符显示导入的文件内容
参数:
parent: 父窗口
imported_content: 从主窗口传递的导入内容
current_position: 当前学习进度位置
"""
super().__init__(parent)
self.parent_window = parent
self.imported_content = imported_content
self.current_position = current_position
self.typing_logic = None
self.is_loading_file = False
# 初始化UI
self.initUI()
# 初始化打字逻辑
self.init_typing_logic()
# 如果有导入内容,初始化显示
if self.imported_content:
self.initialize_with_imported_content()
def initialize_with_imported_content(self):
"""
使用从主窗口传递的导入内容初始化学习模式窗口
"""
if not self.imported_content:
return
# 重置打字逻辑
if self.typing_logic:
self.typing_logic.reset(self.imported_content)
# 显示已学习的内容
display_text = self.imported_content[:self.current_position]
self.text_display_widget.text_display.setPlainText(display_text)
# 更新状态
progress = (self.current_position / len(self.imported_content)) * 100
self.status_label.setText(f"继续学习 - 进度: {self.current_position}/{len(self.imported_content)} 字符")
self.progress_label.setText(f"进度: {progress:.1f}%")
# 设置光标位置到文本末尾
cursor = self.text_display_widget.text_display.textCursor()
cursor.movePosition(cursor.End)
self.text_display_widget.text_display.setTextCursor(cursor)
def initUI(self):
"""
初始化学习模式窗口UI
- 设置窗口标题和大小
- 创建顶部图片区域
- 创建下方输入区域
"""
# 设置窗口属性
self.setWindowTitle("学习模式 - MagicWord")
self.setGeometry(200, 200, 900, 700)
self.setWindowFlags(Qt.Window) # 独立窗口
# 创建中央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.create_top_image_area(main_layout)
# 创建分隔线
separator = QFrame()
separator.setFrameShape(QFrame.HLine)
separator.setFrameShadow(QFrame.Sunken)
separator.setStyleSheet("background-color: #d0d0d0;")
main_layout.addWidget(separator)
# 创建输入区域
self.create_input_area(main_layout)
# 创建菜单栏
self.create_menu_bar()
# 创建状态栏
self.create_status_bar()
# 如果有导入内容,更新状态栏显示
if self.imported_content:
progress = (self.current_position / len(self.imported_content)) * 100
self.status_label.setText(f"继续学习 - 进度: {self.current_position}/{len(self.imported_content)} 字符")
self.progress_label.setText(f"进度: {progress:.1f}%")
def create_top_image_area(self, main_layout):
"""
创建顶部图片区域
- 加载并显示UI.png图片完全铺满窗口上方
- 窗口大小自动适配图片大小
"""
# 创建图片标签
self.image_label = QLabel()
self.image_label.setAlignment(Qt.AlignCenter)
self.image_label.setStyleSheet("""
QLabel {
background-color: #f8f9fa;
border: none;
margin: 0px;
padding: 0px;
}
""")
# 加载UI.png图片
ui_image_path = os.path.join(os.path.dirname(__file__), 'ui', 'UI.png')
if os.path.exists(ui_image_path):
pixmap = QPixmap(ui_image_path)
# 保存原始图片尺寸
self.original_pixmap = pixmap
# 设置图片完全铺满标签
self.image_label.setPixmap(pixmap)
self.image_label.setScaledContents(True) # 关键:让图片缩放填充整个标签
# 设置图片标签的尺寸策略,使其可以扩展
from PyQt5.QtWidgets import QSizePolicy
self.image_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# 设置图片区域的最小高度为图片高度的1/3确保图片可见
min_height = max(200, pixmap.height() // 3)
self.image_label.setMinimumHeight(min_height)
# 重新设置窗口大小以适配图片
self.resize(pixmap.width(), self.height())
else:
self.image_label.setText("UI图片未找到")
self.image_label.setStyleSheet("""
QLabel {
background-color: #f8f9fa;
border: none;
color: #666666;
font-size: 14px;
padding: 20px;
}
""")
self.image_label.setMinimumHeight(200)
# 直接添加图片标签到主布局,不使用滚动区域
main_layout.addWidget(self.image_label)
def resizeEvent(self, event):
"""
窗口大小改变事件
- 动态调整图片显示区域
"""
super().resizeEvent(event)
# 获取窗口宽度
window_width = self.width()
# 如果图片标签存在,调整其大小以适配窗口
if hasattr(self, 'image_label') and self.image_label:
# 设置图片标签的固定宽度为窗口宽度
self.image_label.setFixedWidth(window_width)
# 根据窗口宽度计算合适的高度(保持图片比例)
if hasattr(self, 'original_pixmap') and self.original_pixmap:
original_width = self.original_pixmap.width()
original_height = self.original_pixmap.height()
# 计算保持比例的高度
new_height = int(window_width * original_height / original_width)
self.image_label.setFixedHeight(new_height)
else:
# 如果没有原始图片,使用默认高度
self.image_label.setFixedHeight(300)
def create_input_area(self, main_layout):
"""
创建输入区域
- 创建文本显示组件
- 设置与主系统相同的样式
"""
# 创建文本显示组件(复用主系统的组件)
self.text_display_widget = TextDisplayWidget(self)
# 设置样式,使其与主系统保持一致
self.text_display_widget.setStyleSheet("""
TextDisplayWidget {
background-color: white;
border: 1px solid #d0d0d0;
border-radius: 0px;
}
""")
# 连接文本变化信号
self.text_display_widget.text_display.textChanged.connect(self.on_text_changed)
main_layout.addWidget(self.text_display_widget, 1) # 占据剩余空间
def create_menu_bar(self):
"""
创建菜单栏
- 文件菜单导入退出
- 帮助菜单关于
"""
menu_bar = self.menuBar()
# 文件菜单
file_menu = menu_bar.addMenu('文件')
# 导入动作
import_action = QAction('导入文件', self)
import_action.setShortcut('Ctrl+O')
import_action.triggered.connect(self.import_file)
file_menu.addAction(import_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.show_about)
help_menu.addAction(about_action)
def create_status_bar(self):
"""
创建状态栏
- 显示当前状态和学习进度
"""
self.status_bar = self.statusBar()
# 创建状态标签
self.status_label = QLabel("就绪 - 请先导入文件开始学习")
self.progress_label = QLabel("进度: 0%")
# 添加到状态栏
self.status_bar.addWidget(self.status_label)
self.status_bar.addPermanentWidget(self.progress_label)
def init_typing_logic(self):
"""
初始化打字逻辑
- 创建打字逻辑实例
- 设置默认内容
"""
# 创建打字逻辑实例
self.typing_logic = TypingLogic("欢迎使用学习模式!\n\n请先导入文件开始打字学习。")
# 设置文本显示组件的打字逻辑
if hasattr(self.text_display_widget, 'set_typing_logic'):
self.text_display_widget.set_typing_logic(self.typing_logic)
def import_file(self):
"""
导入文件
- 打开文件对话框选择文件
- 解析文件内容
- 重置打字逻辑
"""
file_path, _ = QFileDialog.getOpenFileName(
self, "导入学习文件", "",
"文档文件 (*.docx *.txt *.pdf);;所有文件 (*.*)"
)
if file_path:
try:
self.is_loading_file = True
# 使用文件解析器
parser = FileParser()
content = parser.parse_file(file_path)
if content:
# 存储导入的内容
self.imported_content = content
self.current_position = 0
# 重置打字逻辑
if self.typing_logic:
self.typing_logic.reset(content)
# 清空文本显示
self.text_display_widget.text_display.clear()
# 更新状态
self.status_label.setText(f"已导入: {os.path.basename(file_path)}")
self.progress_label.setText(f"进度: 0% (0/{len(content)} 字符)")
# 显示成功消息
QMessageBox.information(self, "导入成功",
f"文件导入成功!\n文件: {os.path.basename(file_path)}\n字符数: {len(content)}\n\n开始打字以显示学习内容。")
else:
QMessageBox.warning(self, "导入失败", "无法解析文件内容,请检查文件格式。")
except Exception as e:
QMessageBox.critical(self, "导入错误", f"导入文件时出错:\n{str(e)}")
finally:
self.is_loading_file = False
def on_text_changed(self):
"""
文本变化处理
- 根据导入的内容逐步显示
- 更新学习进度
"""
# 如果正在加载文件,跳过处理
if self.is_loading_file:
return
# 如果没有导入内容,清空文本
if not self.imported_content:
current_text = self.text_display_widget.text_display.toPlainText()
if current_text:
self.text_display_widget.text_display.clear()
self.status_label.setText("请先导入文件开始学习")
return
# 获取当前文本
current_text = self.text_display_widget.text_display.toPlainText()
# 始终确保显示的是导入的文件内容,而不是用户输入的文字
expected_text = self.imported_content[:len(current_text)]
# 如果当前文本与期望文本不一致,强制恢复到期望文本
if current_text != expected_text:
cursor = self.text_display_widget.text_display.textCursor()
self.text_display_widget.text_display.setPlainText(expected_text)
self.text_display_widget.text_display.setTextCursor(cursor)
# 显示错误信息
if len(current_text) > 0:
expected_char = self.imported_content[len(current_text)-1] if len(current_text) <= len(self.imported_content) else ''
self.status_label.setText(f"输入错误!期望字符: '{expected_char}'")
return
# 检查输入是否正确
if self.typing_logic and len(current_text) > 0:
result = self.typing_logic.check_input(current_text)
if not result['correct']:
# 输入错误,恢复到正确的状态
expected_text = self.imported_content[:len(current_text)]
if current_text != expected_text:
cursor = self.text_display_widget.text_display.textCursor()
self.text_display_widget.text_display.setPlainText(expected_text)
self.text_display_widget.text_display.setTextCursor(cursor)
# 显示错误信息
self.status_label.setText(f"输入错误!期望字符: '{result.get('expected', '')}'")
else:
# 输入正确,更新进度
self.current_position = len(current_text)
progress = (self.current_position / len(self.imported_content)) * 100
self.progress_label.setText(
f"进度: {progress:.1f}% ({self.current_position}/{len(self.imported_content)} 字符)"
)
# 检查是否完成
if result.get('completed', False):
self.status_label.setText("恭喜!学习完成!")
QMessageBox.information(self, "学习完成", "恭喜您完成了所有学习内容!")
else:
self.status_label.setText("继续输入以显示更多内容...")
def show_about(self):
"""
显示关于对话框
"""
QMessageBox.about(self, "关于学习模式",
"MagicWord 学习模式\n\n"
"功能特点:\n"
"• 顶部显示UI界面图片\n"
"• 下方为打字输入区域\n"
"• 导入文件后逐步显示内容\n"
"• 实时显示学习进度\n\n"
"使用方法:\n"
"1. 点击'文件'->'导入文件'选择学习材料\n"
"2. 在下方文本区域开始打字\n"
"3. 系统会根据您的输入逐步显示内容")
def closeEvent(self, event):
"""
窗口关闭事件
- 通知父窗口学习模式已关闭
"""
if self.parent_window and hasattr(self.parent_window, 'on_learning_mode_closed'):
self.parent_window.on_learning_mode_closed()
event.accept()
def keyPressEvent(self, event):
"""
按键事件处理
- 处理特殊按键
"""
# 处理Escape键关闭窗口
if event.key() == Qt.Key_Escape:
self.close()
else:
super().keyPressEvent(event)

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

@ -0,0 +1,492 @@
# ui/components.py
from PyQt5.QtWidgets import QWidget, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QTextEdit, QProgressBar
from PyQt5.QtCore import Qt
class CustomTitleBar(QWidget):
def __init__(self, parent=None):
"""
自定义标题栏
- 创建标题栏UI元素
- 添加窗口控制按钮
"""
super().__init__(parent)
self.parent = parent
self.setup_ui()
def setup_ui(self):
"""
设置标题栏UI
- 初始化所有UI组件
- 设置组件属性和样式
"""
# 创建水平布局
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):
"""
最小化窗口
- 触发窗口最小化事件
"""
if self.parent:
self.parent.showMinimized()
def maximize_window(self):
"""
最大化窗口
- 切换窗口最大化状态
"""
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):
"""
关闭窗口
- 触发窗口关闭事件
"""
if self.parent:
self.parent.close()
class ProgressBarWidget(QWidget):
def __init__(self, parent=None):
"""
进度条组件
- 显示打字练习进度
- 显示统计信息
"""
super().__init__(parent)
self.setup_ui()
def setup_ui(self):
"""
设置进度条UI
- 初始化所有UI组件
- 设置组件属性和样式
"""
# 创建垂直布局
layout = QVBoxLayout()
layout.setContentsMargins(10, 10, 10, 10)
layout.setSpacing(5)
# 创建水平布局用于统计信息
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):
"""
更新进度条
- 设置进度值
- 更新显示
"""
self.progress_bar.setValue(int(progress))
def update_stats(self, wpm: int, accuracy: float, time_elapsed: int):
"""
更新统计信息
- wpm: 每分钟字数
- accuracy: 准确率(%)
- time_elapsed: 用时()
"""
self.wpm_label.setText(f"WPM: {wpm}")
self.accuracy_label.setText(f"准确率: {accuracy:.1f}%")
self.time_label.setText(f"用时: {time_elapsed}s")
class StatsDisplayWidget(QWidget):
def __init__(self, parent=None):
"""
统计信息显示组件
- 显示准确率WPM等统计信息
"""
super().__init__(parent)
self.setup_ui()
def setup_ui(self):
"""
设置统计信息显示UI
- 初始化所有UI组件
- 设置组件属性和样式
"""
# 创建水平布局
layout = QHBoxLayout()
layout.setContentsMargins(10, 5, 10, 5)
layout.setSpacing(15)
# 创建统计信息标签
self.wpm_label = QLabel("WPM: 0")
self.accuracy_label = QLabel("准确率: 0%")
# 设置标签样式
label_style = "font-size: 12px; font-weight: normal; color: #333333;"
self.wpm_label.setStyleSheet(label_style)
self.accuracy_label.setStyleSheet(label_style)
# 添加组件到布局
layout.addWidget(self.wpm_label)
layout.addWidget(self.accuracy_label)
layout.addStretch()
self.setLayout(layout)
# 设置样式
self.setStyleSheet("""
StatsDisplayWidget {
background-color: #f0f0f0;
border-bottom: 1px solid #d0d0d0;
}
""")
def update_stats(self, wpm: int, accuracy: float):
"""
更新统计信息
- wpm: 每分钟字数
- accuracy: 准确率(%)
"""
self.wpm_label.setText(f"WPM: {wpm}")
self.accuracy_label.setText(f"准确率: {accuracy:.1f}%")
class QuoteDisplayWidget(QWidget):
def __init__(self, parent=None):
"""
每日一言显示组件
- 显示每日一言功能
"""
super().__init__(parent)
self.setup_ui()
def setup_ui(self):
"""
设置每日一言显示UI
- 初始化所有UI组件
- 设置组件属性和样式
"""
# 创建水平布局
layout = QHBoxLayout()
layout.setContentsMargins(10, 5, 10, 5)
layout.setSpacing(15)
# 创建每日一言标签
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.quote_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;
}
""")
# 添加组件到布局
layout.addWidget(self.quote_label)
layout.addStretch()
layout.addWidget(self.refresh_quote_button)
self.setLayout(layout)
# 设置样式
self.setStyleSheet("""
QuoteDisplayWidget {
background-color: #f0f0f0;
border-bottom: 1px solid #d0d0d0;
}
""")
def update_quote(self, quote: str):
"""
更新每日一言
- quote: 每日一言内容
"""
self.quote_label.setText(f"每日一言: {quote}")
class WeatherDisplayWidget(QWidget):
def __init__(self, parent=None):
"""
天气显示组件
- 显示天气信息
"""
super().__init__(parent)
self.setup_ui()
def setup_ui(self):
"""
设置天气显示UI
- 初始化所有UI组件
- 设置组件属性和样式
"""
# 创建水平布局
layout = QHBoxLayout()
layout.setContentsMargins(10, 5, 10, 5)
layout.setSpacing(15)
# 创建天气信息标签
self.weather_label = QLabel("天气: 暂无")
# 设置标签样式
label_style = "font-size: 12px; font-weight: normal; color: #333333;"
self.weather_label.setStyleSheet(label_style)
# 创建天气刷新按钮
self.refresh_weather_button = QPushButton("刷新")
self.refresh_weather_button.setStyleSheet("""
QPushButton {
background-color: #0078d7;
color: white;
border: none;
padding: 5px 10px;
border-radius: 3px;
font-size: 12px;
}
QPushButton:hover {
background-color: #005a9e;
}
""")
# 添加组件到布局
layout.addWidget(self.weather_label)
layout.addStretch()
layout.addWidget(self.refresh_weather_button)
self.setLayout(layout)
# 设置样式
self.setStyleSheet("""
WeatherDisplayWidget {
background-color: #f0f0f0;
border-bottom: 1px solid #d0d0d0;
}
""")
def update_weather(self, weather_info: dict):
"""
更新天气信息
- weather_info: 天气信息字典
"""
if weather_info:
city = weather_info.get("city", "未知")
temperature = weather_info.get("temperature", "N/A")
description = weather_info.get("description", "N/A")
self.weather_label.setText(f"天气: {city} {temperature}°C {description}")
else:
self.weather_label.setText("天气: 获取失败")
class TextDisplayWidget(QWidget):
def __init__(self, parent=None):
"""
文本显示组件
- 显示待练习文本
- 高亮当前字符
- 显示用户输入反馈
"""
super().__init__(parent)
self.text_content = ""
self.current_index = 0
self.setup_ui()
def setup_ui(self):
"""
设置文本显示UI
- 初始化文本显示区域
- 设置样式和布局
"""
# 创建垂直布局
layout = QVBoxLayout()
layout.setContentsMargins(20, 20, 20, 20)
# 创建文本显示区域
self.text_display = QTextEdit()
self.text_display.setReadOnly(False) # 设置为可编辑
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):
"""
设置显示文本
- text: 要显示的文本内容
"""
self.text_content = text
self.current_index = 0
# 初始不显示内容,通过打字逐步显示
self.text_display.setHtml("")
def highlight_character(self, position: int):
"""
高亮指定位置的字符
- position: 字符位置索引
"""
if 0 <= position < len(self.text_content):
self.current_index = position
# 不再直接高亮字符,而是通过用户输入来显示内容
pass
def _update_display(self, user_input: str = ""):
"""
更新文本显示
- user_input: 用户输入文本(可选)
"""
# 导入需要的模块
from PyQt5.QtGui import QTextCursor
if not self.text_content:
self.text_display.clear()
return
# 简单显示文本,不使用任何高亮
if user_input:
# 只显示用户已输入的部分文本
displayed_text = self.text_content[:len(user_input)]
else:
# 没有用户输入,不显示任何内容
displayed_text = ""
# 更新文本显示
self.text_display.setPlainText(displayed_text)
# 安全地滚动到光标位置
if user_input and displayed_text:
try:
cursor = self.text_display.textCursor()
# 将光标定位到文本末尾
cursor.setPosition(len(displayed_text))
self.text_display.setTextCursor(cursor)
self.text_display.ensureCursorVisible()
except Exception:
# 如果光标定位失败,忽略错误
pass
def show_user_input(self, input_text: str):
"""
显示用户输入的文本
- input_text: 用户输入的文本
"""
self._update_display(input_text)

@ -169,10 +169,10 @@ class ThemeManager(QObject):
/* 菜单栏 */
QMenuBar {
background-color: #2d2d2d;
border: 1px solid #3c3c3c;
background-color: #0078d7;
border: 1px solid #005a9e;
font-size: 12px;
color: #e0e0e0;
color: #ffffff;
}
QMenuBar::item {
@ -182,7 +182,7 @@ class ThemeManager(QObject):
}
QMenuBar::item:selected {
background-color: #3c3c3c;
background-color: #106ebe;
}
/* 菜单 */
@ -356,6 +356,31 @@ class ThemeManager(QObject):
background-color: transparent;
}
/* 消息框 - 修复黑色背景问题 */
QMessageBox {
background-color: #2d2d2d;
color: #e0e0e0;
}
QMessageBox QPushButton {
background-color: #3c3c3c;
color: #e0e0e0;
border: 1px solid #5a5a5a;
border-radius: 3px;
padding: 5px 15px;
min-width: 80px;
}
QMessageBox QPushButton:hover {
background-color: #4a4a4a;
border: 1px solid #6a6a6a;
}
QMessageBox QPushButton:pressed {
background-color: #2a2a2a;
border: 1px solid #1a1a1a;
}
/* 滚动条 */
QScrollBar:vertical {
background-color: #2d2d2d;
@ -396,10 +421,10 @@ class ThemeManager(QObject):
/* 菜单栏 */
QMenuBar {
background-color: #ffffff;
border: 1px solid #d0d0d0;
background-color: #0078d7;
border: 1px solid #005a9e;
font-size: 12px;
color: #333333;
color: #ffffff;
}
QMenuBar::item {
@ -409,7 +434,7 @@ class ThemeManager(QObject):
}
QMenuBar::item:selected {
background-color: #f0f0f0;
background-color: #106ebe;
}
/* 菜单 */
@ -598,6 +623,31 @@ class ThemeManager(QObject):
background-color: transparent;
}
/* 消息框 - 修复黑色背景问题 */
QMessageBox {
background-color: #ffffff;
color: #333333;
}
QMessageBox QPushButton {
background-color: #ffffff;
color: #333333;
border: 1px solid #d0d0d0;
border-radius: 3px;
padding: 5px 15px;
min-width: 80px;
}
QMessageBox QPushButton:hover {
background-color: #f0f0f0;
border: 1px solid #0078d7;
}
QMessageBox QPushButton:pressed {
background-color: #e0e0e0;
border: 1px solid #005a9e;
}
/* 滚动条 */
QScrollBar:vertical {
background-color: #ffffff;

@ -1632,8 +1632,8 @@ class daily_sentence_API:
params['json'] = 'true' # 默认为 JSON
try:
# 发送 GET 请求
response = requests.get(self.api_url, params=params)
# 发送 GET 请求禁用SSL验证以避免证书问题
response = requests.get(self.api_url, params=params, verify=False, timeout=10)
response.raise_for_status() # 如果请求失败(如 4xx 或 5xx抛出异常
# 根据格式类型处理响应
@ -1641,6 +1641,69 @@ class daily_sentence_API:
return response.json() # 解析为字典
else:
return response.text # 返回文本内容
except requests.exceptions.SSLError as e:
print(f"SSL连接失败: {e}")
# 尝试使用备用API
return self.get_sentence_from_backup_api(format_type)
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
# 尝试使用备用API
return self.get_sentence_from_backup_api(format_type)
except Exception as e:
print(f"未知错误: {e}")
return None
def get_sentence_from_backup_api(self, format_type='json'):
"""使用备用API获取每日一言"""
try:
# 备用API古诗词API
response = requests.get("https://v1.jinrishici.com/all.json", timeout=5)
if response.status_code == 200:
data = response.json()
content = data.get('content', '山重水复疑无路,柳暗花明又一村。')
author = data.get('author', '陆游')
title = data.get('origin', '游山西村')
# 格式化返回数据与原API格式保持一致
if format_type == 'json':
return {
'yiyan': f"{content}{author}{title}",
'id': 'backup_001',
'createTime': '2024-01-01',
'nick': '古诗词'
}
else:
return f"{content}{author}{title}"
else:
# 如果备用API也失败返回默认内容
return self.get_default_sentence(format_type)
except Exception as e:
print(f"备用API请求失败: {e}")
return self.get_default_sentence(format_type)
def get_default_sentence(self, format_type='json'):
"""获取默认的每日一言内容"""
default_sentences = [
"学而不思则罔,思而不学则殆。 — 孔子《论语》",
"天行健,君子以自强不息。 — 《周易》",
"千里之行,始于足下。 — 老子《道德经》",
"读书破万卷,下笔如有神。 — 杜甫《奉赠韦左丞丈二十二韵》",
"海内存知己,天涯若比邻。 — 王勃《送杜少府之任蜀州》"
]
import random
import time
# 使用当前时间作为随机种子,确保每天显示不同的内容
random.seed(int(time.time() / 86400)) # 每天变化一次
sentence = random.choice(default_sentences)
if format_type == 'json':
return {
'yiyan': sentence,
'id': 'default_001',
'createTime': '2024-01-01',
'nick': '经典名言'
}
else:
return sentence

@ -131,6 +131,10 @@ class WordStyleMainWindow(QMainWindow):
# 连接主题切换信号
theme_manager.theme_changed.connect(self.on_theme_changed)
# 设置默认为白色模式(禁用自动检测)
theme_manager.enable_auto_detection(False)
theme_manager.set_dark_theme(False)
# 应用当前主题
self.apply_theme()
@ -150,30 +154,35 @@ class WordStyleMainWindow(QMainWindow):
"""更新组件样式"""
colors = theme_manager.get_current_theme_colors()
# 更新菜单栏样式
# 更新菜单栏样式 - 使用微软蓝
if hasattr(self, 'menubar'):
self.menubar.setStyleSheet(f"""
QMenuBar {{
background-color: {colors['surface']};
border: 1px solid {colors['border']};
self.menubar.setStyleSheet("""
QMenuBar {
background-color: #0078d7;
border: 1px solid #005a9e;
font-size: 12px;
color: {colors['text']};
}}
color: #ffffff;
}
QMenuBar::item {{
QMenuBar::item {
background-color: transparent;
padding: 4px 10px;
color: {colors['text']};
}}
color: #ffffff;
}
QMenuBar::item:selected {{
background-color: {colors['surface_hover']};
}}
QMenuBar::item:selected {
background-color: #106ebe;
}
QMenuBar::item:pressed {{
background-color: {colors['accent']};
color: {colors['surface']};
}}
QMenuBar::item:pressed {
background-color: #005a9e;
color: #ffffff;
}
#startMenu {
background-color: white;
color: #000000;
}
""")
# 更新文件菜单样式
@ -538,6 +547,7 @@ class WordStyleMainWindow(QMainWindow):
# 开始菜单
start_menu = menubar.addMenu('开始(S)')
start_menu.setObjectName("startMenu")
self.start_menu = start_menu # 保存为实例变量
# 撤销
@ -657,24 +667,30 @@ class WordStyleMainWindow(QMainWindow):
show_weather_action = QAction('显示详细天气', self)
show_weather_action.triggered.connect(self.show_detailed_weather)
weather_menu.addAction(show_weather_action)
#插入
# 插入菜单
insert_menu = menubar.addMenu('插入(I)')
#绘图
# 绘图菜单
paint_menu = menubar.addMenu('绘图(D)')
#设计
# 设计菜单
design_menu = menubar.addMenu('设计(G)')
#布局
# 布局菜单
layout_menu = menubar.addMenu('布局(L)')
#引用
# 引用菜单
reference_menu = menubar.addMenu('引用(R)')
#邮件
# 邮件菜单
mail_menu = menubar.addMenu('邮件(M)')
#审阅
# 审阅菜单
review_menu = menubar.addMenu('审阅(W)')
#视图
view_menu = menubar.addMenu('视图(V)')
#开发工具
# 开发工具菜单
developer_menu = menubar.addMenu('开发工具(Q)')
# 帮助菜单
help_menu = menubar.addMenu('帮助(H)')
@ -1822,132 +1838,59 @@ class WordStyleMainWindow(QMainWindow):
self.status_bar.showMessage("打印布局功能开发中...", 3000)
def set_view_mode(self, mode):
"""设置视图模式 - 实现文档A和学习文档C的正确交互"""
"""设置视图模式 - 打开学习模式窗口或切换到打字模式"""
if mode not in ["typing", "learning"]:
return
# 保存当前模式的内容
current_content = self.text_edit.toPlainText()
# 根据当前模式保存特定信息
if self.view_mode == "typing":
# 打字模式保存打字内容到文档A
self.typing_mode_content = current_content
print(f"保存打字模式内容到文档A长度: {len(self.typing_mode_content)}")
elif self.view_mode == "learning":
# 学习模式:保存当前学习进度和内容
self.learning_text = current_content
if hasattr(self, 'displayed_chars') and self.imported_content:
self.learning_progress = self.displayed_chars
print(f"保存学习模式内容,学习进度: {self.learning_progress}/{len(self.imported_content) if self.imported_content else 0}")
# 更新模式
self.view_mode = mode
self.last_edit_mode = mode
# 更新菜单项状态
self.typing_mode_action.setChecked(mode == "typing")
self.learning_mode_action.setChecked(mode == "learning")
# 临时禁用文本变化信号,避免递归
self.text_edit.textChanged.disconnect(self.on_text_changed)
try:
if mode == "typing":
# 打字模式显示学习模式下已输入的内容文档A
self.status_bar.showMessage("切换到打字模式 - 显示已输入的内容", 3000)
# 设置文档A的内容学习模式下已输入的内容
self.text_edit.clear()
if hasattr(self, 'learning_progress') and self.learning_progress > 0:
# 显示学习模式下已输入的内容
display_text = self.imported_content[:self.learning_progress]
self.text_edit.setPlainText(display_text)
else:
# 如果没有学习进度,显示默认提示
self.text_edit.setPlainText("请先在学习模式下输入内容")
if mode == "learning":
# 学习模式:打开新的学习模式窗口
try:
from learning_mode_window import LearningModeWindow
# 设置光标位置到文档末尾
cursor = self.text_edit.textCursor()
cursor.movePosition(cursor.End)
self.text_edit.setTextCursor(cursor)
# 准备传递给学习窗口的参数
imported_content = ""
current_position = 0
# 重置打字逻辑,准备接受新的输入
if self.typing_logic:
# 如果有已导入的内容,传递给学习窗口
if hasattr(self, 'imported_content') and self.imported_content:
imported_content = self.imported_content
if hasattr(self, 'learning_progress') and self.learning_progress > 0:
# 使用已输入的内容作为打字逻辑的基础
self.typing_logic.reset(self.imported_content)
# 设置当前位置到已输入的末尾
self.typing_logic.current_index = self.learning_progress
self.typing_logic.typed_chars = self.learning_progress
else:
self.typing_logic.reset("")
current_position = self.learning_progress
# 重置显示字符计数
self.displayed_chars = 0
# 创建学习模式窗口,直接传递导入内容
self.learning_window = LearningModeWindow(self, imported_content, current_position)
# 重置图片插入记录
if hasattr(self, 'inserted_images'):
self.inserted_images.clear()
# 显示学习模式窗口
self.learning_window.show()
elif mode == "learning":
# 学习模式从上次中断的地方继续显示学习文档C的内容
if not self.imported_content:
self.status_bar.showMessage("学习模式需要导入文件 - 请先打开一个文件", 3000)
# 清空文本编辑器,等待导入文件
self.text_edit.clear()
if self.typing_logic:
self.typing_logic.reset("在此输入您的内容...")
# 重置显示字符计数
self.displayed_chars = 0
# 重置图片插入记录
if hasattr(self, 'inserted_images'):
self.inserted_images.clear()
else:
# 检查是否有保存的学习进度
if hasattr(self, 'learning_progress') and self.learning_progress > 0:
self.status_bar.showMessage(f"切换到学习模式 - 从上次中断处继续 ({self.learning_progress}/{len(self.imported_content)})", 3000)
# 恢复学习进度
self.displayed_chars = self.learning_progress
# 重置打字逻辑,准备接受导入内容
if self.typing_logic:
self.typing_logic.reset(self.imported_content)
# 获取应该显示的学习文档C的内容部分
display_text = self.imported_content[:self.displayed_chars]
# 设置文本内容
self.text_edit.clear()
self.text_edit.setPlainText(display_text)
# 设置光标位置到文本末尾
cursor = self.text_edit.textCursor()
cursor.movePosition(cursor.End)
self.text_edit.setTextCursor(cursor)
# 重新插入图片
if hasattr(self, 'inserted_images'):
self.inserted_images.clear()
self.insert_images_in_text()
else:
self.status_bar.showMessage("切换到学习模式 - 准备显示学习文档C内容", 3000)
# 重置打字逻辑,准备接受导入内容
if self.typing_logic:
self.typing_logic.reset(self.imported_content)
# 重置显示字符计数
self.displayed_chars = 0
# 清空文本编辑器,等待用户开始打字
self.text_edit.clear()
# 重置图片插入记录
if hasattr(self, 'inserted_images'):
self.inserted_images.clear()
finally:
# 重新连接文本变化信号
self.text_edit.textChanged.connect(self.on_text_changed)
# 更新菜单状态
self.learning_mode_action.setChecked(True)
self.typing_mode_action.setChecked(False)
# 切换到打字模式(主窗口保持打字模式)
self.view_mode = "typing"
self.status_bar.showMessage("学习模式窗口已打开", 3000)
except ImportError as e:
QMessageBox.critical(self, "错误", f"无法加载学习模式窗口:\n{str(e)}")
except Exception as e:
QMessageBox.critical(self, "错误", f"打开学习模式窗口时出错:\n{str(e)}")
elif mode == "typing":
# 打字模式:保持当前窗口状态
self.view_mode = "typing"
self.typing_mode_action.setChecked(True)
self.learning_mode_action.setChecked(False)
self.status_bar.showMessage("当前为打字模式", 2000)
def on_learning_mode_closed(self):
"""学习模式窗口关闭时的回调"""
# 重置菜单状态
self.learning_mode_action.setChecked(False)
self.typing_mode_action.setChecked(True)
self.view_mode = "typing"
self.status_bar.showMessage("学习模式窗口已关闭", 2000)
def set_page_color(self, color):
"""设置页面颜色"""
@ -2264,10 +2207,59 @@ class WordStyleMainWindow(QMainWindow):
def show_about(self):
"""显示关于对话框"""
QMessageBox.about(
self, "关于 MagicWord",
"MagicWord - 隐私学习软件\n\n"
"版本: 2.0\n"
# 创建自定义对话框
dialog = QDialog(self)
dialog.setWindowTitle("关于 MagicWord")
dialog.setModal(True)
dialog.setFixedSize(500, 400)
# 创建布局
layout = QVBoxLayout()
# 添加图标和标题布局
header_layout = QHBoxLayout()
# 添加应用程序图标
try:
icon_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "resources", "icons", "app_icon_128X128.png")
if os.path.exists(icon_path):
icon_label = QLabel()
icon_label.setAlignment(Qt.AlignCenter)
pixmap = QPixmap(icon_path)
if not pixmap.isNull():
# 缩放图标到合适大小
scaled_pixmap = pixmap.scaled(80, 80, Qt.KeepAspectRatio, Qt.SmoothTransformation)
icon_label.setPixmap(scaled_pixmap)
header_layout.addWidget(icon_label)
except Exception as e:
print(f"加载图标失败: {e}")
# 添加标题和版本信息
title_layout = QVBoxLayout()
title_label = QLabel("MagicWord")
title_label.setStyleSheet("font-size: 24px; font-weight: bold; color: #0078d7;")
title_label.setAlignment(Qt.AlignCenter)
version_label = QLabel("版本 1.0")
version_label.setStyleSheet("font-size: 14px; color: #666;")
version_label.setAlignment(Qt.AlignCenter)
title_layout.addWidget(title_label)
title_layout.addWidget(version_label)
header_layout.addLayout(title_layout)
layout.addLayout(header_layout)
# 添加分隔线
separator = QFrame()
separator.setFrameShape(QFrame.HLine)
separator.setFrameShadow(QFrame.Sunken)
separator.setStyleSheet("color: #ddd;")
layout.addWidget(separator)
# 添加关于信息
about_text = QLabel(
"隐私学习软件\n\n"
"基于 Microsoft Word 界面设计\n\n"
"功能特色:\n"
"• 仿Word界面设计\n"
@ -2276,6 +2268,79 @@ class WordStyleMainWindow(QMainWindow):
"• 实时进度跟踪\n"
"• 天气和名言显示"
)
about_text.setAlignment(Qt.AlignCenter)
about_text.setStyleSheet("font-size: 12px; line-height: 1.5;")
layout.addWidget(about_text)
# 添加按钮布局
button_layout = QHBoxLayout()
button_layout.addStretch() # 添加弹性空间使按钮居中
# 添加"沃式烁"按钮
woshishuo_button = QPushButton("沃式烁")
woshishuo_button.clicked.connect(lambda: self.show_woshishuo_image())
button_layout.addWidget(woshishuo_button)
# 添加OK按钮
ok_button = QPushButton("OK")
ok_button.clicked.connect(dialog.accept)
button_layout.addWidget(ok_button)
button_layout.addStretch() # 添加弹性空间使按钮居中
layout.addLayout(button_layout)
dialog.setLayout(layout)
# 显示对话框
dialog.exec_()
def show_woshishuo_image(self):
"""显示沃式烁图片"""
try:
# 图片路径
image_path = os.path.join(os.path.dirname(__file__), "ui", "114514.png")
# 检查图片是否存在
if not os.path.exists(image_path):
QMessageBox.warning(self, "错误", "图片文件不存在: 114514.png")
return
# 创建图片查看对话框
image_dialog = QDialog(self)
image_dialog.setWindowTitle("沃式烁")
image_dialog.setModal(True)
image_dialog.setFixedSize(500, 400)
# 创建布局
layout = QVBoxLayout()
# 添加图片标签
image_label = QLabel()
image_label.setAlignment(Qt.AlignCenter)
image_label.setScaledContents(True)
# 加载图片
pixmap = QPixmap(image_path)
if pixmap.isNull():
QMessageBox.warning(self, "错误", "无法加载图片文件")
return
# 缩放图片以适应对话框
scaled_pixmap = pixmap.scaled(450, 300, Qt.KeepAspectRatio, Qt.SmoothTransformation)
image_label.setPixmap(scaled_pixmap)
layout.addWidget(image_label)
# 添加关闭按钮
close_button = QPushButton("关闭")
close_button.clicked.connect(image_dialog.accept)
layout.addWidget(close_button)
image_dialog.setLayout(layout)
image_dialog.exec_()
except Exception as e:
QMessageBox.warning(self, "错误", f"显示图片时出错: {str(e)}")
def highlight_next_char(self, position, expected_char):
"""高亮显示下一个期望字符"""
@ -2337,49 +2402,122 @@ class WordStyleMainWindow(QMainWindow):
self.status_bar.showMessage("已切换到黑色模式", 2000)
def show_image_viewer(self, filename, image_data):
"""显示图片查看器"""
"""显示图片查看器 - 图片真正铺满整个窗口上方"""
try:
# 创建图片查看窗口
# 创建自定义图片查看窗口
viewer = QDialog(self)
viewer.setWindowTitle(f"图片查看 - {filename}")
viewer.setModal(False)
viewer.resize(800, 600)
# 创建布局
layout = QVBoxLayout()
# 移除窗口边框和标题栏装饰,设置为工具窗口样式
viewer.setWindowFlags(Qt.Tool | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
# 创建滚动区域
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
# 设置窗口背景为黑色,完全无边距
viewer.setStyleSheet("""
QDialog {
background-color: #000000;
border: none;
margin: 0px;
padding: 0px;
}
""")
# 创建图片标签
# 创建布局,完全移除边距
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0) # 移除布局边距
layout.setSpacing(0) # 移除组件间距
layout.setAlignment(Qt.AlignCenter) # 布局居中对齐
# 创建图片标签,设置为完全填充模式
image_label = QLabel()
image_label.setAlignment(Qt.AlignCenter)
image_label.setScaledContents(True) # 关键:允许图片缩放以填充标签
image_label.setMinimumSize(1, 1) # 设置最小尺寸
image_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # 设置大小策略为扩展
image_label.setStyleSheet("""
QLabel {
border: none;
margin: 0px;
padding: 0px;
background-color: #000000;
}
""")
# 加载图片
pixmap = QPixmap()
if pixmap.loadFromData(image_data):
image_label.setPixmap(pixmap)
image_label.setScaledContents(False)
# 设置标签大小为图片实际大小
image_label.setFixedSize(pixmap.size())
else:
image_label.setText("无法加载图片")
if not pixmap.loadFromData(image_data):
self.status_bar.showMessage(f"加载图片失败: {filename}", 3000)
return
scroll_area.setWidget(image_label)
layout.addWidget(scroll_area)
layout.addWidget(image_label)
viewer.setLayout(layout)
# 添加关闭按钮
close_btn = QPushButton("关闭")
close_btn.clicked.connect(viewer.close)
layout.addWidget(close_btn)
# 计算位置和大小
if self:
parent_geometry = self.geometry()
screen_geometry = QApplication.primaryScreen().geometry()
# 设置窗口宽度与主窗口相同高度为屏幕高度的40%
window_width = parent_geometry.width()
window_height = int(screen_geometry.height() * 0.4)
# 计算位置:显示在主窗口正上方
x = parent_geometry.x()
y = parent_geometry.y() - window_height
# 确保不会超出屏幕边界
if y < screen_geometry.top():
y = parent_geometry.y() + 50 # 如果上方空间不足,显示在下方
# 调整宽度确保不超出屏幕
if x + window_width > screen_geometry.right():
window_width = screen_geometry.right() - x
viewer.setGeometry(x, y, window_width, window_height)
viewer.setLayout(layout)
viewer.show()
# 关键:强制图片立即填充整个标签区域
def force_image_fill():
try:
if pixmap and not pixmap.isNull():
# 获取标签的实际大小
label_size = image_label.size()
if label_size.width() > 10 and label_size.height() > 10: # 确保尺寸有效
# 完全填充,忽略宽高比,真正铺满
scaled_pixmap = pixmap.scaled(
label_size,
Qt.IgnoreAspectRatio, # 关键:忽略宽高比,强制填充
Qt.SmoothTransformation
)
image_label.setPixmap(scaled_pixmap)
print(f"图片已强制缩放至 {label_size.width()}x{label_size.height()}")
# 确保标签完全填充布局
image_label.setMinimumSize(label_size)
except Exception as e:
print(f"图片缩放失败: {e}")
# 使用多个定时器确保图片正确填充
from PyQt5.QtCore import QTimer
QTimer.singleShot(50, force_image_fill) # 50毫秒后执行
QTimer.singleShot(200, force_image_fill) # 200毫秒后执行
QTimer.singleShot(1000, force_image_fill) # 1000毫秒后再执行一次
# 连接窗口大小变化事件
viewer.resizeEvent = lambda event: force_image_fill()
# 添加点击关闭功能
def close_viewer():
viewer.close()
image_label.mousePressEvent = lambda event: close_viewer()
viewer.mousePressEvent = lambda event: close_viewer()
except Exception as e:
self.status_bar.showMessage(f"创建图片查看器失败: {str(e)}", 3000)
import traceback
traceback.print_exc()
def insert_images_in_text(self):
"""在文本中插入图片 - 修复图片显示逻辑"""

Loading…
Cancel
Save