config_manager.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import json
  2. import os
  3. import copy
  4. def save_json_file(file_path, data):
  5. """Sauvegarde générique de données JSON."""
  6. try:
  7. os.makedirs(os.path.dirname(file_path), exist_ok=True)
  8. with open(file_path, 'w', encoding='utf-8') as f:
  9. json.dump(data, f, indent=4)
  10. return True, None
  11. except Exception as e:
  12. return False, str(e)
  13. def load_json_file(file_path):
  14. """Chargement générique de données JSON."""
  15. if not os.path.exists(file_path):
  16. return None, "File not found"
  17. try:
  18. with open(file_path, 'r', encoding='utf-8') as f:
  19. data = json.load(f)
  20. return data, None
  21. except Exception as e:
  22. return None, str(e)
  23. class ConfigManager:
  24. # Source de vérité unique pour les paramètres d'usine
  25. DEFAULT_CONFIG = {
  26. "machine_settings": {
  27. "theme": "Dark",
  28. "language": "English",
  29. "cmd_mode": "M67 (Analog)",
  30. "firing_mode": "M3/M5",
  31. "m67_e_num": 0,
  32. "ctrl_max": 1000,
  33. "gcode_extension": ".nc",
  34. "laser_latency": 0.0,
  35. "premove": 10.0,
  36. "hor_linestep": 0.1,
  37. "ver_linestep": 0.1,
  38. "enable_thumbnails": True,
  39. "custom_header": "",
  40. "custom_footer": ""
  41. },
  42. "stats": {
  43. "total_lines": 0,
  44. "total_gcodes": 0,
  45. "total_time_seconds": 0.0
  46. }
  47. }
  48. # Clés qui doivent toujours être des entiers
  49. _INT_KEYS = {"m67_e_num", "ctrl_max"}
  50. def __init__(self, config_path):
  51. self.config_path = config_path
  52. self.data = self._load_from_disk()
  53. self._apply_defaults()
  54. def _load_from_disk(self):
  55. """Charge le fichier JSON ou renvoie un dict vide si absent/corrompu"""
  56. if not os.path.exists(self.config_path):
  57. return {}
  58. try:
  59. with open(self.config_path, 'r', encoding='utf-8') as f:
  60. return json.load(f)
  61. except Exception:
  62. return {}
  63. def _apply_defaults(self):
  64. """Injecte les valeurs par défaut manquantes et corrige les types connus."""
  65. for section, content in self.DEFAULT_CONFIG.items():
  66. if section not in self.data:
  67. self.data[section] = copy.deepcopy(content)
  68. else:
  69. for key, val in content.items():
  70. if key not in self.data[section]:
  71. self.data[section][key] = val
  72. # Correction des types : m67_e_num et ctrl_max doivent être int
  73. ms = self.data.get("machine_settings", {})
  74. for key in self._INT_KEYS:
  75. if key in ms:
  76. try:
  77. ms[key] = int(float(str(ms[key])))
  78. except (ValueError, TypeError):
  79. ms[key] = self.DEFAULT_CONFIG["machine_settings"][key]
  80. def get_item(self, section, key, default=None):
  81. """LA MÉTHODE MANQUANTE : Récupère une valeur spécifique"""
  82. section_data = self.data.get(section, {})
  83. return section_data.get(key, default)
  84. def set_item(self, section, key, value):
  85. """
  86. Définit une valeur et la sauvegarde immédiatement.
  87. """
  88. if section not in self.data:
  89. self.data[section] = {}
  90. self.data[section][key] = value
  91. return self.save()
  92. def get_section(self, section_name):
  93. """Récupère une section entière (utile pour SettingsView)"""
  94. return self.data.get(section_name, {})
  95. def set_section(self, section_name, new_data):
  96. """Met à jour une section avant sauvegarde"""
  97. self.data[section_name] = new_data
  98. def save(self):
  99. """Sauvegarde l'état actuel sur le disque"""
  100. try:
  101. with open(self.config_path, 'w', encoding='utf-8') as f:
  102. json.dump(self.data, f, indent=4)
  103. return True
  104. except Exception as e:
  105. print(f"Erreur de sauvegarde : {e}")
  106. return False
  107. def reset_all(self):
  108. """Réinitialisation totale aux paramètres d'usine"""
  109. self.data = copy.deepcopy(self.DEFAULT_CONFIG)
  110. return self.save()