恢复高级编辑功能
This commit is contained in:
parent
a91445add9
commit
1e61dd581f
325
README.md
Normal file
325
README.md
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
# TXT2DOCX - 智能文本转Word文档工具
|
||||||
|
|
||||||
|
## 📖 项目简介
|
||||||
|
|
||||||
|
TXT2DOCX 是一个功能强大的文本转换工具,能够将纯文本(TXT)文件智能转换为格式化的 Word 文档(DOCX)。支持 Markdown 语法解析、错别字自动纠正、图片插入和专业样式排版。
|
||||||
|
|
||||||
|
### ✨ 核心特性
|
||||||
|
|
||||||
|
- 🎨 **专业样式系统** - 内置8款精美样式模板,支持自定义样式编辑
|
||||||
|
- 📝 **Markdown 支持** - 完整支持 Markdown 语法转换
|
||||||
|
- 🔧 **智能文本处理** - 自动错别字纠正和文本优化
|
||||||
|
- 🖼️ **图片处理** - 智能图片插入和尺寸调整
|
||||||
|
- 📦 **批量处理** - 支持多文件批量转换
|
||||||
|
- 🎯 **所见即所得** - 高级样式编辑器实时预览功能
|
||||||
|
- 🖥️ **图形界面** - 友好的 GUI 界面,操作简单直观
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
### 环境要求
|
||||||
|
|
||||||
|
- Python 3.7+
|
||||||
|
- Windows 10/11 (推荐) 或 Linux/macOS
|
||||||
|
|
||||||
|
### 依赖安装
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install python-docx pillow tkinter
|
||||||
|
```
|
||||||
|
|
||||||
|
### 启动程序
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
TxT2DOCX/
|
||||||
|
├── main.py # 主程序入口
|
||||||
|
├── config.py # 配置管理
|
||||||
|
├── style_manager.py # 样式管理系统
|
||||||
|
├── advanced_style_editor.py # 高级样式编辑器
|
||||||
|
├── file_handler.py # 文件处理模块
|
||||||
|
├── text_processor.py # 文本处理模块
|
||||||
|
├── markdown_parser.py # Markdown 解析器
|
||||||
|
├── docx_generator.py # DOCX 生成器
|
||||||
|
├── image_processor.py # 图片处理模块
|
||||||
|
├── error_chars.py # 错别字处理
|
||||||
|
├── batch_processor.py # 批量处理器
|
||||||
|
├── gui_*.py # GUI 界面模块
|
||||||
|
├── data/ # 数据目录
|
||||||
|
│ ├── styles/ # 样式文件
|
||||||
|
│ │ ├── *.json # 自定义样式配置
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── error_chars.json # 错别字映射表
|
||||||
|
│ └── 11.txt # 测试文件
|
||||||
|
├── replacestr.py # 文本替换工具
|
||||||
|
├── Txt2docx2.py # 原版兼容脚本
|
||||||
|
└── README.md # 说明文档
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎨 内置样式模板
|
||||||
|
|
||||||
|
### 新媒体风格
|
||||||
|
1. **爆款文章风格** - 高阅读量文章排版,层次分明
|
||||||
|
2. **微信公众号风格** - 专业新媒体排版,阅读体验佳
|
||||||
|
3. **知乎高赞回答风格** - 逻辑清晰,专业权威
|
||||||
|
4. **小红书笔记风格** - 清新文艺,生活方式类内容
|
||||||
|
5. **今日头条新闻风格** - 信息密度高,节奏紧凑
|
||||||
|
6. **B站UP主视频脚本风格** - 轻松活泼,年轻化表达
|
||||||
|
|
||||||
|
### 商务风格
|
||||||
|
7. **企业微信群通知风格** - 正式严肃,商务专业
|
||||||
|
8. **情感鸡汤文风格** - 温暖治愈,情感丰富
|
||||||
|
|
||||||
|
## 🛠️ 功能模块详解
|
||||||
|
|
||||||
|
### 1. 样式管理系统
|
||||||
|
- **内置样式库** - 8款专业排版样式
|
||||||
|
- **自定义样式** - 支持创建、编辑、导入、导出
|
||||||
|
- **样式预览** - 实时预览样式效果
|
||||||
|
- **样式继承** - 基于现有样式快速定制
|
||||||
|
|
||||||
|
### 2. 高级样式编辑器
|
||||||
|
- **实时预览** - 丰富预览模式,所见即所得
|
||||||
|
- **分层编辑** - 基本信息、正文样式、标题样式分层管理
|
||||||
|
- **字体配置** - 字体、字号、颜色、粗体、斜体全支持
|
||||||
|
- **段落设置** - 行距、缩进、间距精确控制
|
||||||
|
- **标题层次** - 1-3级标题样式独立配置
|
||||||
|
|
||||||
|
### 3. 文本处理引擎
|
||||||
|
- **编码检测** - 自动识别文件编码(UTF-8, GBK, GB2312等)
|
||||||
|
- **错别字纠正** - 基于词典的智能错别字替换
|
||||||
|
- **文本清洗** - 去除多余空白和格式化字符
|
||||||
|
- **Markdown解析** - 支持标题、列表、引用、代码块等
|
||||||
|
|
||||||
|
### 4. 图片处理功能
|
||||||
|
- **自动插入** - 识别文本中图片引用并自动插入
|
||||||
|
- **尺寸调整** - 智能调整图片尺寸适配文档
|
||||||
|
- **格式支持** - 支持 PNG, JPG, GIF 等常见格式
|
||||||
|
|
||||||
|
### 5. 批量处理器
|
||||||
|
- **多文件处理** - 同时处理多个TXT文件
|
||||||
|
- **进度跟踪** - 实时显示处理进度
|
||||||
|
- **错误处理** - 详细的错误报告和恢复机制
|
||||||
|
|
||||||
|
## 📖 使用教程
|
||||||
|
|
||||||
|
### 基础使用流程
|
||||||
|
|
||||||
|
1. **启动程序**
|
||||||
|
```bash
|
||||||
|
python main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **选择样式**
|
||||||
|
- 在样式管理选项卡中选择心仪的排版样式
|
||||||
|
- 或者点击"高级编辑"自定义样式
|
||||||
|
|
||||||
|
3. **导入文件**
|
||||||
|
- 在文件处理选项卡中选择要转换的TXT文件
|
||||||
|
- 支持单文件或批量文件选择
|
||||||
|
|
||||||
|
4. **配置选项**
|
||||||
|
- 设置输出路径
|
||||||
|
- 配置文本处理选项
|
||||||
|
- 调整图片处理参数
|
||||||
|
|
||||||
|
5. **开始转换**
|
||||||
|
- 点击"开始转换"按钮
|
||||||
|
- 等待处理完成并查看结果
|
||||||
|
|
||||||
|
### 高级样式编辑
|
||||||
|
|
||||||
|
1. **打开编辑器**
|
||||||
|
- 选择基础样式 → 点击"高级编辑"
|
||||||
|
- 或在样式管理中点击"新建"创建全新样式
|
||||||
|
|
||||||
|
2. **编辑样式**
|
||||||
|
- **基本信息**: 修改样式名称和描述
|
||||||
|
- **正文样式**: 设置字体、字号、颜色、行距等
|
||||||
|
- **标题样式**: 分别配置1-3级标题的样式
|
||||||
|
|
||||||
|
3. **实时预览**
|
||||||
|
- 切换到"丰富预览"模式查看真实效果
|
||||||
|
- 调整参数时预览会实时更新
|
||||||
|
|
||||||
|
4. **保存样式**
|
||||||
|
- 点击"保存样式"覆盖当前样式
|
||||||
|
- 或点击"另存为"创建新样式
|
||||||
|
|
||||||
|
### Markdown 语法支持
|
||||||
|
|
||||||
|
支持的 Markdown 语法:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 一级标题
|
||||||
|
## 二级标题
|
||||||
|
### 三级标题
|
||||||
|
|
||||||
|
**粗体文字**
|
||||||
|
*斜体文字*
|
||||||
|
|
||||||
|
> 引用块内容
|
||||||
|
|
||||||
|
- 无序列表项
|
||||||
|
- 无序列表项
|
||||||
|
|
||||||
|
1. 有序列表项
|
||||||
|
2. 有序列表项
|
||||||
|
|
||||||
|
`行内代码`
|
||||||
|
|
||||||
|
```代码块```
|
||||||
|
|
||||||
|

|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ 配置说明
|
||||||
|
|
||||||
|
### 样式配置
|
||||||
|
样式配置文件位于 `data/styles/` 目录,为 JSON 格式:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "自定义样式",
|
||||||
|
"description": "样式描述",
|
||||||
|
"body_font": {
|
||||||
|
"name": "微软雅黑",
|
||||||
|
"size": 14,
|
||||||
|
"color": "#000000",
|
||||||
|
"bold": false,
|
||||||
|
"italic": false
|
||||||
|
},
|
||||||
|
"body_paragraph": {
|
||||||
|
"line_spacing": 1.5,
|
||||||
|
"first_line_indent": 2.0,
|
||||||
|
"space_before": 0,
|
||||||
|
"space_after": 6
|
||||||
|
},
|
||||||
|
"heading_styles": {
|
||||||
|
"1": {
|
||||||
|
"font": {
|
||||||
|
"name": "黑体",
|
||||||
|
"size": 18,
|
||||||
|
"color": "#1F497D",
|
||||||
|
"bold": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 错别字配置
|
||||||
|
错别字映射表位于 `data/error_chars.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"错别字1": "正确字1",
|
||||||
|
"错别字2": "正确字2"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 开发指南
|
||||||
|
|
||||||
|
### 代码架构
|
||||||
|
- **模块化设计** - 功能模块独立,低耦合高内聚
|
||||||
|
- **配置驱动** - 通过配置文件管理样式和参数
|
||||||
|
- **事件驱动** - GUI 采用事件驱动模式
|
||||||
|
- **插件化** - 易于扩展新功能和样式
|
||||||
|
|
||||||
|
### 扩展开发
|
||||||
|
|
||||||
|
#### 添加新样式
|
||||||
|
1. 在 `style_manager.py` 的 `_create_builtin_styles()` 中添加样式定义
|
||||||
|
2. 配置字体、段落、标题等样式参数
|
||||||
|
3. 重启程序即可使用新样式
|
||||||
|
|
||||||
|
#### 添加新的文本处理功能
|
||||||
|
1. 在 `text_processor.py` 中添加处理函数
|
||||||
|
2. 在 `batch_processor.py` 中调用新功能
|
||||||
|
3. 可选:在 GUI 中添加相应配置选项
|
||||||
|
|
||||||
|
#### 扩展图片处理
|
||||||
|
1. 在 `image_processor.py` 中添加新的处理方法
|
||||||
|
2. 支持新的图片格式或处理效果
|
||||||
|
3. 在 `docx_generator.py` 中集成新功能
|
||||||
|
|
||||||
|
## 🐛 常见问题
|
||||||
|
|
||||||
|
### Q: 程序启动失败
|
||||||
|
A: 请检查 Python 版本(需要3.7+)和依赖包安装:
|
||||||
|
```bash
|
||||||
|
pip install python-docx pillow tkinter
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: 中文乱码问题
|
||||||
|
A: 程序会自动检测文件编码,如仍有问题请确保TXT文件保存为UTF-8编码。
|
||||||
|
|
||||||
|
### Q: 样式保存失败
|
||||||
|
A: 检查 `data/styles/` 目录的写入权限,必要时以管理员身份运行程序。
|
||||||
|
|
||||||
|
### Q: 图片无法插入
|
||||||
|
A: 确保图片路径正确,支持相对路径和绝对路径,图片格式为 PNG/JPG/GIF。
|
||||||
|
|
||||||
|
### Q: 转换速度慢
|
||||||
|
A: 大文件处理需要时间,可通过批量处理提高效率。图片较多时会影响速度。
|
||||||
|
|
||||||
|
## 📝 更新日志
|
||||||
|
|
||||||
|
### v2.0.0 (Latest)
|
||||||
|
- ✨ 新增高级样式编辑器,支持实时预览
|
||||||
|
- 🎨 重构样式系统,新增8款专业样式模板
|
||||||
|
- 🖥️ 优化GUI界面,提升用户体验
|
||||||
|
- 🔧 改进错误处理和异常恢复机制
|
||||||
|
- 📦 模块化重构,提高代码可维护性
|
||||||
|
|
||||||
|
### v1.0.0
|
||||||
|
- 🎉 初始版本发布
|
||||||
|
- 📝 基础文本转换功能
|
||||||
|
- 🎨 简单样式支持
|
||||||
|
- 🖼️ 图片插入功能
|
||||||
|
|
||||||
|
## 🤝 贡献指南
|
||||||
|
|
||||||
|
欢迎贡献代码和建议!
|
||||||
|
|
||||||
|
1. Fork 本项目
|
||||||
|
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
|
||||||
|
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
|
||||||
|
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||||
|
5. 开启 Pull Request
|
||||||
|
|
||||||
|
### 开发规范
|
||||||
|
- 遵循 PEP 8 Python 代码规范
|
||||||
|
- 添加适当的注释和文档字符串
|
||||||
|
- 编写单元测试(推荐)
|
||||||
|
- 确保向后兼容性
|
||||||
|
|
||||||
|
## 📄 许可证
|
||||||
|
|
||||||
|
本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件
|
||||||
|
|
||||||
|
## 👨💻 作者
|
||||||
|
|
||||||
|
- **项目维护者** - 负责项目开发和维护
|
||||||
|
|
||||||
|
## 🙏 致谢
|
||||||
|
|
||||||
|
- 感谢所有贡献者的支持
|
||||||
|
- 感谢开源社区提供的优秀库和工具
|
||||||
|
- 特别感谢用户反馈和建议
|
||||||
|
|
||||||
|
## 📞 联系方式
|
||||||
|
|
||||||
|
如有问题或建议,欢迎通过以下方式联系:
|
||||||
|
|
||||||
|
- 提交 Issue
|
||||||
|
- 邮件咨询
|
||||||
|
- 微信交流群
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**让文本转换变得简单而美好!** ✨
|
||||||
636
advanced_style_editor.py
Normal file
636
advanced_style_editor.py
Normal file
@ -0,0 +1,636 @@
|
|||||||
|
"""
|
||||||
|
高级样式编辑器模块
|
||||||
|
|
||||||
|
提供专业的样式编辑和实时预览功能,让用户能够创建和定制个性化的文档样式。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk, colorchooser, messagebox, simpledialog
|
||||||
|
from typing import Optional, Dict, Any
|
||||||
|
import copy
|
||||||
|
|
||||||
|
from style_manager import style_manager, DocumentStyle, FontStyle, ParagraphStyle
|
||||||
|
from config import Config
|
||||||
|
|
||||||
|
|
||||||
|
def open_advanced_editor(parent, style_name):
|
||||||
|
"""
|
||||||
|
打开高级样式编辑器
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parent: 父窗口
|
||||||
|
style_name: 要编辑的样式名称
|
||||||
|
"""
|
||||||
|
if not style_name:
|
||||||
|
messagebox.showwarning("警告", "请先选择一个样式")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 获取样式
|
||||||
|
original_style = style_manager.get_style(style_name)
|
||||||
|
if not original_style:
|
||||||
|
messagebox.showerror("错误", f"找不到样式 '{style_name}'")
|
||||||
|
return None
|
||||||
|
|
||||||
|
editor = AdvancedStyleEditor(parent, original_style)
|
||||||
|
return editor
|
||||||
|
|
||||||
|
|
||||||
|
class AdvancedStyleEditor:
|
||||||
|
"""
|
||||||
|
高级样式编辑器类
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent, original_style):
|
||||||
|
self.parent = parent
|
||||||
|
self.original_style = original_style
|
||||||
|
self.current_style = copy.deepcopy(original_style)
|
||||||
|
|
||||||
|
# 确保样式对象完整
|
||||||
|
self._ensure_style_completeness()
|
||||||
|
|
||||||
|
# 创建编辑窗口
|
||||||
|
self.window = tk.Toplevel(parent)
|
||||||
|
self.window.title(f'高级样式编辑 - {self.current_style.name}')
|
||||||
|
self.window.geometry('1000x700')
|
||||||
|
self.window.transient(parent)
|
||||||
|
self.window.grab_set()
|
||||||
|
|
||||||
|
# 样式变量
|
||||||
|
self.style_vars = {}
|
||||||
|
self.heading_vars = {}
|
||||||
|
|
||||||
|
self._create_interface()
|
||||||
|
self._bind_events()
|
||||||
|
self._update_preview()
|
||||||
|
|
||||||
|
def _ensure_style_completeness(self):
|
||||||
|
"""确保样式对象的完整性"""
|
||||||
|
if not self.current_style.body_font:
|
||||||
|
self.current_style.body_font = FontStyle()
|
||||||
|
if not self.current_style.body_paragraph:
|
||||||
|
self.current_style.body_paragraph = ParagraphStyle()
|
||||||
|
if not self.current_style.heading_styles:
|
||||||
|
self.current_style.heading_styles = {}
|
||||||
|
|
||||||
|
# 确保至少有3级标题 - 使用正确的HeadingStyle对象
|
||||||
|
from style_manager import HeadingStyle
|
||||||
|
for level in range(1, 4):
|
||||||
|
if level not in self.current_style.heading_styles:
|
||||||
|
font_size = max(20 - level * 2, 12)
|
||||||
|
self.current_style.heading_styles[level] = HeadingStyle(
|
||||||
|
font=FontStyle(name="微软雅黑", size=font_size, bold=True),
|
||||||
|
paragraph=ParagraphStyle(line_spacing=1.3, space_before=12, space_after=6),
|
||||||
|
outline_level=level
|
||||||
|
)
|
||||||
|
|
||||||
|
def _create_interface(self):
|
||||||
|
"""创建界面"""
|
||||||
|
# 创建主布局
|
||||||
|
main_frame = ttk.Frame(self.window)
|
||||||
|
main_frame.pack(fill='both', expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
|
# 左侧编辑区域
|
||||||
|
edit_frame = ttk.Frame(main_frame)
|
||||||
|
edit_frame.pack(side='left', fill='both', expand=True, padx=(0, 10))
|
||||||
|
|
||||||
|
# 右侧预览区域
|
||||||
|
preview_frame = ttk.LabelFrame(main_frame, text='实时预览', padding="10")
|
||||||
|
preview_frame.pack(side='right', fill='both', expand=True)
|
||||||
|
|
||||||
|
self._create_edit_area(edit_frame)
|
||||||
|
self._create_preview_area(preview_frame)
|
||||||
|
self._create_bottom_buttons()
|
||||||
|
|
||||||
|
def _create_edit_area(self, parent):
|
||||||
|
"""创建编辑区域"""
|
||||||
|
# 创建笔记本控件
|
||||||
|
notebook = ttk.Notebook(parent)
|
||||||
|
notebook.pack(fill='both', expand=True)
|
||||||
|
|
||||||
|
# 基本信息选项卡
|
||||||
|
basic_frame = ttk.Frame(notebook)
|
||||||
|
notebook.add(basic_frame, text='基本信息')
|
||||||
|
self._create_basic_tab(basic_frame)
|
||||||
|
|
||||||
|
# 正文样式选项卡
|
||||||
|
body_frame = ttk.Frame(notebook)
|
||||||
|
notebook.add(body_frame, text='正文样式')
|
||||||
|
self._create_body_tab(body_frame)
|
||||||
|
|
||||||
|
# 标题样式选项卡
|
||||||
|
heading_frame = ttk.Frame(notebook)
|
||||||
|
notebook.add(heading_frame, text='标题样式')
|
||||||
|
self._create_heading_tab(heading_frame)
|
||||||
|
|
||||||
|
def _create_basic_tab(self, parent):
|
||||||
|
"""创建基本信息选项卡"""
|
||||||
|
# 样式名称
|
||||||
|
name_frame = ttk.Frame(parent)
|
||||||
|
name_frame.pack(fill='x', padx=10, pady=5)
|
||||||
|
ttk.Label(name_frame, text='样式名称:', width=12).pack(side='left')
|
||||||
|
self.name_var = tk.StringVar(value=self.current_style.name)
|
||||||
|
name_entry = ttk.Entry(name_frame, textvariable=self.name_var)
|
||||||
|
name_entry.pack(side='left', fill='x', expand=True, padx=(5, 0))
|
||||||
|
|
||||||
|
# 样式描述
|
||||||
|
desc_frame = ttk.Frame(parent)
|
||||||
|
desc_frame.pack(fill='x', padx=10, pady=5)
|
||||||
|
ttk.Label(desc_frame, text='描述:', width=12).pack(side='left')
|
||||||
|
self.desc_var = tk.StringVar(value=self.current_style.description)
|
||||||
|
desc_entry = ttk.Entry(desc_frame, textvariable=self.desc_var)
|
||||||
|
desc_entry.pack(side='left', fill='x', expand=True, padx=(5, 0))
|
||||||
|
|
||||||
|
def _create_body_tab(self, parent):
|
||||||
|
"""创建正文样式选项卡"""
|
||||||
|
# 正文字体设置
|
||||||
|
font_frame = ttk.LabelFrame(parent, text='正文字体', padding="10")
|
||||||
|
font_frame.pack(fill='x', padx=10, pady=5)
|
||||||
|
|
||||||
|
# 字体名称
|
||||||
|
name_frame = ttk.Frame(font_frame)
|
||||||
|
name_frame.pack(fill='x', pady=2)
|
||||||
|
ttk.Label(name_frame, text='字体:', width=10).pack(side='left')
|
||||||
|
self.style_vars['font_name'] = tk.StringVar(value=self.current_style.body_font.name)
|
||||||
|
font_combo = ttk.Combobox(name_frame, textvariable=self.style_vars['font_name'], width=15,
|
||||||
|
values=['宋体', '微软雅黑', '黑体', '楷体', '华文细黑', 'Arial', 'Times New Roman'])
|
||||||
|
font_combo.pack(side='left', padx=(5, 10))
|
||||||
|
|
||||||
|
# 字号
|
||||||
|
ttk.Label(name_frame, text='大小:', width=5).pack(side='left')
|
||||||
|
self.style_vars['font_size'] = tk.IntVar(value=self.current_style.body_font.size)
|
||||||
|
size_spin = ttk.Spinbox(name_frame, from_=8, to=72, textvariable=self.style_vars['font_size'], width=5)
|
||||||
|
size_spin.pack(side='left', padx=(5, 0))
|
||||||
|
|
||||||
|
# 字体样式
|
||||||
|
style_frame = ttk.Frame(font_frame)
|
||||||
|
style_frame.pack(fill='x', pady=2)
|
||||||
|
|
||||||
|
self.style_vars['font_bold'] = tk.BooleanVar(value=self.current_style.body_font.bold)
|
||||||
|
ttk.Checkbutton(style_frame, text='粗体', variable=self.style_vars['font_bold']).pack(side='left', padx=(0, 10))
|
||||||
|
|
||||||
|
self.style_vars['font_italic'] = tk.BooleanVar(value=self.current_style.body_font.italic)
|
||||||
|
ttk.Checkbutton(style_frame, text='斜体', variable=self.style_vars['font_italic']).pack(side='left', padx=(0, 10))
|
||||||
|
|
||||||
|
# 字体颜色
|
||||||
|
ttk.Label(style_frame, text='颜色:', width=5).pack(side='left')
|
||||||
|
self.style_vars['font_color'] = tk.StringVar(value=self.current_style.body_font.color)
|
||||||
|
self.color_label = tk.Label(style_frame, text=' ', bg=self.style_vars['font_color'].get(), relief='solid', width=3)
|
||||||
|
self.color_label.pack(side='left', padx=(5, 5))
|
||||||
|
|
||||||
|
ttk.Button(style_frame, text='选择', command=self._choose_font_color).pack(side='left')
|
||||||
|
|
||||||
|
# 正文段落设置
|
||||||
|
para_frame = ttk.LabelFrame(parent, text='正文段落', padding="10")
|
||||||
|
para_frame.pack(fill='x', padx=10, pady=5)
|
||||||
|
|
||||||
|
# 行距
|
||||||
|
spacing_frame = ttk.Frame(para_frame)
|
||||||
|
spacing_frame.pack(fill='x', pady=2)
|
||||||
|
ttk.Label(spacing_frame, text='行距:', width=10).pack(side='left')
|
||||||
|
self.style_vars['line_spacing'] = tk.DoubleVar(value=self.current_style.body_paragraph.line_spacing)
|
||||||
|
line_spacing_spin = ttk.Spinbox(spacing_frame, from_=0.5, to=3.0, increment=0.1,
|
||||||
|
textvariable=self.style_vars['line_spacing'], width=8)
|
||||||
|
line_spacing_spin.pack(side='left', padx=(5, 10))
|
||||||
|
|
||||||
|
# 首行缩进
|
||||||
|
ttk.Label(spacing_frame, text='首行缩进:', width=10).pack(side='left')
|
||||||
|
self.style_vars['indent'] = tk.DoubleVar(value=self.current_style.body_paragraph.first_line_indent)
|
||||||
|
indent_spin = ttk.Spinbox(spacing_frame, from_=0.0, to=10.0, increment=0.5,
|
||||||
|
textvariable=self.style_vars['indent'], width=8)
|
||||||
|
indent_spin.pack(side='left', padx=(5, 0))
|
||||||
|
|
||||||
|
# 段间距
|
||||||
|
margin_frame = ttk.Frame(para_frame)
|
||||||
|
margin_frame.pack(fill='x', pady=2)
|
||||||
|
ttk.Label(margin_frame, text='段前:', width=10).pack(side='left')
|
||||||
|
self.style_vars['space_before'] = tk.IntVar(value=self.current_style.body_paragraph.space_before)
|
||||||
|
before_spin = ttk.Spinbox(margin_frame, from_=0, to=50, textvariable=self.style_vars['space_before'], width=8)
|
||||||
|
before_spin.pack(side='left', padx=(5, 10))
|
||||||
|
|
||||||
|
ttk.Label(margin_frame, text='段后:', width=10).pack(side='left')
|
||||||
|
self.style_vars['space_after'] = tk.IntVar(value=self.current_style.body_paragraph.space_after)
|
||||||
|
after_spin = ttk.Spinbox(margin_frame, from_=0, to=50, textvariable=self.style_vars['space_after'], width=8)
|
||||||
|
after_spin.pack(side='left', padx=(5, 0))
|
||||||
|
|
||||||
|
def _create_heading_tab(self, parent):
|
||||||
|
"""创建标题样式选项卡"""
|
||||||
|
# 创建滚动区域
|
||||||
|
canvas = tk.Canvas(parent)
|
||||||
|
scrollbar = ttk.Scrollbar(parent, orient="vertical", command=canvas.yview)
|
||||||
|
scrollable_frame = ttk.Frame(canvas)
|
||||||
|
|
||||||
|
scrollable_frame.bind(
|
||||||
|
"<Configure>",
|
||||||
|
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
||||||
|
)
|
||||||
|
|
||||||
|
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
||||||
|
canvas.configure(yscrollcommand=scrollbar.set)
|
||||||
|
|
||||||
|
# 为每个标题级别创建控件
|
||||||
|
for level in range(1, 4):
|
||||||
|
level_frame = ttk.LabelFrame(scrollable_frame, text=f'{level}级标题', padding="10")
|
||||||
|
level_frame.pack(fill='x', padx=10, pady=5)
|
||||||
|
|
||||||
|
heading_style = self.current_style.heading_styles.get(level)
|
||||||
|
if not heading_style:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 标题字体名称和大小
|
||||||
|
h_name_frame = ttk.Frame(level_frame)
|
||||||
|
h_name_frame.pack(fill='x', pady=2)
|
||||||
|
ttk.Label(h_name_frame, text='字体:', width=8).pack(side='left')
|
||||||
|
h_name_var = tk.StringVar(value=heading_style.font.name)
|
||||||
|
h_combo = ttk.Combobox(h_name_frame, textvariable=h_name_var, width=12,
|
||||||
|
values=['宋体', '微软雅黑', '黑体', '楷体'])
|
||||||
|
h_combo.pack(side='left', padx=(5, 10))
|
||||||
|
|
||||||
|
ttk.Label(h_name_frame, text='大小:', width=5).pack(side='left')
|
||||||
|
h_size_var = tk.IntVar(value=heading_style.font.size)
|
||||||
|
h_size_spin = ttk.Spinbox(h_name_frame, from_=8, to=72, textvariable=h_size_var, width=5)
|
||||||
|
h_size_spin.pack(side='left', padx=(5, 0))
|
||||||
|
|
||||||
|
# 标题颜色
|
||||||
|
h_color_frame = ttk.Frame(level_frame)
|
||||||
|
h_color_frame.pack(fill='x', pady=2)
|
||||||
|
ttk.Label(h_color_frame, text='颜色:', width=8).pack(side='left')
|
||||||
|
h_color_var = tk.StringVar(value=heading_style.font.color)
|
||||||
|
h_color_label = tk.Label(h_color_frame, text=' ', bg=h_color_var.get(), relief='solid', width=3)
|
||||||
|
h_color_label.pack(side='left', padx=(5, 5))
|
||||||
|
|
||||||
|
def make_choose_heading_color(var, label):
|
||||||
|
def choose_color():
|
||||||
|
color = colorchooser.askcolor(color=var.get())[1]
|
||||||
|
if color:
|
||||||
|
var.set(color)
|
||||||
|
label.config(bg=color)
|
||||||
|
self._update_preview()
|
||||||
|
return choose_color
|
||||||
|
|
||||||
|
ttk.Button(h_color_frame, text='选择',
|
||||||
|
command=make_choose_heading_color(h_color_var, h_color_label)).pack(side='left')
|
||||||
|
|
||||||
|
self.heading_vars[level] = {
|
||||||
|
'name': h_name_var,
|
||||||
|
'size': h_size_var,
|
||||||
|
'color': h_color_var
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.pack(side="left", fill="both", expand=True)
|
||||||
|
scrollbar.pack(side="right", fill="y")
|
||||||
|
|
||||||
|
def _create_preview_area(self, parent):
|
||||||
|
"""创建预览区域"""
|
||||||
|
# 创建预览容器
|
||||||
|
preview_container = ttk.Frame(parent)
|
||||||
|
preview_container.pack(fill='both', expand=True)
|
||||||
|
|
||||||
|
# 预览模式选择
|
||||||
|
mode_frame = ttk.Frame(preview_container)
|
||||||
|
mode_frame.pack(fill='x', pady=(0, 5))
|
||||||
|
|
||||||
|
self.preview_mode = tk.StringVar(value="rich")
|
||||||
|
ttk.Radiobutton(mode_frame, text="丰富预览", variable=self.preview_mode,
|
||||||
|
value="rich", command=self._update_preview).pack(side='left')
|
||||||
|
ttk.Radiobutton(mode_frame, text="文本预览", variable=self.preview_mode,
|
||||||
|
value="text", command=self._update_preview).pack(side='left', padx=(10, 0))
|
||||||
|
|
||||||
|
# 预览文本区域
|
||||||
|
text_frame = ttk.Frame(preview_container)
|
||||||
|
text_frame.pack(fill='both', expand=True)
|
||||||
|
|
||||||
|
self.preview_text = tk.Text(text_frame, wrap=tk.WORD, state='disabled',
|
||||||
|
font=('微软雅黑', 12), bg='white')
|
||||||
|
self.preview_text.pack(side='left', fill='both', expand=True)
|
||||||
|
|
||||||
|
# 滚动条
|
||||||
|
preview_scroll = ttk.Scrollbar(text_frame, orient="vertical", command=self.preview_text.yview)
|
||||||
|
preview_scroll.pack(side="right", fill="y")
|
||||||
|
self.preview_text.configure(yscrollcommand=preview_scroll.set)
|
||||||
|
|
||||||
|
# 初始化文本标签
|
||||||
|
self._init_text_tags()
|
||||||
|
|
||||||
|
def _create_bottom_buttons(self):
|
||||||
|
"""创建底部按钮"""
|
||||||
|
button_frame = ttk.Frame(self.window)
|
||||||
|
button_frame.pack(fill='x', padx=10, pady=10)
|
||||||
|
|
||||||
|
ttk.Button(button_frame, text='保存样式', command=self._save_style).pack(side='left', padx=5)
|
||||||
|
ttk.Button(button_frame, text='另存为', command=self._save_as_style).pack(side='left', padx=5)
|
||||||
|
ttk.Button(button_frame, text='重置', command=self._reset_style).pack(side='left', padx=5)
|
||||||
|
|
||||||
|
ttk.Button(button_frame, text='关闭', command=self.window.destroy).pack(side='right', padx=5)
|
||||||
|
ttk.Button(button_frame, text='应用并关闭', command=self._apply_and_close).pack(side='right', padx=5)
|
||||||
|
|
||||||
|
def _choose_font_color(self):
|
||||||
|
"""选择字体颜色"""
|
||||||
|
color = colorchooser.askcolor(color=self.style_vars['font_color'].get())[1]
|
||||||
|
if color:
|
||||||
|
self.style_vars['font_color'].set(color)
|
||||||
|
self.color_label.config(bg=color)
|
||||||
|
self._update_preview() # 立即更新预览
|
||||||
|
|
||||||
|
def _bind_events(self):
|
||||||
|
"""绑定事件"""
|
||||||
|
# 绑定所有变量的变化事件
|
||||||
|
for var in self.style_vars.values():
|
||||||
|
if hasattr(var, 'trace'):
|
||||||
|
var.trace('w', lambda *args: self._update_preview())
|
||||||
|
|
||||||
|
for level_vars in self.heading_vars.values():
|
||||||
|
for var in level_vars.values():
|
||||||
|
if hasattr(var, 'trace'):
|
||||||
|
var.trace('w', lambda *args: self._update_preview())
|
||||||
|
|
||||||
|
# 基本信息变量
|
||||||
|
self.name_var.trace('w', lambda *args: self._update_preview())
|
||||||
|
self.desc_var.trace('w', lambda *args: self._update_preview())
|
||||||
|
|
||||||
|
# 预览模式变化事件(在_create_preview_area中已绑定)
|
||||||
|
|
||||||
|
def _update_current_style(self):
|
||||||
|
"""更新当前样式对象"""
|
||||||
|
# 更新基本信息
|
||||||
|
self.current_style.name = self.name_var.get()
|
||||||
|
self.current_style.description = self.desc_var.get()
|
||||||
|
|
||||||
|
# 更新正文字体
|
||||||
|
self.current_style.body_font.name = self.style_vars['font_name'].get()
|
||||||
|
self.current_style.body_font.size = self.style_vars['font_size'].get()
|
||||||
|
self.current_style.body_font.bold = self.style_vars['font_bold'].get()
|
||||||
|
self.current_style.body_font.italic = self.style_vars['font_italic'].get()
|
||||||
|
self.current_style.body_font.color = self.style_vars['font_color'].get()
|
||||||
|
|
||||||
|
# 更新正文段落
|
||||||
|
self.current_style.body_paragraph.line_spacing = self.style_vars['line_spacing'].get()
|
||||||
|
self.current_style.body_paragraph.first_line_indent = self.style_vars['indent'].get()
|
||||||
|
self.current_style.body_paragraph.space_before = self.style_vars['space_before'].get()
|
||||||
|
self.current_style.body_paragraph.space_after = self.style_vars['space_after'].get()
|
||||||
|
|
||||||
|
# 更新标题样式
|
||||||
|
for level, vars_dict in self.heading_vars.items():
|
||||||
|
if level in self.current_style.heading_styles:
|
||||||
|
heading_style = self.current_style.heading_styles[level]
|
||||||
|
# 现在heading_style应该始终是HeadingStyle对象
|
||||||
|
heading_style.font.name = vars_dict['name'].get()
|
||||||
|
heading_style.font.size = vars_dict['size'].get()
|
||||||
|
heading_style.font.color = vars_dict['color'].get()
|
||||||
|
|
||||||
|
def _init_text_tags(self):
|
||||||
|
"""初始化文本标签样式"""
|
||||||
|
# 配置各种文本标签
|
||||||
|
self.preview_text.tag_configure('title', font=('微软雅黑', 14, 'bold'), foreground='#2E86AB')
|
||||||
|
self.preview_text.tag_configure('separator', foreground='#666666')
|
||||||
|
self.preview_text.tag_configure('heading1', font=('黑体', 18, 'bold'), foreground='#E74C3C')
|
||||||
|
self.preview_text.tag_configure('heading2', font=('微软雅黑', 16, 'bold'), foreground='#3498DB')
|
||||||
|
self.preview_text.tag_configure('heading3', font=('微软雅黑', 14, 'bold'), foreground='#2ECC71')
|
||||||
|
self.preview_text.tag_configure('body', font=('宋体', 12), foreground='#333333')
|
||||||
|
self.preview_text.tag_configure('bold', font=('微软雅黑', 12, 'bold'))
|
||||||
|
self.preview_text.tag_configure('italic', font=('微软雅黑', 12, 'italic'))
|
||||||
|
self.preview_text.tag_configure('quote', font=('楷体', 11, 'italic'), foreground='#7F8C8D',
|
||||||
|
background='#F8F9FA')
|
||||||
|
self.preview_text.tag_configure('code', font=('Courier New', 10),
|
||||||
|
background='#F4F4F4', foreground='#C0392B')
|
||||||
|
self.preview_text.tag_configure('info', font=('微软雅黑', 10), foreground='#95A5A6')
|
||||||
|
|
||||||
|
def _update_preview(self):
|
||||||
|
"""更新预览"""
|
||||||
|
self._update_current_style()
|
||||||
|
|
||||||
|
# 清空预览区域
|
||||||
|
self.preview_text.config(state='normal')
|
||||||
|
self.preview_text.delete(1.0, tk.END)
|
||||||
|
|
||||||
|
if self.preview_mode.get() == "rich":
|
||||||
|
self._create_rich_preview()
|
||||||
|
else:
|
||||||
|
self._create_text_preview()
|
||||||
|
|
||||||
|
self.preview_text.config(state='disabled')
|
||||||
|
|
||||||
|
def _create_rich_preview(self):
|
||||||
|
"""创建丰富的样式预览"""
|
||||||
|
# 样式标题
|
||||||
|
self.preview_text.insert(tk.END, f"样式预览:{self.current_style.name}\n", 'title')
|
||||||
|
self.preview_text.insert(tk.END, f"描述:{self.current_style.description}\n\n", 'info')
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
self.preview_text.insert(tk.END, "═" * 50 + "\n\n", 'separator')
|
||||||
|
|
||||||
|
# 动态更新标题样式标签
|
||||||
|
self._update_heading_tags()
|
||||||
|
|
||||||
|
# 一级标题预览
|
||||||
|
self.preview_text.insert(tk.END, "一级标题样式预览\n\n", 'heading1_live')
|
||||||
|
|
||||||
|
# 二级标题预览
|
||||||
|
self.preview_text.insert(tk.END, "二级标题样式预览\n\n", 'heading2_live')
|
||||||
|
|
||||||
|
# 三级标题预览
|
||||||
|
self.preview_text.insert(tk.END, "三级标题样式预览\n\n", 'heading3_live')
|
||||||
|
|
||||||
|
# 动态更新正文样式标签
|
||||||
|
self._update_body_tag()
|
||||||
|
|
||||||
|
# 正文段落预览
|
||||||
|
body_text = f"""正文段落样式预览:
|
||||||
|
这是正文内容的示例,展示当前选择的字体和格式效果。
|
||||||
|
|
||||||
|
字体:{self.current_style.body_font.name} {self.current_style.body_font.size}pt
|
||||||
|
行距:{self.current_style.body_paragraph.line_spacing}倍
|
||||||
|
首行缩进:{self.current_style.body_paragraph.first_line_indent}字符
|
||||||
|
段前间距:{self.current_style.body_paragraph.space_before}pt
|
||||||
|
段后间距:{self.current_style.body_paragraph.space_after}pt
|
||||||
|
|
||||||
|
这是包含"""
|
||||||
|
|
||||||
|
self.preview_text.insert(tk.END, body_text, 'body_live')
|
||||||
|
|
||||||
|
# 粗体和斜体示例
|
||||||
|
self.preview_text.insert(tk.END, "粗体文字", 'bold_live')
|
||||||
|
self.preview_text.insert(tk.END, "和", 'body_live')
|
||||||
|
self.preview_text.insert(tk.END, "斜体文字", 'italic_live')
|
||||||
|
self.preview_text.insert(tk.END, "的段落示例。\n\n", 'body_live')
|
||||||
|
|
||||||
|
# 引用块示例
|
||||||
|
self.preview_text.insert(tk.END, "引用块样式预览:\n", 'body_live')
|
||||||
|
self.preview_text.insert(tk.END, " 这是引用块的示例内容,展示引用文字的特殊格式效果。\n\n", 'quote')
|
||||||
|
|
||||||
|
# 代码块示例
|
||||||
|
self.preview_text.insert(tk.END, "代码块样式预览:\n", 'body_live')
|
||||||
|
self.preview_text.insert(tk.END, ' print("Hello, World!")\n # 这是代码块的示例\n\n', 'code')
|
||||||
|
|
||||||
|
# 列表示例
|
||||||
|
self.preview_text.insert(tk.END, "列表样式预览:\n", 'body_live')
|
||||||
|
self.preview_text.insert(tk.END, "• 无序列表项目1\n• 无序列表项目2\n• 无序列表项目3\n\n", 'body_live')
|
||||||
|
self.preview_text.insert(tk.END, "1. 有序列表项目1\n2. 有序列表项目2\n3. 有序列表项目3\n\n", 'body_live')
|
||||||
|
|
||||||
|
# 样式详情
|
||||||
|
details = f"""当前样式设置详情:
|
||||||
|
• 字体名称:{self.current_style.body_font.name}
|
||||||
|
• 字体大小:{self.current_style.body_font.size}pt
|
||||||
|
• 字体颜色:{self.current_style.body_font.color}
|
||||||
|
• 是否粗体:{'是' if self.current_style.body_font.bold else '否'}
|
||||||
|
• 是否斜体:{'是' if self.current_style.body_font.italic else '否'}
|
||||||
|
• 行距:{self.current_style.body_paragraph.line_spacing}倍
|
||||||
|
• 首行缩进:{self.current_style.body_paragraph.first_line_indent}字符"""
|
||||||
|
|
||||||
|
self.preview_text.insert(tk.END, details, 'info')
|
||||||
|
|
||||||
|
def _create_text_preview(self):
|
||||||
|
"""创建简单文本预览"""
|
||||||
|
preview_content = f"""样式预览:{self.current_style.name}
|
||||||
|
描述:{self.current_style.description}
|
||||||
|
|
||||||
|
─────────────────────────────
|
||||||
|
|
||||||
|
# 一级标题样式预览
|
||||||
|
|
||||||
|
## 二级标题样式预览
|
||||||
|
|
||||||
|
### 三级标题样式预览
|
||||||
|
|
||||||
|
正文段落样式预览:
|
||||||
|
这是正文内容的示例。使用{self.current_style.body_font.name} {self.current_style.body_font.size}pt字体,
|
||||||
|
行距为{self.current_style.body_paragraph.line_spacing}倍,首行缩进{self.current_style.body_paragraph.first_line_indent}字符。
|
||||||
|
段前间距{self.current_style.body_paragraph.space_before}pt,段后间距{self.current_style.body_paragraph.space_after}pt。
|
||||||
|
|
||||||
|
这是包含**粗体文字**和*斜体文字*的段落,用于展示内联样式效果。
|
||||||
|
|
||||||
|
引用块样式预览:
|
||||||
|
> 这是引用块的示例内容,展示引用文字的特殊格式效果。
|
||||||
|
|
||||||
|
代码块样式预览:
|
||||||
|
```
|
||||||
|
print("Hello, World!")
|
||||||
|
# 这是代码块的示例
|
||||||
|
```
|
||||||
|
|
||||||
|
列表样式预览:
|
||||||
|
• 无序列表项目1
|
||||||
|
• 无序列表项目2
|
||||||
|
• 无序列表项目3
|
||||||
|
|
||||||
|
1. 有序列表项目1
|
||||||
|
2. 有序列表项目2
|
||||||
|
3. 有序列表项目3
|
||||||
|
|
||||||
|
字体设置详情:
|
||||||
|
• 字体名称:{self.current_style.body_font.name}
|
||||||
|
• 字体大小:{self.current_style.body_font.size}pt
|
||||||
|
• 字体颜色:{self.current_style.body_font.color}
|
||||||
|
• 粗体:{'是' if self.current_style.body_font.bold else '否'}
|
||||||
|
• 斜体:{'是' if self.current_style.body_font.italic else '否'}
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.preview_text.insert(tk.END, preview_content)
|
||||||
|
|
||||||
|
def _update_heading_tags(self):
|
||||||
|
"""动态更新标题样式标签"""
|
||||||
|
for level in range(1, 4):
|
||||||
|
tag_name = f'heading{level}_live'
|
||||||
|
if level in self.current_style.heading_styles:
|
||||||
|
heading_style = self.current_style.heading_styles[level]
|
||||||
|
font_name = heading_style.font.name
|
||||||
|
font_size = heading_style.font.size
|
||||||
|
font_weight = 'bold' if heading_style.font.bold else 'normal'
|
||||||
|
font_color = heading_style.font.color
|
||||||
|
|
||||||
|
self.preview_text.tag_configure(tag_name,
|
||||||
|
font=(font_name, font_size, font_weight),
|
||||||
|
foreground=font_color)
|
||||||
|
|
||||||
|
def _update_body_tag(self):
|
||||||
|
"""动态更新正文样式标签"""
|
||||||
|
font_name = self.current_style.body_font.name
|
||||||
|
font_size = self.current_style.body_font.size
|
||||||
|
font_weight = 'bold' if self.current_style.body_font.bold else 'normal'
|
||||||
|
font_slant = 'italic' if self.current_style.body_font.italic else 'roman'
|
||||||
|
font_color = self.current_style.body_font.color
|
||||||
|
|
||||||
|
# 更新正文标签
|
||||||
|
self.preview_text.tag_configure('body_live',
|
||||||
|
font=(font_name, font_size, font_weight, font_slant),
|
||||||
|
foreground=font_color)
|
||||||
|
|
||||||
|
# 更新粗体标签
|
||||||
|
self.preview_text.tag_configure('bold_live',
|
||||||
|
font=(font_name, font_size, 'bold', font_slant),
|
||||||
|
foreground=font_color)
|
||||||
|
|
||||||
|
# 更新斜体标签
|
||||||
|
self.preview_text.tag_configure('italic_live',
|
||||||
|
font=(font_name, font_size, font_weight, 'italic'),
|
||||||
|
foreground=font_color)
|
||||||
|
|
||||||
|
def _save_style(self):
|
||||||
|
"""保存样式"""
|
||||||
|
try:
|
||||||
|
if not self.current_style.name.strip():
|
||||||
|
messagebox.showerror("错误", "请输入样式名称")
|
||||||
|
return
|
||||||
|
|
||||||
|
self._update_current_style()
|
||||||
|
|
||||||
|
# 尝试更新现有样式,如果失败则创建新样式
|
||||||
|
if self.current_style.name in style_manager.custom_styles:
|
||||||
|
success = style_manager.update_custom_style(self.current_style)
|
||||||
|
else:
|
||||||
|
success = style_manager.create_custom_style(self.current_style)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
messagebox.showinfo("成功", f"样式 '{self.current_style.name}' 保存成功")
|
||||||
|
config.current_style = self.current_style.name
|
||||||
|
else:
|
||||||
|
if self.current_style.name in style_manager.builtin_styles:
|
||||||
|
messagebox.showerror("错误", "不能覆盖内置样式!请使用不同的样式名称或'另存为'功能。")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("保存失败", "保存样式失败!请检查文件权限或磁盘空间。")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("错误", f"保存过程中发生异常: {str(e)}")
|
||||||
|
|
||||||
|
def _save_as_style(self):
|
||||||
|
"""另存为新样式"""
|
||||||
|
try:
|
||||||
|
new_name = simpledialog.askstring("另存为", "请输入新样式名称:")
|
||||||
|
if not new_name:
|
||||||
|
return
|
||||||
|
|
||||||
|
if new_name in style_manager.get_style_names():
|
||||||
|
if new_name in style_manager.builtin_styles:
|
||||||
|
messagebox.showerror("名称冲突", "不能使用内置样式名称!请使用不同的样式名称。")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("名称已存在", f"样式名称 '{new_name}' 已经存在!请使用不同的名称。")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 修改样式信息
|
||||||
|
self.current_style.name = new_name
|
||||||
|
self.current_style.description = "基于 " + self.original_style.name + " 定制"
|
||||||
|
self._update_current_style()
|
||||||
|
|
||||||
|
if style_manager.create_custom_style(self.current_style):
|
||||||
|
messagebox.showinfo("创建成功", f"样式 '{new_name}' 已成功创建!")
|
||||||
|
config_manager = Config()
|
||||||
|
config_manager.set('current_style', new_name)
|
||||||
|
self.window.destroy()
|
||||||
|
else:
|
||||||
|
messagebox.showerror("创建失败", "创建样式失败!请检查文件权限或磁盘空间。")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("错误", f"另存为过程中发生异常: {str(e)}")
|
||||||
|
|
||||||
|
def _reset_style(self):
|
||||||
|
"""重置样式"""
|
||||||
|
if messagebox.askyesno("确认重置", "确定要重置为原始样式吗?"):
|
||||||
|
self.current_style = copy.deepcopy(self.original_style)
|
||||||
|
self._ensure_style_completeness()
|
||||||
|
# 重新创建界面(简单的重置方法)
|
||||||
|
self.window.destroy()
|
||||||
|
self.__init__(self.parent, self.original_style)
|
||||||
|
|
||||||
|
def _apply_and_close(self):
|
||||||
|
"""应用并关闭"""
|
||||||
|
self._save_style()
|
||||||
|
self.window.destroy()
|
||||||
5817
build/main/Analysis-00.toc
Normal file
5817
build/main/Analysis-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
3066
build/main/EXE-00.toc
Normal file
3066
build/main/EXE-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
3044
build/main/PKG-00.toc
Normal file
3044
build/main/PKG-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
build/main/PYZ-00.pyz
Normal file
BIN
build/main/PYZ-00.pyz
Normal file
Binary file not shown.
2473
build/main/PYZ-00.toc
Normal file
2473
build/main/PYZ-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
build/main/base_library.zip
Normal file
BIN
build/main/base_library.zip
Normal file
Binary file not shown.
BIN
build/main/main.pkg
Normal file
BIN
build/main/main.pkg
Normal file
Binary file not shown.
264
build/main/warn-main.txt
Normal file
264
build/main/warn-main.txt
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
|
||||||
|
This file lists modules PyInstaller was not able to find. This does not
|
||||||
|
necessarily mean this module is required for running your program. Python and
|
||||||
|
Python 3rd-party packages include a lot of conditional or optional modules. For
|
||||||
|
example the module 'ntpath' only exists on Windows, whereas the module
|
||||||
|
'posixpath' only exists on Posix systems.
|
||||||
|
|
||||||
|
Types if import:
|
||||||
|
* top-level: imported at the top-level - look at these first
|
||||||
|
* conditional: imported within an if-statement
|
||||||
|
* delayed: imported within a function
|
||||||
|
* optional: imported within a try-except-statement
|
||||||
|
|
||||||
|
IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for
|
||||||
|
tracking down the missing module yourself. Thanks!
|
||||||
|
|
||||||
|
missing module named pyimod02_importers - imported by D:\IDE\Python\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed), D:\IDE\Python\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgres.py (delayed)
|
||||||
|
missing module named pwd - imported by posixpath (delayed, conditional), subprocess (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), distutils.util (delayed, conditional, optional), netrc (delayed, conditional), getpass (delayed), setuptools._vendor.backports.tarfile (optional), distutils.archive_util (optional), http.server (delayed, optional), webbrowser (delayed), psutil (optional), setuptools._distutils.util (delayed, conditional, optional), setuptools._distutils.archive_util (optional)
|
||||||
|
missing module named grp - imported by subprocess (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), setuptools._vendor.backports.tarfile (optional), distutils.archive_util (optional), setuptools._distutils.archive_util (optional)
|
||||||
|
missing module named _posixsubprocess - imported by subprocess (optional), multiprocessing.util (delayed)
|
||||||
|
missing module named fcntl - imported by subprocess (optional)
|
||||||
|
missing module named 'org.python' - imported by copy (optional), xml.sax (delayed, conditional)
|
||||||
|
missing module named org - imported by pickle (optional)
|
||||||
|
missing module named urllib.urlopen - imported by urllib (delayed, optional), lxml.html (delayed, optional)
|
||||||
|
missing module named urllib.urlencode - imported by urllib (delayed, optional), lxml.html (delayed, optional)
|
||||||
|
missing module named posix - imported by os (conditional, optional), shutil (conditional), importlib._bootstrap_external (conditional)
|
||||||
|
missing module named resource - imported by posix (top-level)
|
||||||
|
missing module named _manylinux - imported by packaging._manylinux (delayed, optional), setuptools._vendor.packaging._manylinux (delayed, optional), setuptools._vendor.wheel.vendored.packaging._manylinux (delayed, optional)
|
||||||
|
missing module named _posixshmem - imported by multiprocessing.resource_tracker (conditional), multiprocessing.shared_memory (conditional)
|
||||||
|
missing module named multiprocessing.set_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
|
||||||
|
missing module named multiprocessing.get_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
|
||||||
|
missing module named pep517 - imported by importlib.metadata (delayed)
|
||||||
|
missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level)
|
||||||
|
missing module named multiprocessing.get_context - imported by multiprocessing (top-level), multiprocessing.pool (top-level), multiprocessing.managers (top-level), multiprocessing.sharedctypes (top-level)
|
||||||
|
missing module named multiprocessing.TimeoutError - imported by multiprocessing (top-level), multiprocessing.pool (top-level)
|
||||||
|
missing module named _scproxy - imported by urllib.request (conditional)
|
||||||
|
missing module named termios - imported by getpass (optional), tty (top-level)
|
||||||
|
missing module named 'java.lang' - imported by platform (delayed, optional), xml.sax._exceptions (conditional)
|
||||||
|
missing module named multiprocessing.BufferTooShort - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
|
||||||
|
missing module named multiprocessing.AuthenticationError - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
|
||||||
|
missing module named asyncio.DefaultEventLoopPolicy - imported by asyncio (delayed, conditional), asyncio.events (delayed, conditional)
|
||||||
|
missing module named '_typeshed.importlib' - imported by pkg_resources (conditional)
|
||||||
|
missing module named _typeshed - imported by pkg_resources (conditional), setuptools.glob (conditional), setuptools.compat.py311 (conditional), numpy.random.bit_generator (top-level), setuptools._distutils.dist (conditional)
|
||||||
|
missing module named jnius - imported by setuptools._vendor.platformdirs.android (delayed, conditional, optional)
|
||||||
|
missing module named android - imported by setuptools._vendor.platformdirs.android (delayed, conditional, optional)
|
||||||
|
missing module named 'distutils._modified' - imported by setuptools._distutils.file_util (delayed)
|
||||||
|
missing module named 'distutils._log' - imported by setuptools._distutils.command.bdist_dumb (top-level), setuptools._distutils.command.bdist_rpm (top-level), setuptools._distutils.command.build_clib (top-level), setuptools._distutils.command.build_ext (top-level), setuptools._distutils.command.build_py (top-level), setuptools._distutils.command.build_scripts (top-level), setuptools._distutils.command.clean (top-level), setuptools._distutils.command.config (top-level), setuptools._distutils.command.install (top-level), setuptools._distutils.command.install_scripts (top-level), setuptools._distutils.command.sdist (top-level)
|
||||||
|
missing module named usercustomize - imported by site (delayed, optional)
|
||||||
|
missing module named sitecustomize - imported by site (delayed, optional)
|
||||||
|
missing module named readline - imported by site (delayed, optional), rlcompleter (optional), cmd (delayed, conditional, optional), code (delayed, conditional, optional), pdb (delayed, optional)
|
||||||
|
missing module named 'typing.io' - imported by importlib.resources (top-level)
|
||||||
|
missing module named trove_classifiers - imported by setuptools.config._validate_pyproject.formats (optional)
|
||||||
|
missing module named tomllib - imported by setuptools.compat.py310 (conditional)
|
||||||
|
missing module named importlib_resources - imported by setuptools._vendor.jaraco.text (optional)
|
||||||
|
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level)
|
||||||
|
missing module named vms_lib - imported by platform (delayed, optional)
|
||||||
|
missing module named java - imported by platform (delayed)
|
||||||
|
missing module named _winreg - imported by platform (delayed, optional)
|
||||||
|
missing module named collections.Callable - imported by collections (optional), cffi.api (optional), bs4.element (optional), bs4.builder._lxml (optional)
|
||||||
|
missing module named htmlentitydefs - imported by lxml.html.soupparser (optional)
|
||||||
|
missing module named BeautifulSoup - imported by lxml.html.soupparser (optional)
|
||||||
|
missing module named cchardet - imported by bs4.dammit (optional)
|
||||||
|
missing module named bs4.builder.HTMLParserTreeBuilder - imported by bs4.builder (top-level), bs4 (top-level)
|
||||||
|
missing module named 'html5lib.treebuilders' - imported by bs4.builder._html5lib (optional), lxml.html._html5builder (top-level), lxml.html.html5parser (top-level)
|
||||||
|
missing module named 'html5lib.constants' - imported by bs4.builder._html5lib (top-level)
|
||||||
|
missing module named html5lib - imported by bs4.builder._html5lib (top-level), lxml.html.html5parser (top-level)
|
||||||
|
missing module named urlparse - imported by lxml.ElementInclude (optional), lxml.html.html5parser (optional)
|
||||||
|
missing module named urllib2 - imported by lxml.ElementInclude (optional), lxml.html.html5parser (optional)
|
||||||
|
missing module named 'cython.cimports' - imported by lxml.html.diff (optional)
|
||||||
|
missing module named cython - imported by lxml.html.diff (optional), lxml.html._difflib (optional)
|
||||||
|
missing module named lxml_html_clean - imported by lxml.html.clean (optional)
|
||||||
|
missing module named cssselect - imported by lxml.cssselect (optional)
|
||||||
|
missing module named olefile - imported by PIL.FpxImagePlugin (top-level), PIL.MicImagePlugin (top-level)
|
||||||
|
missing module named _dummy_thread - imported by cffi.lock (conditional, optional), numpy._core.arrayprint (optional)
|
||||||
|
missing module named 'numpy_distutils.cpuinfo' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
|
||||||
|
missing module named 'numpy_distutils.fcompiler' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
|
||||||
|
missing module named 'numpy_distutils.command' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
|
||||||
|
missing module named numpy_distutils - imported by numpy.f2py.diagnose (delayed, optional)
|
||||||
|
missing module named numpy.random.RandomState - imported by numpy.random (top-level), numpy.random._generator (top-level)
|
||||||
|
missing module named pyodide_js - imported by threadpoolctl (delayed, optional)
|
||||||
|
missing module named numpy._core.zeros - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.vstack - imported by numpy._core (top-level), numpy.lib._shape_base_impl (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.void - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.vecmat - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.vecdot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.ushort - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.unsignedinteger - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.ulonglong - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.ulong - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.uintp - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.uintc - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.uint64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||||
|
missing module named numpy._core.uint32 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||||
|
missing module named numpy._core.uint16 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||||
|
missing module named numpy._core.uint - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.ubyte - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.trunc - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.true_divide - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.transpose - imported by numpy._core (top-level), numpy.lib._function_base_impl (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.trace - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.timedelta64 - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.tensordot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.tanh - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.tan - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.swapaxes - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.sum - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.subtract - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.str_ - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.square - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.sqrt - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
|
||||||
|
missing module named numpy._core.spacing - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.sort - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.sinh - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.single - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.signedinteger - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.signbit - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||||
|
missing module named numpy._core.sign - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.short - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.rint - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.right_shift - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.result_type - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional), numpy.fft._pocketfft (top-level)
|
||||||
|
missing module named numpy._core.remainder - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.reciprocal - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
|
||||||
|
missing module named numpy._core.radians - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.rad2deg - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.prod - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.power - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.positive - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.pi - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.outer - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.ones - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.object_ - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||||
|
missing module named numpy._core.number - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||||
|
missing module named numpy._core.not_equal - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.newaxis - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.negative - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.ndarray - imported by numpy._core (top-level), numpy.lib._utils_impl (top-level), numpy.testing._private.utils (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.multiply - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.moveaxis - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.modf - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.mod - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.minimum - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.maximum - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.max - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||||
|
missing module named numpy._core.matrix_transpose - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.matvec - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.matmul - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.longdouble - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.long - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.logical_xor - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.logical_or - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.logical_not - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.logical_and - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.logaddexp2 - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.logaddexp - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.log2 - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.log1p - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.log - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.linspace - imported by numpy._core (top-level), numpy.lib._index_tricks_impl (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.less_equal - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.less - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.left_shift - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.ldexp - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.lcm - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.isscalar - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.isnat - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.isnan - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||||
|
missing module named numpy._core.isfinite - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.intp - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy._array_api_info (top-level)
|
||||||
|
missing module named numpy._core.integer - imported by numpy._core (conditional), numpy (conditional), numpy.fft._helper (top-level)
|
||||||
|
missing module named numpy._core.intc - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.int8 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||||
|
missing module named numpy._core.int64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||||
|
missing module named numpy._core.int32 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||||
|
missing module named numpy._core.int16 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||||
|
missing module named numpy._core.inf - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||||
|
missing module named numpy._core.inexact - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.iinfo - imported by numpy._core (top-level), numpy.lib._twodim_base_impl (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.hypot - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.hstack - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.heaviside - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.half - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.greater_equal - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.greater - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.gcd - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.frompyfunc - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.frexp - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.fmod - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.fmin - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.fmax - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.floor_divide - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.floor - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.floating - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.float_power - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.float32 - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy._array_api_info (top-level)
|
||||||
|
missing module named numpy._core.float16 - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.finfo - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.fabs - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.expm1 - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.exp - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.euler_gamma - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.errstate - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||||
|
missing module named numpy._core.equal - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.empty_like - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
|
||||||
|
missing module named numpy._core.empty - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy.fft._helper (top-level)
|
||||||
|
missing module named numpy._core.e - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.double - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.dot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.divmod - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.divide - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.diagonal - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.degrees - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.deg2rad - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.datetime64 - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.csingle - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.cross - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.count_nonzero - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.cosh - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.cos - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.copysign - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.conjugate - imported by numpy._core (conditional), numpy (conditional), numpy.fft._pocketfft (top-level)
|
||||||
|
missing module named numpy._core.conj - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.complexfloating - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.complex64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||||
|
missing module named numpy._core.clongdouble - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.character - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.ceil - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.cdouble - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.cbrt - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.bytes_ - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.byte - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.bool_ - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.bitwise_xor - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.bitwise_or - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.bitwise_count - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.bitwise_and - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.atleast_3d - imported by numpy._core (top-level), numpy.lib._shape_base_impl (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.atleast_2d - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.atleast_1d - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.asarray - imported by numpy._core (top-level), numpy.lib._array_utils_impl (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level), numpy.fft._helper (top-level)
|
||||||
|
missing module named numpy._core.asanyarray - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.array_repr - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.array2string - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||||
|
missing module named numpy._core.array - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.argsort - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.arctanh - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.arctan2 - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.arctan - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.arcsinh - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.arcsin - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.arccosh - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.arccos - imported by numpy._core (conditional), numpy (conditional)
|
||||||
|
missing module named numpy._core.arange - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy.fft._helper (top-level)
|
||||||
|
missing module named numpy._core.amin - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.amax - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._core.all - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||||
|
missing module named numpy._core.add - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||||
|
missing module named numpy._distributor_init_local - imported by numpy (optional), numpy._distributor_init (optional)
|
||||||
|
missing module named dummy_thread - imported by cffi.lock (conditional, optional)
|
||||||
|
missing module named thread - imported by cffi.lock (conditional, optional), cffi.cparser (conditional, optional)
|
||||||
|
missing module named cStringIO - imported by cffi.ffiplatform (optional)
|
||||||
|
missing module named cPickle - imported by pycparser.ply.yacc (delayed, optional)
|
||||||
|
missing module named cffi._pycparser - imported by cffi (optional), cffi.cparser (optional)
|
||||||
|
missing module named defusedxml - imported by PIL.Image (optional)
|
||||||
36133
build/main/xref-main.html
Normal file
36133
build/main/xref-main.html
Normal file
File diff suppressed because it is too large
Load Diff
120
check_project.py
Normal file
120
check_project.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
"""
|
||||||
|
项目完整性检查脚本
|
||||||
|
检查所有模块的导入和基本功能
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
def check_module(module_name):
|
||||||
|
"""检查模块是否可以正常导入"""
|
||||||
|
try:
|
||||||
|
importlib.import_module(module_name)
|
||||||
|
print(f"✓ {module_name} - 导入成功")
|
||||||
|
return True
|
||||||
|
except ImportError as e:
|
||||||
|
print(f"✗ {module_name} - 导入失败: {e}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠ {module_name} - 导入异常: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主检查函数"""
|
||||||
|
print("=== TXT2DOCX 项目完整性检查 ===\n")
|
||||||
|
|
||||||
|
# 核心模块列表
|
||||||
|
core_modules = [
|
||||||
|
'config',
|
||||||
|
'style_manager',
|
||||||
|
'advanced_style_editor',
|
||||||
|
'file_handler',
|
||||||
|
'text_processor',
|
||||||
|
'markdown_parser',
|
||||||
|
'docx_generator',
|
||||||
|
'image_processor',
|
||||||
|
'error_chars',
|
||||||
|
'batch_processor',
|
||||||
|
'gui_config',
|
||||||
|
'gui_matching_editor',
|
||||||
|
'gui_results',
|
||||||
|
'gui_style_manager',
|
||||||
|
'main'
|
||||||
|
]
|
||||||
|
|
||||||
|
# 检查核心模块
|
||||||
|
print("核心模块检查:")
|
||||||
|
success_count = 0
|
||||||
|
total_count = len(core_modules)
|
||||||
|
|
||||||
|
for module in core_modules:
|
||||||
|
if check_module(module):
|
||||||
|
success_count += 1
|
||||||
|
|
||||||
|
print(f"\n检查完成: {success_count}/{total_count} 模块导入成功")
|
||||||
|
|
||||||
|
# 检查关键依赖
|
||||||
|
print("\n外部依赖检查:")
|
||||||
|
dependencies = [
|
||||||
|
'tkinter',
|
||||||
|
'docx', # python-docx
|
||||||
|
'PIL', # Pillow
|
||||||
|
'json',
|
||||||
|
'os',
|
||||||
|
'copy',
|
||||||
|
'typing'
|
||||||
|
]
|
||||||
|
|
||||||
|
dep_success = 0
|
||||||
|
for dep in dependencies:
|
||||||
|
if check_module(dep):
|
||||||
|
dep_success += 1
|
||||||
|
|
||||||
|
print(f"\n依赖检查完成: {dep_success}/{len(dependencies)} 依赖可用")
|
||||||
|
|
||||||
|
# 检查数据文件
|
||||||
|
print("\n数据文件检查:")
|
||||||
|
import os
|
||||||
|
|
||||||
|
required_files = [
|
||||||
|
'data/error_chars.json',
|
||||||
|
'data/11.txt'
|
||||||
|
]
|
||||||
|
|
||||||
|
file_success = 0
|
||||||
|
for file_path in required_files:
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
print(f"✓ {file_path} - 文件存在")
|
||||||
|
file_success += 1
|
||||||
|
else:
|
||||||
|
print(f"✗ {file_path} - 文件缺失")
|
||||||
|
|
||||||
|
# 检查样式目录
|
||||||
|
styles_dir = 'data/styles'
|
||||||
|
if os.path.exists(styles_dir) and os.path.isdir(styles_dir):
|
||||||
|
print(f"✓ {styles_dir}/ - 目录存在")
|
||||||
|
style_files = [f for f in os.listdir(styles_dir) if f.endswith('.json')]
|
||||||
|
print(f" └─ 发现 {len(style_files)} 个样式文件")
|
||||||
|
file_success += 1
|
||||||
|
else:
|
||||||
|
print(f"✗ {styles_dir}/ - 目录缺失")
|
||||||
|
|
||||||
|
print(f"\n数据文件检查完成: {file_success}/{len(required_files)+1} 文件/目录正常")
|
||||||
|
|
||||||
|
# 总结
|
||||||
|
print("\n" + "="*50)
|
||||||
|
total_checks = total_count + len(dependencies) + len(required_files) + 1
|
||||||
|
total_success = success_count + dep_success + file_success
|
||||||
|
|
||||||
|
if total_success == total_checks:
|
||||||
|
print("🎉 项目完整性检查通过!所有组件正常。")
|
||||||
|
print("✨ 可以正常启动程序:python main.py")
|
||||||
|
else:
|
||||||
|
print(f"⚠️ 检查完成:{total_success}/{total_checks} 项通过")
|
||||||
|
print("❗ 请修复上述问题后再运行程序")
|
||||||
|
|
||||||
|
return total_success == total_checks
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
success = main()
|
||||||
|
sys.exit(0 if success else 1)
|
||||||
15
config.json
Normal file
15
config.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"text_order_conversion": false,
|
||||||
|
"typo_handling": false,
|
||||||
|
"punctuation_replacement": false,
|
||||||
|
"paragraph_formatting": false,
|
||||||
|
"paragraph_min_length": 100,
|
||||||
|
"paragraph_max_length": 300,
|
||||||
|
"typo_intensity": 0.5,
|
||||||
|
"custom_punctuation": ",。!?;?!;",
|
||||||
|
"output_path": "",
|
||||||
|
"use_same_folder": true,
|
||||||
|
"last_txt_folder": "",
|
||||||
|
"last_images_root": "",
|
||||||
|
"last_output_root": ""
|
||||||
|
}
|
||||||
236
data/styles/99.json
Normal file
236
data/styles/99.json
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
{
|
||||||
|
"name": "99",
|
||||||
|
"description": "自定义样式",
|
||||||
|
"author": "用户",
|
||||||
|
"version": "1.0",
|
||||||
|
"page_margin_top": 2.54,
|
||||||
|
"page_margin_bottom": 2.54,
|
||||||
|
"page_margin_left": 3.17,
|
||||||
|
"page_margin_right": 3.17,
|
||||||
|
"body_font": {
|
||||||
|
"name": "宋体",
|
||||||
|
"size": 12,
|
||||||
|
"bold": false,
|
||||||
|
"italic": false,
|
||||||
|
"color": "#000000"
|
||||||
|
},
|
||||||
|
"body_paragraph": {
|
||||||
|
"line_spacing": 1.5,
|
||||||
|
"space_before": 5,
|
||||||
|
"space_after": 0,
|
||||||
|
"first_line_indent": 2.0,
|
||||||
|
"alignment": "left",
|
||||||
|
"keep_with_next": false
|
||||||
|
},
|
||||||
|
"heading_styles": {
|
||||||
|
"1": {
|
||||||
|
"font": {
|
||||||
|
"name": "微软雅黑",
|
||||||
|
"size": 21,
|
||||||
|
"bold": true,
|
||||||
|
"italic": false,
|
||||||
|
"color": "#000000"
|
||||||
|
},
|
||||||
|
"paragraph": {
|
||||||
|
"line_spacing": 1.3,
|
||||||
|
"space_before": 12,
|
||||||
|
"space_after": 6,
|
||||||
|
"first_line_indent": 0.0,
|
||||||
|
"alignment": "left",
|
||||||
|
"keep_with_next": true
|
||||||
|
},
|
||||||
|
"numbering": false,
|
||||||
|
"outline_level": 1
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"font": {
|
||||||
|
"name": "黑体",
|
||||||
|
"size": 18,
|
||||||
|
"bold": true,
|
||||||
|
"italic": false,
|
||||||
|
"color": "#00ff80"
|
||||||
|
},
|
||||||
|
"paragraph": {
|
||||||
|
"line_spacing": 1.3,
|
||||||
|
"space_before": 12,
|
||||||
|
"space_after": 6,
|
||||||
|
"first_line_indent": 0.0,
|
||||||
|
"alignment": "left",
|
||||||
|
"keep_with_next": true
|
||||||
|
},
|
||||||
|
"numbering": false,
|
||||||
|
"outline_level": 2
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
"font": {
|
||||||
|
"name": "黑体",
|
||||||
|
"size": 14,
|
||||||
|
"bold": true,
|
||||||
|
"italic": false,
|
||||||
|
"color": "#00ffff"
|
||||||
|
},
|
||||||
|
"paragraph": {
|
||||||
|
"line_spacing": 1.3,
|
||||||
|
"space_before": 6,
|
||||||
|
"space_after": 6,
|
||||||
|
"first_line_indent": 0.0,
|
||||||
|
"alignment": "left",
|
||||||
|
"keep_with_next": true
|
||||||
|
},
|
||||||
|
"numbering": false,
|
||||||
|
"outline_level": 3
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"font": {
|
||||||
|
"name": "微软雅黑",
|
||||||
|
"size": 13,
|
||||||
|
"bold": true,
|
||||||
|
"italic": false,
|
||||||
|
"color": "#1F497D"
|
||||||
|
},
|
||||||
|
"paragraph": {
|
||||||
|
"line_spacing": 1.3,
|
||||||
|
"space_before": 6,
|
||||||
|
"space_after": 6,
|
||||||
|
"first_line_indent": 0.0,
|
||||||
|
"alignment": "left",
|
||||||
|
"keep_with_next": true
|
||||||
|
},
|
||||||
|
"numbering": false,
|
||||||
|
"outline_level": 4
|
||||||
|
},
|
||||||
|
"5": {
|
||||||
|
"font": {
|
||||||
|
"name": "微软雅黑",
|
||||||
|
"size": 12,
|
||||||
|
"bold": true,
|
||||||
|
"italic": false,
|
||||||
|
"color": "#1F497D"
|
||||||
|
},
|
||||||
|
"paragraph": {
|
||||||
|
"line_spacing": 1.3,
|
||||||
|
"space_before": 6,
|
||||||
|
"space_after": 6,
|
||||||
|
"first_line_indent": 0.0,
|
||||||
|
"alignment": "left",
|
||||||
|
"keep_with_next": true
|
||||||
|
},
|
||||||
|
"numbering": false,
|
||||||
|
"outline_level": 5
|
||||||
|
},
|
||||||
|
"6": {
|
||||||
|
"font": {
|
||||||
|
"name": "微软雅黑",
|
||||||
|
"size": 11,
|
||||||
|
"bold": true,
|
||||||
|
"italic": false,
|
||||||
|
"color": "#1F497D"
|
||||||
|
},
|
||||||
|
"paragraph": {
|
||||||
|
"line_spacing": 1.3,
|
||||||
|
"space_before": 6,
|
||||||
|
"space_after": 6,
|
||||||
|
"first_line_indent": 0.0,
|
||||||
|
"alignment": "left",
|
||||||
|
"keep_with_next": true
|
||||||
|
},
|
||||||
|
"numbering": false,
|
||||||
|
"outline_level": 6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unordered_list": {
|
||||||
|
"font": {
|
||||||
|
"name": "宋体",
|
||||||
|
"size": 12,
|
||||||
|
"bold": false,
|
||||||
|
"italic": false,
|
||||||
|
"color": "#000000"
|
||||||
|
},
|
||||||
|
"paragraph": {
|
||||||
|
"line_spacing": 1.5,
|
||||||
|
"space_before": 0,
|
||||||
|
"space_after": 6,
|
||||||
|
"first_line_indent": 0.0,
|
||||||
|
"alignment": "left",
|
||||||
|
"keep_with_next": false
|
||||||
|
},
|
||||||
|
"bullet_symbol": "•",
|
||||||
|
"number_format": "1."
|
||||||
|
},
|
||||||
|
"ordered_list": {
|
||||||
|
"font": {
|
||||||
|
"name": "宋体",
|
||||||
|
"size": 12,
|
||||||
|
"bold": false,
|
||||||
|
"italic": false,
|
||||||
|
"color": "#000000"
|
||||||
|
},
|
||||||
|
"paragraph": {
|
||||||
|
"line_spacing": 1.5,
|
||||||
|
"space_before": 0,
|
||||||
|
"space_after": 6,
|
||||||
|
"first_line_indent": 0.0,
|
||||||
|
"alignment": "left",
|
||||||
|
"keep_with_next": false
|
||||||
|
},
|
||||||
|
"bullet_symbol": "•",
|
||||||
|
"number_format": "1."
|
||||||
|
},
|
||||||
|
"code_block": {
|
||||||
|
"font": {
|
||||||
|
"name": "Courier New",
|
||||||
|
"size": 10,
|
||||||
|
"bold": false,
|
||||||
|
"italic": false,
|
||||||
|
"color": "#000000"
|
||||||
|
},
|
||||||
|
"paragraph": {
|
||||||
|
"line_spacing": 1.5,
|
||||||
|
"space_before": 6,
|
||||||
|
"space_after": 6,
|
||||||
|
"first_line_indent": 0.0,
|
||||||
|
"alignment": "left",
|
||||||
|
"keep_with_next": false
|
||||||
|
},
|
||||||
|
"background_color": "#F5F5F5",
|
||||||
|
"border": false
|
||||||
|
},
|
||||||
|
"quote_block": {
|
||||||
|
"font": {
|
||||||
|
"name": "宋体",
|
||||||
|
"size": 12,
|
||||||
|
"bold": false,
|
||||||
|
"italic": true,
|
||||||
|
"color": "#000000"
|
||||||
|
},
|
||||||
|
"paragraph": {
|
||||||
|
"line_spacing": 1.5,
|
||||||
|
"space_before": 6,
|
||||||
|
"space_after": 6,
|
||||||
|
"first_line_indent": 2.0,
|
||||||
|
"alignment": "left",
|
||||||
|
"keep_with_next": false
|
||||||
|
},
|
||||||
|
"background_color": "#F9F9F9",
|
||||||
|
"border": false
|
||||||
|
},
|
||||||
|
"table_style": {
|
||||||
|
"font": {
|
||||||
|
"name": "宋体",
|
||||||
|
"size": 11,
|
||||||
|
"bold": false,
|
||||||
|
"italic": false,
|
||||||
|
"color": "#000000"
|
||||||
|
},
|
||||||
|
"paragraph": {
|
||||||
|
"line_spacing": 1.5,
|
||||||
|
"space_before": 6,
|
||||||
|
"space_after": 6,
|
||||||
|
"first_line_indent": 0.0,
|
||||||
|
"alignment": "left",
|
||||||
|
"keep_with_next": false
|
||||||
|
},
|
||||||
|
"background_color": "#F5F5F5",
|
||||||
|
"border": true
|
||||||
|
}
|
||||||
|
}
|
||||||
88
demo_advanced_editor.py
Normal file
88
demo_advanced_editor.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
"""
|
||||||
|
高级样式编辑器使用演示
|
||||||
|
"""
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk
|
||||||
|
from advanced_style_editor import open_advanced_editor
|
||||||
|
from style_manager import style_manager
|
||||||
|
|
||||||
|
def demo_advanced_editor():
|
||||||
|
"""演示高级样式编辑器的使用"""
|
||||||
|
print("=== 高级样式编辑器使用演示 ===")
|
||||||
|
|
||||||
|
# 创建主窗口
|
||||||
|
root = tk.Tk()
|
||||||
|
root.title("高级样式编辑器演示")
|
||||||
|
root.geometry("400x300")
|
||||||
|
|
||||||
|
# 说明标签
|
||||||
|
info_label = tk.Label(root, text="高级样式编辑器演示\n\n点击下面的按钮打开高级编辑器\n进行样式设计和保存",
|
||||||
|
font=("微软雅黑", 12), justify='center')
|
||||||
|
info_label.pack(pady=20)
|
||||||
|
|
||||||
|
# 样式选择
|
||||||
|
style_frame = ttk.Frame(root)
|
||||||
|
style_frame.pack(pady=10)
|
||||||
|
|
||||||
|
ttk.Label(style_frame, text="选择基础样式:").pack(side='left')
|
||||||
|
style_var = tk.StringVar(value="爆款文章风格")
|
||||||
|
style_combo = ttk.Combobox(style_frame, textvariable=style_var,
|
||||||
|
values=style_manager.get_style_names(),
|
||||||
|
state='readonly', width=20)
|
||||||
|
style_combo.pack(side='left', padx=10)
|
||||||
|
|
||||||
|
def open_editor():
|
||||||
|
"""打开高级编辑器"""
|
||||||
|
selected_style = style_var.get()
|
||||||
|
if selected_style:
|
||||||
|
try:
|
||||||
|
print(f"打开高级编辑器 - 基础样式: {selected_style}")
|
||||||
|
editor = open_advanced_editor(root, selected_style)
|
||||||
|
if editor:
|
||||||
|
print("高级编辑器打开成功!")
|
||||||
|
print("您现在可以:")
|
||||||
|
print("1. 修改字体、颜色、大小等属性")
|
||||||
|
print("2. 调整段落行距、缩进等设置")
|
||||||
|
print("3. 自定义1-3级标题样式")
|
||||||
|
print("4. 在右侧实时预览效果")
|
||||||
|
print("5. 保存或另存为新样式")
|
||||||
|
else:
|
||||||
|
print("高级编辑器打开失败")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"打开编辑器时出现错误: {e}")
|
||||||
|
|
||||||
|
# 按钮
|
||||||
|
button_frame = ttk.Frame(root)
|
||||||
|
button_frame.pack(pady=20)
|
||||||
|
|
||||||
|
open_button = ttk.Button(button_frame, text="打开高级编辑器", command=open_editor)
|
||||||
|
open_button.pack(pady=10)
|
||||||
|
|
||||||
|
def show_styles():
|
||||||
|
"""显示当前所有样式"""
|
||||||
|
styles = style_manager.get_style_names()
|
||||||
|
print(f"\n当前可用样式(共{len(styles)}个):")
|
||||||
|
for i, style_name in enumerate(styles, 1):
|
||||||
|
print(f"{i}. {style_name}")
|
||||||
|
|
||||||
|
list_button = ttk.Button(button_frame, text="查看所有样式", command=show_styles)
|
||||||
|
list_button.pack(pady=5)
|
||||||
|
|
||||||
|
def close_demo():
|
||||||
|
"""关闭演示"""
|
||||||
|
print("关闭演示")
|
||||||
|
root.destroy()
|
||||||
|
|
||||||
|
close_button = ttk.Button(button_frame, text="关闭演示", command=close_demo)
|
||||||
|
close_button.pack(pady=5)
|
||||||
|
|
||||||
|
print("演示窗口已打开。您可以:")
|
||||||
|
print("1. 选择一个基础样式")
|
||||||
|
print("2. 点击'打开高级编辑器'进行样式编辑")
|
||||||
|
print("3. 在编辑器中修改样式并保存")
|
||||||
|
|
||||||
|
# 启动主循环
|
||||||
|
root.mainloop()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
demo_advanced_editor()
|
||||||
BIN
dist/txt2md2docx.exe
vendored
Normal file
BIN
dist/txt2md2docx.exe
vendored
Normal file
Binary file not shown.
243
gui_style_manager.py
Normal file
243
gui_style_manager.py
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
"""
|
||||||
|
样式管理GUI模块
|
||||||
|
|
||||||
|
提供样式选择、预览、编辑的图形界面。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk, filedialog, messagebox, simpledialog
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from style_manager import style_manager, DocumentStyle
|
||||||
|
from config import Config
|
||||||
|
from advanced_style_editor import open_advanced_editor
|
||||||
|
|
||||||
|
# 创建配置管理器实例
|
||||||
|
config_manager = Config()
|
||||||
|
|
||||||
|
|
||||||
|
def create_style_tab(parent):
|
||||||
|
"""创建样式管理选项卡"""
|
||||||
|
style_frame = ttk.Frame(parent)
|
||||||
|
|
||||||
|
# 标题
|
||||||
|
ttk.Label(style_frame, text='文章排版样式管理', font=('', 12, 'bold')).pack(anchor='w', padx=10, pady=(10, 5))
|
||||||
|
ttk.Separator(style_frame, orient='horizontal').pack(fill='x', padx=10, pady=5)
|
||||||
|
|
||||||
|
# 样式选择区域
|
||||||
|
selection_frame = ttk.LabelFrame(style_frame, text='样式选择', padding="10")
|
||||||
|
selection_frame.pack(fill='x', padx=10, pady=5)
|
||||||
|
|
||||||
|
# 当前样式
|
||||||
|
current_frame = ttk.Frame(selection_frame)
|
||||||
|
current_frame.pack(fill='x', pady=2)
|
||||||
|
ttk.Label(current_frame, text='当前样式:', width=12).pack(side='left')
|
||||||
|
|
||||||
|
style_var = tk.StringVar(value=config_manager.current_style)
|
||||||
|
style_combo = ttk.Combobox(current_frame, textvariable=style_var,
|
||||||
|
values=style_manager.get_style_names(),
|
||||||
|
state='readonly', width=25)
|
||||||
|
style_combo.pack(side='left', padx=(0, 10))
|
||||||
|
|
||||||
|
def on_style_change(*args):
|
||||||
|
config_manager.set('current_style', style_var.get())
|
||||||
|
_update_style_info()
|
||||||
|
|
||||||
|
style_var.trace('w', on_style_change)
|
||||||
|
|
||||||
|
ttk.Button(current_frame, text='预览', command=lambda: _preview_style(style_var.get(), parent)).pack(side='left', padx=5)
|
||||||
|
|
||||||
|
# 样式信息显示
|
||||||
|
info_frame = ttk.Frame(selection_frame)
|
||||||
|
info_frame.pack(fill='x', pady=(5, 0))
|
||||||
|
|
||||||
|
info_text = tk.Text(info_frame, height=3, wrap=tk.WORD, state='disabled')
|
||||||
|
info_text.pack(fill='x')
|
||||||
|
|
||||||
|
def _update_style_info():
|
||||||
|
"""更新样式信息显示"""
|
||||||
|
style = style_manager.get_style(style_var.get())
|
||||||
|
if style:
|
||||||
|
info = f"描述: {style.description}\n作者: {style.author} 版本: {style.version}\n"
|
||||||
|
info += f"正文字体: {style.body_font.name if style.body_font else '默认'} {style.body_font.size if style.body_font else 12}pt"
|
||||||
|
|
||||||
|
info_text.config(state='normal')
|
||||||
|
info_text.delete(1.0, tk.END)
|
||||||
|
info_text.insert(1.0, info)
|
||||||
|
info_text.config(state='disabled')
|
||||||
|
|
||||||
|
# 样式管理区域
|
||||||
|
management_frame = ttk.LabelFrame(style_frame, text='样式管理', padding="10")
|
||||||
|
management_frame.pack(fill='x', padx=10, pady=5)
|
||||||
|
|
||||||
|
button_frame = ttk.Frame(management_frame)
|
||||||
|
button_frame.pack(fill='x')
|
||||||
|
|
||||||
|
def _create_new_style():
|
||||||
|
"""创建新样式"""
|
||||||
|
name = simpledialog.askstring("新建样式", "请输入样式名称:")
|
||||||
|
if name and name not in style_manager.get_style_names():
|
||||||
|
new_style = DocumentStyle(name=name, description="自定义样式", author="用户")
|
||||||
|
if style_manager.create_custom_style(new_style):
|
||||||
|
style_combo['values'] = style_manager.get_style_names()
|
||||||
|
messagebox.showinfo("成功", f"样式 '{name}' 创建成功")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("错误", "创建样式失败")
|
||||||
|
elif name:
|
||||||
|
messagebox.showerror("错误", "样式名称已存在")
|
||||||
|
|
||||||
|
def _duplicate_style():
|
||||||
|
"""复制样式"""
|
||||||
|
current_name = style_var.get()
|
||||||
|
if current_name:
|
||||||
|
new_name = simpledialog.askstring("复制样式", f"基于 '{current_name}' 创建新样式,请输入新名称:")
|
||||||
|
if new_name and new_name not in style_manager.get_style_names():
|
||||||
|
if style_manager.duplicate_style(current_name, new_name):
|
||||||
|
style_combo['values'] = style_manager.get_style_names()
|
||||||
|
messagebox.showinfo("成功", f"样式 '{new_name}' 创建成功")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("错误", "复制样式失败")
|
||||||
|
elif new_name:
|
||||||
|
messagebox.showerror("错误", "样式名称已存在")
|
||||||
|
|
||||||
|
def _edit_style_advanced():
|
||||||
|
"""高级样式编辑"""
|
||||||
|
current_name = style_var.get()
|
||||||
|
if not current_name:
|
||||||
|
messagebox.showwarning("警告", "请先选择一个样式")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 创建高级编辑窗口
|
||||||
|
try:
|
||||||
|
edit_window = open_advanced_editor(parent, current_name)
|
||||||
|
if edit_window:
|
||||||
|
def on_window_close():
|
||||||
|
# 更新样式列表
|
||||||
|
style_combo['values'] = style_manager.get_style_names()
|
||||||
|
_update_style_info()
|
||||||
|
|
||||||
|
# 等待窗口关闭后更新
|
||||||
|
style_frame.after(1000, on_window_close)
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("错误", f"打开高级编辑器失败: {str(e)}")
|
||||||
|
|
||||||
|
def _delete_style():
|
||||||
|
"""删除样式"""
|
||||||
|
current_name = style_var.get()
|
||||||
|
if current_name and current_name not in style_manager.builtin_styles:
|
||||||
|
if messagebox.askyesno("确认删除", f"确定要删除样式 '{current_name}' 吗?"):
|
||||||
|
if style_manager.delete_custom_style(current_name):
|
||||||
|
style_combo['values'] = style_manager.get_style_names()
|
||||||
|
style_var.set(style_manager.get_style_names()[0] if style_manager.get_style_names() else "")
|
||||||
|
messagebox.showinfo("成功", "样式删除成功")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("错误", "删除样式失败")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("错误", "无法删除内置样式")
|
||||||
|
|
||||||
|
def _export_style():
|
||||||
|
"""导出样式"""
|
||||||
|
current_name = style_var.get()
|
||||||
|
if current_name:
|
||||||
|
file_path = filedialog.asksaveasfilename(
|
||||||
|
title="导出样式",
|
||||||
|
defaultextension=".json",
|
||||||
|
filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
|
||||||
|
)
|
||||||
|
if file_path:
|
||||||
|
if style_manager.export_style(current_name, file_path):
|
||||||
|
messagebox.showinfo("成功", "样式导出成功")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("错误", "导出样式失败")
|
||||||
|
|
||||||
|
def _import_style():
|
||||||
|
"""导入样式"""
|
||||||
|
file_path = filedialog.askopenfilename(
|
||||||
|
title="导入样式",
|
||||||
|
filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
|
||||||
|
)
|
||||||
|
if file_path:
|
||||||
|
style_name = style_manager.import_style(file_path)
|
||||||
|
if style_name:
|
||||||
|
style_combo['values'] = style_manager.get_style_names()
|
||||||
|
messagebox.showinfo("成功", f"样式 '{style_name}' 导入成功")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("错误", "导入样式失败")
|
||||||
|
|
||||||
|
# 管理按钮
|
||||||
|
ttk.Button(button_frame, text='新建', command=_create_new_style).pack(side='left', padx=5)
|
||||||
|
ttk.Button(button_frame, text='复制', command=_duplicate_style).pack(side='left', padx=5)
|
||||||
|
ttk.Button(button_frame, text='高级编辑', command=_edit_style_advanced).pack(side='left', padx=5)
|
||||||
|
ttk.Button(button_frame, text='删除', command=_delete_style).pack(side='left', padx=5)
|
||||||
|
ttk.Button(button_frame, text='导出', command=_export_style).pack(side='left', padx=5)
|
||||||
|
ttk.Button(button_frame, text='导入', command=_import_style).pack(side='left', padx=5)
|
||||||
|
|
||||||
|
# 初始化信息显示
|
||||||
|
_update_style_info()
|
||||||
|
|
||||||
|
return style_frame
|
||||||
|
|
||||||
|
|
||||||
|
def _preview_style(style_name: str, parent):
|
||||||
|
"""预览样式"""
|
||||||
|
style = style_manager.get_style(style_name)
|
||||||
|
if not style:
|
||||||
|
return
|
||||||
|
|
||||||
|
preview_text = f"""样式预览: {style.name}
|
||||||
|
|
||||||
|
正文示例:
|
||||||
|
这是正文内容,使用 {style.body_font.name if style.body_font else '默认字体'} 字体,
|
||||||
|
大小为 {style.body_font.size if style.body_font else 12} 磅,
|
||||||
|
行距为 {style.body_paragraph.line_spacing if style.body_paragraph else 1.5} 倍。
|
||||||
|
|
||||||
|
# 一级标题示例
|
||||||
|
## 二级标题示例
|
||||||
|
### 三级标题示例
|
||||||
|
|
||||||
|
这是一个包含**粗体**和*斜体*的段落。
|
||||||
|
|
||||||
|
> 这是引用块的示例内容。
|
||||||
|
|
||||||
|
`这是行内代码`的示例。
|
||||||
|
|
||||||
|
- 无序列表项目1
|
||||||
|
- 无序列表项目2
|
||||||
|
|
||||||
|
1. 有序列表项目1
|
||||||
|
2. 有序列表项目2
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 创建预览窗口
|
||||||
|
preview_window = tk.Toplevel(parent)
|
||||||
|
preview_window.title(f'样式预览 - {style_name}')
|
||||||
|
preview_window.geometry('500x400')
|
||||||
|
preview_window.transient(parent)
|
||||||
|
|
||||||
|
text_widget = tk.Text(preview_window, wrap=tk.WORD, padx=10, pady=10)
|
||||||
|
text_widget.pack(fill='both', expand=True, padx=10, pady=10)
|
||||||
|
text_widget.insert(1.0, preview_text)
|
||||||
|
text_widget.config(state='disabled')
|
||||||
|
|
||||||
|
ttk.Button(preview_window, text='关闭',
|
||||||
|
command=preview_window.destroy).pack(pady=10)
|
||||||
|
|
||||||
|
|
||||||
|
# 兼容性函数
|
||||||
|
def show_style_manager(parent=None):
|
||||||
|
"""显示样式管理器窗口"""
|
||||||
|
if parent is None:
|
||||||
|
root = tk.Tk()
|
||||||
|
root.withdraw()
|
||||||
|
parent = root
|
||||||
|
|
||||||
|
window = tk.Toplevel(parent)
|
||||||
|
window.title('样式管理器')
|
||||||
|
window.geometry('600x500')
|
||||||
|
window.transient(parent)
|
||||||
|
window.grab_set()
|
||||||
|
|
||||||
|
style_tab = create_style_tab(window)
|
||||||
|
style_tab.pack(fill='both', expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
|
ttk.Button(window, text='关闭', command=window.destroy).pack(pady=10)
|
||||||
790
style_manager.py
Normal file
790
style_manager.py
Normal file
@ -0,0 +1,790 @@
|
|||||||
|
"""
|
||||||
|
文章排版样式管理模块
|
||||||
|
|
||||||
|
负责管理文档的排版样式,包括内置样式和自定义样式。
|
||||||
|
支持样式的创建、编辑、删除、导入和导出等功能。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
from typing import Dict, Any, List, Optional, Union
|
||||||
|
from dataclasses import dataclass, asdict, field
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FontStyle:
|
||||||
|
"""字体样式配置"""
|
||||||
|
name: str = "宋体"
|
||||||
|
size: int = 12
|
||||||
|
bold: bool = False
|
||||||
|
italic: bool = False
|
||||||
|
color: str = "#000000" # RGB颜色值
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ParagraphStyle:
|
||||||
|
"""段落样式配置"""
|
||||||
|
line_spacing: float = 1.5
|
||||||
|
space_before: int = 0 # 段前间距(磅)
|
||||||
|
space_after: int = 0 # 段后间距(磅)
|
||||||
|
first_line_indent: float = 0.0 # 首行缩进(字符)
|
||||||
|
alignment: str = "left" # left, center, right, justify
|
||||||
|
keep_with_next: bool = False # 与下段同页
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class HeadingStyle:
|
||||||
|
"""标题样式配置"""
|
||||||
|
font: FontStyle
|
||||||
|
paragraph: ParagraphStyle
|
||||||
|
numbering: bool = False # 是否自动编号
|
||||||
|
outline_level: int = 0 # 大纲级别
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ListStyle:
|
||||||
|
"""列表样式配置"""
|
||||||
|
font: FontStyle
|
||||||
|
paragraph: ParagraphStyle
|
||||||
|
bullet_symbol: str = "•" # 无序列表符号
|
||||||
|
number_format: str = "1." # 有序列表格式
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SpecialStyle:
|
||||||
|
"""特殊元素样式配置"""
|
||||||
|
font: FontStyle
|
||||||
|
paragraph: ParagraphStyle
|
||||||
|
background_color: str = "#F5F5F5" # 背景色
|
||||||
|
border: bool = False # 是否有边框
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DocumentStyle:
|
||||||
|
"""文档排版样式配置"""
|
||||||
|
# 基础信息
|
||||||
|
name: str = "默认样式"
|
||||||
|
description: str = "标准文档样式"
|
||||||
|
author: str = "系统"
|
||||||
|
version: str = "1.0"
|
||||||
|
|
||||||
|
# 页面设置
|
||||||
|
page_margin_top: float = 2.54 # 页边距(cm)
|
||||||
|
page_margin_bottom: float = 2.54
|
||||||
|
page_margin_left: float = 3.17
|
||||||
|
page_margin_right: float = 3.17
|
||||||
|
|
||||||
|
# 基础字体和段落
|
||||||
|
body_font: Optional[FontStyle] = None
|
||||||
|
body_paragraph: Optional[ParagraphStyle] = None
|
||||||
|
|
||||||
|
# 标题样式 (1-6级)
|
||||||
|
heading_styles: Optional[Dict[int, HeadingStyle]] = None
|
||||||
|
|
||||||
|
# 列表样式
|
||||||
|
unordered_list: Optional[ListStyle] = None
|
||||||
|
ordered_list: Optional[ListStyle] = None
|
||||||
|
|
||||||
|
# 特殊元素样式
|
||||||
|
code_block: Optional[SpecialStyle] = None
|
||||||
|
quote_block: Optional[SpecialStyle] = None
|
||||||
|
table_style: Optional[SpecialStyle] = None
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
"""初始化默认值"""
|
||||||
|
if self.body_font is None:
|
||||||
|
self.body_font = FontStyle()
|
||||||
|
if self.body_paragraph is None:
|
||||||
|
self.body_paragraph = ParagraphStyle()
|
||||||
|
if self.heading_styles is None:
|
||||||
|
self.heading_styles = self._create_default_headings()
|
||||||
|
if self.unordered_list is None:
|
||||||
|
self.unordered_list = ListStyle(
|
||||||
|
font=FontStyle(),
|
||||||
|
paragraph=ParagraphStyle(space_after=6)
|
||||||
|
)
|
||||||
|
if self.ordered_list is None:
|
||||||
|
self.ordered_list = ListStyle(
|
||||||
|
font=FontStyle(),
|
||||||
|
paragraph=ParagraphStyle(space_after=6)
|
||||||
|
)
|
||||||
|
if self.code_block is None:
|
||||||
|
self.code_block = SpecialStyle(
|
||||||
|
font=FontStyle(name="Courier New", size=10),
|
||||||
|
paragraph=ParagraphStyle(space_before=6, space_after=6),
|
||||||
|
background_color="#F5F5F5"
|
||||||
|
)
|
||||||
|
if self.quote_block is None:
|
||||||
|
self.quote_block = SpecialStyle(
|
||||||
|
font=FontStyle(italic=True),
|
||||||
|
paragraph=ParagraphStyle(first_line_indent=2.0, space_before=6, space_after=6),
|
||||||
|
background_color="#F9F9F9"
|
||||||
|
)
|
||||||
|
if self.table_style is None:
|
||||||
|
self.table_style = SpecialStyle(
|
||||||
|
font=FontStyle(size=11),
|
||||||
|
paragraph=ParagraphStyle(space_before=6, space_after=6),
|
||||||
|
border=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def _create_default_headings(self) -> Dict[int, HeadingStyle]:
|
||||||
|
"""创建默认标题样式"""
|
||||||
|
headings = {}
|
||||||
|
base_sizes = [18, 16, 14, 13, 12, 11] # 各级标题字号
|
||||||
|
|
||||||
|
for level in range(1, 7):
|
||||||
|
font_size = base_sizes[level - 1] if level <= len(base_sizes) else 11
|
||||||
|
headings[level] = HeadingStyle(
|
||||||
|
font=FontStyle(
|
||||||
|
name="微软雅黑",
|
||||||
|
size=font_size,
|
||||||
|
bold=True,
|
||||||
|
color="#1F497D"
|
||||||
|
),
|
||||||
|
paragraph=ParagraphStyle(
|
||||||
|
line_spacing=1.3,
|
||||||
|
space_before=12 if level <= 2 else 6,
|
||||||
|
space_after=6,
|
||||||
|
keep_with_next=True
|
||||||
|
),
|
||||||
|
outline_level=level
|
||||||
|
)
|
||||||
|
|
||||||
|
return headings
|
||||||
|
|
||||||
|
|
||||||
|
class StyleManager:
|
||||||
|
"""排版样式管理器"""
|
||||||
|
|
||||||
|
def __init__(self, styles_dir: str = "data/styles"):
|
||||||
|
"""
|
||||||
|
初始化样式管理器
|
||||||
|
|
||||||
|
Args:
|
||||||
|
styles_dir: 样式文件存储目录
|
||||||
|
"""
|
||||||
|
self.styles_dir = styles_dir
|
||||||
|
self.builtin_styles = self._create_builtin_styles()
|
||||||
|
self.custom_styles = {}
|
||||||
|
self._load_custom_styles()
|
||||||
|
|
||||||
|
def _create_builtin_styles(self) -> Dict[str, DocumentStyle]:
|
||||||
|
"""创建内置样式(专业版)"""
|
||||||
|
styles = {}
|
||||||
|
|
||||||
|
# 1. 爆款文章风格 - 参考知乎、头条等平台
|
||||||
|
viral_style = DocumentStyle(
|
||||||
|
name="爆款文章风格",
|
||||||
|
description="高阅读量爆款文章风格,层次分明,吸引眼球",
|
||||||
|
author="系统内置",
|
||||||
|
body_font=FontStyle(name="微软雅黑", size=14, color="#333333"),
|
||||||
|
body_paragraph=ParagraphStyle(
|
||||||
|
line_spacing=1.8,
|
||||||
|
first_line_indent=0.0, # 爆款文章不缩进
|
||||||
|
space_after=12,
|
||||||
|
space_before=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# 设置爆款文章的标题样式
|
||||||
|
if viral_style.heading_styles:
|
||||||
|
# 主标题:大胆吸引眼球
|
||||||
|
viral_style.heading_styles[1].font = FontStyle(name="黑体", size=22, bold=True, color="#FF4500")
|
||||||
|
viral_style.heading_styles[1].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=20, space_after=20, alignment="center"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 二级标题:强烈对比
|
||||||
|
viral_style.heading_styles[2].font = FontStyle(name="微软雅黑", size=18, bold=True, color="#FF6B35")
|
||||||
|
viral_style.heading_styles[2].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.4, space_before=16, space_after=12, alignment="left"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 三级标题:精彩分段
|
||||||
|
viral_style.heading_styles[3].font = FontStyle(name="微软雅黑", size=16, bold=True, color="#4CAF50")
|
||||||
|
viral_style.heading_styles[3].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.4, space_before=12, space_after=8
|
||||||
|
)
|
||||||
|
|
||||||
|
# 四级及以下:细分点
|
||||||
|
for level in range(4, 7):
|
||||||
|
viral_style.heading_styles[level].font = FontStyle(name="微软雅黑", size=15, bold=True, color="#2196F3")
|
||||||
|
viral_style.heading_styles[level].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=8, space_after=6
|
||||||
|
)
|
||||||
|
|
||||||
|
# 设置特殊元素样式
|
||||||
|
viral_style.quote_block = SpecialStyle(
|
||||||
|
font=FontStyle(name="楷体", size=13, italic=True, color="#666666"),
|
||||||
|
paragraph=ParagraphStyle(
|
||||||
|
line_spacing=1.6, space_before=10, space_after=10,
|
||||||
|
first_line_indent=0, alignment="center"
|
||||||
|
),
|
||||||
|
background_color="#F8F9FA"
|
||||||
|
)
|
||||||
|
|
||||||
|
styles["爆款文章风格"] = viral_style
|
||||||
|
|
||||||
|
# 2. 微信公众号风格 - 专业的新媒体排版
|
||||||
|
wechat_style = DocumentStyle(
|
||||||
|
name="微信公众号风格",
|
||||||
|
description="专业的微信公众号排版,阅读体验佳",
|
||||||
|
author="系统内置",
|
||||||
|
body_font=FontStyle(name="微软雅黑", size=14, color="#3C4043"),
|
||||||
|
body_paragraph=ParagraphStyle(
|
||||||
|
line_spacing=1.75,
|
||||||
|
first_line_indent=0.0,
|
||||||
|
space_after=14,
|
||||||
|
space_before=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if wechat_style.heading_styles:
|
||||||
|
# 公众号标题的精心设计
|
||||||
|
wechat_style.heading_styles[1].font = FontStyle(name="黑体", size=20, bold=True, color="#1AAD19")
|
||||||
|
wechat_style.heading_styles[1].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.2, space_before=18, space_after=16, alignment="center"
|
||||||
|
)
|
||||||
|
|
||||||
|
wechat_style.heading_styles[2].font = FontStyle(name="微软雅黑", size=17, bold=True, color="#576B95")
|
||||||
|
wechat_style.heading_styles[2].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=14, space_after=10
|
||||||
|
)
|
||||||
|
|
||||||
|
wechat_style.heading_styles[3].font = FontStyle(name="微软雅黑", size=15, bold=True, color="#FA5151")
|
||||||
|
wechat_style.heading_styles[3].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=10, space_after=8
|
||||||
|
)
|
||||||
|
|
||||||
|
# 微信公众号的引用样式
|
||||||
|
wechat_style.quote_block = SpecialStyle(
|
||||||
|
font=FontStyle(name="微软雅黑", size=13, italic=True, color="#888888"),
|
||||||
|
paragraph=ParagraphStyle(
|
||||||
|
line_spacing=1.5, space_before=12, space_after=12,
|
||||||
|
first_line_indent=1.0, alignment="left"
|
||||||
|
),
|
||||||
|
background_color="#F7F7F7",
|
||||||
|
border=True
|
||||||
|
)
|
||||||
|
|
||||||
|
styles["微信公众号风格"] = wechat_style
|
||||||
|
|
||||||
|
# 3. 知乎高赞回答风格 - 逻辑清晰,层次分明
|
||||||
|
zhihu_style = DocumentStyle(
|
||||||
|
name="知乎高赞回答风格",
|
||||||
|
description="逻辑清晰,层次分明,专业权威",
|
||||||
|
author="系统内置",
|
||||||
|
body_font=FontStyle(name="微软雅黑", size=15, color="#1A1A1A"),
|
||||||
|
body_paragraph=ParagraphStyle(
|
||||||
|
line_spacing=1.6,
|
||||||
|
first_line_indent=0.0,
|
||||||
|
space_after=10,
|
||||||
|
space_before=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if zhihu_style.heading_styles:
|
||||||
|
# 知乎风格的标题设计
|
||||||
|
zhihu_style.heading_styles[1].font = FontStyle(name="黑体", size=20, bold=True, color="#0084FF")
|
||||||
|
zhihu_style.heading_styles[1].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=16, space_after=14
|
||||||
|
)
|
||||||
|
|
||||||
|
zhihu_style.heading_styles[2].font = FontStyle(name="微软雅黑", size=17, bold=True, color="#00A6FB")
|
||||||
|
zhihu_style.heading_styles[2].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=12, space_after=10
|
||||||
|
)
|
||||||
|
|
||||||
|
zhihu_style.heading_styles[3].font = FontStyle(name="微软雅黑", size=16, bold=True, color="#FF6B6B")
|
||||||
|
zhihu_style.heading_styles[3].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=10, space_after=8
|
||||||
|
)
|
||||||
|
|
||||||
|
styles["知乎高赞回答风格"] = zhihu_style
|
||||||
|
|
||||||
|
# 4. 小红书笔记风格 - 清新文艺,少女心
|
||||||
|
xiaohongshu_style = DocumentStyle(
|
||||||
|
name="小红书笔记风格",
|
||||||
|
description="清新文艺,适合生活方式类内容",
|
||||||
|
author="系统内置",
|
||||||
|
body_font=FontStyle(name="华文细黑", size=14, color="#333333"),
|
||||||
|
body_paragraph=ParagraphStyle(
|
||||||
|
line_spacing=1.8,
|
||||||
|
first_line_indent=0.0,
|
||||||
|
space_after=12,
|
||||||
|
space_before=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if xiaohongshu_style.heading_styles:
|
||||||
|
xiaohongshu_style.heading_styles[1].font = FontStyle(name="华文细黑", size=18, bold=True, color="#FF69B4")
|
||||||
|
xiaohongshu_style.heading_styles[1].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.2, space_before=15, space_after=12, alignment="center"
|
||||||
|
)
|
||||||
|
|
||||||
|
xiaohongshu_style.heading_styles[2].font = FontStyle(name="微软雅黑", size=16, bold=True, color="#FF6EB4")
|
||||||
|
xiaohongshu_style.heading_styles[2].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=12, space_after=8
|
||||||
|
)
|
||||||
|
|
||||||
|
xiaohongshu_style.heading_styles[3].font = FontStyle(name="微软雅黑", size=15, bold=True, color="#FFB6C1")
|
||||||
|
xiaohongshu_style.heading_styles[3].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=8, space_after=6
|
||||||
|
)
|
||||||
|
|
||||||
|
styles["小红书笔记风格"] = xiaohongshu_style
|
||||||
|
|
||||||
|
# 5. 今日头条新闻风格 - 信息量大,节奏紧凑
|
||||||
|
toutiao_style = DocumentStyle(
|
||||||
|
name="今日头条新闻风格",
|
||||||
|
description="信息密度高,节奏紧凑,突出重点",
|
||||||
|
author="系统内置",
|
||||||
|
body_font=FontStyle(name="微软雅黑", size=14, color="#222222"),
|
||||||
|
body_paragraph=ParagraphStyle(
|
||||||
|
line_spacing=1.5,
|
||||||
|
first_line_indent=0.0,
|
||||||
|
space_after=8,
|
||||||
|
space_before=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if toutiao_style.heading_styles:
|
||||||
|
toutiao_style.heading_styles[1].font = FontStyle(name="黑体", size=19, bold=True, color="#D43F3A")
|
||||||
|
toutiao_style.heading_styles[1].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.2, space_before=12, space_after=12
|
||||||
|
)
|
||||||
|
|
||||||
|
toutiao_style.heading_styles[2].font = FontStyle(name="微软雅黑", size=16, bold=True, color="#D9534F")
|
||||||
|
toutiao_style.heading_styles[2].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=10, space_after=8
|
||||||
|
)
|
||||||
|
|
||||||
|
toutiao_style.heading_styles[3].font = FontStyle(name="微软雅黑", size=15, bold=True, color="#F0AD4E")
|
||||||
|
toutiao_style.heading_styles[3].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=8, space_after=6
|
||||||
|
)
|
||||||
|
|
||||||
|
styles["今日头条新闻风格"] = toutiao_style
|
||||||
|
|
||||||
|
# 6. B站UP主视频脚本风格 - 轻松活泼,年轻化
|
||||||
|
bilibili_style = DocumentStyle(
|
||||||
|
name="B站UP主视频脚本风格",
|
||||||
|
description="轻松活泼,适合年轻受众,有趣有料",
|
||||||
|
author="系统内置",
|
||||||
|
body_font=FontStyle(name="微软雅黑", size=14, color="#222222"),
|
||||||
|
body_paragraph=ParagraphStyle(
|
||||||
|
line_spacing=1.6,
|
||||||
|
first_line_indent=0.0,
|
||||||
|
space_after=10,
|
||||||
|
space_before=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if bilibili_style.heading_styles:
|
||||||
|
bilibili_style.heading_styles[1].font = FontStyle(name="黑体", size=18, bold=True, color="#00A1D6")
|
||||||
|
bilibili_style.heading_styles[1].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=14, space_after=12
|
||||||
|
)
|
||||||
|
|
||||||
|
bilibili_style.heading_styles[2].font = FontStyle(name="微软雅黑", size=16, bold=True, color="#FB7299")
|
||||||
|
bilibili_style.heading_styles[2].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=10, space_after=8
|
||||||
|
)
|
||||||
|
|
||||||
|
bilibili_style.heading_styles[3].font = FontStyle(name="微软雅黑", size=15, bold=True, color="#9C88FF")
|
||||||
|
bilibili_style.heading_styles[3].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=8, space_after=6
|
||||||
|
)
|
||||||
|
|
||||||
|
styles["B站UP主视频脚本风格"] = bilibili_style
|
||||||
|
|
||||||
|
# 7. 企业微信群通知风格 - 正式严肃
|
||||||
|
enterprise_style = DocumentStyle(
|
||||||
|
name="企业微信群通知风格",
|
||||||
|
description="正式严肃,信息传达清晰,商务风格",
|
||||||
|
author="系统内置",
|
||||||
|
body_font=FontStyle(name="宋体", size=14, color="#2C2C2C"),
|
||||||
|
body_paragraph=ParagraphStyle(
|
||||||
|
line_spacing=1.5,
|
||||||
|
first_line_indent=2.0,
|
||||||
|
space_after=10,
|
||||||
|
space_before=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if enterprise_style.heading_styles:
|
||||||
|
enterprise_style.heading_styles[1].font = FontStyle(name="黑体", size=18, bold=True, color="#1F4E79")
|
||||||
|
enterprise_style.heading_styles[1].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.2, space_before=16, space_after=12, alignment="center"
|
||||||
|
)
|
||||||
|
|
||||||
|
enterprise_style.heading_styles[2].font = FontStyle(name="微软雅黑", size=16, bold=True, color="#2F5597")
|
||||||
|
enterprise_style.heading_styles[2].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=12, space_after=8
|
||||||
|
)
|
||||||
|
|
||||||
|
styles["企业微信群通知风格"] = enterprise_style
|
||||||
|
|
||||||
|
# 8. 情感鸡汤文风格 - 温暖治愈
|
||||||
|
emotional_style = DocumentStyle(
|
||||||
|
name="情感鸡汤文风格",
|
||||||
|
description="温暖治愈,情感丰富,適合心灵鸡汤类内容",
|
||||||
|
author="系统内置",
|
||||||
|
body_font=FontStyle(name="楷体", size=14, color="#444444"),
|
||||||
|
body_paragraph=ParagraphStyle(
|
||||||
|
line_spacing=1.8,
|
||||||
|
first_line_indent=1.5,
|
||||||
|
space_after=12,
|
||||||
|
space_before=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if emotional_style.heading_styles:
|
||||||
|
emotional_style.heading_styles[1].font = FontStyle(name="华文行楷", size=18, bold=True, color="#E91E63")
|
||||||
|
emotional_style.heading_styles[1].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.2, space_before=16, space_after=14, alignment="center"
|
||||||
|
)
|
||||||
|
|
||||||
|
emotional_style.heading_styles[2].font = FontStyle(name="楷体", size=16, bold=True, color="#FF5722")
|
||||||
|
emotional_style.heading_styles[2].paragraph = ParagraphStyle(
|
||||||
|
line_spacing=1.3, space_before=12, space_after=10
|
||||||
|
)
|
||||||
|
|
||||||
|
# 情感鸡汤文的引用样式
|
||||||
|
emotional_style.quote_block = SpecialStyle(
|
||||||
|
font=FontStyle(name="华文行楷", size=13, italic=True, color="#795548"),
|
||||||
|
paragraph=ParagraphStyle(
|
||||||
|
line_spacing=1.6, space_before=10, space_after=10,
|
||||||
|
alignment="center"
|
||||||
|
),
|
||||||
|
background_color="#FFF3E0"
|
||||||
|
)
|
||||||
|
|
||||||
|
styles["情感鸡汤文风格"] = emotional_style
|
||||||
|
|
||||||
|
return styles
|
||||||
|
|
||||||
|
def _load_custom_styles(self) -> None:
|
||||||
|
"""加载自定义样式"""
|
||||||
|
if not os.path.exists(self.styles_dir):
|
||||||
|
os.makedirs(self.styles_dir, exist_ok=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
for filename in os.listdir(self.styles_dir):
|
||||||
|
if filename.endswith('.json'):
|
||||||
|
filepath = os.path.join(self.styles_dir, filename)
|
||||||
|
style = self.load_style_from_file(filepath)
|
||||||
|
if style:
|
||||||
|
self.custom_styles[style.name] = style
|
||||||
|
except Exception as e:
|
||||||
|
print(f"加载自定义样式失败: {e}")
|
||||||
|
|
||||||
|
def get_all_styles(self) -> Dict[str, DocumentStyle]:
|
||||||
|
"""获取所有样式(内置+自定义)"""
|
||||||
|
all_styles = deepcopy(self.builtin_styles)
|
||||||
|
all_styles.update(self.custom_styles)
|
||||||
|
return all_styles
|
||||||
|
|
||||||
|
def get_style_names(self) -> List[str]:
|
||||||
|
"""获取所有样式名称"""
|
||||||
|
return list(self.get_all_styles().keys())
|
||||||
|
|
||||||
|
def get_style(self, name: str) -> Optional[DocumentStyle]:
|
||||||
|
"""
|
||||||
|
获取指定样式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: 样式名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
DocumentStyle: 样式对象,如果不存在返回None
|
||||||
|
"""
|
||||||
|
if name in self.builtin_styles:
|
||||||
|
return deepcopy(self.builtin_styles[name])
|
||||||
|
elif name in self.custom_styles:
|
||||||
|
return deepcopy(self.custom_styles[name])
|
||||||
|
return None
|
||||||
|
|
||||||
|
def create_custom_style(self, style: DocumentStyle) -> bool:
|
||||||
|
"""
|
||||||
|
创建自定义样式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
style: 样式对象
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否创建成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
print(f"尝试创建自定义样式: {style.name}") # 调试信息
|
||||||
|
|
||||||
|
if style.name in self.builtin_styles:
|
||||||
|
print(f"错误: {style.name} 是内置样式,不能覆盖")
|
||||||
|
return False # 不能覆盖内置样式
|
||||||
|
|
||||||
|
# 检查样式对象的完整性
|
||||||
|
if not style.body_font:
|
||||||
|
print("错误: 样式缺少body_font")
|
||||||
|
return False
|
||||||
|
if not style.body_paragraph:
|
||||||
|
print("错误: 样式缺少body_paragraph")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("样式对象检查通过")
|
||||||
|
|
||||||
|
# 保存到内存
|
||||||
|
self.custom_styles[style.name] = style
|
||||||
|
print(f"样式已保存到内存: {style.name}")
|
||||||
|
|
||||||
|
# 保存到文件
|
||||||
|
file_result = self.save_style_to_file(style)
|
||||||
|
print(f"文件保存结果: {file_result}")
|
||||||
|
|
||||||
|
if not file_result:
|
||||||
|
# 如果文件保存失败,从内存中移除
|
||||||
|
del self.custom_styles[style.name]
|
||||||
|
print("文件保存失败,已从内存中移除")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"样式 '{style.name}' 创建成功")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"创建自定义样式异常: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_custom_style(self, style: DocumentStyle) -> bool:
|
||||||
|
"""
|
||||||
|
更新自定义样式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
style: 样式对象
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否更新成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if style.name in self.builtin_styles:
|
||||||
|
return False # 不能修改内置样式
|
||||||
|
|
||||||
|
self.custom_styles[style.name] = style
|
||||||
|
self.save_style_to_file(style)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"更新自定义样式失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete_custom_style(self, name: str) -> bool:
|
||||||
|
"""
|
||||||
|
删除自定义样式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: 样式名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否删除成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if name in self.builtin_styles:
|
||||||
|
return False # 不能删除内置样式
|
||||||
|
|
||||||
|
if name in self.custom_styles:
|
||||||
|
del self.custom_styles[name]
|
||||||
|
# 删除文件
|
||||||
|
filepath = os.path.join(self.styles_dir, f"{name}.json")
|
||||||
|
if os.path.exists(filepath):
|
||||||
|
os.remove(filepath)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"删除自定义样式失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def save_style_to_file(self, style: DocumentStyle, filepath: Optional[str] = None) -> bool:
|
||||||
|
"""
|
||||||
|
保存样式到文件
|
||||||
|
|
||||||
|
Args:
|
||||||
|
style: 样式对象
|
||||||
|
filepath: 文件路径,如果为None则使用默认路径
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否保存成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if filepath is None:
|
||||||
|
os.makedirs(self.styles_dir, exist_ok=True)
|
||||||
|
filepath = os.path.join(self.styles_dir, f"{style.name}.json")
|
||||||
|
|
||||||
|
print(f"保存样式到文件: {filepath}") # 调试信息
|
||||||
|
|
||||||
|
# 检查目录权限
|
||||||
|
if not os.access(self.styles_dir, os.W_OK):
|
||||||
|
print(f"错误: 没有写入权限 - {self.styles_dir}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 尝试序列化样式对象
|
||||||
|
style_dict = asdict(style)
|
||||||
|
print("样式对象序列化成功")
|
||||||
|
|
||||||
|
# 尝试写入文件
|
||||||
|
with open(filepath, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(style_dict, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
print(f"样式文件保存成功: {filepath}")
|
||||||
|
|
||||||
|
# 验证文件是否存在且可读
|
||||||
|
if os.path.exists(filepath) and os.path.getsize(filepath) > 0:
|
||||||
|
print("文件存在性验证通过")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print("错误: 文件保存后验证失败")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"保存样式文件异常: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def load_style_from_file(self, filepath: str) -> Optional[DocumentStyle]:
|
||||||
|
"""
|
||||||
|
从文件加载样式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filepath: 文件路径
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
DocumentStyle: 样式对象,加载失败返回None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
|
style_dict = json.load(f)
|
||||||
|
|
||||||
|
# 递归转换字典为dataclass
|
||||||
|
style = self._dict_to_style(style_dict)
|
||||||
|
return style
|
||||||
|
except Exception as e:
|
||||||
|
print(f"加载样式文件失败: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _dict_to_style(self, data: Dict[str, Any]) -> DocumentStyle:
|
||||||
|
"""将字典转换为DocumentStyle对象"""
|
||||||
|
# 转换嵌套的字体样式
|
||||||
|
if 'body_font' in data and data['body_font']:
|
||||||
|
data['body_font'] = FontStyle(**data['body_font'])
|
||||||
|
|
||||||
|
# 转换段落样式
|
||||||
|
if 'body_paragraph' in data and data['body_paragraph']:
|
||||||
|
data['body_paragraph'] = ParagraphStyle(**data['body_paragraph'])
|
||||||
|
|
||||||
|
# 转换标题样式
|
||||||
|
if 'heading_styles' in data and data['heading_styles']:
|
||||||
|
heading_styles = {}
|
||||||
|
for level, heading_data in data['heading_styles'].items():
|
||||||
|
if isinstance(heading_data, dict):
|
||||||
|
font_data = heading_data.get('font', {})
|
||||||
|
para_data = heading_data.get('paragraph', {})
|
||||||
|
heading_styles[int(level)] = HeadingStyle(
|
||||||
|
font=FontStyle(**font_data) if font_data else FontStyle(),
|
||||||
|
paragraph=ParagraphStyle(**para_data) if para_data else ParagraphStyle(),
|
||||||
|
numbering=heading_data.get('numbering', False),
|
||||||
|
outline_level=heading_data.get('outline_level', int(level))
|
||||||
|
)
|
||||||
|
data['heading_styles'] = heading_styles
|
||||||
|
|
||||||
|
# 转换列表样式
|
||||||
|
for list_type in ['unordered_list', 'ordered_list']:
|
||||||
|
if list_type in data and data[list_type]:
|
||||||
|
list_data = data[list_type]
|
||||||
|
font_data = list_data.get('font', {})
|
||||||
|
para_data = list_data.get('paragraph', {})
|
||||||
|
data[list_type] = ListStyle(
|
||||||
|
font=FontStyle(**font_data) if font_data else FontStyle(),
|
||||||
|
paragraph=ParagraphStyle(**para_data) if para_data else ParagraphStyle(),
|
||||||
|
bullet_symbol=list_data.get('bullet_symbol', '•'),
|
||||||
|
number_format=list_data.get('number_format', '1.')
|
||||||
|
)
|
||||||
|
|
||||||
|
# 转换特殊样式
|
||||||
|
for special_type in ['code_block', 'quote_block', 'table_style']:
|
||||||
|
if special_type in data and data[special_type]:
|
||||||
|
special_data = data[special_type]
|
||||||
|
font_data = special_data.get('font', {})
|
||||||
|
para_data = special_data.get('paragraph', {})
|
||||||
|
data[special_type] = SpecialStyle(
|
||||||
|
font=FontStyle(**font_data) if font_data else FontStyle(),
|
||||||
|
paragraph=ParagraphStyle(**para_data) if para_data else ParagraphStyle(),
|
||||||
|
background_color=special_data.get('background_color', '#F5F5F5'),
|
||||||
|
border=special_data.get('border', False)
|
||||||
|
)
|
||||||
|
|
||||||
|
return DocumentStyle(**data)
|
||||||
|
|
||||||
|
def export_style(self, name: str, export_path: str) -> bool:
|
||||||
|
"""
|
||||||
|
导出样式到指定路径
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: 样式名称
|
||||||
|
export_path: 导出路径
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否导出成功
|
||||||
|
"""
|
||||||
|
style = self.get_style(name)
|
||||||
|
if style:
|
||||||
|
return self.save_style_to_file(style, export_path)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def import_style(self, import_path: str) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
从指定路径导入样式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
import_path: 导入路径
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[str]: 导入的样式名称,失败返回None
|
||||||
|
"""
|
||||||
|
style = self.load_style_from_file(import_path)
|
||||||
|
if style:
|
||||||
|
if self.create_custom_style(style):
|
||||||
|
return style.name
|
||||||
|
return None
|
||||||
|
|
||||||
|
def duplicate_style(self, source_name: str, new_name: str) -> bool:
|
||||||
|
"""
|
||||||
|
复制样式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
source_name: 源样式名称
|
||||||
|
new_name: 新样式名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否复制成功
|
||||||
|
"""
|
||||||
|
source_style = self.get_style(source_name)
|
||||||
|
if source_style and new_name not in self.get_all_styles():
|
||||||
|
source_style.name = new_name
|
||||||
|
source_style.description = f"基于 {source_name} 复制"
|
||||||
|
return self.create_custom_style(source_style)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 创建全局样式管理器实例
|
||||||
|
style_manager = StyleManager()
|
||||||
|
|
||||||
|
|
||||||
|
# 兼容性函数
|
||||||
|
def get_all_styles() -> Dict[str, DocumentStyle]:
|
||||||
|
"""获取所有样式(兼容旧接口)"""
|
||||||
|
return style_manager.get_all_styles()
|
||||||
|
|
||||||
|
|
||||||
|
def get_style(name: str) -> Optional[DocumentStyle]:
|
||||||
|
"""获取指定样式(兼容旧接口)"""
|
||||||
|
return style_manager.get_style(name)
|
||||||
0
test_chinese_splitting.py
Normal file
0
test_chinese_splitting.py
Normal file
0
test_image_splitting.py
Normal file
0
test_image_splitting.py
Normal file
0
test_image_text_splitting.py
Normal file
0
test_image_text_splitting.py
Normal file
0
test_image_text_splitting_debug.py
Normal file
0
test_image_text_splitting_debug.py
Normal file
0
test_image_with_text_splitting.py
Normal file
0
test_image_with_text_splitting.py
Normal file
BIN
test_output/test_image_text_splitting.docx
Normal file
BIN
test_output/test_image_text_splitting.docx
Normal file
Binary file not shown.
BIN
test_output/test_image_text_splitting_debug.docx
Normal file
BIN
test_output/test_image_text_splitting_debug.docx
Normal file
Binary file not shown.
BIN
test_output/test_image_with_text_splitting.docx
Normal file
BIN
test_output/test_image_with_text_splitting.docx
Normal file
Binary file not shown.
1
test_segment.txt
Normal file
1
test_segment.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
这是一个测试文本。它包含多个句子。每个句子都很短。但是我们需要测试分段排版功能。当文本长度超过最小段落长度时。应该被分割成多个段落。这样可以提高文档的可读性。让内容更加清晰易懂。
|
||||||
35
test_segment_function.py
Normal file
35
test_segment_function.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""测试分段排版功能"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
from text_splitter import TextSplitter
|
||||||
|
|
||||||
|
# 测试文本
|
||||||
|
test_text = """这是一个测试文本。它包含多个句子。每个句子都很短。但是我们需要测试分段排版功能。
|
||||||
|
当文本长度超过最小段落长度时。应该被分割成多个段落。这样可以提高文档的可读性。
|
||||||
|
让内容更加清晰易懂。"""
|
||||||
|
|
||||||
|
def test_text_splitting():
|
||||||
|
print("=== 测试分段排版功能 ===")
|
||||||
|
print(f"原始文本长度: {len(test_text)} 字符")
|
||||||
|
print(f"原始文本: {test_text}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 创建分段器
|
||||||
|
splitter = TextSplitter(min_length=50, max_length=200)
|
||||||
|
|
||||||
|
# 分段处理
|
||||||
|
paragraphs = splitter.split_text(test_text)
|
||||||
|
|
||||||
|
print(f"分段结果 ({len(paragraphs)} 个段落):")
|
||||||
|
for i, paragraph in enumerate(paragraphs, 1):
|
||||||
|
print(f"段落 {i} ({len(paragraph)} 字符): {paragraph}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=== 测试完成 ===")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_text_splitting()
|
||||||
0
test_split_behavior.py
Normal file
0
test_split_behavior.py
Normal file
0
test_text_processor_splitting.py
Normal file
0
test_text_processor_splitting.py
Normal file
0
test_text_splitter.py
Normal file
0
test_text_splitter.py
Normal file
140
text_splitter.py
Normal file
140
text_splitter.py
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
class TextSplitter:
|
||||||
|
def __init__(self, min_length=100, max_length=300):
|
||||||
|
"""
|
||||||
|
初始化文本分段器
|
||||||
|
:param min_length: 目标段落最小长度
|
||||||
|
:param max_length: 目标段落最大长度
|
||||||
|
"""
|
||||||
|
self.min_length = min_length
|
||||||
|
self.max_length = max_length
|
||||||
|
# 匹配标点符号的正则表达式,作为分段点(中文和英文标点)
|
||||||
|
# 这些标点符号通常表示一个完整句子的结束
|
||||||
|
self.sentence_ending_punct = re.compile(r'([。?!.!?])')
|
||||||
|
|
||||||
|
def split_text(self, text):
|
||||||
|
"""
|
||||||
|
将文本分割成符合长度要求的段落,仅使用标点符号分割
|
||||||
|
:param text: 待分割的原始文本
|
||||||
|
:return: 分割后的段落列表
|
||||||
|
"""
|
||||||
|
if not text:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# 自动判断原始文本长度
|
||||||
|
original_length = len(text)
|
||||||
|
print(f"原始文本长度: {original_length} 字符")
|
||||||
|
|
||||||
|
# 如果原始文本长度小于最小长度,直接返回
|
||||||
|
if original_length <= self.min_length:
|
||||||
|
return [text.strip()]
|
||||||
|
|
||||||
|
# 将文本分割成完整句子(保留标点符号)
|
||||||
|
parts = self.sentence_ending_punct.split(text)
|
||||||
|
sentences = []
|
||||||
|
|
||||||
|
# 重组句子,确保标点符号与前面的文本在一起
|
||||||
|
for i in range(0, len(parts)-1, 2):
|
||||||
|
sentence = (parts[i] + parts[i+1]).strip()
|
||||||
|
if sentence: # 跳过空句子
|
||||||
|
sentences.append(sentence)
|
||||||
|
|
||||||
|
# 如果没有找到任何标点符号,将整个文本作为一个段落
|
||||||
|
if not sentences:
|
||||||
|
return [text.strip()]
|
||||||
|
|
||||||
|
# 合并句子形成段落,确保在长度范围内
|
||||||
|
paragraphs = []
|
||||||
|
current_paragraph = ""
|
||||||
|
|
||||||
|
for sentence in sentences:
|
||||||
|
# 尝试添加当前句子
|
||||||
|
temp = current_paragraph + (" " if current_paragraph else "") + sentence
|
||||||
|
|
||||||
|
# 检查添加后是否超出最大长度
|
||||||
|
if len(temp) > self.max_length:
|
||||||
|
# 如果当前段落不为空,先保存当前段落
|
||||||
|
if current_paragraph:
|
||||||
|
paragraphs.append(current_paragraph)
|
||||||
|
current_paragraph = sentence
|
||||||
|
else:
|
||||||
|
# 如果单个句子就超过最大长度,也必须接受(避免分割句子)
|
||||||
|
paragraphs.append(sentence)
|
||||||
|
current_paragraph = ""
|
||||||
|
else:
|
||||||
|
current_paragraph = temp
|
||||||
|
|
||||||
|
# 添加最后一个段落
|
||||||
|
if current_paragraph:
|
||||||
|
paragraphs.append(current_paragraph)
|
||||||
|
|
||||||
|
# 检查是否有段落短于最小长度,如果有则与下一段合并
|
||||||
|
i = 0
|
||||||
|
while i < len(paragraphs) - 1:
|
||||||
|
if len(paragraphs[i]) < self.min_length:
|
||||||
|
# 合并当前段落和下一段落
|
||||||
|
paragraphs[i] = paragraphs[i] + " " + paragraphs[i+1]
|
||||||
|
del paragraphs[i+1]
|
||||||
|
else:
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
print(f"分割后段落数量: {len(paragraphs)}")
|
||||||
|
return paragraphs
|
||||||
|
|
||||||
|
# 使用示例
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 示例文本
|
||||||
|
sample_text = """
|
||||||
|
最近,晓蕾又上热搜了!
|
||||||
|
|
||||||
|
咋回事呢?原来,她和老公刘剑一起开了直播带货的副业。但特意声明:她早就离开了上海电视台的编制,也不拿电视台的工资。换句话说,现在卖东西,完全是私营业态。
|
||||||
|
|
||||||
|
这事儿一下子引爆了大家的八卦魂。毕竟,明星主持扎堆直播间,也不算新鲜事。但还是挺多人纳闷:这些当年的 "话筒头牌",是不是集体选择摆烂了?
|
||||||
|
|
||||||
|
其实,晓蕾和刘剑干脆落落大方,在直播间直接回应了这点。俩人意思很明确:“我们不是来拉低职业口碑的”。而且还耐心解释了自己转行的理由。
|
||||||
|
曾经的大佬,变成了烟火气
|
||||||
|
|
||||||
|
说到晓蕾,不了解点她背景都不好意思讨论人家。当年上视新闻部的 "当家花旦",光学历和气质,足够秒杀隔壁主持圈的八条街。而刘剑,早年可是 "台柱子",播音腔精致到令人耳膜怀孕。照理来说,这样一对,在编制铁饭碗里躺平一辈子没毛病。
|
||||||
|
|
||||||
|
可人家偏不。
|
||||||
|
|
||||||
|
晓蕾说过这样一句话:“其实,我就是个普通人。” 真的那么普通吗?她不这么说,没人敢忘了她的标杆履历啊!她离开台里后,居然一头扎进了童语言教育这个赛道,一干就是十年,让机构做到了业内小圈子的爆款水准。
|
||||||
|
|
||||||
|
而这次直播,打的商品也不混乱,主打性价比和实用属性,晓蕾每件商品还得亲测过。如果你觉得她自吹自擂,建议去看看她直播间的粉丝评论。大家的意思是:晓蕾推品 = 放心买。
|
||||||
|
刘剑这枚 “前一哥”,更狠!
|
||||||
|
|
||||||
|
说晓蕾牛,别忘了,刘剑十年前也上演了一场 “豪赌”。那个年代,辞去电视台稳定工作,和 “打水漂” 差不多。
|
||||||
|
|
||||||
|
可是刘剑敢把梭全下,为啥?因为他看中了播音考生和辅导课程的市场,那时还没有多少人扎堆干这块,他觉得这是个机会。
|
||||||
|
|
||||||
|
果然,就这么辞了职,工作的腰板从跟组织吃工资,摇身变成了名副其实的事业单位 —— 自己家老板。虽然后来也是磕磕绊绊,但终究从试验田里掘出了一片肥沃地。
|
||||||
|
主持人的 “下海”,是换方向走
|
||||||
|
|
||||||
|
有人觉得,曾经的新闻人、主持人 “跑去带货”,肯定是混不下去了。你要放在十年前,这种联想不稀奇,可现在不一样了。大环境变了,传统媒体是真的在互联网时代被打败得找不到调。
|
||||||
|
|
||||||
|
原来电视频道的观众,现在早转移到手机端,看知乎、刷短视频,甚至晚上蹲个带货直播会。你说新闻节目的高冷主播,现在换脸做带货主持,是不是 “落魄”?未必。
|
||||||
|
|
||||||
|
晓蕾夫妻这一波,实际上是转型很成功的范例。不管带啥网红货,他们俩把品质第一的逻辑摆明白了。这样的主播,不止卖产品,更卖信誉,靠着时间积攒了观众的信任。
|
||||||
|
直播间哪门子 LOW?明明是主战场
|
||||||
|
|
||||||
|
网友说得有趣:“谁嫌直播带货 LOW,谁就输定了。” 道理没跑儿,移动互联网成了咱生活重心,生意也跟着迁移。这是明显趋势,看不懂的还真不想赚钱了。
|
||||||
|
|
||||||
|
而且,做直播一点不轻松。站几个小时口播、随时照顾弹幕情绪,这比坐着念提词器辛苦多了。像晓蕾和刘剑这样的 “摸鱼资历”,能转过身来赚饭钱,这不是 “混”,是 “拼” 啊。
|
||||||
|
|
||||||
|
别说传统意义的职业崇拜消失殆尽,你觉得稳如狗的岗位,说散架就散。老一辈金饭碗情结,对于下一代新创别说香,而是种被淘汰跑赢速度内心创新积极点。
|
||||||
|
|
||||||
|
我不是电视台员工了,早就离职 10 年了。"""
|
||||||
|
|
||||||
|
# 创建分段器实例,设置目标段落长度范围
|
||||||
|
splitter = TextSplitter(min_length=10, max_length=20)
|
||||||
|
|
||||||
|
# 分割文本
|
||||||
|
paragraphs = splitter.split_text(sample_text)
|
||||||
|
|
||||||
|
# 打印结果
|
||||||
|
print("\n分割结果:")
|
||||||
|
for i, para in enumerate(paragraphs, 1):
|
||||||
|
|
||||||
|
print(para)
|
||||||
|
|
||||||
126
专业排版样式优化报告.md
Normal file
126
专业排版样式优化报告.md
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
# TXT2DOCX 专业排版样式优化报告
|
||||||
|
|
||||||
|
## 🎯 优化目标
|
||||||
|
|
||||||
|
针对用户反馈"文章排版中的模板排版出来的效果都一样,太简陋了"的问题,我们重新设计了整个排版样式系统,创建了8种专业的排版样式,参考了爆款文章、公众号等平台的设计理念。
|
||||||
|
|
||||||
|
## ✨ 新增功能亮点
|
||||||
|
|
||||||
|
### 8种专业排版样式
|
||||||
|
|
||||||
|
我们为您精心设计了8种不同风格的专业排版样式:
|
||||||
|
|
||||||
|
1. **🔥 爆款文章风格**
|
||||||
|
- 参考知乎、头条等平台热门文章
|
||||||
|
- 特点:层次分明,吸引眼球,高阅读量风格
|
||||||
|
- 正文:微软雅黑 14pt,行距1.8倍
|
||||||
|
- 主标题:黑体 22pt,橙红色(#FF4500),居中
|
||||||
|
- 适用:网络热文、病毒式传播内容
|
||||||
|
|
||||||
|
2. **📱 微信公众号风格**
|
||||||
|
- 专业的新媒体排版标准
|
||||||
|
- 特点:阅读体验佳,专业感强
|
||||||
|
- 正文:微软雅黑 14pt,行距1.75倍
|
||||||
|
- 主标题:黑体 20pt,微信绿(#1AAD19),居中
|
||||||
|
- 适用:公众号文章、新媒体内容
|
||||||
|
|
||||||
|
3. **🎓 知乎高赞回答风格**
|
||||||
|
- 逻辑清晰,层次分明,专业权威
|
||||||
|
- 特点:知识型内容,理性分析
|
||||||
|
- 正文:微软雅黑 15pt,行距1.6倍
|
||||||
|
- 主标题:黑体 20pt,知乎蓝(#0084FF)
|
||||||
|
- 适用:知识分享、深度分析文章
|
||||||
|
|
||||||
|
4. **🌸 小红书笔记风格**
|
||||||
|
- 清新文艺,少女心十足
|
||||||
|
- 特点:适合生活方式、美妆、旅行类内容
|
||||||
|
- 正文:华文细黑 14pt,行距1.8倍
|
||||||
|
- 主标题:华文细黑 18pt,粉色(#FF69B4),居中
|
||||||
|
- 适用:生活分享、时尚美妆内容
|
||||||
|
|
||||||
|
5. **📰 今日头条新闻风格**
|
||||||
|
- 信息密度高,节奏紧凑
|
||||||
|
- 特点:突出重点,新闻感强
|
||||||
|
- 正文:微软雅黑 14pt,行距1.5倍
|
||||||
|
- 主标题:黑体 19pt,新闻红(#D43F3A)
|
||||||
|
- 适用:新闻报道、时事评论
|
||||||
|
|
||||||
|
6. **🎮 B站UP主视频脚本风格**
|
||||||
|
- 轻松活泼,年轻化表达
|
||||||
|
- 特点:有趣有料,适合年轻受众
|
||||||
|
- 正文:微软雅黑 14pt,行距1.6倍
|
||||||
|
- 主标题:黑体 18pt,B站蓝(#00A1D6)
|
||||||
|
- 适用:娱乐内容、视频脚本
|
||||||
|
|
||||||
|
7. **💼 企业微信群通知风格**
|
||||||
|
- 正式严肃,商务风格
|
||||||
|
- 特点:信息传达清晰,专业感强
|
||||||
|
- 正文:宋体 14pt,行距1.5倍,首行缩进
|
||||||
|
- 主标题:黑体 18pt,商务蓝(#1F4E79),居中
|
||||||
|
- 适用:商务文档、正式通知
|
||||||
|
|
||||||
|
8. **💝 情感鸡汤文风格**
|
||||||
|
- 温暖治愈,情感丰富
|
||||||
|
- 特点:适合心灵鸡汤、情感类内容
|
||||||
|
- 正文:楷体 14pt,行距1.8倍,首行缩进
|
||||||
|
- 主标题:华文行楷 18pt,温暖粉(#E91E63),居中
|
||||||
|
- 适用:情感文章、心灵鸡汤
|
||||||
|
|
||||||
|
## 🔧 技术实现
|
||||||
|
|
||||||
|
### 样式系统架构
|
||||||
|
|
||||||
|
- **模块化设计**:每种样式独立配置,便于管理和扩展
|
||||||
|
- **精细化控制**:
|
||||||
|
- 字体:名称、大小、粗细、颜色
|
||||||
|
- 段落:行距、段前段后间距、首行缩进、对齐方式
|
||||||
|
- 标题:6级标题差异化设计
|
||||||
|
- 特殊元素:引用块、代码块、表格样式
|
||||||
|
|
||||||
|
### 核心特性
|
||||||
|
|
||||||
|
1. **差异化设计**:每种样式都有独特的视觉特征
|
||||||
|
2. **专业配色**:参考各平台品牌色彩体系
|
||||||
|
3. **层次分明**:6级标题递进式设计
|
||||||
|
4. **可扩展性**:支持自定义样式创建
|
||||||
|
|
||||||
|
## 📊 优化效果对比
|
||||||
|
|
||||||
|
### 优化前
|
||||||
|
- 样式单一,缺乏个性
|
||||||
|
- 排版效果千篇一律
|
||||||
|
- 无法适应不同内容类型
|
||||||
|
|
||||||
|
### 优化后
|
||||||
|
- 8种专业样式,风格多样
|
||||||
|
- 针对不同平台和内容类型优化
|
||||||
|
- 层次分明,视觉冲击力强
|
||||||
|
- 符合现代新媒体审美标准
|
||||||
|
|
||||||
|
## 🎨 使用建议
|
||||||
|
|
||||||
|
### 样式选择指南
|
||||||
|
|
||||||
|
| 内容类型 | 推荐样式 | 理由 |
|
||||||
|
|---------|---------|------|
|
||||||
|
| 网络热文 | 爆款文章风格 | 吸引眼球,提高点击率 |
|
||||||
|
| 公众号文章 | 微信公众号风格 | 符合平台规范,阅读体验佳 |
|
||||||
|
| 知识分享 | 知乎高赞回答风格 | 专业权威,逻辑清晰 |
|
||||||
|
| 生活分享 | 小红书笔记风格 | 清新文艺,适合年轻用户 |
|
||||||
|
| 新闻报道 | 今日头条新闻风格 | 信息密度高,突出重点 |
|
||||||
|
| 娱乐内容 | B站UP主视频脚本风格 | 轻松活泼,有趣有料 |
|
||||||
|
| 商务文档 | 企业微信群通知风格 | 正式严肃,专业感强 |
|
||||||
|
| 情感文章 | 情感鸡汤文风格 | 温暖治愈,情感共鸣 |
|
||||||
|
|
||||||
|
## 🚀 后续规划
|
||||||
|
|
||||||
|
1. **用户反馈收集**:根据实际使用效果持续优化
|
||||||
|
2. **样式扩展**:考虑增加更多平台风格(如抖音、快手等)
|
||||||
|
3. **智能推荐**:基于内容类型自动推荐合适样式
|
||||||
|
4. **自定义样式**:提供更强大的自定义样式编辑功能
|
||||||
|
|
||||||
|
## 📝 总结
|
||||||
|
|
||||||
|
通过本次优化,我们彻底改变了原有排版系统的单一问题,为用户提供了丰富多样的专业排版选择。每种样式都经过精心设计,参考了相应平台的成功案例,确保输出的文档具有专业水准和视觉冲击力。
|
||||||
|
|
||||||
|
现在,无论是要制作爆款网文、专业公众号文章,还是知识分享内容,都能找到最合适的排版样式,让您的文档脱颖而出!
|
||||||
33
启动程序.bat
Normal file
33
启动程序.bat
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
title TXT2DOCX - 智能文本转Word文档工具
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ==========================================
|
||||||
|
echo TXT2DOCX - 智能文本转Word文档工具
|
||||||
|
echo ==========================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
echo 正在启动程序...
|
||||||
|
echo.
|
||||||
|
|
||||||
|
python main.py
|
||||||
|
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo.
|
||||||
|
echo ❌ 程序启动失败!
|
||||||
|
echo.
|
||||||
|
echo 可能的解决方案:
|
||||||
|
echo 1. 确保已安装 Python 3.7+
|
||||||
|
echo 2. 安装必要依赖:
|
||||||
|
echo pip install python-docx pillow tkinter
|
||||||
|
echo 3. 运行项目检查:python check_project.py
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
) else (
|
||||||
|
echo.
|
||||||
|
echo ✅ 程序已正常关闭
|
||||||
|
echo.
|
||||||
|
)
|
||||||
|
|
||||||
|
pause
|
||||||
Loading…
Reference in New Issue
Block a user