""" GUI配置窗口模块 提供配置设置的图形界面。 """ import tkinter as tk from tkinter import ttk, filedialog, messagebox from config import config from gui_style_manager import create_style_tab def show_config_window(): """显示配置窗口""" # 创建配置窗口 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) # 文章样式选项卡 style_frame = create_style_tab(notebook) notebook.add(style_frame, text='文章样式') # 底部按钮 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('<>', 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) # 免责声明 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 # 添加返回值 } 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) # 图片不足时策略 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): # 如果输入无效,保持当前值不变 pass def _update_image_interval(value): """更新图片插入间隔""" try: config.image_insert_interval = int(value) except (ValueError, tk.TclError): # 如果输入无效,保持当前值不变 pass 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 # 添加这行 # 更新界面变量 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) # 添加这行 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()