@@ -47,27 +47,23 @@ def __init__(self, root_dir: str):
4747 # Use get_default_config_path() for PyInstaller compatibility
4848 # Temporarily set a placeholder, will be properly set after initialization
4949 self .default_config_path = None
50+ self .user_config_path = None
5051
5152 self .config_path = None # This will hold the path of a loaded file
5253 self .current_config : AppSettings = AppSettings ()
5354
5455 # Set the correct default config path
5556 self .default_config_path = self .get_default_config_path ()
56- self .logger .info (f"默认配置文件路径: { self .default_config_path } " )
57- self .logger .info (f"配置文件是否存在: { os .path .exists (self .default_config_path )} " )
57+ self .user_config_path = self .get_user_config_path ()
58+ self .logger .debug (f"默认配置: { os .path .basename (self .default_config_path )} " )
59+ self .logger .debug (f"用户配置: { os .path .basename (self .user_config_path )} " )
60+ self .logger .debug (f"默认配置存在: { os .path .exists (self .default_config_path )} " )
61+ self .logger .debug (f"用户配置存在: { os .path .exists (self .user_config_path )} " )
5862 if hasattr (sys , '_MEIPASS' ):
59- self .logger .info (f"打包环境,sys._MEIPASS = { sys ._MEIPASS } " )
63+ self .logger .debug (f"打包环境,sys._MEIPASS = { sys ._MEIPASS } " )
6064
61- # Try to load the default config on startup
62- if os .path .exists (self .default_config_path ):
63- self .logger .info (f"正在加载配置文件: { self .default_config_path } " )
64- success = self .load_config_file (self .default_config_path )
65- if success :
66- self .logger .info ("配置文件加载成功" )
67- else :
68- self .logger .error ("配置文件加载失败" )
69- else :
70- self .logger .warning (f"Default config file not found at: { self .default_config_path } " )
65+ # 加载配置:优先级 用户配置 > 默认配置 > 代码默认值
66+ self ._load_configs_with_priority ()
7167
7268 self ._translator_configs = None
7369 self ._env_cache = None
@@ -159,7 +155,7 @@ def deep_update(target, source):
159155 self .current_config = AppSettings .parse_obj (new_config_dict )
160156
161157 self .config_path = config_path
162- self .logger .info (f"成功加载配置文件 : { config_path } " )
158+ self .logger .debug (f"加载配置 : { os . path . basename ( config_path ) } " )
163159 self .config_changed .emit (self .current_config .dict ())
164160 return True
165161
@@ -168,21 +164,58 @@ def deep_update(target, source):
168164 return False
169165
170166 def save_config_file (self , config_path : Optional [str ] = None ) -> bool :
171- """保存JSON配置文件"""
167+ """
168+ 保存JSON配置文件
169+ 默认同时保存到用户配置和模板配置
170+ - 模板配置:临时UI状态强制设为默认值
171+ - 用户配置:包含所有配置(保留实际值)
172+ """
172173 try :
173- save_path = config_path or self . config_path or self . default_config_path
174- if not save_path :
175- self . logger . error ( "没有指定保存路径,且无默认路径" )
176- return False
177-
178- os . makedirs ( os . path . dirname ( save_path ), exist_ok = True )
174+ if config_path :
175+ # 如果指定了路径,只保存到指定路径
176+ save_paths = [ config_path ]
177+ else :
178+ # 默认同时保存到两个文件
179+ save_paths = [ self . user_config_path , self . default_config_path ]
179180
180- with open (save_path , 'w' , encoding = 'utf-8' ) as f :
181- json .dump (self .current_config .dict (), f , indent = 2 , ensure_ascii = False )
181+ success_count = 0
182+ for save_path in save_paths :
183+ if not save_path :
184+ continue
182185
183- self .config_path = save_path
184- self .logger .info (f"成功保存配置文件: { save_path } " )
185- return True
186+ # 获取当前配置
187+ config_dict = self .current_config .dict ()
188+
189+ # 只有保存到模板配置时才重置临时状态
190+ is_default_config = save_path == self .default_config_path
191+ if is_default_config :
192+ # 重置临时UI状态为默认值
193+ if 'app' not in config_dict :
194+ config_dict ['app' ] = {}
195+ config_dict ['app' ]['last_open_dir' ] = '.'
196+ config_dict ['app' ]['last_output_path' ] = ''
197+
198+ if 'cli' in config_dict :
199+ config_dict ['cli' ]['verbose' ] = False
200+
201+ try :
202+ os .makedirs (os .path .dirname (save_path ), exist_ok = True )
203+
204+ with open (save_path , 'w' , encoding = 'utf-8' ) as f :
205+ json .dump (config_dict , f , indent = 2 , ensure_ascii = False )
206+
207+ filename = os .path .basename (save_path )
208+ self .logger .debug (f"保存配置: { filename } " )
209+ success_count += 1
210+ except Exception as e :
211+ self .logger .error (f"保存配置失败 ({ os .path .basename (save_path )} ): { e } " )
212+
213+ if success_count > 0 :
214+ self .config_path = self .user_config_path
215+ return True
216+ else :
217+ self .logger .error ("所有配置文件保存失败" )
218+ return False
186219
187220 except Exception as e :
188221 self .logger .error (f"保存配置文件失败: { e } " )
@@ -202,10 +235,8 @@ def reload_config(self):
202235 # 2. 重新创建 AppSettings 对象 (用于UI设置)
203236 self .current_config = AppSettings ()
204237
205- # 3. 在新创建的 AppSettings 对象之上,重新应用 JSON 配置文件中的设置
206- config_file_to_load = self .config_path or self .default_config_path
207- if config_file_to_load and os .path .exists (config_file_to_load ):
208- self .load_config_file (config_file_to_load )
238+ # 3. 按优先级重新加载配置文件
239+ self ._load_configs_with_priority ()
209240
210241 # 4. 通知所有监听者配置已更改
211242 self .config_changed .emit (self .current_config .dict ())
@@ -216,7 +247,7 @@ def reload_from_disk(self):
216247 强制从当前设置的 config_path 重新加载配置, 并通知所有监听者。
217248 """
218249 if self .config_path and os .path .exists (self .config_path ):
219- self .logger .info (f"正在从 { self .config_path } 强制重载配置... " )
250+ self .logger .debug (f"从磁盘重载配置: { os . path . basename ( self .config_path ) } " )
220251 self .load_config_file (self .config_path )
221252 else :
222253 self .logger .warning ("无法重载配置:config_path 未设置或文件不存在。" )
@@ -232,7 +263,7 @@ def get_config_reference(self) -> AppSettings:
232263 def set_config (self , config : AppSettings ) -> None :
233264 """设置配置并通知监听者"""
234265 self .current_config = config .copy (deep = True )
235- self .logger .info ("配置已更新,正在通知监听者..." )
266+ self .logger .debug ("配置已更新,正在通知监听者..." )
236267 self .config_changed .emit (self .current_config .dict ())
237268
238269 def update_config (self , updates : Dict [str , Any ]) -> None :
@@ -249,7 +280,7 @@ def deep_update(target, source):
249280 deep_update (new_config_dict , updates )
250281
251282 self .current_config = AppSettings .parse_obj (new_config_dict )
252- self .logger .info ("配置已更新,正在通知监听者..." )
283+ self .logger .debug ("配置已更新,正在通知监听者..." )
253284 self .config_changed .emit (self .current_config .dict ())
254285
255286 def load_env_vars (self ) -> Dict [str , str ]:
@@ -354,6 +385,106 @@ def get_default_config_path(self) -> str:
354385 # 开发环境
355386 return os .path .join (self .root_dir , "examples" , "config-example.json" )
356387
388+ def get_user_config_path (self ) -> str :
389+ """
390+ 获取用户配置文件路径
391+
392+ 打包后和开发时都在examples目录
393+ """
394+ if hasattr (sys , '_MEIPASS' ):
395+ # 打包环境:用户配置在_internal/examples目录
396+ return os .path .join (sys ._MEIPASS , 'examples' , 'config.json' )
397+ else :
398+ # 开发环境:用户配置在项目根目录的examples目录
399+ return os .path .join (self .root_dir , "examples" , "config.json" )
400+
401+ def _load_configs_with_priority (self ):
402+ """
403+ 按优先级加载配置文件
404+ 优先级:用户配置 > 默认配置 > 代码默认值
405+ """
406+ # 1. 先加载默认配置(如果存在)
407+ if os .path .exists (self .default_config_path ):
408+ self .logger .debug (f"加载默认配置: { os .path .basename (self .default_config_path )} " )
409+ self .load_config_file (self .default_config_path )
410+ else :
411+ self .logger .warning (f"默认配置不存在: { os .path .basename (self .default_config_path )} " )
412+
413+ # 2. 再加载用户配置(如果存在),覆盖默认配置
414+ if os .path .exists (self .user_config_path ):
415+ self .logger .debug (f"加载用户配置: { os .path .basename (self .user_config_path )} " )
416+ self .load_config_file (self .user_config_path )
417+ self .config_path = self .user_config_path
418+ else :
419+ self .logger .debug (f"用户配置不存在,使用默认配置" )
420+ # 如果用户配置不存在,使用默认配置路径作为保存目标
421+ if os .path .exists (self .default_config_path ):
422+ self .config_path = self .default_config_path
423+
424+ # 3. 同步用户配置(添加新字段、删除旧字段)
425+ self ._sync_user_config ()
426+
427+ def _sync_user_config (self ):
428+ """
429+ 同步用户配置文件
430+ - 如果默认配置新增字段 → 添加到用户配置
431+ - 如果默认配置删除字段 → 从用户配置删除
432+ - 保持用户修改的值不变
433+ """
434+ if not os .path .exists (self .default_config_path ):
435+ self .logger .warning ("默认配置不存在,跳过同步" )
436+ return
437+
438+ try :
439+ # 读取默认配置(作为模板)
440+ with open (self .default_config_path , 'r' , encoding = 'utf-8' ) as f :
441+ default_data = json .load (f )
442+
443+ # 如果用户配置存在,读取并同步
444+ if os .path .exists (self .user_config_path ):
445+ with open (self .user_config_path , 'r' , encoding = 'utf-8' ) as f :
446+ user_data = json .load (f )
447+
448+ # 同步配置(递归处理嵌套字典)
449+ synced_data = self ._sync_dict (default_data , user_data )
450+
451+ # 如果有变化,保存回用户配置
452+ if synced_data != user_data :
453+ self .logger .info ("检测到配置结构变化,正在同步用户配置" )
454+ with open (self .user_config_path , 'w' , encoding = 'utf-8' ) as f :
455+ json .dump (synced_data , f , indent = 2 , ensure_ascii = False )
456+ self .logger .info ("用户配置同步完成" )
457+ else :
458+ # 用户配置不存在,创建一个空的(只包含用户修改的值)
459+ self .logger .info ("用户配置不存在,将在首次保存时创建" )
460+
461+ except Exception as e :
462+ self .logger .error (f"同步用户配置失败: { e } " )
463+
464+ def _sync_dict (self , template : dict , user : dict ) -> dict :
465+ """
466+ 递归同步字典
467+ - 保留模板中存在的键
468+ - 删除模板中不存在的键
469+ - 保持用户设置的值
470+ """
471+ result = {}
472+
473+ for key in template .keys ():
474+ if key in user :
475+ # 用户配置有这个键
476+ if isinstance (template [key ], dict ) and isinstance (user [key ], dict ):
477+ # 递归处理嵌套字典
478+ result [key ] = self ._sync_dict (template [key ], user [key ])
479+ else :
480+ # 使用用户的值
481+ result [key ] = user [key ]
482+ else :
483+ # 用户配置没有这个键,使用模板的值
484+ result [key ] = template [key ]
485+
486+ return result
487+
357488 def load_default_config (self ) -> bool :
358489 """加载默认配置"""
359490 default_path = self .get_default_config_path ()
0 commit comments