Skip to content

Commit 145c35d

Browse files
author
PyCompiler ARK++
committed
i18n cleanup for BCASL and Cleaner; add simple engines UI-state persistence:\n- Core: default ARK config includes engines (pyinstaller, nuitka, cx_freeze) UI widgets\n- Registry: apply saved widgets on tab creation; expose save_engine_ui\n- SDK: provide save_engine_ui() helper for engines\n- Engines: PyInstaller, Nuitka, cx_Freeze auto-save widget state (checked/text/index)\n- Remove obsolete ACASL hooks and BCASL i18n propagation
1 parent f72744b commit 145c35d

6 files changed

Lines changed: 309 additions & 1 deletion

File tree

Core/ark_config_loader.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,20 @@
115115
"bcasl_enabled": True,
116116
"plugin_timeout": 0.0,
117117
},
118+
119+
# Engines UI state (persisted widget states per engine)
120+
"engines": {
121+
"pyinstaller": {
122+
"ui": {
123+
"widgets": {}
124+
}
125+
},
126+
"nuitka": {
127+
"ui": {
128+
"widgets": {}
129+
}
130+
}
131+
},
118132
}
119133

120134

@@ -335,6 +349,15 @@ def create_default_ark_config(workspace_dir: str) -> bool:
335349
- "pip"
336350
auto_detect: true
337351
fallback_to_pip: true
352+
353+
# ENGINES (UI state persistence)
354+
engines:
355+
pyinstaller:
356+
ui:
357+
widgets: {}
358+
nuitka:
359+
ui:
360+
widgets: {}
338361
"""
339362

340363
with open(config_file, 'w', encoding='utf-8') as f:
@@ -345,3 +368,92 @@ def create_default_ark_config(workspace_dir: str) -> bool:
345368
except Exception as e:
346369
print(f"Warning: Failed to create ARK_Main_Config.yml: {e}")
347370
return False
371+
372+
373+
# --- Simple Engine UI state helpers (for non-dev friendly persistence) ---
374+
375+
def _read_yaml(path: Path) -> dict:
376+
try:
377+
if path.exists():
378+
with open(path, 'r', encoding='utf-8') as f:
379+
data = yaml.safe_load(f) or {}
380+
return data if isinstance(data, dict) else {}
381+
except Exception:
382+
pass
383+
return {}
384+
385+
386+
def _write_yaml_atomic(path: Path, data: dict) -> bool:
387+
try:
388+
path.parent.mkdir(parents=True, exist_ok=True)
389+
tmp = path.with_suffix(path.suffix + ".tmp")
390+
with open(tmp, 'w', encoding='utf-8') as f:
391+
yaml.safe_dump(data, f, allow_unicode=True, sort_keys=False)
392+
os.replace(tmp, path)
393+
return True
394+
except Exception:
395+
return False
396+
397+
398+
essential_props = ("checked", "text", "enabled", "visible", "currentIndex")
399+
400+
401+
def load_engine_ui_state(workspace_dir: str, engine_id: str) -> dict:
402+
"""Load saved UI state for an engine from ARK_Main_Config.yml.
403+
Returns a mapping {widgetName: {prop: value}} or {}.
404+
"""
405+
try:
406+
cfg = _read_yaml(Path(workspace_dir) / "ARK_Main_Config.yml") if workspace_dir else {}
407+
engines = cfg.get("engines", {}) if isinstance(cfg, dict) else {}
408+
e = engines.get(str(engine_id), {}) if isinstance(engines, dict) else {}
409+
ui = e.get("ui", {}) if isinstance(e, dict) else {}
410+
widgets = ui.get("widgets", {}) if isinstance(ui, dict) else {}
411+
return widgets if isinstance(widgets, dict) else {}
412+
except Exception:
413+
return {}
414+
415+
416+
def save_engine_ui_state(workspace_dir: str, engine_id: str, updates: dict) -> bool:
417+
"""Save UI state for an engine into ARK_Main_Config.yml.
418+
- workspace_dir: project workspace path
419+
- engine_id: id string (e.g. 'pyinstaller')
420+
- updates: {widgetName: {prop: value}}
421+
Returns True on success, False otherwise.
422+
"""
423+
if not workspace_dir:
424+
return False
425+
try:
426+
cfg_path = Path(workspace_dir) / "ARK_Main_Config.yml"
427+
cfg = _read_yaml(cfg_path)
428+
if not isinstance(cfg, dict):
429+
cfg = {}
430+
engines = cfg.get("engines")
431+
if not isinstance(engines, dict):
432+
engines = {}
433+
cfg["engines"] = engines
434+
e = engines.get(str(engine_id))
435+
if not isinstance(e, dict):
436+
e = {}
437+
engines[str(engine_id)] = e
438+
ui = e.get("ui")
439+
if not isinstance(ui, dict):
440+
ui = {}
441+
e["ui"] = ui
442+
widgets = ui.get("widgets")
443+
if not isinstance(widgets, dict):
444+
widgets = {}
445+
ui["widgets"] = widgets
446+
# Merge per widget
447+
for wname, props in (updates or {}).items():
448+
if not isinstance(wname, str) or not isinstance(props, dict):
449+
continue
450+
cur = widgets.get(wname)
451+
if not isinstance(cur, dict):
452+
cur = {}
453+
widgets[wname] = cur
454+
for k, v in props.items():
455+
if k in essential_props:
456+
cur[k] = v
457+
return _write_yaml_atomic(cfg_path, cfg)
458+
except Exception:
459+
return False

Core/engines_loader/registry.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515

1616
from __future__ import annotations
1717

18-
from typing import Optional
18+
from typing import Optional, Any
1919

2020
from .base import CompilerEngine
21+
from Core.ark_config_loader import load_engine_ui_state, save_engine_ui_state
2122

2223
_REGISTRY: dict[str, type[CompilerEngine]] = {}
2324
_ORDER: list[str] = []
@@ -63,6 +64,49 @@ def register(engine_cls: type[CompilerEngine]):
6364
return engine_cls
6465

6566

67+
def _apply_widgets_state(container, widgets_state: dict) -> None:
68+
"""Apply basic properties to child widgets given a simple state mapping.
69+
Supported props: checked, text, enabled, visible, currentIndex.
70+
"""
71+
try:
72+
for wname, props in widgets_state.items():
73+
try:
74+
w = container.findChild(object, wname)
75+
if w is None:
76+
continue
77+
for k, v in (props or {}).items():
78+
try:
79+
if k == "checked" and hasattr(w, "setChecked"):
80+
w.setChecked(bool(v))
81+
elif k == "text" and hasattr(w, "setText"):
82+
w.setText(str(v))
83+
elif k == "enabled" and hasattr(w, "setEnabled"):
84+
w.setEnabled(bool(v))
85+
elif k == "visible" and hasattr(w, "setVisible"):
86+
w.setVisible(bool(v))
87+
elif k == "currentIndex" and hasattr(w, "setCurrentIndex"):
88+
w.setCurrentIndex(int(v))
89+
except Exception:
90+
continue
91+
except Exception:
92+
continue
93+
except Exception:
94+
pass
95+
96+
97+
def save_engine_ui(gui, engine_id: str, updates: dict[str, dict[str, Any]]) -> bool:
98+
"""Public helper for engines to persist UI state into ARK_Main_Config.yml.
99+
Engines pass a mapping {widgetName: {prop: value}}. Returns True on success.
100+
"""
101+
try:
102+
ws = getattr(gui, "workspace_dir", None)
103+
if not ws:
104+
return False
105+
return save_engine_ui_state(ws, engine_id, updates)
106+
except Exception:
107+
return False
108+
109+
66110
def get_engine(eid: str) -> Optional[type[CompilerEngine]]:
67111
try:
68112
return _REGISTRY.get(eid)
@@ -97,6 +141,15 @@ def bind_tabs(gui) -> None:
97141
if not pair:
98142
continue
99143
widget, label = pair
144+
# Apply saved UI state for this engine (simple mapping by objectName)
145+
try:
146+
ws = getattr(gui, "workspace_dir", None)
147+
if ws:
148+
widgets_state = load_engine_ui_state(ws, eid)
149+
if isinstance(widgets_state, dict) and widgets_state:
150+
_apply_widgets_state(widget, widgets_state)
151+
except Exception:
152+
pass
100153
try:
101154
existing = tabs.indexOf(widget)
102155
except Exception:

ENGINES/cx_freeze/engine.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,14 @@ def create_tab(self, gui):
797797
row = QHBoxLayout()
798798
out_edit = QLineEdit()
799799
out_edit.setObjectName("cx_output_dir")
800+
# Persist changes to ARK config
801+
try:
802+
from engine_sdk import save_engine_ui as _save
803+
out_edit.textChanged.connect(
804+
lambda s: _save(gui, "cx_freeze", {"cx_output_dir": {"text": str(s)}})
805+
)
806+
except Exception:
807+
pass
800808
try:
801809
out_edit.setPlaceholderText(
802810
gui.tr(
@@ -859,6 +867,14 @@ def _browse():
859867
self._cx_tn_label = None
860868
tn_edit = QLineEdit()
861869
tn_edit.setObjectName("cx_target_name")
870+
# Persist target name
871+
try:
872+
from engine_sdk import save_engine_ui as _save
873+
tn_edit.textChanged.connect(
874+
lambda s: _save(gui, "cx_freeze", {"cx_target_name": {"text": str(s)}})
875+
)
876+
except Exception:
877+
pass
862878
try:
863879
tn_edit.setPlaceholderText(
864880
gui.tr(
@@ -875,6 +891,15 @@ def _browse():
875891
row2 = QHBoxLayout()
876892
base_label = QLabel(gui.tr("Base", "Base"))
877893
base_combo = QComboBox()
894+
base_combo.setObjectName("cx_base_combo")
895+
# Persist base combo index
896+
try:
897+
from engine_sdk import save_engine_ui as _save
898+
base_combo.currentIndexChanged.connect(
899+
lambda idx: _save(gui, "cx_freeze", {"cx_base_combo": {"currentIndex": int(idx)}})
900+
)
901+
except Exception:
902+
pass
878903
try:
879904
if platform.system() == "Windows":
880905
base_combo.addItems(["Console", "Win32GUI"])
@@ -883,11 +908,27 @@ def _browse():
883908
except Exception:
884909
pass
885910
cb_deps = QCheckBox(gui.tr("Inclure dépendances", "Include dependencies"))
911+
# Persist deps toggle
912+
try:
913+
from engine_sdk import save_engine_ui as _save
914+
cb_deps.toggled.connect(
915+
lambda v: _save(gui, "cx_freeze", {"cx_include_deps": {"checked": bool(v)}})
916+
)
917+
except Exception:
918+
pass
886919
try:
887920
cb_deps.setChecked(True)
888921
except Exception:
889922
pass
890923
cb_enc = QCheckBox(gui.tr("Inclure encodings", "Include encodings"))
924+
# Persist encodings toggle
925+
try:
926+
from engine_sdk import save_engine_ui as _save
927+
cb_enc.toggled.connect(
928+
lambda v: _save(gui, "cx_freeze", {"cx_include_encodings": {"checked": bool(v)}})
929+
)
930+
except Exception:
931+
pass
891932
row2.addWidget(base_label)
892933
row2.addWidget(base_combo)
893934
row2.addStretch(1)
@@ -908,6 +949,15 @@ def _browse():
908949
except Exception:
909950
self._cx_icon_title = None
910951
icon_edit = QLineEdit()
952+
icon_edit.setObjectName("cx_icon_path")
953+
# Persist icon path
954+
try:
955+
from engine_sdk import save_engine_ui as _save
956+
icon_edit.textChanged.connect(
957+
lambda s: _save(gui, "cx_freeze", {"cx_icon_path": {"text": str(s)}})
958+
)
959+
except Exception:
960+
pass
911961
icon_btn = QPushButton(gui.tr("Parcourir…", "Browse…"))
912962
self._cx_icon_browse_btn = icon_btn
913963

@@ -945,6 +995,15 @@ def _browse_icon():
945995
except Exception:
946996
self._cx_optimize_title = None
947997
opt_combo = QComboBox()
998+
opt_combo.setObjectName("cx_optimize_level")
999+
# Persist optimization level
1000+
try:
1001+
from engine_sdk import save_engine_ui as _save
1002+
opt_combo.currentIndexChanged.connect(
1003+
lambda idx: _save(gui, "cx_freeze", {"cx_optimize_level": {"currentIndex": int(idx)}})
1004+
)
1005+
except Exception:
1006+
pass
9481007
opt_combo.addItems(["0", "1", "2"]) # python -O / -OO equivalent
9491008
opt_row.addWidget(opt_combo)
9501009
opt_row.addStretch(1)

ENGINES/nuitka/engine.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,39 @@ def create_tab(self, gui):
424424

425425
tab = getattr(gui, "tab_nuitka", None)
426426
if tab and isinstance(tab, QWidget):
427+
# Save UI state automatically when user toggles/edits widgets
428+
try:
429+
from engine_sdk import save_engine_ui as _save
430+
# Checkboxes for nuitka
431+
for _name in (
432+
"nuitka_onefile",
433+
"nuitka_standalone",
434+
"nuitka_disable_console",
435+
"nuitka_show_progress",
436+
):
437+
try:
438+
_w = getattr(gui, _name, None)
439+
if _w is not None and hasattr(_w, "toggled"):
440+
_w.toggled.connect(
441+
lambda v, n=_name: _save(
442+
gui, "nuitka", {n: {"checked": bool(v)}}
443+
)
444+
)
445+
except Exception:
446+
pass
447+
# Output dir for nuitka
448+
try:
449+
_w = getattr(gui, "nuitka_output_dir", None)
450+
if _w is not None and hasattr(_w, "textChanged"):
451+
_w.textChanged.connect(
452+
lambda s: _save(
453+
gui, "nuitka", {"nuitka_output_dir": {"text": str(s)}}
454+
)
455+
)
456+
except Exception:
457+
pass
458+
except Exception:
459+
pass
427460
return tab, _tr("Nuitka", "Nuitka")
428461
except Exception:
429462
pass

ENGINES/pyinstaller/engine.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,43 @@ def create_tab(self, gui):
303303

304304
tab = getattr(gui, "tab_pyinstaller", None)
305305
if tab and isinstance(tab, QWidget):
306+
# Save UI state automatically when user toggles/edits widgets
307+
try:
308+
from engine_sdk import save_engine_ui as _save
309+
# Checkboxes to persist
310+
for _name in (
311+
"opt_onefile",
312+
"opt_windowed",
313+
"opt_noconfirm",
314+
"opt_clean",
315+
"opt_noupx",
316+
"opt_debug",
317+
):
318+
try:
319+
_w = getattr(gui, _name, None)
320+
if _w is not None and hasattr(_w, "toggled"):
321+
_w.toggled.connect(
322+
lambda v, n=_name: _save(
323+
gui, "pyinstaller", {n: {"checked": bool(v)}}
324+
)
325+
)
326+
except Exception:
327+
pass
328+
# Text fields to persist
329+
try:
330+
_w = getattr(gui, "output_dir_input", None)
331+
if _w is not None and hasattr(_w, "textChanged"):
332+
_w.textChanged.connect(
333+
lambda s: _save(
334+
gui,
335+
"pyinstaller",
336+
{"output_dir_input": {"text": str(s)}},
337+
)
338+
)
339+
except Exception:
340+
pass
341+
except Exception:
342+
pass
306343
return tab, gui.tr("PyInstaller", "PyInstaller")
307344
except Exception:
308345
pass

0 commit comments

Comments
 (0)