恢复高级编辑功能

This commit is contained in:
taiyi 2025-09-26 15:49:06 +08:00
parent a91445add9
commit 1e61dd581f
34 changed files with 53585 additions and 0 deletions

325
README.md Normal file
View 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. 有序列表项
`行内代码`
```代码块```
![图片](path/to/image.png)
```
## ⚙️ 配置说明
### 样式配置
样式配置文件位于 `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
View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

BIN
build/main/PYZ-00.pyz Normal file

Binary file not shown.

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

Binary file not shown.

BIN
build/main/main.pkg Normal file

Binary file not shown.

264
build/main/warn-main.txt Normal file
View 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

File diff suppressed because it is too large Load Diff

120
check_project.py Normal file
View 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
View 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
View 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
View 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

Binary file not shown.

243
gui_style_manager.py Normal file
View 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
View 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)

View File

0
test_image_splitting.py Normal file
View File

View File

View File

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
test_segment.txt Normal file
View File

@ -0,0 +1 @@
这是一个测试文本。它包含多个句子。每个句子都很短。但是我们需要测试分段排版功能。当文本长度超过最小段落长度时。应该被分割成多个段落。这样可以提高文档的可读性。让内容更加清晰易懂。

35
test_segment_function.py Normal file
View 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
View File

View File

0
test_text_splitter.py Normal file
View File

140
text_splitter.py Normal file
View 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)

View 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倍
- 主标题:黑体 18ptB站蓝#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
View 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