123 #91

Merged
pshjeamgr merged 15 commits from main into llllllllllllllCC 3 months ago

4
.gitignore vendored

@ -198,6 +198,9 @@ temp/
*.orig
# Project specific
# Documentation folder
doc/
dist_package/
dist_package_v0.3/
*.zip
@ -231,6 +234,7 @@ venv/
env/
.venv/
.env/
new_venv/
# IDE
.idea/

@ -0,0 +1,120 @@
# PyQt5 平台插件问题完全解决方案
## 问题描述
在使用PyQt5时可能会遇到以下错误
```
qt.qpa.plugin: Could not find the Qt platform plugin "cocoa" in ""
This application failed to start because no Qt platform plugin could be initialized.
```
## 解决方案
### 方法一:一键修复(推荐)
运行完整的修复脚本:
```bash
python fix_pyqt5_complete.py
```
### 方法二:手动修复
1. **清理现有安装**
```bash
pip uninstall PyQt5 PyQt5-Qt5 PyQt5-sip -y
rm -rf /Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/.venv/lib/python3.9/site-packages/PyQt5*
```
2. **重新安装**
```bash
pip install PyQt5==5.15.10 --force-reinstall --no-cache-dir
```
3. **设置环境变量**
```bash
source set_pyqt5_env.sh
```
### 方法三:安全安装
使用安全安装脚本:
```bash
python install_pyqt5_safe.py
```
## 预防措施
### 1. 在main.py中集成环境设置
确保你的 `main.py` 包含了增强的Qt插件路径设置函数。
### 2. 创建启动脚本
创建 `start_app.sh`
```bash
#!/bin/bash
# PyQt5应用程序启动脚本
# 设置环境变量
export QT_PLUGIN_PATH="/Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/.venv/lib/python3.9/site-packages/PyQt5/Qt5/plugins"
export QT_QPA_PLATFORM_PLUGIN_PATH="/Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/.venv/lib/python3.9/site-packages/PyQt5/Qt5/plugins/platforms"
export QT_QPA_PLATFORM="cocoa"
export QT_MAC_WANTS_LAYER="1"
# 启动应用
python src/main.py
```
### 3. 使用虚拟环境专用安装
```bash
# 激活虚拟环境
source .venv/bin/activate
# 在虚拟环境中安装
python fix_pyqt5_complete.py
```
## 环境变量说明
| 变量名 | 作用 | 推荐值 |
|--------|------|--------|
| QT_PLUGIN_PATH | Qt插件主路径 | PyQt5/Qt5/plugins |
| QT_QPA_PLATFORM_PLUGIN_PATH | 平台插件路径 | PyQt5/Qt5/plugins/platforms |
| QT_QPA_PLATFORM | 指定平台 | cocoa (macOS) |
| QT_MAC_WANTS_LAYER | macOS图层支持 | 1 |
| QT_LOGGING_RULES | 日志级别 | qt.qpa.*=false |
## 常见问题
### Q: 为什么PyQt5会丢失平台插件
A: 常见原因:
- 安装过程中断或失败
- 虚拟环境迁移
- 系统Qt库冲突
- 文件权限问题
### Q: 如何验证修复是否成功?
A: 运行测试命令:
```python
python -c "from PyQt5.QtWidgets import QApplication; print('成功!')"
```
### Q: 修复后仍然有问题?
A: 尝试:
1. 完全删除虚拟环境重新创建
2. 使用系统包管理器安装Qt5
3. 检查Python版本兼容性
## 最佳实践
1. **始终使用虚拟环境**
2. **固定PyQt5版本**推荐5.15.10
3. **在代码中设置插件路径**
4. **创建启动脚本**
5. **定期验证安装**
## 一键修复命令
```bash
# 完整的修复流程
cd /Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design
python fix_pyqt5_complete.py
source set_pyqt5_env.sh
python src/main.py
```
这样应该能完全避免PyQt5平台插件问题

@ -0,0 +1,55 @@
#!/bin/bash
# PyQt5 紧急修复脚本 - 终极解决方案
echo "🚨 PyQt5 紧急修复中..."
# 1. 完全清理现有安装
echo "📦 步骤1: 清理PyQt5安装..."
pip uninstall PyQt5 PyQt5-Qt5 PyQt5-sip -y
rm -rf /Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/.venv/lib/python3.9/site-packages/PyQt5*
# 2. 重新安装PyQt5
echo "📦 步骤2: 重新安装PyQt5..."
pip install PyQt5==5.15.10 --force-reinstall --no-cache-dir
# 3. 设置环境变量
echo "🔧 步骤3: 设置环境变量..."
export QT_PLUGIN_PATH="/Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/.venv/lib/python3.9/site-packages/PyQt5/Qt5/plugins"
export QT_QPA_PLATFORM_PLUGIN_PATH="/Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/.venv/lib/python3.9/site-packages/PyQt5/Qt5/plugins/platforms"
export QT_QPA_PLATFORM="cocoa"
export QT_MAC_WANTS_LAYER="1"
export QT_LOGGING_RULES="qt.qpa.*=false"
# 4. 验证安装
echo "✅ 步骤4: 验证安装..."
python -c "
import sys
import os
os.environ['QT_PLUGIN_PATH'] = '$QT_PLUGIN_PATH'
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = '$QT_QPA_PLATFORM_PLUGIN_PATH'
os.environ['QT_QPA_PLATFORM'] = 'cocoa'
os.environ['QT_MAC_WANTS_LAYER'] = '1'
try:
from PyQt5.QtWidgets import QApplication, QLabel
from PyQt5.QtCore import Qt
app = QApplication(sys.argv)
label = QLabel('PyQt5修复成功✅')
label.setAlignment(Qt.AlignCenter)
label.resize(200, 100)
label.show()
from PyQt5.QtCore import QTimer
QTimer.singleShot(1500, app.quit)
app.exec_()
print('✅ PyQt5验证成功')
except Exception as e:
print(f'❌ PyQt5验证失败: {e}')
import traceback
traceback.print_exc()
"
echo "🎉 修复完成!"
echo "现在可以运行: python src/main.py"

@ -0,0 +1,237 @@
#!/usr/bin/env python3
"""
完整的PyQt5问题解决方案
运行此脚本可以完全避免PyQt5平台插件问题
"""
import subprocess
import sys
import os
import shutil
import platform
def run_command(cmd, description):
"""运行命令并显示进度"""
print(f"正在执行: {description}")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"❌ 失败: {result.stderr}")
return False
print(f"✅ 完成")
return True
def clean_pyqt5_installation():
"""彻底清理PyQt5安装"""
print("=== 清理PyQt5安装 ===")
# 1. 卸载PyQt5包
run_command([sys.executable, "-m", "pip", "uninstall", "PyQt5", "PyQt5-Qt5", "PyQt5-sip", "-y"],
"卸载PyQt5包")
# 2. 清理残留文件
venv_path = os.environ.get('VIRTUAL_ENV', '')
if venv_path:
site_packages = os.path.join(venv_path, 'lib', f'python{sys.version_info.major}.{sys.version_info.minor}', 'site-packages')
if os.path.exists(site_packages):
removed_count = 0
for item in os.listdir(site_packages):
if 'pyqt5' in item.lower():
item_path = os.path.join(site_packages, item)
try:
if os.path.isdir(item_path):
shutil.rmtree(item_path)
else:
os.remove(item_path)
removed_count += 1
print(f" 删除: {item}")
except Exception as e:
print(f" 删除失败 {item}: {e}")
print(f"✅ 清理完成,删除了 {removed_count} 个项目")
return True
def install_pyqt5_properly():
"""正确安装PyQt5"""
print("=== 安装PyQt5 ===")
# 使用清华镜像源加速下载(可选)
pip_args = [sys.executable, "-m", "pip", "install"]
# 检查是否有国内镜像源可用
try:
import requests
response = requests.get("https://pypi.tuna.tsinghua.edu.cn/simple", timeout=5)
if response.status_code == 200:
pip_args.extend(["-i", "https://pypi.tuna.tsinghua.edu.cn/simple"])
print("使用清华镜像源")
except:
pass
# 安装PyQt5
pip_args.extend(["PyQt5==5.15.10", "--no-cache-dir", "--force-reinstall"])
return run_command(pip_args, "安装PyQt5")
def setup_environment_variables():
"""设置环境变量"""
print("=== 设置环境变量 ===")
system = platform.system()
venv_path = os.environ.get('VIRTUAL_ENV', '')
if not venv_path:
print("❌ 未检测到虚拟环境")
return False
python_version = f"python{sys.version_info.major}.{sys.version_info.minor}"
# 可能的Qt插件路径
possible_paths = []
if system == "Darwin": # macOS
possible_paths = [
os.path.join(venv_path, 'lib', python_version, 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
'/usr/local/opt/qt5/plugins',
'/opt/homebrew/opt/qt5/plugins',
]
elif system == "Windows":
possible_paths = [
os.path.join(venv_path, 'Lib', 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
]
elif system == "Linux":
possible_paths = [
os.path.join(venv_path, 'lib', python_version, 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
'/usr/lib/x86_64-linux-gnu/qt5/plugins',
]
# 找到有效的路径
valid_path = None
for path in possible_paths:
if os.path.exists(path) and os.path.exists(os.path.join(path, 'platforms')):
valid_path = path
break
if valid_path:
# 创建环境变量设置脚本
env_script = f"""
# PyQt5环境变量设置
export QT_PLUGIN_PATH="{valid_path}"
export QT_QPA_PLATFORM_PLUGIN_PATH="{os.path.join(valid_path, 'platforms')}"
"""
if system == "Darwin":
env_script += """
export QT_QPA_PLATFORM="cocoa"
export QT_MAC_WANTS_LAYER="1"
export QT_LOGGING_RULES="qt.qpa.*=false"
"""
elif system == "Windows":
env_script += """
export QT_QPA_PLATFORM="windows"
"""
elif system == "Linux":
env_script += """
export QT_QPA_PLATFORM="xcb"
"""
# 保存环境变量脚本
project_root = os.path.dirname(os.path.abspath(__file__))
env_file = os.path.join(project_root, 'set_pyqt5_env.sh')
with open(env_file, 'w') as f:
f.write(env_script.strip())
print(f"✅ 环境变量脚本已创建: {env_file}")
print(f" QT_PLUGIN_PATH: {valid_path}")
return True
else:
print("❌ 未找到有效的Qt插件路径")
return False
def verify_installation():
"""验证安装"""
print("=== 验证安装 ===")
# 测试导入
test_code = """
import sys
import os
# 设置环境变量
if 'QT_PLUGIN_PATH' in os.environ:
os.environ['QT_PLUGIN_PATH'] = os.environ['QT_PLUGIN_PATH']
if 'QT_QPA_PLATFORM_PLUGIN_PATH' in os.environ:
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.environ['QT_QPA_PLATFORM_PLUGIN_PATH']
try:
from PyQt5.QtWidgets import QApplication, QLabel
from PyQt5.QtCore import Qt
app = QApplication(sys.argv)
label = QLabel("PyQt5安装成功")
label.setAlignment(Qt.AlignCenter)
label.resize(200, 100)
label.show()
# 只显示2秒后自动关闭
from PyQt5.QtCore import QTimer
QTimer.singleShot(2000, app.quit)
app.exec_()
print("✅ PyQt5验证成功")
except Exception as e:
print(f"❌ PyQt5验证失败: {e}")
import traceback
traceback.print_exc()
"""
with open('test_pyqt5.py', 'w') as f:
f.write(test_code)
result = subprocess.run([sys.executable, 'test_pyqt5.py'], capture_output=True, text=True)
# 清理测试文件
if os.path.exists('test_pyqt5.py'):
os.remove('test_pyqt5.py')
return result.returncode == 0
def main():
"""主函数"""
print("=== PyQt5完整修复方案 ===")
print(f"系统: {platform.system()}")
print(f"Python: {sys.version}")
print(f"虚拟环境: {os.environ.get('VIRTUAL_ENV', '未激活')}")
print()
# 获取项目根目录
project_root = os.path.dirname(os.path.abspath(__file__))
os.chdir(project_root)
steps = [
("清理安装", clean_pyqt5_installation),
("重新安装", install_pyqt5_properly),
("设置环境", setup_environment_variables),
("验证安装", verify_installation),
]
success = True
for step_name, step_func in steps:
if not step_func():
print(f"{step_name} 失败")
success = False
break
print()
if success:
print("🎉 PyQt5修复完成")
print("\n使用方法:")
print("1. 运行: source set_pyqt5_env.sh")
print("2. 然后运行: python src/main.py")
else:
print("❌ PyQt5修复失败请检查错误信息")
return success
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)

@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""
安全安装PyQt5的脚本避免平台插件问题
"""
import subprocess
import sys
import os
def install_pyqt5_safely():
"""安全安装PyQt5的方法"""
print("正在安全安装PyQt5...")
# 1. 首先完全卸载现有的PyQt5
print("1. 卸载现有PyQt5...")
subprocess.run([sys.executable, "-m", "pip", "uninstall", "PyQt5", "PyQt5-Qt5", "PyQt5-sip", "-y"],
capture_output=True, text=True)
# 2. 清理可能残留的文件
print("2. 清理残留文件...")
venv_path = os.environ.get('VIRTUAL_ENV', '')
if venv_path:
site_packages = os.path.join(venv_path, 'lib', f'python{sys.version_info.major}.{sys.version_info.minor}', 'site-packages')
if os.path.exists(site_packages):
for item in os.listdir(site_packages):
if 'pyqt5' in item.lower():
item_path = os.path.join(site_packages, item)
if os.path.isdir(item_path):
import shutil
shutil.rmtree(item_path)
print(f" 删除目录: {item}")
# 3. 使用特定版本安装PyQt5
print("3. 安装PyQt5...")
result = subprocess.run([
sys.executable, "-m", "pip", "install",
"PyQt5==5.15.10",
"--no-cache-dir", # 不使用缓存,确保重新下载
"--force-reinstall" # 强制重新安装
], capture_output=True, text=True)
if result.returncode == 0:
print("✅ PyQt5安装成功")
# 4. 验证安装
print("4. 验证安装...")
test_result = subprocess.run([
sys.executable, "-c",
"from PyQt5.QtWidgets import QApplication; print('PyQt5导入成功')"
], capture_output=True, text=True)
if test_result.returncode == 0:
print("✅ PyQt5验证通过")
return True
else:
print("❌ PyQt5验证失败:", test_result.stderr)
return False
else:
print("❌ PyQt5安装失败:", result.stderr)
return False
if __name__ == "__main__":
success = install_pyqt5_safely()
sys.exit(0 if success else 1)

Binary file not shown.

@ -0,0 +1,7 @@
#!/bin/bash
echo "设置Qt调试环境变量..."
export QT_DEBUG_PLUGINS=1
echo "Qt调试模式已启用"
echo ""
echo "运行MagicWord应用程序..."
python src/main.py

@ -0,0 +1,20 @@
#!/bin/bash
# MagicWord 修复版启动脚本
echo "🚀 正在启动 MagicWord (修复版)..."
# 使用新的虚拟环境
source new_venv/bin/activate
# 设置Qt环境变量
export QT_PLUGIN_PATH="/Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/new_venv/lib/python3.9/site-packages/PyQt5/Qt5/plugins"
export QT_QPA_PLATFORM_PLUGIN_PATH="/Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/new_venv/lib/python3.9/site-packages/PyQt5/Qt5/plugins/platforms"
export QT_QPA_PLATFORM="cocoa"
export QT_MAC_WANTS_LAYER="1"
export QT_LOGGING_RULES="qt.qpa.*=false"
echo "✅ 环境设置完成"
echo "✅ 正在启动 MagicWord 应用..."
# 启动应用
cd src && python main.py

@ -0,0 +1,29 @@
#!/bin/bash
# MagicWord 一键启动脚本
# 自动处理PyQt5平台插件问题
echo "🚀 正在启动 MagicWord..."
# 设置PyQt5环境变量
export QT_PLUGIN_PATH="/Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/.venv/lib/python3.9/site-packages/PyQt5/Qt5/plugins"
export QT_QPA_PLATFORM_PLUGIN_PATH="/Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/.venv/lib/python3.9/site-packages/PyQt5/Qt5/plugins/platforms"
export QT_QPA_PLATFORM="cocoa"
export QT_MAC_WANTS_LAYER="1"
export QT_LOGGING_RULES="qt.qpa.*=false"
# 激活虚拟环境(如果未激活)
if [ -z "$VIRTUAL_ENV" ]; then
echo "📦 激活虚拟环境..."
source .venv/bin/activate
fi
# 检查PyQt5是否可用
python -c "from PyQt5.QtWidgets import QApplication" 2>/dev/null
if [ $? -ne 0 ]; then
echo "❌ PyQt5出现问题正在修复..."
./emergency_fix.sh
fi
# 启动应用
echo "✅ 启动 MagicWord 应用..."
cd src && python main.py

@ -0,0 +1,7 @@
# PyQt5环境变量设置
export QT_PLUGIN_PATH="/Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/.venv/lib/python3.9/site-packages/PyQt5/Qt5/plugins"
export QT_QPA_PLATFORM_PLUGIN_PATH="/Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/.venv/lib/python3.9/site-packages/PyQt5/Qt5/plugins/platforms"
export QT_QPA_PLATFORM="cocoa"
export QT_MAC_WANTS_LAYER="1"
export QT_LOGGING_RULES="qt.qpa.*=false"

@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""
Qt环境配置脚本确保平台插件正确加载
"""
import os
import sys
def setup_qt_environment():
"""设置Qt环境变量避免平台插件问题"""
# 获取虚拟环境的site-packages路径
venv_path = os.environ.get('VIRTUAL_ENV', '')
if not venv_path:
print("警告:未检测到虚拟环境")
return False
# 构建Qt插件路径
python_version = f"python{sys.version_info.major}.{sys.version_info.minor}"
qt_plugin_path = os.path.join(venv_path, 'lib', python_version, 'site-packages', 'PyQt5', 'Qt5', 'plugins')
if not os.path.exists(qt_plugin_path):
print(f"错误Qt插件路径不存在: {qt_plugin_path}")
return False
# 设置环境变量
os.environ['QT_PLUGIN_PATH'] = qt_plugin_path
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.join(qt_plugin_path, 'platforms')
# 对于macOS还需要设置其他重要变量
if sys.platform == 'darwin':
os.environ['QT_QPA_PLATFORM'] = 'cocoa'
# 禁用Qt的某些可能导致问题的功能
os.environ['QT_MAC_WANTS_LAYER'] = '1'
os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = '1'
print(f"✅ Qt环境变量设置完成")
print(f" QT_PLUGIN_PATH: {qt_plugin_path}")
print(f" QT_QPA_PLATFORM_PLUGIN_PATH: {os.environ['QT_QPA_PLATFORM_PLUGIN_PATH']}")
return True
def verify_qt_setup():
"""验证Qt设置是否正确"""
try:
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtWidgets import QApplication
# 创建QApplication实例来测试
app = QCoreApplication.instance()
if app is None:
app = QApplication([])
# 获取平台信息
platform = app.platformName()
print(f"✅ Qt平台检测成功: {platform}")
return True
except Exception as e:
print(f"❌ Qt验证失败: {e}")
return False
if __name__ == "__main__":
if setup_qt_environment():
verify_qt_setup()

@ -23,6 +23,8 @@ class FileParser:
return FileParser.parse_docx(file_path)
elif ext == '.pdf':
return FileParser.parse_pdf(file_path)
elif ext == '.html':
return FileParser.parse_html(file_path)
else:
raise ValueError(f"Unsupported file format: {ext}")
except Exception as e:
@ -80,6 +82,11 @@ class FileParser:
content = FileParser.parse_pdf(file_path)
images = [] # PDF图片提取较复杂暂时跳过
elif ext == '.html':
# HTML文件提取文本内容
content = FileParser.parse_html(file_path)
images = [] # HTML图片提取较复杂暂时跳过
else:
return {
'success': False,
@ -271,6 +278,46 @@ class FileParser:
except Exception as e:
raise Exception(f"Error parsing pdf file {file_path}: {str(e)}")
@staticmethod
def parse_html(file_path: str) -> str:
"""解析HTML文件提取文本内容"""
# 验证文件路径
if not FileParser.validate_file_path(file_path):
raise ValueError(f"Invalid file path: {file_path}")
try:
from bs4 import BeautifulSoup
except ImportError:
raise ImportError("BeautifulSoup4 library is required for parsing .html files. Please install it using 'pip install beautifulsoup4'")
try:
# 检测文件编码
encoding = FileParser.detect_file_encoding(file_path)
# 读取HTML文件
with open(file_path, 'r', encoding=encoding, errors='ignore') as f:
html_content = f.read()
# 使用BeautifulSoup解析HTML
soup = BeautifulSoup(html_content, 'html.parser')
# 移除script和style标签
for script in soup(["script", "style"]):
script.decompose()
# 提取文本内容
text = soup.get_text()
# 清理多余的空白字符
lines = (line.strip() for line in text.splitlines())
chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
text = '\n'.join(chunk for chunk in chunks if chunk)
return text
except Exception as e:
raise Exception(f"Error parsing html file {file_path}: {str(e)}")
@staticmethod
def validate_file_path(file_path: str) -> bool:
"""验证文件路径是否有效"""

@ -8,59 +8,74 @@ import platform
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, project_root)
# 设置Qt平台插件路径 - 根据操作系统设置正确的Qt插件路径
# 设置Qt平台插件路径 - 增强版本,完全避免平台插件问题
def set_qt_plugin_path():
"""设置Qt平台插件路径确保所有平台插件都能正确加载"""
system = platform.system()
# 获取Python版本
python_version = f"python{sys.version_info.major}.{sys.version_info.minor}"
# 可能的Qt插件路径列表
possible_paths = []
if system == "Windows":
# Windows环境下查找Qt插件路径
# 首先检查虚拟环境中的Qt插件
venv_qt_plugins_path = os.path.join(project_root, '.venv', 'Lib', 'site-packages', 'PyQt5', 'Qt5', 'plugins')
if os.path.exists(venv_qt_plugins_path):
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = venv_qt_plugins_path
return
# 检查全局Python安装中的Qt插件
global_qt_plugins_path = os.path.join(sys.prefix, 'Lib', 'site-packages', 'PyQt5', 'Qt5', 'plugins')
if os.path.exists(global_qt_plugins_path):
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = global_qt_plugins_path
return
# 尝试在常见的Windows PyQt5安装路径中查找
common_paths = [
# Windows环境下的路径
possible_paths.extend([
os.path.join(project_root, '.venv', 'Lib', 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
os.path.join(sys.prefix, 'Lib', 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
os.path.join(os.path.expanduser('~'), 'AppData', 'Local', 'Programs', 'Python', 'Python39', 'Lib', 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
os.path.join(os.path.expanduser('~'), 'AppData', 'Roaming', 'Python', 'Python39', 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
]
for path in common_paths:
if os.path.exists(path):
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = path
return
])
elif system == "Darwin": # macOS
# macOS环境下查找Qt插件路径
system_qt_plugins_path = '/usr/local/opt/qt5/plugins' # macOS Homebrew Qt5路径
venv_qt_plugins_path = os.path.join(project_root, '.venv', 'lib', 'python3.9', 'site-packages', 'PyQt5', 'Qt5', 'plugins')
# 优先检查系统Qt插件路径
if os.path.exists(system_qt_plugins_path):
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = system_qt_plugins_path
return
elif os.path.exists(venv_qt_plugins_path):
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = venv_qt_plugins_path
return
# macOS环境下的路径
possible_paths.extend([
os.path.join(project_root, '.venv', 'lib', python_version, 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
os.path.join(sys.prefix, 'lib', python_version, 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
'/usr/local/opt/qt5/plugins', # Homebrew Qt5
'/opt/homebrew/opt/qt5/plugins', # Apple Silicon Homebrew
os.path.expanduser('~/Qt/5.15.2/clang_64/plugins'), # Qt官方安装
])
elif system == "Linux":
# Linux环境下查找Qt插件路径
venv_qt_plugins_path = os.path.join(project_root, '.venv', 'lib', 'python3.9', 'site-packages', 'PyQt5', 'Qt5', 'plugins')
global_qt_plugins_path = os.path.join(sys.prefix, 'lib', 'python3.9', 'site-packages', 'PyQt5', 'Qt5', 'plugins')
# Linux环境下的路径
possible_paths.extend([
os.path.join(project_root, '.venv', 'lib', python_version, 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
os.path.join(sys.prefix, 'lib', python_version, 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
'/usr/lib/x86_64-linux-gnu/qt5/plugins',
'/usr/lib/qt5/plugins',
])
# 查找第一个存在的路径
valid_path = None
for path in possible_paths:
if os.path.exists(path) and os.path.exists(os.path.join(path, 'platforms')):
valid_path = path
break
if valid_path:
# 设置Qt插件路径
os.environ['QT_PLUGIN_PATH'] = valid_path
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.join(valid_path, 'platforms')
# 设置平台特定的环境变量
if system == "Darwin": # macOS
os.environ['QT_QPA_PLATFORM'] = 'cocoa'
os.environ['QT_MAC_WANTS_LAYER'] = '1'
# 禁用可能导致问题的Qt功能
os.environ['QT_LOGGING_RULES'] = 'qt.qpa.*=false' # 禁用Qt警告日志
elif system == "Windows":
os.environ['QT_QPA_PLATFORM'] = 'windows'
elif system == "Linux":
os.environ['QT_QPA_PLATFORM'] = 'xcb'
# 对于Linux可能需要设置DISPLAY
if 'DISPLAY' not in os.environ:
os.environ['DISPLAY'] = ':0'
if os.path.exists(venv_qt_plugins_path):
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = venv_qt_plugins_path
return
elif os.path.exists(global_qt_plugins_path):
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = global_qt_plugins_path
return
print(f"✅ Qt插件路径设置成功: {valid_path}")
return True
else:
print("⚠️ 警告未找到Qt插件路径")
return False
# 设置Qt平台插件路径
set_qt_plugin_path()
@ -80,8 +95,10 @@ def main():
# 创建QApplication实例
app = QApplication(sys.argv)
# 设置应用程序样式为Windows风格更接近Word界面
app.setStyle('WindowsVista')
# 在macOS上使用系统原生样式在其他平台上使用WindowsVista样式
if platform.system() != "Darwin": # 不是macOS系统
# 设置应用程序样式为Windows风格更接近Word界面
app.setStyle('WindowsVista')
# 设置应用程序属性
app.setApplicationName("MagicWord")

@ -153,69 +153,100 @@ class ThemeManager(QObject):
return self._get_light_stylesheet()
def _get_dark_stylesheet(self):
"""深色主题样式表"""
"""深色主题样式表 - Apple设计风格"""
return """
/* 深色主题样式 */
/* Apple设计风格深色主题样式 */
/* 全局文字颜色 */
/* 全局文字颜色和字体 - 使用Apple系统字体 */
QWidget {
color: #e0e0e0;
color: #f0f0f0;
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif;
font-size: 13px;
}
/* 主窗口 */
/* 主窗口 - Apple深色背景 */
QMainWindow {
background-color: #1e1e1e;
background-color: #2c2c2e;
}
/* 菜单栏 */
/* 菜单栏 - Apple深色风格 */
QMenuBar {
background-color: #0078d7;
border: 1px solid #005a9e;
font-size: 12px;
color: #ffffff;
background-color: #2c2c2e;
border: none;
border-bottom: 1px solid #404040;
font-size: 13px;
color: #f0f0f0;
padding: 4px 0;
}
QMenuBar::item {
background-color: transparent;
padding: 4px 10px;
color: #e0e0e0;
padding: 6px 12px;
color: #f0f0f0;
border-radius: 4px;
margin: 0 1px;
}
QMenuBar::item:selected {
background-color: #106ebe;
background-color: #404040;
color: #f0f0f0;
}
/* 菜单 */
QMenuBar::item:pressed {
background-color: #505050;
color: #f0f0f0;
}
/* 菜单 - Apple深色风格 */
QMenu {
background-color: #2d2d2d;
border: 1px solid #3c3c3c;
font-size: 12px;
color: #e0e0e0;
background-color: #2c2c2e;
border: 1px solid #404040;
border-radius: 8px;
font-size: 13px;
color: #f0f0f0;
padding: 4px 0;
margin: 2px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
QMenu::item {
color: #f0f0f0;
background-color: transparent;
border-radius: 4px;
margin: 0 4px;
padding: 4px 20px;
color: #e0e0e0;
}
QMenu::item:selected {
background-color: #3c3c3c;
background-color: #0a84ff;
color: #ffffff;
}
QMenu::item:pressed {
background-color: #0066cc;
color: #ffffff;
}
QMenu::separator {
height: 1px;
background-color: #404040;
margin: 4px 8px;
}
/* 功能区 */
QFrame {
background-color: #2d2d2d;
border: 1px solid #3c3c3c;
background-color: #2c2c2e;
border: none;
}
/* 组框 */
QGroupBox {
font-size: 11px;
font-size: 12px;
font-weight: normal;
color: #e0e0e0;
background-color: #2d2d2d;
border: 1px solid #3c3c3c;
border-radius: 0px;
color: #f0f0f0;
background-color: #2c2c2e;
border: none;
border-radius: 8px;
margin-top: 5px;
padding-top: 5px;
}
@ -224,250 +255,320 @@ class ThemeManager(QObject):
subcontrol-origin: margin;
left: 10px;
padding: 0 5px 0 5px;
color: #e0e0e0;
color: #a0a0a0;
}
/* 按钮 */
/* 工具按钮 - Apple深色风格 */
QToolButton {
border: 1px solid #3c3c3c;
border-radius: 3px;
background-color: #3c3c3c;
font-size: 11px;
color: #e0e0e0;
padding: 3px 6px;
border: 1px solid transparent;
border-radius: 6px;
background-color: #3a3a3c;
font-size: 13px;
color: #f0f0f0;
padding: 6px 12px;
}
QToolButton:hover {
background-color: #4a4a4a;
border: 1px solid #5a5a5a;
background-color: #4a4a4c;
border: 1px solid #5a5a5c;
}
QToolButton:pressed {
background-color: #2a2a2a;
border: 1px solid #1a1a1a;
background-color: #5a5a5c;
border: 1px solid #6a6a6c;
}
QToolButton:checked {
background-color: #0078d4;
border: 1px solid #106ebe;
background-color: #0a84ff;
border: 1px solid #0a84ff;
color: #ffffff;
}
/* 切换按钮 */
QToolButton[checkable="true"] {
border: 1px solid #3c3c3c;
border-radius: 2px;
background-color: #3c3c3c;
border: 1px solid #4a4a4c;
border-radius: 6px;
background-color: #3a3a3c;
font-size: 12px;
font-weight: bold;
color: #e0e0e0;
color: #f0f0f0;
padding: 6px 12px;
}
QToolButton[checkable="true"]:hover {
background-color: #4a4a4a;
background-color: #4a4a4c;
}
QToolButton[checkable="true"]:checked {
background-color: #0078d4;
border: 1px solid #106ebe;
background-color: #0a84ff;
border: 1px solid #0a84ff;
color: #ffffff;
}
/* 下拉框 - 修复文字不可见问题 */
/* 下拉框 - Apple深色风格 */
QComboBox {
background-color: #3c3c3c;
border: 1px solid #5a5a5a;
border-radius: 2px;
color: #e0e0e0;
padding: 2px 5px;
selection-background-color: #4a4a4a;
selection-color: #e0e0e0;
background-color: #3a3a3c;
border: 1px solid #4a4a4c;
border-radius: 6px;
color: #f0f0f0;
padding: 4px 8px;
selection-background-color: #0a84ff;
selection-color: #ffffff;
}
QComboBox:hover {
background-color: #4a4a4a;
border: 1px solid #6a6a6a;
background-color: #4a4a4c;
border: 1px solid #5a5a5c;
}
QComboBox::drop-down {
border: none;
width: 15px;
background-color: #3c3c3c;
width: 20px;
border-left: 1px solid #4a4a4c;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
}
QComboBox::down-arrow {
image: none;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 5px solid #e0e0e0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 6px solid #a0a0a0;
margin: 6px;
}
/* 下拉框弹出列表 */
QComboBox QAbstractItemView {
background-color: #3c3c3c;
border: 1px solid #5a5a5a;
color: #e0e0e0;
selection-background-color: #4a4a4a;
selection-color: #e0e0e0;
background-color: #2c2c2e;
border: 1px solid #4a4a4c;
color: #f0f0f0;
selection-background-color: #0a84ff;
selection-color: #ffffff;
}
/* 字体下拉框特殊处理 */
QFontComboBox {
background-color: #3c3c3c;
border: 1px solid #5a5a5a;
border-radius: 2px;
color: #e0e0e0;
padding: 2px 5px;
selection-background-color: #4a4a4a;
selection-color: #e0e0e0;
}
QFontComboBox:hover {
background-color: #4a4a4a;
border: 1px solid #6a6a6a;
}
QFontComboBox QAbstractItemView {
background-color: #3c3c3c;
border: 1px solid #5a5a5a;
color: #e0e0e0;
selection-background-color: #4a4a4a;
selection-color: #e0e0e0;
}
/* 文本编辑器 */
/* 文本编辑区域 - Apple深色风格 */
QTextEdit {
background-color: #1e1e1e;
border: 1px solid #3c3c3c;
color: #e0e0e0;
padding: 20px;
background-color: #1c1c1e;
border: none;
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif;
font-size: 15px;
color: #f0f0f0;
padding: 32px;
line-height: 1.5;
selection-background-color: #0066cc;
selection-color: #ffffff;
}
/* 状态栏 */
/* 状态栏 - Apple深色风格 */
QStatusBar {
background-color: #2d2d2d;
border-top: 1px solid #3c3c3c;
font-size: 11px;
color: #e0e0e0;
background-color: #3a3a3c;
border-top: 1px solid #4a4a4c;
font-size: 12px;
color: #a0a0a0;
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif;
padding: 6px 12px;
}
/* 标签 */
QLabel {
color: #e0e0e0;
color: #f0f0f0;
background-color: transparent;
}
/* 消息框 - 修复黑色背景问题 */
/* 消息框 - Apple深色风格 */
QMessageBox {
background-color: #2d2d2d;
color: #e0e0e0;
background-color: #2c2c2e;
color: #f0f0f0;
border-radius: 12px;
}
QMessageBox QPushButton {
background-color: #3c3c3c;
color: #e0e0e0;
border: 1px solid #5a5a5a;
border-radius: 3px;
padding: 5px 15px;
background-color: #3a3a3c;
color: #f0f0f0;
border: 1px solid #4a4a4c;
border-radius: 6px;
padding: 6px 16px;
min-width: 80px;
}
QMessageBox QPushButton:hover {
background-color: #4a4a4a;
border: 1px solid #6a6a6a;
background-color: #4a4a4c;
border: 1px solid #5a5a5c;
}
QMessageBox QPushButton:pressed {
background-color: #2a2a2a;
border: 1px solid #1a1a1a;
background-color: #5a5a5c;
border: 1px solid #6a6a6c;
}
QMessageBox QPushButton:default {
background-color: #0a84ff;
color: #ffffff;
border: 1px solid #0a84ff;
}
QMessageBox QPushButton:default:hover {
background-color: #0066cc;
border: 1px solid #0066cc;
}
QMessageBox QPushButton:default:pressed {
background-color: #004d99;
border: 1px solid #004d99;
}
/* 滚动条 */
/* 滚动条 - Apple深色风格 */
QScrollBar:vertical {
background-color: #2d2d2d;
width: 12px;
background-color: transparent;
width: 8px;
border: none;
}
QScrollBar::handle:vertical {
background-color: #5a5a5a;
border-radius: 6px;
background-color: #5a5a5c;
border-radius: 4px;
min-height: 20px;
}
QScrollBar::handle:vertical:hover {
background-color: #6a6a6a;
background-color: #6a6a6c;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
border: none;
background: none;
}
/* 按钮 - Apple深色风格 */
QPushButton {
background-color: #3a3a3c;
color: #f0f0f0;
border: 1px solid #4a4a4c;
border-radius: 6px;
padding: 6px 16px;
font-size: 13px;
}
QPushButton:hover {
background-color: #4a4a4c;
border: 1px solid #5a5a5c;
}
QPushButton:pressed {
background-color: #5a5a5c;
border: 1px solid #6a6a6c;
}
QPushButton:default {
background-color: #0a84ff;
color: #ffffff;
border: 1px solid #0a84ff;
}
QPushButton:default:hover {
background-color: #0066cc;
border: 1px solid #0066cc;
}
QPushButton:default:pressed {
background-color: #004d99;
border: 1px solid #004d99;
}
"""
def _get_light_stylesheet(self):
"""浅色主题样式表 - 白底黑字"""
"""浅色主题样式表 - Apple设计风格"""
return """
/* 浅色主题样式 - 白底黑字 */
/* Apple设计风格浅色主题样式 */
/* 全局文字颜色 */
/* 全局文字颜色和字体 - 使用Apple系统字体 */
QWidget {
color: #333333;
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif;
font-size: 13px;
}
/* 主窗口 */
/* 主窗口 - 纯净白色背景 */
QMainWindow {
background-color: #f3f2f1;
background-color: #ffffff;
}
/* 菜单栏 */
/* 菜单栏 - Apple风格 */
QMenuBar {
background-color: #0078d7;
border: 1px solid #005a9e;
font-size: 12px;
color: #ffffff;
background-color: #ffffff;
border: none;
border-bottom: 1px solid #e0e0e0;
font-size: 13px;
color: #333333;
padding: 4px 0;
}
QMenuBar::item {
background-color: transparent;
padding: 4px 10px;
padding: 6px 12px;
color: #333333;
border-radius: 4px;
margin: 0 1px;
}
QMenuBar::item:selected {
background-color: #106ebe;
background-color: #f0f0f0;
color: #333333;
}
/* 菜单 */
QMenuBar::item:pressed {
background-color: #e0e0e0;
color: #333333;
}
/* 菜单 - Apple风格 */
QMenu {
background-color: #ffffff;
border: 1px solid #d0d0d0;
font-size: 12px;
border-radius: 8px;
font-size: 13px;
color: #333333;
padding: 4px 0;
margin: 2px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
QMenu::item {
padding: 4px 20px;
color: #333333;
background-color: transparent;
border-radius: 4px;
margin: 0 4px;
padding: 4px 20px;
}
QMenu::item:selected {
background-color: #f0f0f0;
background-color: #007aff;
color: #ffffff;
}
QMenu::item:pressed {
background-color: #0062cc;
color: #ffffff;
}
QMenu::separator {
height: 1px;
background-color: #e0e0e0;
margin: 4px 8px;
}
/* 功能区 */
QFrame {
background-color: #ffffff;
border: 1px solid #d0d0d0;
border: none;
}
/* 组框 */
QGroupBox {
font-size: 11px;
font-size: 12px;
font-weight: normal;
color: #333333;
background-color: #ffffff;
border: 1px solid #d0d0d0;
border-radius: 0px;
border: none;
border-radius: 8px;
margin-top: 5px;
padding-top: 5px;
}
@ -476,145 +577,116 @@ class ThemeManager(QObject):
subcontrol-origin: margin;
left: 10px;
padding: 0 5px 0 5px;
color: #333333;
color: #666666;
}
/* 按钮 */
/* 工具按钮 - Apple风格 */
QToolButton {
border: 1px solid #d0d0d0;
border-radius: 3px;
background-color: #ffffff;
font-size: 11px;
border: 1px solid transparent;
border-radius: 6px;
background-color: #f6f6f6;
font-size: 13px;
color: #333333;
padding: 3px 6px;
padding: 6px 12px;
}
QToolButton:hover {
background-color: #f0f0f0;
border: 1px solid #0078d7;
background-color: #e0e0e0;
border: 1px solid #d0d0d0;
}
QToolButton:pressed {
background-color: #e0e0e0;
border: 1px solid #005a9e;
background-color: #d0d0d0;
border: 1px solid #c0c0c0;
}
QToolButton:checked {
background-color: #0078d7;
border: 1px solid #005a9e;
background-color: #007aff;
border: 1px solid #007aff;
color: #ffffff;
}
/* 切换按钮 */
QToolButton[checkable="true"] {
border: 1px solid #d0d0d0;
border-radius: 2px;
background-color: #ffffff;
border-radius: 6px;
background-color: #f6f6f6;
font-size: 12px;
font-weight: bold;
color: #333333;
padding: 6px 12px;
}
QToolButton[checkable="true"]:hover {
background-color: #f0f0f0;
background-color: #e0e0e0;
}
QToolButton[checkable="true"]:checked {
background-color: #0078d7;
border: 1px solid #005a9e;
background-color: #007aff;
border: 1px solid #007aff;
color: #ffffff;
}
/* 下拉框 - 白底黑字 */
/* 下拉框 - Apple风格 */
QComboBox {
background-color: #ffffff;
background-color: #f6f6f6;
border: 1px solid #d0d0d0;
border-radius: 2px;
border-radius: 6px;
color: #333333;
padding: 2px 5px;
selection-background-color: #f0f0f0;
selection-color: #333333;
padding: 4px 8px;
selection-background-color: #007aff;
selection-color: #ffffff;
}
QComboBox:hover {
background-color: #f0f0f0;
border: 1px solid #0078d7;
background-color: #e0e0e0;
border: 1px solid #c0c0c0;
}
QComboBox::drop-down {
border: none;
width: 15px;
background-color: #ffffff;
width: 20px;
border-left: 1px solid #d0d0d0;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
}
QComboBox::down-arrow {
image: none;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 5px solid #333333;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 6px solid #666666;
margin: 6px;
}
/* 下拉框弹出列表 */
QComboBox QAbstractItemView {
background-color: #ffffff;
border: 1px solid #d0d0d0;
color: #333333;
selection-background-color: #f0f0f0;
selection-color: #333333;
}
/* 字体下拉框特殊处理 - 白底黑字 */
QFontComboBox {
background-color: #ffffff;
border: 1px solid #d0d0d0;
border-radius: 2px;
color: #333333;
padding: 2px 5px;
selection-background-color: #f0f0f0;
selection-color: #333333;
}
QFontComboBox:hover {
background-color: #f0f0f0;
border: 1px solid #0078d7;
}
QFontComboBox::drop-down {
border: none;
width: 15px;
background-color: #ffffff;
selection-background-color: #007aff;
selection-color: #ffffff;
}
QFontComboBox::down-arrow {
image: none;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 5px solid #333333;
}
QFontComboBox QAbstractItemView {
background-color: #ffffff;
border: 1px solid #d0d0d0;
color: #333333;
selection-background-color: #f0f0f0;
selection-color: #333333;
}
/* 文本编辑器 */
/* 文本编辑区域 - Apple风格 */
QTextEdit {
background-color: #ffffff;
border: 1px solid #d0d0d0;
color: #000000;
padding: 20px;
border: none;
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif;
font-size: 15px;
color: #333333;
padding: 32px;
line-height: 1.5;
selection-background-color: #b3d9ff;
selection-color: #333333;
}
/* 状态栏 */
/* 状态栏 - Apple风格 */
QStatusBar {
background-color: #ffffff;
border-top: 1px solid #d0d0d0;
font-size: 11px;
color: #333333;
background-color: #f6f6f6;
border-top: 1px solid #e0e0e0;
font-size: 12px;
color: #666666;
font-family: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Helvetica', 'Arial', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', sans-serif;
padding: 6px 12px;
}
/* 标签 */
@ -623,41 +695,58 @@ class ThemeManager(QObject):
background-color: transparent;
}
/* 消息框 - 修复黑色背景问题 */
/* 消息框 - Apple风格 */
QMessageBox {
background-color: #ffffff;
color: #333333;
border-radius: 12px;
}
QMessageBox QPushButton {
background-color: #ffffff;
background-color: #f6f6f6;
color: #333333;
border: 1px solid #d0d0d0;
border-radius: 3px;
padding: 5px 15px;
border-radius: 6px;
padding: 6px 16px;
min-width: 80px;
}
QMessageBox QPushButton:hover {
background-color: #f0f0f0;
border: 1px solid #0078d7;
background-color: #e0e0e0;
border: 1px solid #c0c0c0;
}
QMessageBox QPushButton:pressed {
background-color: #e0e0e0;
border: 1px solid #005a9e;
background-color: #d0d0d0;
border: 1px solid #a0a0a0;
}
QMessageBox QPushButton:default {
background-color: #007aff;
color: #ffffff;
border: 1px solid #007aff;
}
QMessageBox QPushButton:default:hover {
background-color: #0062cc;
border: 1px solid #0062cc;
}
QMessageBox QPushButton:default:pressed {
background-color: #004a99;
border: 1px solid #004a99;
}
/* 滚动条 */
/* 滚动条 - Apple风格 */
QScrollBar:vertical {
background-color: #ffffff;
width: 12px;
background-color: transparent;
width: 8px;
border: none;
}
QScrollBar::handle:vertical {
background-color: #c0c0c0;
border-radius: 6px;
border-radius: 4px;
min-height: 20px;
}
@ -669,6 +758,42 @@ class ThemeManager(QObject):
border: none;
background: none;
}
/* 按钮 - Apple风格 */
QPushButton {
background-color: #f6f6f6;
color: #333333;
border: 1px solid #d0d0d0;
border-radius: 6px;
padding: 6px 16px;
font-size: 13px;
}
QPushButton:hover {
background-color: #e0e0e0;
border: 1px solid #c0c0c0;
}
QPushButton:pressed {
background-color: #d0d0d0;
border: 1px solid #a0a0a0;
}
QPushButton:default {
background-color: #007aff;
color: #ffffff;
border: 1px solid #007aff;
}
QPushButton:default:hover {
background-color: #0062cc;
border: 1px solid #0062cc;
}
QPushButton:default:pressed {
background-color: #004a99;
border: 1px solid #004a99;
}
"""
def set_dark_theme(self, is_dark):

File diff suppressed because it is too large Load Diff

@ -1,13 +1,14 @@
# word_main_window.py
import sys
import os
import platform
from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QTextEdit, QLabel, QSplitter, QFrame, QMenuBar,
QAction, QFileDialog, QMessageBox, QApplication,
QDialog, QLineEdit, QCheckBox, QPushButton, QListWidget,
QListWidgetItem, QScrollArea)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QRect, QByteArray, QBuffer, QIODevice
from PyQt5.QtGui import QFont, QPalette, QColor, QIcon, QPixmap, QTextCharFormat, QTextCursor, QTextDocument, QImage, QTextImageFormat, QTextFormat
from PyQt5.QtGui import QFont, QPalette, QColor, QIcon, QPixmap, QTextCharFormat, QTextCursor, QTextDocument, QImage, QTextImageFormat, QTextFormat, QTextBlockFormat
from ui.word_style_ui import (WordRibbon, WordStatusBar, WordTextEdit,
)
@ -135,9 +136,8 @@ class WordStyleMainWindow(QMainWindow):
# 连接主题切换信号
theme_manager.theme_changed.connect(self.on_theme_changed)
# 设置默认为白色模式(禁用自动检测)
theme_manager.enable_auto_detection(False)
theme_manager.set_dark_theme(False)
# 启用系统主题自动检测
theme_manager.enable_auto_detection(True)
# 应用当前主题
self.apply_theme()
@ -637,6 +637,8 @@ class WordStyleMainWindow(QMainWindow):
self.learning_mode_action = QAction('学习模式', self)
self.learning_mode_action.setCheckable(True)
self.learning_mode_action.setChecked(False)
# 设置学习模式快捷键 (Qt会自动在macOS上映射Ctrl为Cmd)
self.learning_mode_action.setShortcut('Ctrl+L')
self.learning_mode_action.triggered.connect(lambda: self.set_view_mode("learning"))
view_mode_menu.addAction(self.learning_mode_action)
@ -674,12 +676,40 @@ class WordStyleMainWindow(QMainWindow):
# 插入菜单
insert_menu = menubar.addMenu('插入(I)')
# 插入图片功能
insert_image_action = QAction('插入图片', self)
insert_image_action.triggered.connect(self.insert_image_in_typing_mode)
insert_menu.addAction(insert_image_action)
# 绘图菜单
paint_menu = menubar.addMenu('绘图(D)')
# 设计菜单
design_menu = menubar.addMenu('设计(G)')
# 导出子菜单
export_menu = design_menu.addMenu('导出')
# 导出为HTML
export_html_action = QAction('导出为HTML', self)
export_html_action.triggered.connect(self.export_as_html)
export_menu.addAction(export_html_action)
# 导出为PDF
export_pdf_action = QAction('导出为PDF', self)
export_pdf_action.triggered.connect(self.export_as_pdf)
export_menu.addAction(export_pdf_action)
# 导出为TXT
export_txt_action = QAction('导出为TXT', self)
export_txt_action.triggered.connect(self.export_as_txt)
export_menu.addAction(export_txt_action)
# 导出为DOCX
export_docx_action = QAction('导出为DOCX', self)
export_docx_action.triggered.connect(self.export_as_docx)
export_menu.addAction(export_docx_action)
# 布局菜单
layout_menu = menubar.addMenu('布局(L)')
@ -809,6 +839,9 @@ class WordStyleMainWindow(QMainWindow):
# 文本变化信号
self.text_edit.textChanged.connect(self.on_text_changed)
# 光标位置变化信号,用于更新按钮状态
self.text_edit.cursorPositionChanged.connect(self.update_format_buttons)
# Ribbon按钮信号
# 标签栏已删除,相关代码已移除
@ -821,6 +854,18 @@ class WordStyleMainWindow(QMainWindow):
self.ribbon.underline_btn.clicked.connect(self.on_underline_clicked)
self.ribbon.color_btn.clicked.connect(self.on_color_clicked)
# 样式按钮信号
if hasattr(self.ribbon, 'heading1_btn'):
self.ribbon.heading1_btn.clicked.connect(self.on_heading1_clicked)
if hasattr(self.ribbon, 'heading2_btn'):
self.ribbon.heading2_btn.clicked.connect(self.on_heading2_clicked)
if hasattr(self.ribbon, 'heading3_btn'):
self.ribbon.heading3_btn.clicked.connect(self.on_heading3_clicked)
if hasattr(self.ribbon, 'heading4_btn'):
self.ribbon.heading4_btn.clicked.connect(self.on_heading4_clicked)
if hasattr(self.ribbon, 'body_text_btn'):
self.ribbon.body_text_btn.clicked.connect(self.on_body_text_clicked)
# 查找和替换按钮信号
if hasattr(self.ribbon, 'find_btn'):
self.ribbon.find_btn.clicked.connect(self.show_find_dialog)
@ -829,6 +874,16 @@ class WordStyleMainWindow(QMainWindow):
# 页面布局信号已在菜单中直接连接,无需在此重复连接
# 段落对齐按钮信号
if hasattr(self.ribbon, 'align_left_btn'):
self.ribbon.align_left_btn.clicked.connect(self.on_align_left_clicked)
if hasattr(self.ribbon, 'align_center_btn'):
self.ribbon.align_center_btn.clicked.connect(self.on_align_center_clicked)
if hasattr(self.ribbon, 'align_right_btn'):
self.ribbon.align_right_btn.clicked.connect(self.on_align_right_clicked)
if hasattr(self.ribbon, 'align_justify_btn'):
self.ribbon.align_justify_btn.clicked.connect(self.on_align_justify_clicked)
# 天气功能信号
if hasattr(self.ribbon, 'city_combo'):
self.ribbon.city_combo.currentTextChanged.connect(self.on_city_changed)
@ -1291,10 +1346,144 @@ class WordStyleMainWindow(QMainWindow):
if cursor.hasSelection():
self.status_bar.showMessage("字体颜色已设置,新输入的文本将使用该颜色", 2000)
def on_heading1_clicked(self):
"""一级标题按钮点击处理"""
self.apply_heading_style(1)
def on_heading2_clicked(self):
"""二级标题按钮点击处理"""
self.apply_heading_style(2)
def on_heading3_clicked(self):
"""三级标题按钮点击处理"""
self.apply_heading_style(3)
def on_heading4_clicked(self):
"""四级标题按钮点击处理"""
self.apply_heading_style(4)
def on_body_text_clicked(self):
"""正文按钮点击处理"""
self.apply_body_text_style()
def on_align_left_clicked(self):
"""左对齐按钮点击处理"""
self.apply_alignment(Qt.AlignLeft)
def on_align_center_clicked(self):
"""居中对齐按钮点击处理"""
self.apply_alignment(Qt.AlignCenter)
def on_align_right_clicked(self):
"""右对齐按钮点击处理"""
self.apply_alignment(Qt.AlignRight)
def on_align_justify_clicked(self):
"""两端对齐按钮点击处理"""
self.apply_alignment(Qt.AlignJustify)
def apply_heading_style(self, level):
"""应用标题样式"""
cursor = self.text_edit.textCursor()
# 创建字符格式
char_format = QTextCharFormat()
# 创建块格式(段落格式)
block_format = QTextBlockFormat()
block_format.setTopMargin(12)
block_format.setBottomMargin(6)
# 根据标题级别设置样式
if level == 1:
# 一级标题24pt, 加粗
char_format.setFontPointSize(24)
char_format.setFontWeight(QFont.Bold)
elif level == 2:
# 二级标题18pt, 加粗
char_format.setFontPointSize(18)
char_format.setFontWeight(QFont.Bold)
elif level == 3:
# 三级标题16pt, 加粗
char_format.setFontPointSize(16)
char_format.setFontWeight(QFont.Bold)
elif level == 4:
# 四级标题14pt, 加粗
char_format.setFontPointSize(14)
char_format.setFontWeight(QFont.Bold)
# 应用格式
if cursor.hasSelection():
# 如果有选中文本,只更改选中文本的格式
cursor.mergeCharFormat(char_format)
else:
# 如果没有选中文本,更改当前段落的格式
cursor.setBlockFormat(block_format)
cursor.mergeCharFormat(char_format)
# 将光标移动到段落末尾并添加换行
cursor.movePosition(QTextCursor.EndOfBlock)
cursor.insertText("\n")
# 设置文本编辑器的默认格式
self.text_edit.setCurrentCharFormat(char_format)
self.text_edit.textCursor().setBlockFormat(block_format)
def apply_body_text_style(self):
"""应用正文样式"""
cursor = self.text_edit.textCursor()
# 创建字符格式
char_format = QTextCharFormat()
char_format.setFontPointSize(12) # 正文字号
char_format.setFontWeight(QFont.Normal) # 正常粗细
# 创建块格式(段落格式)
block_format = QTextBlockFormat()
block_format.setTopMargin(0)
block_format.setBottomMargin(6)
# 应用格式
if cursor.hasSelection():
# 如果有选中文本,只更改选中文本的格式
cursor.mergeCharFormat(char_format)
else:
# 如果没有选中文本,更改当前段落的格式
cursor.setBlockFormat(block_format)
cursor.mergeCharFormat(char_format)
# 设置文本编辑器的默认格式
self.text_edit.setCurrentCharFormat(char_format)
self.text_edit.textCursor().setBlockFormat(block_format)
def apply_alignment(self, alignment):
"""应用段落对齐方式"""
cursor = self.text_edit.textCursor()
# 创建块格式(段落格式)
block_format = QTextBlockFormat()
block_format.setAlignment(alignment)
# 应用格式
if cursor.hasSelection():
# 如果有选中文本,更改选中文本所在段落的对齐方式
cursor.mergeBlockFormat(block_format)
else:
# 如果没有选中文本,更改当前段落的对齐方式
cursor.setBlockFormat(block_format)
# 更新文本编辑器的默认段落格式
self.text_edit.textCursor().setBlockFormat(block_format)
def update_weather_display(self, weather_data):
"""更新天气显示"""
if 'error' in weather_data:
self.status_bar.showMessage(f"天气数据获取失败: {weather_data['error']}", 3000)
# 更新工具栏天气显示为错误状态
if hasattr(self, 'ribbon'):
if hasattr(self.ribbon, 'weather_icon_label'):
self.ribbon.weather_icon_label.setText("")
if hasattr(self.ribbon, 'weather_temp_label'):
self.ribbon.weather_temp_label.setText("--°C")
else:
# 处理嵌套的天气数据结构
city = weather_data.get('city', '未知城市')
@ -1318,6 +1507,32 @@ class WordStyleMainWindow(QMainWindow):
weather_message = f"{city}: {desc}, {temp}°C{temp_range}"
self.status_bar.showMessage(weather_message, 5000)
# 更新工具栏天气图标和温度显示
if hasattr(self, 'ribbon'):
# 更新天气图标
if hasattr(self.ribbon, 'weather_icon_label') and desc != 'N/A':
emoji = self.ribbon.get_weather_emoji(desc)
self.ribbon.weather_icon_label.setText(emoji)
# 更新温度显示
if hasattr(self.ribbon, 'weather_temp_label') and temp != 'N/A':
# 计算平均温度(使用最高温和最低温的平均值)
avg_temp = temp
if 'forecast' in weather_data and weather_data['forecast']:
forecast_data = weather_data['forecast'][0]
if isinstance(forecast_data, dict):
temp_max = forecast_data.get('temp_max', 'N/A')
temp_min = forecast_data.get('temp_min', 'N/A')
if temp_max != 'N/A' and temp_min != 'N/A':
try:
avg_temp = (float(temp_max) + float(temp_min)) / 2
avg_temp = round(avg_temp, 1)
except (ValueError, TypeError):
avg_temp = temp
temp_str = f"{avg_temp}°C" if isinstance(avg_temp, (int, float)) else f"{temp}°C"
self.ribbon.weather_temp_label.setText(temp_str)
# 存储天气数据供其他功能使用(确保包含生活提示)
self.current_weather_data = weather_data
print(f"update_weather_display - 存储的current_weather_data包含life_tips: {self.current_weather_data.get('life_tips', [])}")
@ -1581,7 +1796,7 @@ class WordStyleMainWindow(QMainWindow):
"""导入文件 - 仅在导入时存储内容,不立即显示"""
file_path, _ = QFileDialog.getOpenFileName(
self, "导入文件", "",
"文档文件 (*.docx *.txt *.pdf);;所有文件 (*.*)"
"文档文件 (*.docx *.txt *.pdf *.html);;所有文件 (*.*)"
)
if file_path:
@ -1646,7 +1861,7 @@ class WordStyleMainWindow(QMainWindow):
# 提取并显示图片(如果有)
if images:
self.extract_and_display_images(content, images)
self.extract_and_display_images(file_path=None, images=images)
else:
# 转换失败,显示错误信息
@ -2697,6 +2912,79 @@ class WordStyleMainWindow(QMainWindow):
# 这个方法现在不需要了,因为图片会直接插入到文本中
pass
def insert_image_in_typing_mode(self):
"""在打字模式下插入图片"""
try:
# 检查当前是否在打字模式下
if self.view_mode != "typing":
self.status_bar.showMessage("请在打字模式下使用插入图片功能", 3000)
return
# 打开文件对话框选择图片
from PyQt5.QtWidgets import QFileDialog
file_path, _ = QFileDialog.getOpenFileName(
self,
"选择图片文件",
"",
"图片文件 (*.png *.jpg *.jpeg *.bmp *.gif *.ico)"
)
if not file_path:
return
# 加载图片文件
pixmap = QPixmap(file_path)
if pixmap.isNull():
self.status_bar.showMessage("无法加载图片文件", 3000)
return
# 获取当前光标位置
cursor = self.text_edit.textCursor()
# 创建图片格式
image_format = QTextImageFormat()
# 调整图片大小
scaled_pixmap = pixmap.scaled(200, 150, Qt.KeepAspectRatio, Qt.SmoothTransformation)
# 将图片保存到临时文件
import tempfile
import os
temp_dir = tempfile.gettempdir()
filename = os.path.basename(file_path)
safe_filename = "".join(c for c in filename if c.isalnum() or c in ('.', '_', '-'))
temp_file = os.path.join(temp_dir, safe_filename)
if scaled_pixmap.save(temp_file):
# 设置图片格式
image_format.setName(temp_file)
image_format.setWidth(200)
image_format.setHeight(150)
# 在光标位置插入图片
cursor.insertImage(image_format)
# 在图片后插入一个空格,让文字继续
cursor.insertText(" ")
# 标记文档为已修改
if not self.is_modified:
self.is_modified = True
self.update_window_title()
# 显示成功消息
self.status_bar.showMessage(f"图片已插入: {filename}", 3000)
# 添加到临时文件列表以便清理
self.temp_files.append(temp_file)
else:
self.status_bar.showMessage("保存临时图片文件失败", 3000)
except Exception as e:
self.status_bar.showMessage(f"插入图片失败: {str(e)}", 3000)
import traceback
traceback.print_exc()
def closeEvent(self, event):
"""关闭事件处理"""
# 清理临时文件
@ -2731,11 +3019,310 @@ class WordStyleMainWindow(QMainWindow):
print(f"删除临时文件失败 {temp_file}: {str(e)}")
self.temp_files.clear()
def extract_and_display_images(self, file_path):
def export_as_html(self):
"""导出为HTML"""
file_path, _ = QFileDialog.getSaveFileName(
self, "导出为HTML", "", "HTML文件 (*.html);;所有文件 (*.*)"
)
if file_path:
try:
# 获取当前文本内容
content = self.text_edit.toPlainText()
# 处理图片标签
html_body = ""
lines = content.split('\n')
for line in lines:
if line.strip().startswith('[图片:') and line.strip().endswith(']'):
# 提取图片文件名
img_name = line.strip()[4:-1].strip()
# 查找对应的图片数据
img_data = None
for filename, image_data in self.extracted_images:
if filename == img_name:
img_data = image_data
break
if img_data:
# 创建图片的base64编码
import base64
img_base64 = base64.b64encode(img_data).decode('utf-8')
# 检测图片类型
if img_name.lower().endswith('.png'):
img_type = 'png'
elif img_name.lower().endswith(('.jpg', '.jpeg')):
img_type = 'jpeg'
elif img_name.lower().endswith('.gif'):
img_type = 'gif'
else:
img_type = 'png' # 默认
html_body += f'<div class="image-container">\n'
html_body += f'<img src="data:image/{img_type};base64,{img_base64}" alt="{img_name}" style="max-width: 100%; height: auto; margin: 10px 0;">\n'
html_body += f'<p class="image-caption">{img_name}</p>\n'
html_body += f'</div>\n'
else:
html_body += f'<p class="image-placeholder">[图片: {img_name}]</p>\n'
else:
# 普通文本,使用段落标签
if line.strip():
html_body += f'<p>{line}</p>\n'
else:
html_body += '<br>\n'
# 创建完整的HTML内容
html_content = f"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MagicWord文档</title>
<style>
body {{
font-family: 'Calibri', 'Microsoft YaHei', '微软雅黑', sans-serif;
font-size: 12pt;
line-height: 1.6;
margin: 40px;
background-color: #ffffff;
color: #000000;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}}
.container {{
padding: 20px;
}}
p {{
margin: 10px 0;
white-space: pre-wrap;
}}
.image-container {{
text-align: center;
margin: 20px 0;
padding: 15px;
border: 1px solid #e0e0e0;
border-radius: 5px;
background-color: #f9f9f9;
}}
.image-container img {{
max-width: 100%;
height: auto;
border-radius: 3px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}}
.image-caption {{
margin-top: 8px;
font-size: 10pt;
color: #666;
font-style: italic;
}}
.image-placeholder {{
background-color: #f0f0f0;
border: 2px dashed #ccc;
padding: 20px;
text-align: center;
color: #999;
font-style: italic;
margin: 20px 0;
}}
</style>
</head>
<body>
<div class="container">
{html_body}
</div>
</body>
</html>"""
# 写入HTML文件
with open(file_path, 'w', encoding='utf-8') as f:
f.write(html_content)
self.status_bar.showMessage(f"已导出为HTML: {os.path.basename(file_path)}", 3000)
except Exception as e:
QMessageBox.critical(self, "错误", f"导出HTML失败: {str(e)}")
def export_as_pdf(self):
"""导出为PDF"""
file_path, _ = QFileDialog.getSaveFileName(
self, "导出为PDF", "", "PDF文件 (*.pdf);;所有文件 (*.*)"
)
if file_path:
try:
from PyQt5.QtGui import QTextDocument
from PyQt5.QtPrintSupport import QPrinter
# 获取当前文本内容
content = self.text_edit.toPlainText()
# 处理图片标签 - 创建HTML内容以便更好地处理图片
html_content = "<html><body style='font-family: Calibri, Microsoft YaHei, 微软雅黑, sans-serif; font-size: 12pt; line-height: 1.6; margin: 40px;'>"
lines = content.split('\n')
for line in lines:
if line.strip().startswith('[图片:') and line.strip().endswith(']'):
# 提取图片文件名
img_name = line.strip()[4:-1].strip()
# 在PDF中显示图片占位符
html_content += f'<div style="text-align: center; margin: 20px 0; padding: 15px; border: 1px solid #e0e0e0; border-radius: 5px; background-color: #f9f9f9;">'
html_content += f'<div style="background-color: #f0f0f0; border: 2px dashed #ccc; padding: 20px; text-align: center; color: #999; font-style: italic;">'
html_content += f'[图片: {img_name}]'
html_content += f'</div>'
html_content += f'<p style="margin-top: 8px; font-size: 10pt; color: #666; font-style: italic;">{img_name}</p>'
html_content += f'</div>'
else:
# 普通文本,使用段落标签
if line.strip():
html_content += f'<p style="margin: 10px 0; white-space: pre-wrap;">{line}</p>'
else:
html_content += '<br>'
html_content += "</body></html>"
# 创建文本文档
doc = QTextDocument()
doc.setHtml(html_content)
# 设置文档样式
doc.setDefaultFont(self.text_edit.currentFont())
# 创建PDF打印机
printer = QPrinter(QPrinter.HighResolution)
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName(file_path)
printer.setPageSize(QPrinter.A4)
printer.setPageMargins(20, 20, 20, 20, QPrinter.Millimeter)
# 打印文档到PDF
doc.print_(printer)
self.status_bar.showMessage(f"已导出为PDF: {os.path.basename(file_path)}", 3000)
except Exception as e:
QMessageBox.critical(self, "错误", f"导出PDF失败: {str(e)}")
def export_as_txt(self):
"""导出为TXT"""
file_path, _ = QFileDialog.getSaveFileName(
self, "导出为TXT", "", "文本文档 (*.txt);;所有文件 (*.*)"
)
if file_path:
try:
# 获取当前文本内容
content = self.text_edit.toPlainText()
# 处理图片标签 - 在TXT中保留图片标记
processed_content = content
# 写入TXT文件
with open(file_path, 'w', encoding='utf-8') as f:
f.write(processed_content)
self.status_bar.showMessage(f"已导出为TXT: {os.path.basename(file_path)}", 3000)
except Exception as e:
QMessageBox.critical(self, "错误", f"导出TXT失败: {str(e)}")
def export_as_docx(self):
"""导出为DOCX"""
file_path, _ = QFileDialog.getSaveFileName(
self, "导出为DOCX", "", "Word文档 (*.docx);;所有文件 (*.*)"
)
if file_path:
try:
from docx import Document
from docx.shared import Inches
# 创建Word文档
doc = Document()
# 获取当前文本内容
content = self.text_edit.toPlainText()
lines = content.split('\n')
# 逐行处理内容
for line in lines:
if line.strip().startswith('[图片:') and line.strip().endswith(']'):
# 提取图片文件名
img_name = line.strip()[4:-1].strip()
# 查找对应的图片数据
img_data = None
for filename, image_data in self.extracted_images:
if filename == img_name:
img_data = image_data
break
if img_data:
# 创建临时图片文件
import tempfile
import os
# 检测图片类型
if img_name.lower().endswith('.png'):
img_ext = '.png'
elif img_name.lower().endswith(('.jpg', '.jpeg')):
img_ext = '.jpg'
elif img_name.lower().endswith('.gif'):
img_ext = '.gif'
else:
img_ext = '.png' # 默认
# 创建临时文件
with tempfile.NamedTemporaryFile(mode='wb', suffix=img_ext, delete=False) as temp_file:
temp_file.write(img_data)
temp_img_path = temp_file.name
try:
# 在Word文档中添加图片
doc.add_picture(temp_img_path, width=Inches(4))
# 添加图片说明
doc.add_paragraph(f'图片: {img_name}')
finally:
# 清理临时文件
if os.path.exists(temp_img_path):
os.remove(temp_img_path)
else:
# 图片未找到,添加占位符文本
doc.add_paragraph(f'[图片: {img_name}]')
else:
# 普通文本
if line.strip():
doc.add_paragraph(line)
else:
# 空行,添加空段落
doc.add_paragraph()
# 保存文档
doc.save(file_path)
self.status_bar.showMessage(f"已导出为DOCX: {os.path.basename(file_path)}", 3000)
except ImportError:
QMessageBox.critical(self, "错误", "需要安装python-docx库才能导出DOCX文件")
except Exception as e:
QMessageBox.critical(self, "错误", f"导出DOCX失败: {str(e)}")
def extract_and_display_images(self, file_path=None, images=None):
"""提取并显示Word文档中的图片 - 修复图片位置计算"""
try:
# 提取图片
images = FileParser.extract_images_from_docx(file_path)
# 如果没有提供图片数据,则从文件中提取
if images is None:
if file_path is None:
return
# 提取图片
images = FileParser.extract_images_from_docx(file_path)
if not images:
return
@ -2828,6 +3415,40 @@ class WordStyleMainWindow(QMainWindow):
except Exception as e:
self.status_bar.showMessage(f"提取图片失败: {str(e)}", 3000)
def update_format_buttons(self):
"""更新格式按钮的状态,根据当前光标位置的格式"""
try:
# 获取当前光标位置的字符格式
cursor = self.text_edit.textCursor()
char_format = cursor.charFormat()
block_format = cursor.blockFormat()
# 更新粗体按钮状态
if hasattr(self, 'ribbon') and hasattr(self.ribbon, 'bold_btn'):
is_bold = char_format.font().weight() == QFont.Bold
self.ribbon.bold_btn.setChecked(is_bold)
# 更新斜体按钮状态
if hasattr(self, 'ribbon') and hasattr(self.ribbon, 'italic_btn'):
is_italic = char_format.font().italic()
self.ribbon.italic_btn.setChecked(is_italic)
# 更新下划线按钮状态
if hasattr(self, 'ribbon') and hasattr(self.ribbon, 'underline_btn'):
is_underline = char_format.font().underline()
self.ribbon.underline_btn.setChecked(is_underline)
# 更新对齐按钮状态
if hasattr(self, 'ribbon') and hasattr(self.ribbon, 'align_left_btn'):
alignment = block_format.alignment()
self.ribbon.align_left_btn.setChecked(alignment == Qt.AlignLeft)
self.ribbon.align_center_btn.setChecked(alignment == Qt.AlignCenter)
self.ribbon.align_right_btn.setChecked(alignment == Qt.AlignRight)
self.ribbon.align_justify_btn.setChecked(alignment == Qt.AlignJustify)
except Exception as e:
print(f"更新格式按钮状态时出错: {e}")
if __name__ == "__main__":
app = QApplication(sys.argv)

@ -0,0 +1,29 @@
#!/bin/bash
# MagicWord PyQt5 安全启动脚本
# 此脚本自动设置所有必要的环境变量并启动应用
echo "🚀 正在启动 MagicWord 应用..."
# 设置PyQt5环境变量
export QT_PLUGIN_PATH="/Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/.venv/lib/python3.9/site-packages/PyQt5/Qt5/plugins"
export QT_QPA_PLATFORM_PLUGIN_PATH="/Users/maziang/Documents/CodingWorkPlace/Code/Curriculum_Design/.venv/lib/python3.9/site-packages/PyQt5/Qt5/plugins/platforms"
export QT_QPA_PLATFORM="cocoa"
export QT_MAC_WANTS_LAYER="1"
export QT_LOGGING_RULES="qt.qpa.*=false"
# 检查虚拟环境
if [ -z "$VIRTUAL_ENV" ]; then
echo "⚠️ 虚拟环境未激活,正在激活..."
source .venv/bin/activate
fi
# 检查PyQt5是否安装
python -c "import PyQt5.QtWidgets" 2>/dev/null
if [ $? -ne 0 ]; then
echo "❌ PyQt5未正确安装正在修复..."
python fix_pyqt5_complete.py
fi
# 启动应用
echo "✅ 环境设置完成,正在启动应用..."
cd src && python main.py
Loading…
Cancel
Save