TxT2Docx/gui_config.py

348 lines
16 KiB
Python
Raw Permalink Normal View History

2025-09-21 19:01:40 +08:00
"""
GUI配置窗口模块
提供配置设置的图形界面
"""
2025-09-21 20:40:36 +08:00
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
2025-09-21 19:01:40 +08:00
from config import config
2025-09-22 21:10:29 +08:00
from gui_style_manager import create_style_tab
2025-09-21 19:01:40 +08:00
def show_config_window():
"""显示配置窗口"""
2025-09-21 20:40:36 +08:00
# 创建配置窗口
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)
2025-09-22 21:10:29 +08:00
# 文章样式选项卡
style_frame = create_style_tab(notebook)
notebook.add(style_frame, text='文章样式')
2025-09-21 20:40:36 +08:00
# 底部按钮
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
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()))
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)
# 段落句子数控制
ttk.Label(parent, text='段落控制', font=('', 11, 'bold'), foreground='darkblue').pack(anchor='w', padx=10, pady=(0, 5))
# 每段最大句子数
sentence_frame = ttk.Frame(parent)
sentence_frame.pack(fill='x', padx=10, pady=5)
ttk.Label(sentence_frame, text='每段最大句子数:', width=15).pack(side='left')
sentence_var = tk.IntVar(value=config.max_sentences_per_paragraph)
sentence_spin = ttk.Spinbox(sentence_frame, from_=0, to=100, textvariable=sentence_var, width=10)
sentence_spin.pack(side='left', padx=(0, 10))
ttk.Label(sentence_frame, text='(0表示不限制)').pack(side='left')
def update_sentence_limit(*args):
try:
config.max_sentences_per_paragraph = sentence_var.get()
except (tk.TclError, ValueError):
# 如果输入无效设置为默认值0
config.max_sentences_per_paragraph = 0
sentence_var.set(0)
sentence_var.trace('w', update_sentence_limit)
ttk.Separator(parent, orient='horizontal').pack(fill='x', padx=10, pady=15)
2025-09-21 20:40:36 +08:00
# 免责声明
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,
'max_sentences': sentence_var # 添加返回值
2025-09-21 20:40:36 +08:00
}
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))
# 有标题时的图片插入位置
ttk.Label(parent, text='有标题时:', font=('', 9)).pack(anchor='w', padx=20, pady=(0, 2))
position_var = tk.StringVar(value=getattr(config, 'image_insert_position', 'after_title'))
ttk.Radiobutton(parent, text='标题前插入', variable=position_var, value='before_title').pack(anchor='w', padx=30, pady=1)
ttk.Radiobutton(parent, text='标题后插入', variable=position_var, value='after_title').pack(anchor='w', padx=30, pady=1)
position_var.trace('w', lambda *args: setattr(config, 'image_insert_position', position_var.get()))
# 无标题时的图片插入间隔
interval_frame = ttk.Frame(parent)
interval_frame.pack(anchor='w', padx=20, pady=5)
ttk.Label(interval_frame, text='无标题时每隔', font=('', 9)).pack(side='left')
interval_var = tk.StringVar(value=str(getattr(config, 'image_insert_interval', 5)))
interval_entry = ttk.Entry(interval_frame, textvariable=interval_var, width=5)
interval_entry.pack(side='left', padx=2)
ttk.Label(interval_frame, text='段插入一张图片', font=('', 9)).pack(side='left')
interval_var.trace('w', lambda *args: _update_image_interval(interval_var.get()))
ttk.Separator(parent, orient='horizontal').pack(fill='x', padx=10, pady=15)
2025-09-21 20:40:36 +08:00
# 图片不足时策略
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()))
def _update_image_width(value):
"""更新图片宽度"""
try:
config.image_width = float(value)
except (ValueError, tk.TclError):
# 如果输入无效,保持当前值不变
2025-09-21 20:40:36 +08:00
pass
def _update_image_interval(value):
"""更新图片插入间隔"""
try:
config.image_insert_interval = int(value)
except (ValueError, tk.TclError):
# 如果输入无效,保持当前值不变
pass
2025-09-21 20:40:36 +08:00
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
config.max_sentences_per_paragraph = default_config.max_sentences_per_paragraph # 添加这行
2025-09-21 20:40:36 +08:00
# 更新界面变量
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)
char_vars['max_sentences'].set(default_config.max_sentences_per_paragraph) # 添加这行
2025-09-21 20:40:36 +08:00
messagebox.showinfo('信息', '配置已重置为默认值')
def _save_config(window, char_vars):
"""保存配置"""
from config import CONFIG_FILE_PATH
config.save_to_file(CONFIG_FILE_PATH)
messagebox.showinfo('信息', '配置已保存')
window.destroy()