Skip to content

Commit 71dde59

Browse files
committed
Added engine configuration management for GUI
1 parent 095042e commit 71dde59

7 files changed

Lines changed: 467 additions & 14 deletions

File tree

Core/Compiler/__init__.py

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -187,15 +187,37 @@ def compile_all(self) -> None:
187187
if not engine_id:
188188
engine_id = "pyinstaller" # Moteur par défaut
189189

190+
# Sauvegarder la configuration UI de l'engine actif dans le workspace
191+
try:
192+
from Core.EngineConfigManager import save_engine_config_for_gui
193+
194+
save_engine_config_for_gui(self, engine_id)
195+
except Exception:
196+
pass
197+
190198
# Obtenir le moteur (instance)
199+
engine = None
191200
try:
192-
engine = create(engine_id)
193-
except Exception as e:
194-
self.log_i18n(
195-
f"❌ Erreur création moteur '{engine_id}': {e}",
196-
f"❌ Engine creation error '{engine_id}': {e}",
197-
)
198-
return
201+
import EngineLoader as engines_loader
202+
203+
engine = engines_loader.registry.get_instance(engine_id)
204+
except Exception:
205+
engine = None
206+
if engine is None:
207+
try:
208+
engine = create(engine_id)
209+
except Exception as e:
210+
self.log_i18n(
211+
f"❌ Erreur création moteur '{engine_id}': {e}",
212+
f"❌ Engine creation error '{engine_id}': {e}",
213+
)
214+
return
215+
else:
216+
try:
217+
if not getattr(engine, "_gui", None):
218+
engine._gui = self
219+
except Exception:
220+
pass
199221

200222
self.log_i18n(
201223
f"🚀 Démarrage de la compilation avec {engine.name}...",
@@ -400,15 +422,37 @@ def start_compilation_process(self, engine_id: str, file_path: str) -> bool:
400422
Returns:
401423
True if compilation started, False otherwise
402424
"""
425+
# Sauvegarder la configuration UI de l'engine actif dans le workspace
426+
try:
427+
from Core.EngineConfigManager import save_engine_config_for_gui
428+
429+
save_engine_config_for_gui(self, engine_id)
430+
except Exception:
431+
pass
432+
403433
# Obtenir le moteur (instance)
434+
engine = None
404435
try:
405-
engine = create(engine_id)
406-
except Exception as e:
407-
self.log_i18n(
408-
f"❌ Erreur création moteur '{engine_id}': {e}",
409-
f"❌ Engine creation error '{engine_id}': {e}",
410-
)
411-
return False
436+
import EngineLoader as engines_loader
437+
438+
engine = engines_loader.registry.get_instance(engine_id)
439+
except Exception:
440+
engine = None
441+
if engine is None:
442+
try:
443+
engine = create(engine_id)
444+
except Exception as e:
445+
self.log_i18n(
446+
f"❌ Erreur création moteur '{engine_id}': {e}",
447+
f"❌ Engine creation error '{engine_id}': {e}",
448+
)
449+
return False
450+
else:
451+
try:
452+
if not getattr(engine, "_gui", None):
453+
engine._gui = self
454+
except Exception:
455+
pass
412456

413457
# Vérifier les prérequis
414458
if not engine.ensure_tools_installed(self):

Core/EngineConfigManager.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# Copyright 2026 Ague Samuel Amen
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
"""
17+
EngineConfigManager
18+
19+
Persists engine UI configuration per workspace at:
20+
<workspace>/.ark/<engine_id>/config.json
21+
"""
22+
23+
from __future__ import annotations
24+
25+
import json
26+
import os
27+
import re
28+
from datetime import datetime, timezone
29+
from typing import Any, Optional
30+
31+
ENGINE_CONFIG_DIRNAME = ".ark"
32+
ENGINE_CONFIG_BASENAME = "config.json"
33+
ENGINE_CONFIG_VERSION = 1
34+
35+
36+
def _safe_engine_id(engine_id: str) -> str:
37+
try:
38+
raw = str(engine_id or "").strip()
39+
safe = re.sub(r"[^A-Za-z0-9._-]+", "_", raw)
40+
return safe or "engine"
41+
except Exception:
42+
return "engine"
43+
44+
45+
def _engine_config_dir(workspace_dir: str, engine_id: str) -> str:
46+
return os.path.join(workspace_dir, ENGINE_CONFIG_DIRNAME, _safe_engine_id(engine_id))
47+
48+
49+
def _engine_config_path(workspace_dir: str, engine_id: str) -> str:
50+
return os.path.join(_engine_config_dir(workspace_dir, engine_id), ENGINE_CONFIG_BASENAME)
51+
52+
53+
def _atomic_write_json(path: str, data: dict) -> None:
54+
tmp = path + ".tmp"
55+
with open(tmp, "w", encoding="utf-8") as f:
56+
json.dump(data, f, indent=4)
57+
os.replace(tmp, path)
58+
59+
60+
def load_engine_config(workspace_dir: str, engine_id: str) -> dict[str, Any]:
61+
if not workspace_dir or not engine_id:
62+
return {}
63+
path = _engine_config_path(workspace_dir, engine_id)
64+
if not os.path.isfile(path):
65+
return {}
66+
try:
67+
with open(path, encoding="utf-8") as f:
68+
data = json.load(f)
69+
return data if isinstance(data, dict) else {}
70+
except Exception:
71+
return {}
72+
73+
74+
def save_engine_config(
75+
workspace_dir: str,
76+
engine_id: str,
77+
options: Optional[dict],
78+
engine_version: Optional[str] = None,
79+
) -> bool:
80+
if not workspace_dir or not engine_id:
81+
return False
82+
try:
83+
cfg_dir = _engine_config_dir(workspace_dir, engine_id)
84+
os.makedirs(cfg_dir, exist_ok=True)
85+
payload = {
86+
"meta": {
87+
"engine_id": str(engine_id),
88+
"version": ENGINE_CONFIG_VERSION,
89+
"updated_at": datetime.now(timezone.utc).isoformat(),
90+
},
91+
"options": options if isinstance(options, dict) else {},
92+
}
93+
if engine_version:
94+
payload["meta"]["engine_version"] = str(engine_version)
95+
_atomic_write_json(_engine_config_path(workspace_dir, engine_id), payload)
96+
return True
97+
except Exception:
98+
return False
99+
100+
101+
def apply_engine_config(gui, engine, data: dict) -> None:
102+
if not isinstance(data, dict):
103+
return
104+
opts = data.get("options") if isinstance(data, dict) else None
105+
if not isinstance(opts, dict):
106+
opts = data if isinstance(data, dict) else {}
107+
try:
108+
fn = getattr(engine, "set_config", None)
109+
if callable(fn):
110+
fn(gui, opts)
111+
except Exception:
112+
pass
113+
114+
115+
def apply_engine_configs_for_workspace(gui, workspace_dir: str) -> None:
116+
if not workspace_dir:
117+
return
118+
try:
119+
import EngineLoader as engines_loader
120+
121+
for eid in engines_loader.registry.available_engines():
122+
try:
123+
engine = engines_loader.registry.get_instance(eid)
124+
if not engine:
125+
continue
126+
data = load_engine_config(workspace_dir, eid)
127+
if data:
128+
apply_engine_config(gui, engine, data)
129+
except Exception:
130+
continue
131+
except Exception:
132+
pass
133+
134+
try:
135+
if hasattr(gui, "update_command_preview"):
136+
gui.update_command_preview()
137+
except Exception:
138+
pass
139+
140+
141+
def save_engine_config_for_gui(gui, engine_id: str) -> bool:
142+
try:
143+
workspace_dir = getattr(gui, "workspace_dir", None)
144+
if not workspace_dir or not engine_id:
145+
return False
146+
import EngineLoader as engines_loader
147+
148+
engine = engines_loader.registry.get_instance(engine_id)
149+
if not engine:
150+
return False
151+
options = {}
152+
try:
153+
fn = getattr(engine, "get_config", None)
154+
if callable(fn):
155+
options = fn(gui) or {}
156+
except Exception:
157+
options = {}
158+
return save_engine_config(
159+
workspace_dir,
160+
engine_id,
161+
options,
162+
getattr(engine, "version", None),
163+
)
164+
except Exception:
165+
return False

Core/WorkSpaceManager/SetupWorkspace.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,14 @@ def apply_workspace_selection(
220220
f"⚠️ Error during workspace setup: {e}",
221221
)
222222

223+
# Appliquer la configuration des engines depuis .ark/<engine_id>/config.json
224+
try:
225+
from Core.EngineConfigManager import apply_engine_configs_for_workspace
226+
227+
apply_engine_configs_for_workspace(gui_instance, folder)
228+
except Exception:
229+
pass
230+
223231
# Fermer le dialog de chargement
224232
try:
225233
if loading_dialog:

ENGINES/cx_freeze/__init__.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,51 @@ def create_tab(self, gui):
238238
pass
239239
return None
240240

241+
def get_config(self, gui) -> dict:
242+
"""Return a JSON-serializable snapshot of current CX_Freeze UI options."""
243+
try:
244+
cfg = {}
245+
if hasattr(self, "_cx_onefile") and self._cx_onefile is not None:
246+
cfg["onefile"] = bool(self._cx_onefile.isChecked())
247+
if hasattr(self, "_cx_windowed") and self._cx_windowed is not None:
248+
cfg["windowed"] = bool(self._cx_windowed.isChecked())
249+
if hasattr(self, "_cx_output_dir") and self._cx_output_dir is not None:
250+
cfg["output_dir"] = self._cx_output_dir.text().strip()
251+
if hasattr(self, "_selected_icon") and self._selected_icon:
252+
cfg["selected_icon"] = self._selected_icon
253+
return cfg
254+
except Exception:
255+
return {}
256+
257+
def set_config(self, gui, cfg: dict) -> None:
258+
"""Apply a config dict to CX_Freeze UI widgets."""
259+
if not isinstance(cfg, dict):
260+
return
261+
try:
262+
if (
263+
hasattr(self, "_cx_onefile")
264+
and self._cx_onefile is not None
265+
and "onefile" in cfg
266+
):
267+
self._cx_onefile.setChecked(bool(cfg.get("onefile")))
268+
if (
269+
hasattr(self, "_cx_windowed")
270+
and self._cx_windowed is not None
271+
and "windowed" in cfg
272+
):
273+
self._cx_windowed.setChecked(bool(cfg.get("windowed")))
274+
if (
275+
hasattr(self, "_cx_output_dir")
276+
and self._cx_output_dir is not None
277+
and "output_dir" in cfg
278+
):
279+
val = cfg.get("output_dir") or ""
280+
self._cx_output_dir.setText(str(val))
281+
if "selected_icon" in cfg:
282+
self._selected_icon = cfg.get("selected_icon") or None
283+
except Exception:
284+
pass
285+
241286
def _get_opt(self, name: str):
242287
"""Get option widget from engine instance or GUI."""
243288
# Try engine instance first (dynamic tabs)

0 commit comments

Comments
 (0)