更新 ArticleReplaceBatch/txt2md2docx.py
更新打包软件
This commit is contained in:
parent
1d52220187
commit
b0a6e3e3bf
@ -7,6 +7,9 @@ from docx.shared import Inches, Pt
|
|||||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||||
import PySimpleGUI as sg
|
import PySimpleGUI as sg
|
||||||
from replacestr import replace_text
|
from replacestr import replace_text
|
||||||
|
import configparser # 新增:导入配置文件处理模块
|
||||||
|
|
||||||
|
CONFIG_FILE_PATH = os.path.join(os.path.expanduser("~"), ".txt2md2docx.ini")
|
||||||
|
|
||||||
# 配置设置
|
# 配置设置
|
||||||
class Config:
|
class Config:
|
||||||
@ -15,9 +18,12 @@ class Config:
|
|||||||
self.txt_encoding = "utf-8"
|
self.txt_encoding = "utf-8"
|
||||||
self.match_pattern = "exact" # exact: 完全匹配, prefix: 前缀匹配, contains: 包含
|
self.match_pattern = "exact" # exact: 完全匹配, prefix: 前缀匹配, contains: 包含
|
||||||
self.output_location = "txt_folder" # txt_folder or custom
|
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_sort_by = "name" # name or time
|
||||||
self.image_resize = "none" # none or width
|
self.image_resize = "none" # none or width
|
||||||
@ -27,14 +33,137 @@ class Config:
|
|||||||
# 文档格式配置
|
# 文档格式配置
|
||||||
self.line_spacing = 1.5
|
self.line_spacing = 1.5
|
||||||
self.title_levels = 6 # 支持的最大标题层级
|
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 = Config()
|
||||||
|
|
||||||
|
# 新增:尝试加载配置文件
|
||||||
|
config.load_from_file(CONFIG_FILE_PATH)
|
||||||
|
|
||||||
|
|
||||||
# 添加文字处理工具类(可放在FileHandler类之后)
|
# 添加文字处理工具类(可放在FileHandler类之后)
|
||||||
class TextProcessor:
|
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
|
@staticmethod
|
||||||
def reverse_text_order(content):
|
def reverse_text_order(content):
|
||||||
"""反转文本顺序(按字符级反转)"""
|
"""反转文本顺序(按字符级反转)"""
|
||||||
@ -389,6 +518,7 @@ class ImageProcessor:
|
|||||||
else:
|
else:
|
||||||
return WD_ALIGN_PARAGRAPH.CENTER
|
return WD_ALIGN_PARAGRAPH.CENTER
|
||||||
|
|
||||||
|
DISCLAIMER_TEXT = """[免责声明]文章的时间、过程、图片均来自于网络,文章旨在传播正能量,均无低俗等不良引导,请观众勿对号入座,并上升到人身攻击等方面。观众理性看待本事件,切勿留下主观臆断的恶意评论,互联网不是法外之地。本文如若真实性存在争议、事件版权或图片侵权问题,请及时联系作者,我们将予以删除。"""
|
||||||
|
|
||||||
# DOCX生成模块
|
# DOCX生成模块
|
||||||
class DocxGenerator:
|
class DocxGenerator:
|
||||||
@ -460,6 +590,16 @@ class DocxGenerator:
|
|||||||
for para in paragraphs[1:]:
|
for para in paragraphs[1:]:
|
||||||
DocxGenerator.add_formatted_paragraph(doc, para)
|
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:
|
try:
|
||||||
doc.save(output_path)
|
doc.save(output_path)
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
@ -475,6 +615,10 @@ class DocxGenerator:
|
|||||||
para_type = paragraph_data['type']
|
para_type = paragraph_data['type']
|
||||||
formatting = paragraph_data['formatting']
|
formatting = paragraph_data['formatting']
|
||||||
|
|
||||||
|
# 新增:处理标点符号替换
|
||||||
|
if config.replace_punctuation:
|
||||||
|
content = TextProcessor.replace_periods(content)
|
||||||
|
|
||||||
if para_type == 'unordered_list':
|
if para_type == 'unordered_list':
|
||||||
para = doc.add_paragraph(style='List Bullet')
|
para = doc.add_paragraph(style='List Bullet')
|
||||||
text = content[2:].strip()
|
text = content[2:].strip()
|
||||||
@ -625,7 +769,13 @@ def show_config_window():
|
|||||||
sg.Radio('包含匹配', 'match',
|
sg.Radio('包含匹配', 'match',
|
||||||
default=config.match_pattern == "contains", key='match_contains')],
|
default=config.match_pattern == "contains", key='match_contains')],
|
||||||
[sg.HSeparator()],
|
[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.HSeparator()],
|
||||||
[sg.Radio('输出到TXT文件所在文件夹', 'output_loc',
|
[sg.Radio('输出到TXT文件所在文件夹', 'output_loc',
|
||||||
default=config.output_location == "txt_folder", key='output_txt_folder'),
|
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.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_sort_by = "name" if values['sort_name'] else "time"
|
||||||
config.image_resize = "none" if values['resize_none'] else "width"
|
config.image_resize = "none" if values['resize_none'] else "width"
|
||||||
config.reverse_text_order = values['-REVERSE_TEXT-']
|
config.reverse_text_order = values['-REVERSE_TEXT-']
|
||||||
|
config.replace_punctuation = values['-REPLACE_PUNCTUATION-']
|
||||||
|
config.add_disclaimer = values['-ADD_DISCLAIMER-']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config.image_width = float(values['image_width'])
|
config.image_width = float(values['image_width'])
|
||||||
@ -697,6 +848,9 @@ def show_config_window():
|
|||||||
else:
|
else:
|
||||||
config.image_strategy = "repeat_last"
|
config.image_strategy = "repeat_last"
|
||||||
|
|
||||||
|
# 新增:保存配置到文件
|
||||||
|
config.save_to_file(CONFIG_FILE_PATH)
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
window.close()
|
window.close()
|
||||||
@ -909,6 +1063,12 @@ def main_window():
|
|||||||
event, values = window.read()
|
event, values = window.read()
|
||||||
|
|
||||||
if event in (sg.WIN_CLOSED, '退出'):
|
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
|
break
|
||||||
|
|
||||||
if event == '转换设置':
|
if event == '转换设置':
|
||||||
@ -936,6 +1096,13 @@ def main_window():
|
|||||||
sg.popup_error('请选择图片根文件夹')
|
sg.popup_error('请选择图片根文件夹')
|
||||||
continue
|
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:
|
try:
|
||||||
status_text.update('正在扫描TXT文件...')
|
status_text.update('正在扫描TXT文件...')
|
||||||
window.refresh()
|
window.refresh()
|
||||||
@ -959,6 +1126,8 @@ def main_window():
|
|||||||
status_text.update(f'扫描完成: 找到 {len(matched_pairs)} 个TXT文件')
|
status_text.update(f'扫描完成: 找到 {len(matched_pairs)} 个TXT文件')
|
||||||
|
|
||||||
# 启用相关按钮
|
# 启用相关按钮
|
||||||
|
window['-PREVIEW_TABLE-'].update(values=table_data)
|
||||||
|
|
||||||
window['编辑匹配'].update(disabled=False)
|
window['编辑匹配'].update(disabled=False)
|
||||||
window['开始批量转换'].update(disabled=False)
|
window['开始批量转换'].update(disabled=False)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user