更换GUI框架
This commit is contained in:
parent
45dab3d892
commit
93781d3ebf
@ -37,7 +37,7 @@ class ErrorCharProcessor:
|
||||
if dir_name and not os.path.exists(dir_name):
|
||||
os.makedirs(dir_name)
|
||||
|
||||
print(f"加载错别字库文件: {self.db_path}")
|
||||
# print(f"加载错别字库文件: {self.db_path}")
|
||||
|
||||
# 检查文件是否存在,不存在则创建默认库
|
||||
if not os.path.exists(self.db_path):
|
||||
|
||||
456
gui_config.py
456
gui_config.py
@ -4,180 +4,328 @@ GUI配置窗口模块
|
||||
提供配置设置的图形界面。
|
||||
"""
|
||||
|
||||
import PySimpleGUI as sg
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, messagebox
|
||||
from config import config
|
||||
|
||||
|
||||
def show_config_window():
|
||||
"""显示配置窗口"""
|
||||
# 创建标签页布局
|
||||
tab_file_layout = [
|
||||
[sg.Text('文件处理设置', font=('bold', 12))],
|
||||
[sg.HSeparator()],
|
||||
[sg.Text('TXT编码:', size=(12, 1)),
|
||||
sg.Combo(['utf-8', 'gbk', 'utf-16'], default_value=config.txt_encoding, key='txt_encoding', size=(15, 1))],
|
||||
[sg.Text('匹配模式:', size=(12, 1))],
|
||||
[sg.Radio('完全匹配(文件名与文件夹名相同)', 'match', default=config.match_pattern == "exact",
|
||||
key='match_exact')],
|
||||
[sg.Radio('前缀匹配', 'match', default=config.match_pattern == "prefix", key='match_prefix')],
|
||||
[sg.Radio('包含匹配', 'match', default=config.match_pattern == "contains", key='match_contains')],
|
||||
[sg.HSeparator()],
|
||||
[sg.Text('输出位置:', size=(12, 1))],
|
||||
[sg.Radio('输出到TXT文件所在文件夹', 'output_loc', default=config.output_location == "txt_folder",
|
||||
key='output_txt_folder')],
|
||||
[sg.Radio('输出到指定文件夹', 'output_loc', default=config.output_location == "custom", key='output_custom')]
|
||||
]
|
||||
# 创建配置窗口
|
||||
config_window = tk.Toplevel()
|
||||
config_window.title('转换设置')
|
||||
config_window.geometry('600x700')
|
||||
config_window.transient()
|
||||
config_window.grab_set()
|
||||
|
||||
# 创建笔记本组件
|
||||
notebook = ttk.Notebook(config_window)
|
||||
notebook.pack(fill='both', expand=True, padx=10, pady=10)
|
||||
|
||||
# 文件处理选项卡
|
||||
file_frame = ttk.Frame(notebook)
|
||||
notebook.add(file_frame, text='文件处理')
|
||||
_create_file_tab(file_frame)
|
||||
|
||||
# 文字处理选项卡
|
||||
text_frame = ttk.Frame(notebook)
|
||||
notebook.add(text_frame, text='文字处理')
|
||||
char_vars = _create_text_tab(text_frame)
|
||||
|
||||
# 图片处理选项卡
|
||||
image_frame = ttk.Frame(notebook)
|
||||
notebook.add(image_frame, text='图片处理')
|
||||
_create_image_tab(image_frame)
|
||||
|
||||
# 文档格式选项卅
|
||||
format_frame = ttk.Frame(notebook)
|
||||
notebook.add(format_frame, text='文档格式')
|
||||
_create_format_tab(format_frame)
|
||||
|
||||
# 底部按钮
|
||||
button_frame = ttk.Frame(config_window)
|
||||
button_frame.pack(fill='x', padx=10, pady=10)
|
||||
|
||||
ttk.Button(button_frame, text='确定', command=lambda: _save_config(config_window, char_vars)).pack(side='left', padx=5)
|
||||
ttk.Button(button_frame, text='取消', command=config_window.destroy).pack(side='left', padx=5)
|
||||
ttk.Button(button_frame, text='重置为默认', command=lambda: _reset_to_default(char_vars)).pack(side='left', padx=5)
|
||||
|
||||
return char_vars
|
||||
|
||||
tab_text_layout = [
|
||||
[sg.Text('文字处理设置', font=('bold', 12))],
|
||||
[sg.HSeparator()],
|
||||
[sg.Checkbox('转换文字顺序', key='-REVERSE_TEXT-', default=config.reverse_text_order)],
|
||||
[sg.Checkbox('替换标点符号(句号转逗号,保留结尾句号)', key='-REPLACE_PUNCTUATION-',
|
||||
default=config.replace_punctuation)],
|
||||
[sg.HSeparator()],
|
||||
[sg.Text('错别字处理', font=('bold', 11), text_color='darkblue')],
|
||||
[sg.Checkbox('启用错别字处理', key='-ENABLE_CHAR_ERRORS-', default=config.enable_char_errors,
|
||||
enable_events=True)],
|
||||
[sg.Text('错误强度:', size=(10, 1)),
|
||||
sg.Slider(range=(0.0, 1.0), default_value=config.char_error_intensity, resolution=0.1,
|
||||
orientation='h', size=(20, 15), key='char_error_intensity', disabled=not config.enable_char_errors)],
|
||||
[sg.Text('错别字库路径:', size=(12, 1)),
|
||||
sg.InputText(config.char_error_db_path, key='char_error_db_path', size=(30, 1),
|
||||
disabled=not config.enable_char_errors),
|
||||
sg.FileBrowse('浏览', file_types=(("JSON Files", "*.json"),), disabled=not config.enable_char_errors)],
|
||||
[sg.HSeparator()],
|
||||
[sg.Checkbox('添加免责声明', key='-ADD_DISCLAIMER-', default=config.add_disclaimer)]
|
||||
]
|
||||
|
||||
tab_image_layout = [
|
||||
[sg.Text('图片处理设置', font=('bold', 12))],
|
||||
[sg.HSeparator()],
|
||||
[sg.Text('图片排序方式:', size=(12, 1))],
|
||||
[sg.Radio('按名称', 'sort', default=config.image_sort_by == "name", key='sort_name'),
|
||||
sg.Radio('按修改时间', 'sort', default=config.image_sort_by == "time", key='sort_time')],
|
||||
[sg.HSeparator()],
|
||||
[sg.Text('图片尺寸调整:', size=(12, 1))],
|
||||
[sg.Radio('不调整', 'resize', default=config.image_resize == "none", key='resize_none')],
|
||||
[sg.Radio('按宽度:', 'resize', default=config.image_resize == "width", key='resize_width'),
|
||||
sg.InputText(str(config.image_width), size=(8, 1), key='image_width'),
|
||||
sg.Text('英寸')],
|
||||
[sg.HSeparator()],
|
||||
[sg.Text('图片对齐方式:', size=(12, 1))],
|
||||
[sg.Radio('左对齐', 'align', default=config.image_alignment == "left", key='align_left'),
|
||||
sg.Radio('居中', 'align', default=config.image_alignment == "center", key='align_center'),
|
||||
sg.Radio('右对齐', 'align', default=config.image_alignment == "right", key='align_right')],
|
||||
[sg.HSeparator()],
|
||||
[sg.Text('图片不足时策略:', size=(12, 1))],
|
||||
[sg.Radio('循环使用', 'strategy', default=config.image_strategy == "cycle", key='strategy_cycle')],
|
||||
[sg.Radio('忽略多余标题', 'strategy', default=config.image_strategy == "truncate", key='strategy_truncate')],
|
||||
[sg.Radio('重复最后一张', 'strategy', default=config.image_strategy == "repeat_last", key='strategy_repeat')]
|
||||
]
|
||||
def _create_file_tab(parent):
|
||||
"""创建文件处理选项卡"""
|
||||
# 标题
|
||||
ttk.Label(parent, text='文件处理设置', font=('', 12, 'bold')).pack(anchor='w', padx=10, pady=(10, 5))
|
||||
ttk.Separator(parent, orient='horizontal').pack(fill='x', padx=10, pady=5)
|
||||
|
||||
# TXT编码
|
||||
encoding_frame = ttk.Frame(parent)
|
||||
encoding_frame.pack(fill='x', padx=10, pady=5)
|
||||
ttk.Label(encoding_frame, text='TXT编码:', width=12).pack(side='left')
|
||||
encoding_var = tk.StringVar(value=config.txt_encoding)
|
||||
encoding_combo = ttk.Combobox(encoding_frame, textvariable=encoding_var,
|
||||
values=['utf-8', 'gbk', 'utf-16'], state='readonly', width=15)
|
||||
encoding_combo.pack(side='left', padx=(0, 10))
|
||||
encoding_combo.bind('<<ComboboxSelected>>', lambda e: setattr(config, 'txt_encoding', encoding_var.get()))
|
||||
|
||||
# 匹配模式
|
||||
ttk.Label(parent, text='匹配模式:', font=('', 10, 'bold')).pack(anchor='w', padx=10, pady=(15, 5))
|
||||
|
||||
match_var = tk.StringVar(value=config.match_pattern)
|
||||
ttk.Radiobutton(parent, text='完全匹配(文件名与文件夹名相同)',
|
||||
variable=match_var, value='exact').pack(anchor='w', padx=20, pady=2)
|
||||
ttk.Radiobutton(parent, text='前缀匹配',
|
||||
variable=match_var, value='prefix').pack(anchor='w', padx=20, pady=2)
|
||||
ttk.Radiobutton(parent, text='包含匹配',
|
||||
variable=match_var, value='contains').pack(anchor='w', padx=20, pady=2)
|
||||
match_var.trace('w', lambda *args: setattr(config, 'match_pattern', match_var.get()))
|
||||
|
||||
ttk.Separator(parent, orient='horizontal').pack(fill='x', padx=10, pady=15)
|
||||
|
||||
# 输出位置
|
||||
ttk.Label(parent, text='输出位置:', font=('', 10, 'bold')).pack(anchor='w', padx=10, pady=(0, 5))
|
||||
|
||||
output_var = tk.StringVar(value=config.output_location)
|
||||
ttk.Radiobutton(parent, text='输出到TXT文件所在文件夹',
|
||||
variable=output_var, value='txt_folder').pack(anchor='w', padx=20, pady=2)
|
||||
ttk.Radiobutton(parent, text='输出到指定文件夹',
|
||||
variable=output_var, value='custom').pack(anchor='w', padx=20, pady=2)
|
||||
output_var.trace('w', lambda *args: setattr(config, 'output_location', output_var.get()))
|
||||
|
||||
tab_format_layout = [
|
||||
[sg.Text('文档格式设置', font=('bold', 12))],
|
||||
[sg.HSeparator()],
|
||||
[sg.Text('行间距:', size=(12, 1)),
|
||||
sg.InputText(str(config.line_spacing), size=(8, 1), key='line_spacing')],
|
||||
[sg.Text('最大标题层级:', size=(12, 1)),
|
||||
sg.Combo([1, 2, 3, 4, 5, 6], default_value=config.title_levels, key='title_levels', size=(8, 1))]
|
||||
]
|
||||
|
||||
layout = [
|
||||
[sg.TabGroup([
|
||||
[sg.Tab('文件处理', tab_file_layout, key='tab_file')],
|
||||
[sg.Tab('文字处理', tab_text_layout, key='tab_text')],
|
||||
[sg.Tab('图片处理', tab_image_layout, key='tab_image')],
|
||||
[sg.Tab('文档格式', tab_format_layout, key='tab_format')]
|
||||
])],
|
||||
[sg.HSeparator()],
|
||||
[sg.Button('确定', size=(10, 1)), sg.Button('取消', size=(10, 1)), sg.Button('重置为默认', size=(12, 1))]
|
||||
]
|
||||
def _create_text_tab(parent):
|
||||
"""创建文字处理选项卡"""
|
||||
# 标题
|
||||
ttk.Label(parent, text='文字处理设置', font=('', 12, 'bold')).pack(anchor='w', padx=10, pady=(10, 5))
|
||||
ttk.Separator(parent, orient='horizontal').pack(fill='x', padx=10, pady=5)
|
||||
|
||||
# 文字处理选项
|
||||
reverse_var = tk.BooleanVar(value=config.reverse_text_order)
|
||||
ttk.Checkbutton(parent, text='转换文字顺序', variable=reverse_var).pack(anchor='w', padx=10, pady=5)
|
||||
reverse_var.trace('w', lambda *args: setattr(config, 'reverse_text_order', reverse_var.get()))
|
||||
|
||||
punctuation_var = tk.BooleanVar(value=config.replace_punctuation)
|
||||
ttk.Checkbutton(parent, text='替换标点符号(句号转逗号,保留结尾句号)',
|
||||
variable=punctuation_var).pack(anchor='w', padx=10, pady=5)
|
||||
punctuation_var.trace('w', lambda *args: setattr(config, 'replace_punctuation', punctuation_var.get()))
|
||||
|
||||
ttk.Separator(parent, orient='horizontal').pack(fill='x', padx=10, pady=15)
|
||||
|
||||
# 错别字处理
|
||||
ttk.Label(parent, text='错别字处理', font=('', 11, 'bold'), foreground='darkblue').pack(anchor='w', padx=10, pady=(0, 5))
|
||||
|
||||
enable_errors_var = tk.BooleanVar(value=config.enable_char_errors)
|
||||
enable_checkbox = ttk.Checkbutton(parent, text='启用错别字处理',
|
||||
variable=enable_errors_var)
|
||||
enable_checkbox.pack(anchor='w', padx=10, pady=5)
|
||||
|
||||
# 错误强度
|
||||
intensity_frame = ttk.Frame(parent)
|
||||
intensity_frame.pack(fill='x', padx=10, pady=5)
|
||||
ttk.Label(intensity_frame, text='错误强度:', width=10).pack(side='left')
|
||||
intensity_var = tk.DoubleVar(value=config.char_error_intensity)
|
||||
intensity_scale = ttk.Scale(intensity_frame, from_=0.0, to=1.0, variable=intensity_var,
|
||||
orient='horizontal', length=200)
|
||||
intensity_scale.pack(side='left', padx=(0, 10))
|
||||
intensity_label = ttk.Label(intensity_frame, text=f'{config.char_error_intensity:.1f}')
|
||||
intensity_label.pack(side='left')
|
||||
|
||||
def update_intensity_label(*args):
|
||||
intensity_label.config(text=f'{intensity_var.get():.1f}')
|
||||
config.char_error_intensity = intensity_var.get()
|
||||
|
||||
intensity_var.trace('w', update_intensity_label)
|
||||
|
||||
# 错别字库路径
|
||||
db_frame = ttk.Frame(parent)
|
||||
db_frame.pack(fill='x', padx=10, pady=5)
|
||||
ttk.Label(db_frame, text='错别字库路径:', width=12).pack(side='left')
|
||||
db_var = tk.StringVar(value=config.char_error_db_path)
|
||||
db_entry = ttk.Entry(db_frame, textvariable=db_var, width=30)
|
||||
db_entry.pack(side='left', fill='x', expand=True, padx=(0, 5))
|
||||
|
||||
def browse_db_file():
|
||||
filename = filedialog.askopenfilename(title='选择错别字库文件',
|
||||
filetypes=[('JSON Files', '*.json')])
|
||||
if filename:
|
||||
db_var.set(filename)
|
||||
|
||||
ttk.Button(db_frame, text='浏览', command=browse_db_file).pack(side='right')
|
||||
db_var.trace('w', lambda *args: setattr(config, 'char_error_db_path', db_var.get()))
|
||||
|
||||
ttk.Separator(parent, orient='horizontal').pack(fill='x', padx=10, pady=15)
|
||||
|
||||
# 免责声明
|
||||
disclaimer_var = tk.BooleanVar(value=config.add_disclaimer)
|
||||
ttk.Checkbutton(parent, text='添加免责声明', variable=disclaimer_var).pack(anchor='w', padx=10, pady=5)
|
||||
disclaimer_var.trace('w', lambda *args: setattr(config, 'add_disclaimer', disclaimer_var.get()))
|
||||
|
||||
# 启用/禁用错别字相关控件
|
||||
def toggle_error_controls(*args):
|
||||
state = 'normal' if enable_errors_var.get() else 'disabled'
|
||||
intensity_scale.configure(state=state)
|
||||
db_entry.configure(state=state)
|
||||
config.enable_char_errors = enable_errors_var.get()
|
||||
|
||||
enable_errors_var.trace('w', toggle_error_controls)
|
||||
toggle_error_controls() # 初始化状态
|
||||
|
||||
return {
|
||||
'enable_errors': enable_errors_var,
|
||||
'intensity': intensity_var,
|
||||
'db_path': db_var,
|
||||
'reverse_text': reverse_var,
|
||||
'punctuation': punctuation_var,
|
||||
'disclaimer': disclaimer_var
|
||||
}
|
||||
|
||||
window = sg.Window('转换设置', layout, modal=True, resizable=True, size=(500, 450))
|
||||
|
||||
while True:
|
||||
event, values = window.read()
|
||||
def _create_image_tab(parent):
|
||||
"""创建图片处理选项卡"""
|
||||
# 标题
|
||||
ttk.Label(parent, text='图片处理设置', font=('', 12, 'bold')).pack(anchor='w', padx=10, pady=(10, 5))
|
||||
ttk.Separator(parent, orient='horizontal').pack(fill='x', padx=10, pady=5)
|
||||
|
||||
# 图片排序方式
|
||||
ttk.Label(parent, text='图片排序方式:', font=('', 10, 'bold')).pack(anchor='w', padx=10, pady=(0, 5))
|
||||
|
||||
sort_var = tk.StringVar(value=config.image_sort_by)
|
||||
ttk.Radiobutton(parent, text='按名称', variable=sort_var, value='name').pack(anchor='w', padx=20, pady=2)
|
||||
ttk.Radiobutton(parent, text='按修改时间', variable=sort_var, value='time').pack(anchor='w', padx=20, pady=2)
|
||||
sort_var.trace('w', lambda *args: setattr(config, 'image_sort_by', sort_var.get()))
|
||||
|
||||
ttk.Separator(parent, orient='horizontal').pack(fill='x', padx=10, pady=15)
|
||||
|
||||
# 图片尺寸调整
|
||||
ttk.Label(parent, text='图片尺寸调整:', font=('', 10, 'bold')).pack(anchor='w', padx=10, pady=(0, 5))
|
||||
|
||||
resize_var = tk.StringVar(value=config.image_resize)
|
||||
ttk.Radiobutton(parent, text='不调整', variable=resize_var, value='none').pack(anchor='w', padx=20, pady=2)
|
||||
|
||||
width_frame = ttk.Frame(parent)
|
||||
width_frame.pack(anchor='w', padx=20, pady=2)
|
||||
width_radio = ttk.Radiobutton(width_frame, text='按宽度:', variable=resize_var, value='width')
|
||||
width_radio.pack(side='left')
|
||||
width_var = tk.StringVar(value=str(config.image_width))
|
||||
width_entry = ttk.Entry(width_frame, textvariable=width_var, width=8)
|
||||
width_entry.pack(side='left', padx=(5, 5))
|
||||
ttk.Label(width_frame, text='英寸').pack(side='left')
|
||||
|
||||
resize_var.trace('w', lambda *args: setattr(config, 'image_resize', resize_var.get()))
|
||||
width_var.trace('w', lambda *args: _update_image_width(width_var.get()))
|
||||
|
||||
ttk.Separator(parent, orient='horizontal').pack(fill='x', padx=10, pady=15)
|
||||
|
||||
# 图片对齐方式
|
||||
ttk.Label(parent, text='图片对齐方式:', font=('', 10, 'bold')).pack(anchor='w', padx=10, pady=(0, 5))
|
||||
|
||||
align_var = tk.StringVar(value=config.image_alignment)
|
||||
ttk.Radiobutton(parent, text='左对齐', variable=align_var, value='left').pack(anchor='w', padx=20, pady=2)
|
||||
ttk.Radiobutton(parent, text='居中', variable=align_var, value='center').pack(anchor='w', padx=20, pady=2)
|
||||
ttk.Radiobutton(parent, text='右对齐', variable=align_var, value='right').pack(anchor='w', padx=20, pady=2)
|
||||
align_var.trace('w', lambda *args: setattr(config, 'image_alignment', align_var.get()))
|
||||
|
||||
ttk.Separator(parent, orient='horizontal').pack(fill='x', padx=10, pady=15)
|
||||
|
||||
# 图片不足时策略
|
||||
ttk.Label(parent, text='图片不足时策略:', font=('', 10, 'bold')).pack(anchor='w', padx=10, pady=(0, 5))
|
||||
|
||||
strategy_var = tk.StringVar(value=config.image_strategy)
|
||||
ttk.Radiobutton(parent, text='循环使用', variable=strategy_var, value='cycle').pack(anchor='w', padx=20, pady=2)
|
||||
ttk.Radiobutton(parent, text='忽略多余标题', variable=strategy_var, value='truncate').pack(anchor='w', padx=20, pady=2)
|
||||
ttk.Radiobutton(parent, text='重复最后一张', variable=strategy_var, value='repeat_last').pack(anchor='w', padx=20, pady=2)
|
||||
strategy_var.trace('w', lambda *args: setattr(config, 'image_strategy', strategy_var.get()))
|
||||
|
||||
if event in (sg.WIN_CLOSED, '取消'):
|
||||
break
|
||||
|
||||
# 处理错别字启用/禁用事件
|
||||
if event == '-ENABLE_CHAR_ERRORS-':
|
||||
enabled = values['-ENABLE_CHAR_ERRORS-']
|
||||
window['char_error_intensity'].update(disabled=not enabled)
|
||||
window['char_error_db_path'].update(disabled=not enabled)
|
||||
def _create_format_tab(parent):
|
||||
"""创建文档格式选项卡"""
|
||||
# 标题
|
||||
ttk.Label(parent, text='文档格式设置', font=('', 12, 'bold')).pack(anchor='w', padx=10, pady=(10, 5))
|
||||
ttk.Separator(parent, orient='horizontal').pack(fill='x', padx=10, pady=5)
|
||||
|
||||
# 行间距
|
||||
spacing_frame = ttk.Frame(parent)
|
||||
spacing_frame.pack(fill='x', padx=10, pady=5)
|
||||
ttk.Label(spacing_frame, text='行间距:', width=12).pack(side='left')
|
||||
spacing_var = tk.StringVar(value=str(config.line_spacing))
|
||||
spacing_entry = ttk.Entry(spacing_frame, textvariable=spacing_var, width=8)
|
||||
spacing_entry.pack(side='left')
|
||||
spacing_var.trace('w', lambda *args: _update_line_spacing(spacing_var.get()))
|
||||
|
||||
# 最大标题层级
|
||||
title_frame = ttk.Frame(parent)
|
||||
title_frame.pack(fill='x', padx=10, pady=5)
|
||||
ttk.Label(title_frame, text='最大标题层级:', width=12).pack(side='left')
|
||||
title_var = tk.StringVar(value=str(config.title_levels))
|
||||
title_combo = ttk.Combobox(title_frame, textvariable=title_var, values=['1', '2', '3', '4', '5', '6'],
|
||||
state='readonly', width=8)
|
||||
title_combo.pack(side='left')
|
||||
title_var.trace('w', lambda *args: _update_title_levels(title_var.get()))
|
||||
|
||||
if event == '重置为默认':
|
||||
# 重置为默认值
|
||||
from config import Config
|
||||
default_config = Config()
|
||||
window['txt_encoding'].update(default_config.txt_encoding)
|
||||
window['match_exact'].update(True)
|
||||
window['output_txt_folder'].update(True)
|
||||
window['-REVERSE_TEXT-'].update(default_config.reverse_text_order)
|
||||
window['-REPLACE_PUNCTUATION-'].update(default_config.replace_punctuation)
|
||||
window['-ENABLE_CHAR_ERRORS-'].update(default_config.enable_char_errors)
|
||||
window['char_error_intensity'].update(default_config.char_error_intensity)
|
||||
window['char_error_db_path'].update(default_config.char_error_db_path)
|
||||
window['-ADD_DISCLAIMER-'].update(default_config.add_disclaimer)
|
||||
window['sort_name'].update(True)
|
||||
window['resize_none'].update(True)
|
||||
window['image_width'].update(str(default_config.image_width))
|
||||
window['align_center'].update(True)
|
||||
window['strategy_cycle'].update(True)
|
||||
window['line_spacing'].update(str(default_config.line_spacing))
|
||||
window['title_levels'].update(default_config.title_levels)
|
||||
|
||||
if event == '确定':
|
||||
# 保存配置
|
||||
config.txt_encoding = values['txt_encoding']
|
||||
def _update_image_width(value):
|
||||
"""更新图片宽度"""
|
||||
try:
|
||||
config.image_width = float(value)
|
||||
except:
|
||||
pass
|
||||
|
||||
if values['match_exact']:
|
||||
config.match_pattern = "exact"
|
||||
elif values['match_prefix']:
|
||||
config.match_pattern = "prefix"
|
||||
else:
|
||||
config.match_pattern = "contains"
|
||||
|
||||
config.output_location = "txt_folder" if values['output_txt_folder'] else "custom"
|
||||
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-']
|
||||
def _update_line_spacing(value):
|
||||
"""更新行间距"""
|
||||
try:
|
||||
config.line_spacing = float(value)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 错别字处理配置
|
||||
config.enable_char_errors = values['-ENABLE_CHAR_ERRORS-']
|
||||
config.char_error_intensity = values['char_error_intensity']
|
||||
config.char_error_db_path = values['char_error_db_path']
|
||||
|
||||
try:
|
||||
config.image_width = float(values['image_width'])
|
||||
except:
|
||||
pass
|
||||
def _update_title_levels(value):
|
||||
"""更新标题层级"""
|
||||
try:
|
||||
config.title_levels = int(value)
|
||||
except:
|
||||
pass
|
||||
|
||||
if values['align_left']:
|
||||
config.image_alignment = "left"
|
||||
elif values['align_right']:
|
||||
config.image_alignment = "right"
|
||||
else:
|
||||
config.image_alignment = "center"
|
||||
|
||||
if values['strategy_cycle']:
|
||||
config.image_strategy = "cycle"
|
||||
elif values['strategy_truncate']:
|
||||
config.image_strategy = "truncate"
|
||||
else:
|
||||
config.image_strategy = "repeat_last"
|
||||
def _reset_to_default(char_vars):
|
||||
"""重置为默认值"""
|
||||
from config import Config
|
||||
default_config = Config()
|
||||
|
||||
# 更新所有配置
|
||||
config.txt_encoding = default_config.txt_encoding
|
||||
config.match_pattern = default_config.match_pattern
|
||||
config.output_location = default_config.output_location
|
||||
config.reverse_text_order = default_config.reverse_text_order
|
||||
config.replace_punctuation = default_config.replace_punctuation
|
||||
config.enable_char_errors = default_config.enable_char_errors
|
||||
config.char_error_intensity = default_config.char_error_intensity
|
||||
config.char_error_db_path = default_config.char_error_db_path
|
||||
config.add_disclaimer = default_config.add_disclaimer
|
||||
config.image_sort_by = default_config.image_sort_by
|
||||
config.image_resize = default_config.image_resize
|
||||
config.image_width = default_config.image_width
|
||||
config.image_alignment = default_config.image_alignment
|
||||
config.image_strategy = default_config.image_strategy
|
||||
config.line_spacing = default_config.line_spacing
|
||||
config.title_levels = default_config.title_levels
|
||||
|
||||
# 更新界面变量
|
||||
if char_vars:
|
||||
char_vars['enable_errors'].set(default_config.enable_char_errors)
|
||||
char_vars['intensity'].set(default_config.char_error_intensity)
|
||||
char_vars['db_path'].set(default_config.char_error_db_path)
|
||||
char_vars['reverse_text'].set(default_config.reverse_text_order)
|
||||
char_vars['punctuation'].set(default_config.replace_punctuation)
|
||||
char_vars['disclaimer'].set(default_config.add_disclaimer)
|
||||
|
||||
messagebox.showinfo('信息', '配置已重置为默认值')
|
||||
|
||||
try:
|
||||
config.line_spacing = float(values['line_spacing'])
|
||||
config.title_levels = int(values['title_levels'])
|
||||
except:
|
||||
pass
|
||||
|
||||
from config import CONFIG_FILE_PATH
|
||||
config.save_to_file(CONFIG_FILE_PATH)
|
||||
break
|
||||
|
||||
window.close()
|
||||
def _save_config(window, char_vars):
|
||||
"""保存配置"""
|
||||
from config import CONFIG_FILE_PATH
|
||||
config.save_to_file(CONFIG_FILE_PATH)
|
||||
messagebox.showinfo('信息', '配置已保存')
|
||||
window.destroy()
|
||||
@ -5,11 +5,19 @@ GUI匹配编辑器模块
|
||||
"""
|
||||
|
||||
import os
|
||||
import PySimpleGUI as sg
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox
|
||||
|
||||
|
||||
def show_matching_editor(matched_pairs, images_root):
|
||||
"""显示匹配编辑窗口,允许手动调整匹配关系"""
|
||||
# 创建匹配编辑窗口
|
||||
editor_window = tk.Toplevel()
|
||||
editor_window.title('文件匹配编辑')
|
||||
editor_window.geometry('900x600')
|
||||
editor_window.transient()
|
||||
editor_window.grab_set()
|
||||
|
||||
# 获取所有图片文件夹
|
||||
all_image_folders = []
|
||||
if os.path.isdir(images_root):
|
||||
@ -18,68 +26,146 @@ def show_matching_editor(matched_pairs, images_root):
|
||||
folder_path = os.path.join(root, dir_name)
|
||||
rel_path = os.path.relpath(folder_path, images_root)
|
||||
all_image_folders.append((folder_path, rel_path))
|
||||
|
||||
# 准备表格数据
|
||||
table_data = []
|
||||
|
||||
# 标题
|
||||
title_frame = ttk.Frame(editor_window)
|
||||
title_frame.pack(fill='x', padx=10, pady=10)
|
||||
ttk.Label(title_frame, text='文件匹配编辑', font=('', 14, 'bold')).pack()
|
||||
ttk.Label(title_frame, text='选择要修改的项目,然后从右侧选择图片文件夹').pack()
|
||||
|
||||
# 主体区域(使用PanedWindow分割)
|
||||
paned = ttk.PanedWindow(editor_window, orient='horizontal')
|
||||
paned.pack(fill='both', expand=True, padx=10, pady=10)
|
||||
|
||||
# 左侧表格区域
|
||||
left_frame = ttk.Frame(paned)
|
||||
paned.add(left_frame, weight=2)
|
||||
|
||||
# 表格标题
|
||||
ttk.Label(left_frame, text='TXT文件匹配列表:', font=('', 10, 'bold')).pack(anchor='w', pady=(0, 5))
|
||||
|
||||
# 表格区域
|
||||
table_frame = ttk.Frame(left_frame)
|
||||
table_frame.pack(fill='both', expand=True)
|
||||
|
||||
# 创建Treeview表格
|
||||
columns = ('index', 'txt_name', 'image_folder')
|
||||
tree = ttk.Treeview(table_frame, columns=columns, show='headings', height=15)
|
||||
tree.heading('index', text='序号')
|
||||
tree.heading('txt_name', text='TXT文件名')
|
||||
tree.heading('image_folder', text='匹配的图片文件夹')
|
||||
tree.column('index', width=60)
|
||||
tree.column('txt_name', width=200)
|
||||
tree.column('image_folder', width=250)
|
||||
|
||||
# 表格滚动条
|
||||
table_scrollbar = ttk.Scrollbar(table_frame, orient='vertical', command=tree.yview)
|
||||
tree.configure(yscrollcommand=table_scrollbar.set)
|
||||
tree.pack(side='left', fill='both', expand=True)
|
||||
table_scrollbar.pack(side='right', fill='y')
|
||||
|
||||
# 右侧文件夹列表区域
|
||||
right_frame = ttk.Frame(paned)
|
||||
paned.add(right_frame, weight=1)
|
||||
|
||||
# 文件夹列表标题
|
||||
ttk.Label(right_frame, text='可用的图片文件夹:', font=('', 10, 'bold')).pack(anchor='w', pady=(0, 5))
|
||||
|
||||
# 文件夹列表区域
|
||||
folders_frame = ttk.Frame(right_frame)
|
||||
folders_frame.pack(fill='both', expand=True)
|
||||
|
||||
# 创建文件夹列表
|
||||
folders_listbox = tk.Listbox(folders_frame, selectmode='single')
|
||||
for folder_path, rel_path in all_image_folders:
|
||||
folders_listbox.insert('end', rel_path)
|
||||
|
||||
# 文件夹列表滚动条
|
||||
folders_scrollbar = ttk.Scrollbar(folders_frame, orient='vertical', command=folders_listbox.yview)
|
||||
folders_listbox.configure(yscrollcommand=folders_scrollbar.set)
|
||||
folders_listbox.pack(side='left', fill='both', expand=True)
|
||||
folders_scrollbar.pack(side='right', fill='y')
|
||||
|
||||
# 操作按钮区域
|
||||
button_frame = ttk.Frame(editor_window)
|
||||
button_frame.pack(fill='x', padx=10, pady=10)
|
||||
|
||||
# 提供通过闭包访问变量的方式
|
||||
selected_item_id = [None] # type: ignore
|
||||
|
||||
def on_tree_select(event):
|
||||
"""TreeView选中事件"""
|
||||
selection = tree.selection()
|
||||
if selection:
|
||||
selected_item_id[0] = selection[0] # type: ignore
|
||||
|
||||
tree.bind('<<TreeviewSelect>>', on_tree_select)
|
||||
|
||||
def set_matching():
|
||||
"""设置选中项的匹配"""
|
||||
if not selected_item_id[0]:
|
||||
messagebox.showwarning('警告', '请先选择一个TXT文件')
|
||||
return
|
||||
|
||||
folder_selection = folders_listbox.curselection()
|
||||
if not folder_selection:
|
||||
messagebox.showwarning('警告', '请先选择一个图片文件夹')
|
||||
return
|
||||
|
||||
# 获取选中的索引
|
||||
item_values = tree.item(selected_item_id[0], 'values')
|
||||
row_index = int(item_values[0])
|
||||
|
||||
# 获取选中的文件夹
|
||||
folder_index = folder_selection[0]
|
||||
folder_path, folder_rel = all_image_folders[folder_index]
|
||||
|
||||
# 更新匹配关系
|
||||
matched_pairs[row_index]['image_folder'] = {
|
||||
"path": folder_path,
|
||||
"name": os.path.basename(folder_path),
|
||||
"relative_path": folder_rel
|
||||
}
|
||||
|
||||
# 更新表格显示
|
||||
tree.item(selected_item_id[0], values=(row_index, item_values[1], folder_rel))
|
||||
|
||||
messagebox.showinfo('成功', f'已将 "{item_values[1]}" 匹配到 "{folder_rel}"')
|
||||
|
||||
def clear_matching():
|
||||
"""清除选中项的匹配"""
|
||||
if not selected_item_id[0]:
|
||||
messagebox.showwarning('警告', '请先选择一个TXT文件')
|
||||
return
|
||||
|
||||
# 获取选中的索引
|
||||
item_values = tree.item(selected_item_id[0], 'values')
|
||||
row_index = int(item_values[0])
|
||||
|
||||
# 清除匹配关系
|
||||
matched_pairs[row_index]['image_folder'] = None
|
||||
|
||||
# 更新表格显示
|
||||
tree.item(selected_item_id[0], values=(row_index, item_values[1], '无匹配'))
|
||||
|
||||
messagebox.showinfo('成功', f'已清除 "{item_values[1]}" 的匹配关系')
|
||||
|
||||
def apply_all():
|
||||
"""应用所有修改"""
|
||||
editor_window.destroy()
|
||||
|
||||
# 按钮
|
||||
ttk.Button(button_frame, text='设置选中项', command=set_matching).pack(side='left', padx=5)
|
||||
ttk.Button(button_frame, text='清除选中项', command=clear_matching).pack(side='left', padx=5)
|
||||
ttk.Button(button_frame, text='应用所有', command=apply_all).pack(side='right', padx=5)
|
||||
|
||||
# 填充表格数据
|
||||
for i, pair in enumerate(matched_pairs):
|
||||
txt_name = pair['txt']['name']
|
||||
img_folder = pair['image_folder']['relative_path'] if pair['image_folder'] else "无匹配"
|
||||
table_data.append([i, txt_name, img_folder])
|
||||
|
||||
layout = [
|
||||
[sg.Text('文件匹配编辑', font=('bold', 14))],
|
||||
[sg.Text('选择要修改的项目,然后从右侧选择图片文件夹')],
|
||||
[
|
||||
sg.Table(
|
||||
values=table_data,
|
||||
headings=['序号', 'TXT文件名', '匹配的图片文件夹'],
|
||||
key='-TABLE-',
|
||||
select_mode=sg.TABLE_SELECT_MODE_BROWSE,
|
||||
enable_events=True,
|
||||
justification='left',
|
||||
size=(None, 15)
|
||||
),
|
||||
sg.VSeparator(),
|
||||
sg.Listbox(
|
||||
values=[f[1] for f in all_image_folders],
|
||||
key='-FOLDERS-',
|
||||
size=(40, 15),
|
||||
enable_events=True
|
||||
)
|
||||
],
|
||||
[sg.Button('设置选中项'), sg.Button('清除选中项'), sg.Button('应用所有')]
|
||||
]
|
||||
|
||||
window = sg.Window('匹配编辑', layout, resizable=True)
|
||||
selected_row = None
|
||||
|
||||
while True:
|
||||
event, values = window.read()
|
||||
|
||||
if event in (sg.WIN_CLOSED, '应用所有'):
|
||||
break
|
||||
|
||||
if event == '-TABLE-':
|
||||
if values['-TABLE-']:
|
||||
selected_row = values['-TABLE-'][0]
|
||||
|
||||
if event == '设置选中项' and selected_row is not None and values['-FOLDERS-']:
|
||||
folder_idx = [i for i, f in enumerate(all_image_folders) if f[1] == values['-FOLDERS-'][0]][0]
|
||||
folder_path, folder_rel = all_image_folders[folder_idx]
|
||||
|
||||
matched_pairs[selected_row]['image_folder'] = {
|
||||
"path": folder_path,
|
||||
"name": os.path.basename(folder_path),
|
||||
"relative_path": folder_rel
|
||||
}
|
||||
|
||||
table_data[selected_row][2] = folder_rel
|
||||
window['-TABLE-'].update(values=table_data)
|
||||
|
||||
if event == '清除选中项' and selected_row is not None:
|
||||
matched_pairs[selected_row]['image_folder'] = None
|
||||
table_data[selected_row][2] = "无匹配"
|
||||
window['-TABLE-'].update(values=table_data)
|
||||
|
||||
window.close()
|
||||
img_folder = pair['image_folder']['relative_path'] if pair['image_folder'] else '无匹配'
|
||||
tree.insert('', 'end', values=(i, txt_name, img_folder))
|
||||
|
||||
# 等待窗口关闭
|
||||
editor_window.wait_window()
|
||||
|
||||
return matched_pairs
|
||||
120
gui_results.py
120
gui_results.py
@ -6,39 +6,133 @@ GUI结果显示模块
|
||||
|
||||
import os
|
||||
import sys
|
||||
import PySimpleGUI as sg
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, scrolledtext
|
||||
import subprocess
|
||||
import platform
|
||||
|
||||
|
||||
def show_results_window(results):
|
||||
"""显示批量处理结果窗口"""
|
||||
# 准备结果消息
|
||||
if results['failed'] == 0:
|
||||
title = '处理完成'
|
||||
message = f"全部成功!\n共处理 {results['total']} 个文件,全部转换成功。"
|
||||
if results['main_output_folder']:
|
||||
message += f"\n主要输出文件夹: {results['main_output_folder']}"
|
||||
sg.popup('处理完成', message)
|
||||
_show_simple_result(title, message, results['main_output_folder'])
|
||||
else:
|
||||
title = '处理完成'
|
||||
failed_text = "\n".join([f"- {item['name']}: {item['error']}" for item in results['failed_items']])
|
||||
message = (f"处理完成!\n共处理 {results['total']} 个文件,"
|
||||
f"{results['success']} 个成功,{results['failed']} 个失败。\n\n"
|
||||
f"失败项:\n{failed_text}")
|
||||
if results['main_output_folder']:
|
||||
message += f"\n主要输出文件夹: {results['main_output_folder']}"
|
||||
sg.popup_scrolled('处理完成', message, size=(60, 20))
|
||||
_show_detailed_result(title, message, results['main_output_folder'])
|
||||
|
||||
# 询问是否打开输出文件夹
|
||||
if results['main_output_folder'] and os.path.exists(results['main_output_folder']):
|
||||
if sg.popup_yes_no('是否打开主要输出文件夹?') == 'Yes':
|
||||
_open_folder(results['main_output_folder'])
|
||||
|
||||
def _show_simple_result(title, message, output_folder):
|
||||
"""显示简单结果对话框"""
|
||||
# 创建结果窗口
|
||||
result_window = tk.Toplevel()
|
||||
result_window.title(title)
|
||||
result_window.geometry('500x200')
|
||||
result_window.transient()
|
||||
result_window.grab_set()
|
||||
|
||||
# 居中窗口
|
||||
result_window.geometry('+{}+{}'.format(
|
||||
result_window.winfo_screenwidth() // 2 - 250,
|
||||
result_window.winfo_screenheight() // 2 - 100
|
||||
))
|
||||
|
||||
# 消息区域
|
||||
message_frame = ttk.Frame(result_window)
|
||||
message_frame.pack(fill='both', expand=True, padx=20, pady=20)
|
||||
|
||||
# 消息文本
|
||||
message_label = ttk.Label(message_frame, text=message, justify='left', wraplength=450)
|
||||
message_label.pack(expand=True)
|
||||
|
||||
# 按钮区域
|
||||
button_frame = ttk.Frame(result_window)
|
||||
button_frame.pack(fill='x', padx=20, pady=(0, 20))
|
||||
|
||||
def open_folder_and_close():
|
||||
if output_folder and os.path.exists(output_folder):
|
||||
_open_folder(output_folder)
|
||||
result_window.destroy()
|
||||
|
||||
def close_window():
|
||||
result_window.destroy()
|
||||
|
||||
# 按钮
|
||||
if output_folder and os.path.exists(output_folder):
|
||||
ttk.Button(button_frame, text='打开输出文件夹', command=open_folder_and_close).pack(side='left', padx=(0, 10))
|
||||
|
||||
ttk.Button(button_frame, text='关闭', command=close_window).pack(side='right')
|
||||
|
||||
# 等待窗口关闭
|
||||
result_window.wait_window()
|
||||
|
||||
|
||||
def _show_detailed_result(title, message, output_folder):
|
||||
"""显示详细结果对话框"""
|
||||
# 创建结果窗口
|
||||
result_window = tk.Toplevel()
|
||||
result_window.title(title)
|
||||
result_window.geometry('700x500')
|
||||
result_window.transient()
|
||||
result_window.grab_set()
|
||||
|
||||
# 居中窗口
|
||||
result_window.geometry('+{}+{}'.format(
|
||||
result_window.winfo_screenwidth() // 2 - 350,
|
||||
result_window.winfo_screenheight() // 2 - 250
|
||||
))
|
||||
|
||||
# 消息区域
|
||||
message_frame = ttk.Frame(result_window)
|
||||
message_frame.pack(fill='both', expand=True, padx=20, pady=20)
|
||||
|
||||
# 滚动文本区域
|
||||
text_widget = scrolledtext.ScrolledText(message_frame, wrap=tk.WORD, state='normal')
|
||||
text_widget.pack(fill='both', expand=True)
|
||||
text_widget.insert('1.0', message)
|
||||
text_widget.configure(state='disabled')
|
||||
|
||||
# 按钮区域
|
||||
button_frame = ttk.Frame(result_window)
|
||||
button_frame.pack(fill='x', padx=20, pady=(0, 20))
|
||||
|
||||
def open_folder_and_close():
|
||||
if output_folder and os.path.exists(output_folder):
|
||||
_open_folder(output_folder)
|
||||
result_window.destroy()
|
||||
|
||||
def close_window():
|
||||
result_window.destroy()
|
||||
|
||||
# 按钮
|
||||
if output_folder and os.path.exists(output_folder):
|
||||
ttk.Button(button_frame, text='打开输出文件夹', command=open_folder_and_close).pack(side='left', padx=(0, 10))
|
||||
|
||||
ttk.Button(button_frame, text='关闭', command=close_window).pack(side='right')
|
||||
|
||||
# 等待窗口关闭
|
||||
result_window.wait_window()
|
||||
|
||||
|
||||
def _open_folder(folder_path):
|
||||
"""打开文件夹"""
|
||||
try:
|
||||
if sys.platform.startswith('win'):
|
||||
system = platform.system()
|
||||
if system == 'Windows':
|
||||
os.startfile(folder_path)
|
||||
elif sys.platform.startswith('darwin'):
|
||||
os.system(f'open "{folder_path}"')
|
||||
else:
|
||||
os.system(f'xdg-open "{folder_path}"')
|
||||
elif system == 'Darwin': # macOS
|
||||
subprocess.run(['open', folder_path])
|
||||
else: # Linux
|
||||
subprocess.run(['xdg-open', folder_path])
|
||||
except Exception as e:
|
||||
sg.popup_error(f"无法打开文件夹: {e}")
|
||||
messagebox.showerror('错误', f'无法打开文件夹: {e}')
|
||||
505
main.py
505
main.py
@ -23,7 +23,10 @@ try:
|
||||
from batch_processor import BatchProcessor
|
||||
|
||||
# GUI相关导入
|
||||
import PySimpleGUI as sg
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, messagebox, scrolledtext
|
||||
from tkinter.scrolledtext import ScrolledText
|
||||
import threading
|
||||
|
||||
except ImportError as e:
|
||||
print(f"导入模块失败: {e}")
|
||||
@ -40,244 +43,418 @@ class TxtToDocxApp:
|
||||
self.file_handler = FileHandler()
|
||||
self.batch_processor = BatchProcessor()
|
||||
|
||||
# 设置GUI主题
|
||||
sg.theme('BlueMono')
|
||||
# 初始化主窗口
|
||||
self.root = tk.Tk()
|
||||
self.root.title('批量Markdown TXT转DOCX工具')
|
||||
self.root.geometry('900x700')
|
||||
self.root.minsize(800, 600)
|
||||
|
||||
# 设置窗口图标和样式
|
||||
try:
|
||||
# 设置主题
|
||||
self.root.configure(bg='#f0f0f0')
|
||||
# 设置窗口居中
|
||||
self.root.geometry('+{}+{}'.format(
|
||||
self.root.winfo_screenwidth() // 2 - 450,
|
||||
self.root.winfo_screenheight() // 2 - 350
|
||||
))
|
||||
except:
|
||||
pass
|
||||
|
||||
# 加载配置
|
||||
config.load_from_file(CONFIG_FILE_PATH)
|
||||
|
||||
# 界面变量
|
||||
self.txt_folder_var = tk.StringVar(value=config.last_txt_folder)
|
||||
self.images_root_var = tk.StringVar(value=config.last_images_root)
|
||||
self.output_root_var = tk.StringVar(value=config.last_output_root)
|
||||
|
||||
# 创建界面
|
||||
self._create_main_interface()
|
||||
|
||||
def run(self):
|
||||
"""运行应用程序"""
|
||||
try:
|
||||
self._show_main_window()
|
||||
self.root.protocol("WM_DELETE_WINDOW", self._on_closing)
|
||||
self.root.mainloop()
|
||||
except Exception as e:
|
||||
sg.popup_error(f"应用程序运行出错: {str(e)}")
|
||||
messagebox.showerror("错误", f"应用程序运行出错: {str(e)}")
|
||||
finally:
|
||||
# 保存配置
|
||||
config.save_to_file(CONFIG_FILE_PATH)
|
||||
|
||||
def _show_main_window(self):
|
||||
"""显示主界面"""
|
||||
layout = self._create_main_layout()
|
||||
window = sg.Window('批量Markdown TXT转DOCX工具', layout, resizable=True)
|
||||
def _on_closing(self):
|
||||
"""窗口关闭处理"""
|
||||
self._save_current_settings()
|
||||
self.root.destroy()
|
||||
|
||||
def _create_main_interface(self):
|
||||
"""创建主界面"""
|
||||
# 主容器框架
|
||||
main_frame = ttk.Frame(self.root, padding="10")
|
||||
main_frame.pack(fill='both', expand=True)
|
||||
|
||||
try:
|
||||
self._handle_main_window_events(window)
|
||||
finally:
|
||||
window.close()
|
||||
|
||||
def _create_main_layout(self):
|
||||
"""创建主界面布局"""
|
||||
return [
|
||||
[sg.Text('批量Markdown TXT转DOCX工具', font=('bold', 16))],
|
||||
[sg.Text('(按文件名匹配TXT文件和图片文件夹,支持完整Markdown格式)', text_color='gray')],
|
||||
[sg.HSeparator()],
|
||||
[sg.Text('TXT文件文件夹:', size=(15, 1)),
|
||||
sg.InputText(key='txt_folder', enable_events=True, default_text=config.last_txt_folder),
|
||||
sg.FolderBrowse('浏览')],
|
||||
[sg.Text('图片根文件夹:', size=(15, 1)),
|
||||
sg.InputText(key='images_root', enable_events=True, default_text=config.last_images_root),
|
||||
sg.FolderBrowse('浏览')],
|
||||
[sg.Text('输出根文件夹:', size=(15, 1)),
|
||||
sg.InputText(key='output_root', enable_events=True, default_text=config.last_output_root),
|
||||
sg.FolderBrowse('浏览'),
|
||||
sg.Text('(当选择"输出到指定文件夹"时有效)', text_color='gray')],
|
||||
[sg.Button('扫描文件', size=(12, 1)),
|
||||
sg.Button('编辑匹配', size=(12, 1), disabled=True),
|
||||
sg.Button('转换设置', size=(12, 1)),
|
||||
sg.Button('帮助', size=(8, 1))],
|
||||
[sg.HSeparator()],
|
||||
[sg.Text('匹配结果预览:', font=('bold', 10))],
|
||||
[sg.Table(
|
||||
values=[],
|
||||
headings=['TXT文件名', '相对路径', '匹配的图片文件夹'],
|
||||
key='-PREVIEW_TABLE-',
|
||||
auto_size_columns=False,
|
||||
col_widths=[20, 30, 30],
|
||||
justification='left',
|
||||
size=(None, 10)
|
||||
)],
|
||||
[sg.ProgressBar(100, orientation='h', size=(80, 20), key='progress_bar', visible=False)],
|
||||
[sg.Text('状态: 就绪', key='status_text', size=(80, 1))],
|
||||
[sg.Button('开始批量转换', size=(15, 1), disabled=True), sg.Button('退出')]
|
||||
]
|
||||
|
||||
def _handle_main_window_events(self, window):
|
||||
"""处理主窗口事件"""
|
||||
progress_bar = window['progress_bar']
|
||||
status_text = window['status_text']
|
||||
preview_table = window['-PREVIEW_TABLE-']
|
||||
output_root_input = window['output_root']
|
||||
# 标题
|
||||
title_frame = ttk.Frame(main_frame)
|
||||
title_frame.pack(fill='x', pady=(0, 10))
|
||||
title_label = ttk.Label(title_frame, text='批量Markdown TXT转DOCX工具',
|
||||
font=('', 16, 'bold'))
|
||||
title_label.pack()
|
||||
subtitle_label = ttk.Label(title_frame,
|
||||
text='(按文件名匹配TXT文件和图片文件夹,支持完整Markdown格式)',
|
||||
foreground='gray')
|
||||
subtitle_label.pack()
|
||||
|
||||
# 初始化窗口,避免更新元素时的警告
|
||||
window.read(timeout=1)
|
||||
# 分隔线
|
||||
ttk.Separator(main_frame, orient='horizontal').pack(fill='x', pady=10)
|
||||
|
||||
# 初始化输出根文件夹输入框状态
|
||||
self._update_output_root_state(output_root_input)
|
||||
# 路径选择区域
|
||||
path_frame = ttk.LabelFrame(main_frame, text='文件路径设置', padding="10")
|
||||
path_frame.pack(fill='x', pady=(0, 10))
|
||||
|
||||
# TXT文件夹
|
||||
txt_frame = ttk.Frame(path_frame)
|
||||
txt_frame.pack(fill='x', pady=5)
|
||||
ttk.Label(txt_frame, text='TXT文件文件夹:', width=15).pack(side='left')
|
||||
txt_entry = ttk.Entry(txt_frame, textvariable=self.txt_folder_var, width=50)
|
||||
txt_entry.pack(side='left', fill='x', expand=True, padx=(0, 5))
|
||||
txt_entry.bind('<KeyRelease>', self._on_path_change)
|
||||
ttk.Button(txt_frame, text='浏览', command=self._browse_txt_folder, width=8).pack(side='right')
|
||||
|
||||
# 图片根文件夹
|
||||
img_frame = ttk.Frame(path_frame)
|
||||
img_frame.pack(fill='x', pady=5)
|
||||
ttk.Label(img_frame, text='图片根文件夹:', width=15).pack(side='left')
|
||||
img_entry = ttk.Entry(img_frame, textvariable=self.images_root_var, width=50)
|
||||
img_entry.pack(side='left', fill='x', expand=True, padx=(0, 5))
|
||||
img_entry.bind('<KeyRelease>', self._on_path_change)
|
||||
ttk.Button(img_frame, text='浏览', command=self._browse_images_root, width=8).pack(side='right')
|
||||
|
||||
# 输出根文件夹
|
||||
output_frame = ttk.Frame(path_frame)
|
||||
output_frame.pack(fill='x', pady=5)
|
||||
ttk.Label(output_frame, text='输出根文件夹:', width=15).pack(side='left')
|
||||
self.output_entry = ttk.Entry(output_frame, textvariable=self.output_root_var, width=50)
|
||||
self.output_entry.pack(side='left', fill='x', expand=True, padx=(0, 5))
|
||||
ttk.Button(output_frame, text='浏览', command=self._browse_output_root, width=8).pack(side='right')
|
||||
|
||||
# 提示文本
|
||||
hint_label = ttk.Label(path_frame, text='提示:输出根文件夹在选择"输出到指定文件夹"时有效',
|
||||
foreground='gray', font=('', 8))
|
||||
hint_label.pack(anchor='w', pady=(5, 0))
|
||||
|
||||
# 按钮区域
|
||||
button_frame = ttk.Frame(main_frame)
|
||||
button_frame.pack(fill='x', pady=10)
|
||||
|
||||
self.scan_btn = ttk.Button(button_frame, text='扫描文件', command=self._scan_files, width=12)
|
||||
self.scan_btn.pack(side='left', padx=(0, 10))
|
||||
|
||||
self.edit_btn = ttk.Button(button_frame, text='编辑匹配', command=self._edit_matching,
|
||||
state='disabled', width=12)
|
||||
self.edit_btn.pack(side='left', padx=(0, 10))
|
||||
|
||||
ttk.Button(button_frame, text='转换设置', command=self._show_config, width=12).pack(side='left', padx=(0, 10))
|
||||
ttk.Button(button_frame, text='帮助', command=self._show_help, width=8).pack(side='left')
|
||||
|
||||
# 预览区域
|
||||
preview_frame = ttk.LabelFrame(main_frame, text='匹配结果预览', padding="10")
|
||||
preview_frame.pack(fill='both', expand=True, pady=(0, 10))
|
||||
|
||||
# 表格区域
|
||||
table_frame = ttk.Frame(preview_frame)
|
||||
table_frame.pack(fill='both', expand=True)
|
||||
|
||||
# 创建Treeview表格
|
||||
columns = ('txt_name', 'relative_path', 'image_folder')
|
||||
self.tree = ttk.Treeview(table_frame, columns=columns, show='headings', height=12)
|
||||
self.tree.heading('txt_name', text='TXT文件名')
|
||||
self.tree.heading('relative_path', text='相对路径')
|
||||
self.tree.heading('image_folder', text='匹配的图片文件夹')
|
||||
self.tree.column('txt_name', width=200)
|
||||
self.tree.column('relative_path', width=300)
|
||||
self.tree.column('image_folder', width=300)
|
||||
|
||||
# 滚动条
|
||||
v_scrollbar = ttk.Scrollbar(table_frame, orient='vertical', command=self.tree.yview)
|
||||
h_scrollbar = ttk.Scrollbar(table_frame, orient='horizontal', command=self.tree.xview)
|
||||
self.tree.configure(yscrollcommand=v_scrollbar.set, xscrollcommand=h_scrollbar.set)
|
||||
|
||||
# 布局表格和滚动条
|
||||
self.tree.grid(row=0, column=0, sticky='nsew')
|
||||
v_scrollbar.grid(row=0, column=1, sticky='ns')
|
||||
h_scrollbar.grid(row=1, column=0, sticky='ew')
|
||||
|
||||
table_frame.grid_rowconfigure(0, weight=1)
|
||||
table_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# 进度条(初始隐藏)
|
||||
self.progress_var = tk.DoubleVar()
|
||||
self.progress_bar = ttk.Progressbar(main_frame, variable=self.progress_var, maximum=100)
|
||||
|
||||
# 状态栏和按钮区域
|
||||
bottom_frame = ttk.Frame(main_frame)
|
||||
bottom_frame.pack(fill='x', pady=(0, 0))
|
||||
|
||||
# 状态文本
|
||||
self.status_text = tk.StringVar(value='状态: 就绪')
|
||||
status_label = ttk.Label(bottom_frame, textvariable=self.status_text)
|
||||
status_label.pack(side='left')
|
||||
|
||||
# 右侧按钮
|
||||
right_buttons = ttk.Frame(bottom_frame)
|
||||
right_buttons.pack(side='right')
|
||||
|
||||
self.convert_btn = ttk.Button(right_buttons, text='开始批量转换',
|
||||
command=self._start_conversion, state='disabled', width=15)
|
||||
self.convert_btn.pack(side='left', padx=(0, 10))
|
||||
|
||||
ttk.Button(right_buttons, text='退出', command=self._on_closing, width=8).pack(side='left')
|
||||
|
||||
# 初始化输出路径状态
|
||||
self._update_output_root_state()
|
||||
|
||||
while True:
|
||||
event, values = window.read()
|
||||
def _browse_txt_folder(self):
|
||||
"""浏览TXT文件夹"""
|
||||
folder = filedialog.askdirectory(title='选择TXT文件所在的文件夹')
|
||||
if folder:
|
||||
self.txt_folder_var.set(folder)
|
||||
self._on_path_change()
|
||||
|
||||
def _browse_images_root(self):
|
||||
"""浏览图片根文件夹"""
|
||||
folder = filedialog.askdirectory(title='选择图片根文件夹')
|
||||
if folder:
|
||||
self.images_root_var.set(folder)
|
||||
self._on_path_change()
|
||||
|
||||
def _browse_output_root(self):
|
||||
"""浏览输出根文件夹"""
|
||||
folder = filedialog.askdirectory(title='选择输出根文件夹')
|
||||
if folder:
|
||||
self.output_root_var.set(folder)
|
||||
|
||||
def _on_path_change(self, event=None):
|
||||
"""路径变化时的处理"""
|
||||
# 自动设置输出路径
|
||||
if not self.output_root_var.get():
|
||||
if self.txt_folder_var.get():
|
||||
self.output_root_var.set(self.txt_folder_var.get())
|
||||
elif self.images_root_var.get():
|
||||
self.output_root_var.set(self.images_root_var.get())
|
||||
|
||||
if event in (sg.WIN_CLOSED, '退出'):
|
||||
self._save_current_settings(values)
|
||||
break
|
||||
|
||||
elif event == '转换设置':
|
||||
self._show_config_window()
|
||||
self._update_output_root_state(output_root_input)
|
||||
|
||||
elif event == '帮助':
|
||||
self._show_help_window()
|
||||
|
||||
elif event == '扫描文件':
|
||||
self._handle_scan_files(values, window, status_text, preview_table)
|
||||
|
||||
elif event == '编辑匹配':
|
||||
self._handle_edit_matching(values, preview_table)
|
||||
|
||||
elif event == '开始批量转换':
|
||||
self._handle_batch_conversion(values, window, progress_bar, status_text)
|
||||
|
||||
elif event in ('txt_folder', 'images_root') and values[event] and not values.get('output_root', ''):
|
||||
# 自动设置输出路径
|
||||
default_output = values['txt_folder'] if values['txt_folder'] else values['images_root']
|
||||
window['output_root'].update(default_output)
|
||||
|
||||
def _update_output_root_state(self, output_root_input):
|
||||
def _update_output_root_state(self):
|
||||
"""根据配置更新输出根文件夹输入框的状态"""
|
||||
if config.output_location == "custom":
|
||||
output_root_input.update(disabled=False)
|
||||
try:
|
||||
output_root_input.Widget.configure(foreground='black')
|
||||
except:
|
||||
pass
|
||||
self.output_entry.configure(state='normal')
|
||||
else:
|
||||
output_root_input.update(disabled=True)
|
||||
try:
|
||||
output_root_input.Widget.configure(foreground='gray')
|
||||
except:
|
||||
pass
|
||||
self.output_entry.configure(state='disabled')
|
||||
|
||||
def _save_current_settings(self, values):
|
||||
def _save_current_settings(self):
|
||||
"""保存当前设置"""
|
||||
if values:
|
||||
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)
|
||||
config.last_txt_folder = self.txt_folder_var.get()
|
||||
config.last_images_root = self.images_root_var.get()
|
||||
config.last_output_root = self.output_root_var.get()
|
||||
config.save_to_file(CONFIG_FILE_PATH)
|
||||
|
||||
def _handle_scan_files(self, values, window, status_text, preview_table):
|
||||
"""处理扫描文件事件"""
|
||||
txt_folder = values['txt_folder']
|
||||
images_root = values['images_root']
|
||||
def _scan_files(self):
|
||||
"""扫描文件"""
|
||||
txt_folder = self.txt_folder_var.get()
|
||||
images_root = self.images_root_var.get()
|
||||
|
||||
if not txt_folder:
|
||||
sg.popup_error('请选择TXT文件所在的文件夹')
|
||||
messagebox.showerror('错误', '请选择TXT文件所在的文件夹')
|
||||
return
|
||||
|
||||
if not images_root:
|
||||
sg.popup_error('请选择图片根文件夹')
|
||||
messagebox.showerror('错误', '请选择图片根文件夹')
|
||||
return
|
||||
|
||||
# 保存路径
|
||||
config.last_txt_folder = txt_folder
|
||||
config.last_images_root = images_root
|
||||
if values['output_root']:
|
||||
config.last_output_root = values['output_root']
|
||||
if self.output_root_var.get():
|
||||
config.last_output_root = self.output_root_var.get()
|
||||
config.save_to_file(CONFIG_FILE_PATH)
|
||||
|
||||
try:
|
||||
status_text.update('正在扫描TXT文件...')
|
||||
window.refresh()
|
||||
self.status_text.set('正在扫描TXT文件...')
|
||||
self.root.update()
|
||||
|
||||
txt_files = self.file_handler.scan_txt_files(txt_folder)
|
||||
|
||||
status_text.update('正在匹配图片文件夹...')
|
||||
window.refresh()
|
||||
self.status_text.set('正在匹配图片文件夹...')
|
||||
self.root.update()
|
||||
|
||||
self.matched_pairs = self.file_handler.find_matching_image_folders(txt_files, images_root)
|
||||
|
||||
# 更新预览表格
|
||||
table_data = []
|
||||
for item in self.tree.get_children():
|
||||
self.tree.delete(item)
|
||||
|
||||
for pair in self.matched_pairs:
|
||||
img_folder = pair['image_folder']['relative_path'] if pair['image_folder'] else "无匹配"
|
||||
table_data.append([
|
||||
self.tree.insert('', 'end', values=(
|
||||
pair['txt']['name'],
|
||||
pair['txt']['relative_path'],
|
||||
img_folder
|
||||
])
|
||||
))
|
||||
|
||||
preview_table.update(values=table_data)
|
||||
status_text.update(f'扫描完成: 找到 {len(self.matched_pairs)} 个TXT文件')
|
||||
self.status_text.set(f'扫描完成: 找到 {len(self.matched_pairs)} 个TXT文件')
|
||||
|
||||
# 启用相关按钮
|
||||
window['编辑匹配'].update(disabled=False)
|
||||
window['开始批量转换'].update(disabled=False)
|
||||
self.edit_btn.configure(state='normal')
|
||||
self.convert_btn.configure(state='normal')
|
||||
|
||||
except Exception as e:
|
||||
sg.popup_error(f'扫描失败: {str(e)}')
|
||||
status_text.update('状态: 扫描失败')
|
||||
messagebox.showerror('错误', f'扫描失败: {str(e)}')
|
||||
self.status_text.set('状态: 扫描失败')
|
||||
|
||||
def _handle_edit_matching(self, values, preview_table):
|
||||
"""处理编辑匹配事件"""
|
||||
images_root = values['images_root']
|
||||
def _edit_matching(self):
|
||||
"""编辑匹配"""
|
||||
images_root = self.images_root_var.get()
|
||||
if not images_root:
|
||||
sg.popup_error('请选择图片根文件夹')
|
||||
messagebox.showerror('错误', '请选择图片根文件夹')
|
||||
return
|
||||
|
||||
if not self.matched_pairs:
|
||||
sg.popup_error('请先扫描文件')
|
||||
messagebox.showerror('错误', '请先扫描文件')
|
||||
return
|
||||
|
||||
# 显示匹配编辑窗口
|
||||
self.matched_pairs = self._show_matching_editor(self.matched_pairs, images_root)
|
||||
|
||||
# 更新预览表格
|
||||
table_data = []
|
||||
for item in self.tree.get_children():
|
||||
self.tree.delete(item)
|
||||
|
||||
for pair in self.matched_pairs:
|
||||
img_folder = pair['image_folder']['relative_path'] if pair['image_folder'] else "无匹配"
|
||||
table_data.append([
|
||||
self.tree.insert('', 'end', values=(
|
||||
pair['txt']['name'],
|
||||
pair['txt']['relative_path'],
|
||||
img_folder
|
||||
])
|
||||
))
|
||||
|
||||
preview_table.update(values=table_data)
|
||||
|
||||
def _handle_batch_conversion(self, values, window, progress_bar, status_text):
|
||||
"""处理批量转换事件"""
|
||||
def _start_conversion(self):
|
||||
"""开始批量转换"""
|
||||
if not self.matched_pairs:
|
||||
sg.popup_error('请先扫描文件')
|
||||
messagebox.showerror('错误', '请先扫描文件')
|
||||
return
|
||||
|
||||
if config.output_location == "custom" and not values['output_root']:
|
||||
sg.popup_error('请选择输出根文件夹(在"转换设置"中选择了"输出到指定文件夹")')
|
||||
if config.output_location == "custom" and not self.output_root_var.get():
|
||||
messagebox.showerror('错误', '请选择输出根文件夹(在"转换设置"中选择了"输出到指定文件夹")')
|
||||
return
|
||||
|
||||
try:
|
||||
progress_bar.update(0, visible=True)
|
||||
status_text.update('开始批量转换...')
|
||||
window.refresh()
|
||||
# 禁用按钮,显示进度条
|
||||
self.convert_btn.configure(state='disabled')
|
||||
self.progress_bar.pack(fill='x', padx=10, pady=5, before=self.root.children[list(self.root.children.keys())[-2]])
|
||||
|
||||
# 在线程中执行转换,避免界面卡死
|
||||
def run_conversion():
|
||||
try:
|
||||
self.status_text.set('开始批量转换...')
|
||||
self.root.update()
|
||||
|
||||
def update_batch_progress(progress, text):
|
||||
progress_bar.update(progress)
|
||||
status_text.update(f'状态: {text}')
|
||||
window.refresh()
|
||||
def update_batch_progress(progress, text):
|
||||
self.progress_var.set(progress)
|
||||
self.status_text.set(f'状态: {text}')
|
||||
self.root.update()
|
||||
|
||||
results = self.batch_processor.process_batch(
|
||||
self.matched_pairs,
|
||||
values['output_root'],
|
||||
update_batch_progress
|
||||
)
|
||||
|
||||
self._show_results_window(results)
|
||||
status_text.update('状态: 批量转换完成')
|
||||
results = self.batch_processor.process_batch(
|
||||
self.matched_pairs,
|
||||
self.output_root_var.get(),
|
||||
update_batch_progress
|
||||
)
|
||||
|
||||
# 在主线程中显示结果
|
||||
self.root.after(0, lambda: self._show_results_window(results))
|
||||
self.root.after(0, lambda: self.status_text.set('状态: 批量转换完成'))
|
||||
|
||||
except Exception as e:
|
||||
sg.popup_error(f'批量处理失败: {str(e)}')
|
||||
status_text.update('状态: 批量转换失败')
|
||||
finally:
|
||||
progress_bar.update(0, visible=False)
|
||||
except Exception as e:
|
||||
self.root.after(0, lambda: messagebox.showerror('错误', f'批量处理失败: {str(e)}'))
|
||||
self.root.after(0, lambda: self.status_text.set('状态: 批量转换失败'))
|
||||
finally:
|
||||
self.root.after(0, lambda: self.progress_bar.pack_forget())
|
||||
self.root.after(0, lambda: self.convert_btn.configure(state='normal'))
|
||||
|
||||
# 启动线程
|
||||
thread = threading.Thread(target=run_conversion, daemon=True)
|
||||
thread.start()
|
||||
|
||||
def _show_config(self):
|
||||
"""显示配置窗口"""
|
||||
from gui_config import show_config_window
|
||||
show_config_window()
|
||||
self._update_output_root_state()
|
||||
|
||||
def _show_help(self):
|
||||
"""显示帮助窗口"""
|
||||
help_text = """
|
||||
批量Markdown TXT转DOCX工具使用说明:
|
||||
|
||||
1. 选择包含Markdown内容的TXT文件所在文件夹
|
||||
2. 选择图片文件夹的根目录(程序会自动查找子文件夹)
|
||||
3. 选择输出文件的保存根目录(当选择"输出到指定文件夹"时有效)
|
||||
4. 点击"扫描文件"按钮,程序会自动匹配TXT文件和图片文件夹
|
||||
5. 查看匹配结果,可点击"编辑匹配"调整匹配关系
|
||||
6. 点击"开始批量转换"生成DOCX文件
|
||||
|
||||
支持的Markdown格式:
|
||||
- 标题:# ## ### #### ##### ######
|
||||
- 粗体:**文字** 或 __文字__
|
||||
- 斜体:*文字* 或 _文字_
|
||||
- 行内代码:`代码`
|
||||
- 代码块:```语言\n代码\n```
|
||||
- 删除线:~~文字~~
|
||||
- 链接:[链接文字](URL)
|
||||
- 图片:
|
||||
- 无序列表:- 或 * 或 +
|
||||
- 有序列表:1. 2. 3.
|
||||
- 引用:> 引用内容
|
||||
- 表格:| 列1 | 列2 |
|
||||
- 水平分隔线:--- 或 *** 或 ___
|
||||
|
||||
文字处理功能:
|
||||
- 转换文字顺序:将文字内容进行特定转换处理
|
||||
- 错别字处理:可以按设定强度引入常见的错别字,用于测试或特殊用途
|
||||
- 标点符号替换:将句号转换为逗号,保留文末句号
|
||||
|
||||
输出路径选择:
|
||||
- 输出到TXT文件所在文件夹: 每个DOCX文件会直接保存在对应TXT文件所在的文件夹中
|
||||
- 输出到指定文件夹: 所有DOCX文件会直接保存在您指定的文件夹中
|
||||
|
||||
匹配规则:
|
||||
- 完全匹配: TXT文件名(不含扩展名)与TXT文件夹名完全相同
|
||||
- 前缀匹配: 图片文件夹名以前缀形式包含TXT文件名
|
||||
- 包含匹配: 图片文件夹名中包含TXT文件名
|
||||
|
||||
转换规则:
|
||||
- 每个小标题的第一段后会插入一张图片
|
||||
- 先将Markdown格式转换为DOCX格式,再处理文字内容
|
||||
- 支持文字顺序调换、错别字处理和标点符号替换功能
|
||||
|
||||
错别字处理说明:
|
||||
- 错误强度:控制替换比例,0.0表示不替换,1.0表示替换所有可能的字
|
||||
- 错别字库:可自定义JSON格式的错别字映射文件
|
||||
- 常见映射:的↔地↔得、在↔再、是↔事等
|
||||
"""
|
||||
|
||||
# 创建帮助窗口
|
||||
help_window = tk.Toplevel(self.root)
|
||||
help_window.title('使用帮助')
|
||||
help_window.geometry('600x500')
|
||||
help_window.transient(self.root)
|
||||
help_window.grab_set()
|
||||
|
||||
# 滚动文本区域
|
||||
text_widget = ScrolledText(help_window, wrap=tk.WORD, padx=10, pady=10)
|
||||
text_widget.pack(fill='both', expand=True, padx=10, pady=10)
|
||||
text_widget.insert('1.0', help_text)
|
||||
text_widget.configure(state='disabled')
|
||||
|
||||
# 关闭按钮
|
||||
ttk.Button(help_window, text='关闭', command=help_window.destroy).pack(pady=10)
|
||||
|
||||
def _show_config_window(self):
|
||||
"""显示配置窗口"""
|
||||
@ -335,7 +512,7 @@ class TxtToDocxApp:
|
||||
- 错别字库:可自定义JSON格式的错别字映射文件
|
||||
- 常见映射:的↔地↔得、在↔再、是↔事等
|
||||
"""
|
||||
sg.popup_scrolled('使用帮助', help_text, size=(70, 25))
|
||||
|
||||
|
||||
def _show_matching_editor(self, matched_pairs, images_root):
|
||||
"""显示匹配编辑窗口"""
|
||||
@ -350,7 +527,7 @@ class TxtToDocxApp:
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("正在启动批量Markdown TXT转DOCX工具...")
|
||||
# print("正在启动批量Markdown TXT转DOCX工具...")
|
||||
|
||||
try:
|
||||
app = TxtToDocxApp()
|
||||
@ -359,7 +536,7 @@ def main():
|
||||
print("\n用户中断程序运行")
|
||||
except Exception as e:
|
||||
print(f"程序运行出错: {e}")
|
||||
sg.popup_error(f"程序运行出错: {e}")
|
||||
messagebox.showerror("错误", f"程序运行出错: {e}")
|
||||
finally:
|
||||
print("程序已退出")
|
||||
|
||||
|
||||
@ -215,7 +215,7 @@ class FileHandler:
|
||||
except UnicodeDecodeError:
|
||||
continue
|
||||
|
||||
raise UnicodeDecodeError(f"无法解码文件 '{filename}',尝试的编码格式:{encodings}")
|
||||
raise ValueError(f"无法解码文件 '{filename}',尝试的编码格式:{encodings}")
|
||||
|
||||
@staticmethod
|
||||
def write_file(filename: str, content: str, encoding: str = 'utf-8') -> None:
|
||||
@ -394,12 +394,15 @@ def replace_text(text):
|
||||
run_tests()
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 命令行模式
|
||||
if len(sys.argv) > 1:
|
||||
main()
|
||||
else:
|
||||
# 示例演示
|
||||
sample_text = text
|
||||
sample_text = """阅读此文之前,麻烦您点击一下"关注",既方便您进行讨论和分享,又能给您带来不一样的参与感,创作不易,感谢您的支持。
|
||||
|
||||
曾经"半路出家",如今黯然无声,他的故事值得一品"""
|
||||
|
||||
print("示例演示:")
|
||||
print("原文:")
|
||||
@ -423,8 +426,6 @@ def replace_text(text):
|
||||
print(" python script.py -f input.txt -p '。!?' -s # 自定义标点符号并显示统计")
|
||||
print(" python script.py test # 运行单元测试")
|
||||
|
||||
return processed
|
||||
|
||||
|
||||
|
||||
text = """阅读此文之前,麻烦您点击一下“关注”,既方便您进行讨论和分享,又能给您带来不一样的参与感,创作不易,感谢您的支持。
|
||||
@ -469,5 +470,3 @@ text = """阅读此文之前,麻烦您点击一下“关注”,既方便您
|
||||
欢迎留言讨论,你们的每一次互动,都是创作的动力。"""
|
||||
|
||||
|
||||
result = replace_text(text)
|
||||
print(result)
|
||||
Loading…
Reference in New Issue
Block a user