更新 ArticleReplaceBatch/txt2md2docx.py

更新打包软件
This commit is contained in:
taiyi 2025-08-11 15:43:33 +08:00
parent 1d52220187
commit b0a6e3e3bf

View File

@ -7,6 +7,9 @@ from docx.shared import Inches, Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
import PySimpleGUI as sg
from replacestr import replace_text
import configparser # 新增:导入配置文件处理模块
CONFIG_FILE_PATH = os.path.join(os.path.expanduser("~"), ".txt2md2docx.ini")
# 配置设置
class Config:
@ -15,9 +18,12 @@ class Config:
self.txt_encoding = "utf-8"
self.match_pattern = "exact" # exact: 完全匹配, prefix: 前缀匹配, contains: 包含
self.output_location = "txt_folder" # txt_folder or custom
# 最近使用的文件夹路径 - 新增
self.last_txt_folder = ""
self.last_images_root = ""
self.last_output_root = ""
# 文字处理
# self.is_replace_str = 'true'
self.reverse_text_order = False # 新增:转换文字顺序开关
self.reverse_text_order = False # 转换文字顺序开关
# 图片处理配置
self.image_sort_by = "name" # name or time
self.image_resize = "none" # none or width
@ -27,14 +33,137 @@ class Config:
# 文档格式配置
self.line_spacing = 1.5
self.title_levels = 6 # 支持的最大标题层级
self.replace_punctuation = False # 是否替换标点符号
self.add_disclaimer = False # 是否添加免责声明
# 新增:从配置文件加载配置
def load_from_file(self, file_path):
if not os.path.exists(file_path):
return False
config_parser = configparser.ConfigParser()
config_parser.read(file_path, encoding='utf-8')
# 加载文件处理配置
if 'FileHandling' in config_parser:
self.txt_encoding = config_parser.get('FileHandling', 'txt_encoding', fallback=self.txt_encoding)
self.match_pattern = config_parser.get('FileHandling', 'match_pattern', fallback=self.match_pattern)
self.output_location = config_parser.get('FileHandling', 'output_location',
fallback=self.output_location)
self.last_txt_folder = config_parser.get('FileHandling', 'last_txt_folder',
fallback=self.last_txt_folder)
self.last_images_root = config_parser.get('FileHandling', 'last_images_root',
fallback=self.last_images_root)
self.last_output_root = config_parser.get('FileHandling', 'last_output_root',
fallback=self.last_output_root)
# 加载文字处理配置
if 'TextProcessing' in config_parser:
self.reverse_text_order = config_parser.getboolean('TextProcessing', 'reverse_text_order',
fallback=self.reverse_text_order)
self.replace_punctuation = config_parser.getboolean('TextProcessing', 'replace_punctuation',
fallback=self.replace_punctuation)
self.add_disclaimer = config_parser.getboolean('TextProcessing', 'add_disclaimer',
fallback=self.add_disclaimer)
# 加载图片处理配置
if 'ImageProcessing' in config_parser:
self.image_sort_by = config_parser.get('ImageProcessing', 'image_sort_by', fallback=self.image_sort_by)
self.image_resize = config_parser.get('ImageProcessing', 'image_resize', fallback=self.image_resize)
self.image_width = config_parser.getfloat('ImageProcessing', 'image_width', fallback=self.image_width)
self.image_alignment = config_parser.get('ImageProcessing', 'image_alignment',
fallback=self.image_alignment)
self.image_strategy = config_parser.get('ImageProcessing', 'image_strategy',
fallback=self.image_strategy)
# 加载文档格式配置
if 'DocumentFormat' in config_parser:
self.line_spacing = config_parser.getfloat('DocumentFormat', 'line_spacing', fallback=self.line_spacing)
self.title_levels = config_parser.getint('DocumentFormat', 'title_levels', fallback=self.title_levels)
return True
# 新增:保存配置到文件
def save_to_file(self, file_path):
config_parser = configparser.ConfigParser()
# 保存文件处理配置
config_parser['FileHandling'] = {
'txt_encoding': self.txt_encoding,
'match_pattern': self.match_pattern,
'output_location': self.output_location,
'last_txt_folder': self.last_txt_folder,
'last_images_root': self.last_images_root,
'last_output_root': self.last_output_root
}
# 保存文字处理配置
config_parser['TextProcessing'] = {
'reverse_text_order': str(self.reverse_text_order),
'replace_punctuation': str(self.replace_punctuation),
'add_disclaimer': str(self.add_disclaimer)
}
# 保存图片处理配置
config_parser['ImageProcessing'] = {
'image_sort_by': self.image_sort_by,
'image_resize': self.image_resize,
'image_width': str(self.image_width),
'image_alignment': self.image_alignment,
'image_strategy': self.image_strategy
}
# 保存文档格式配置
config_parser['DocumentFormat'] = {
'line_spacing': str(self.line_spacing),
'title_levels': str(self.title_levels)
}
with open(file_path, 'w', encoding='utf-8') as f:
config_parser.write(f)
return True
# 全局配置实例
config = Config()
# 新增:尝试加载配置文件
config.load_from_file(CONFIG_FILE_PATH)
# 添加文字处理工具类可放在FileHandler类之后
class TextProcessor:
@staticmethod
def replace_periods(text):
# 去除文本首尾的空白字符
text = text.strip()
if not text:
return ""
# 检查最后一个字符是否为句号
last_char = text[-1]
has_ending_period = (last_char == '')
# 如果最后一个字符是句号,先去掉它再处理
if has_ending_period:
content = text[:-1]
else:
content = text
# 将所有句号替换为逗号
content = content.replace('', '')
# 如果原文本最后有句号,在处理后的内容末尾加上句号
if has_ending_period:
result = content + ''
else:
result = content
return result
@staticmethod
def reverse_text_order(content):
"""反转文本顺序(按字符级反转)"""
@ -389,6 +518,7 @@ class ImageProcessor:
else:
return WD_ALIGN_PARAGRAPH.CENTER
DISCLAIMER_TEXT = """[免责声明]文章的时间、过程、图片均来自于网络,文章旨在传播正能量,均无低俗等不良引导,请观众勿对号入座,并上升到人身攻击等方面。观众理性看待本事件,切勿留下主观臆断的恶意评论,互联网不是法外之地。本文如若真实性存在争议、事件版权或图片侵权问题,请及时联系作者,我们将予以删除。"""
# DOCX生成模块
class DocxGenerator:
@ -460,6 +590,16 @@ class DocxGenerator:
for para in paragraphs[1:]:
DocxGenerator.add_formatted_paragraph(doc, para)
# 新增:在文档末尾添加免责声明
if config.add_disclaimer:
# 添加分隔线
doc.add_paragraph("---")
# 添加免责声明段落
para = doc.add_paragraph()
run = para.add_run(DISCLAIMER_TEXT)
run.font.size = Pt(10) # 可设置较小字体
para.paragraph_format.line_spacing = 1.0 # 紧凑行距
try:
doc.save(output_path)
if progress_callback:
@ -475,6 +615,10 @@ class DocxGenerator:
para_type = paragraph_data['type']
formatting = paragraph_data['formatting']
# 新增:处理标点符号替换
if config.replace_punctuation:
content = TextProcessor.replace_periods(content)
if para_type == 'unordered_list':
para = doc.add_paragraph(style='List Bullet')
text = content[2:].strip()
@ -625,7 +769,13 @@ def show_config_window():
sg.Radio('包含匹配', 'match',
default=config.match_pattern == "contains", key='match_contains')],
[sg.HSeparator()],
[sg.Checkbox('转换文字顺序', key='-REVERSE_TEXT-', default=False)],
[sg.Checkbox('转换文字顺序', key='-REVERSE_TEXT-', default=config.reverse_text_order)],
[sg.HSeparator()],
[sg.Checkbox('替换标点符号(句号转逗号,保留结尾句号)',
key='-REPLACE_PUNCTUATION-',
default=config.replace_punctuation)],
[sg.HSeparator()],
[sg.Checkbox('添加免责声明', key='-ADD_DISCLAIMER-', default=config.add_disclaimer)],
[sg.HSeparator()],
[sg.Radio('输出到TXT文件所在文件夹', 'output_loc',
default=config.output_location == "txt_folder", key='output_txt_folder'),
@ -673,10 +823,11 @@ def show_config_window():
# 保存输出位置设置
config.output_location = "txt_folder" if values['output_txt_folder'] else "custom"
# config.is_replace_str = "true" if values['is_replace_str'] else "false"
config.image_sort_by = "name" if values['sort_name'] else "time"
config.image_resize = "none" if values['resize_none'] else "width"
config.reverse_text_order = values['-REVERSE_TEXT-']
config.replace_punctuation = values['-REPLACE_PUNCTUATION-']
config.add_disclaimer = values['-ADD_DISCLAIMER-']
try:
config.image_width = float(values['image_width'])
@ -697,6 +848,9 @@ def show_config_window():
else:
config.image_strategy = "repeat_last"
# 新增:保存配置到文件
config.save_to_file(CONFIG_FILE_PATH)
break
window.close()
@ -909,6 +1063,12 @@ def main_window():
event, values = window.read()
if event in (sg.WIN_CLOSED, '退出'):
# 只有在窗口未关闭时,才尝试读取 values
if values is not None:
config.last_txt_folder = values.get('txt_folder', '')
config.last_images_root = values.get('images_root', '')
config.last_output_root = values.get('output_root', '')
config.save_to_file(CONFIG_FILE_PATH)
break
if event == '转换设置':
@ -936,6 +1096,13 @@ def main_window():
sg.popup_error('请选择图片根文件夹')
continue
# 保存当前选择的文件夹路径 - 新增
config.last_txt_folder = txt_folder
config.last_images_root = images_root
if values['output_root']:
config.last_output_root = values['output_root']
config.save_to_file(CONFIG_FILE_PATH)
try:
status_text.update('正在扫描TXT文件...')
window.refresh()
@ -959,6 +1126,8 @@ def main_window():
status_text.update(f'扫描完成: 找到 {len(matched_pairs)} 个TXT文件')
# 启用相关按钮
window['-PREVIEW_TABLE-'].update(values=table_data)
window['编辑匹配'].update(disabled=False)
window['开始批量转换'].update(disabled=False)