feat(simulation): stabilize IPC layer and verify full workflow

This commit is contained in:
2026-02-23 19:53:32 -05:00
parent ba97ccda3c
commit 8bd280efc1
5 changed files with 205 additions and 32 deletions

54
gui.py
View File

@@ -129,7 +129,7 @@ def _add_text_field(parent: str, label: str, value: str):
if wrap:
with dpg.child_window(height=80, border=True):
# add_input_text for selection
dpg.add_input_text(default_value=value, multiline=True, readonly=True, width=-1, height=-1, border=False)
dpg.add_input_text(default_value=value, multiline=True, readonly=True, width=-1, height=-1)
else:
dpg.add_input_text(
default_value=value,
@@ -140,14 +140,14 @@ def _add_text_field(parent: str, label: str, value: str):
)
else:
# Short selectable text
dpg.add_input_text(default_value=value if value else "(empty)", readonly=True, width=-1, border=False)
dpg.add_input_text(default_value=value if value else "(empty)", readonly=True, width=-1)
def _add_kv_row(parent: str, key: str, val, val_color=None):
"""Single key: value row, horizontally laid out."""
with dpg.group(horizontal=True, parent=parent):
dpg.add_text(f"{key}:", color=_LABEL_COLOR)
dpg.add_input_text(default_value=str(val), readonly=True, width=-1, border=False)
dpg.add_input_text(default_value=str(val), readonly=True, width=-1)
def _render_usage(parent: str, usage: dict):
@@ -1506,6 +1506,28 @@ class App:
self._rebuild_projects_list()
self._update_status(f"created project: {name}")
def _cb_new_project_automated(self, path):
"""Automated version of cb_new_project that doesn't show a dialog."""
if not path:
return
name = Path(path).stem
proj = project_manager.default_project(name)
project_manager.save_project(proj, path)
if path not in self.project_paths:
self.project_paths.append(path)
# Safely queue project switch and list rebuild for the main thread
def main_thread_work():
self._switch_project(path)
self._rebuild_projects_list()
self._update_status(f"created project: {name}")
with self._pending_gui_tasks_lock:
self._pending_gui_tasks.append({
"action": "custom_callback",
"callback": main_thread_work
})
def cb_browse_git_dir(self):
root = hide_tk_root()
d = filedialog.askdirectory(title="Select Git Directory")
@@ -1882,6 +1904,9 @@ class App:
no_close=False,
no_collapse=True,
):
with dpg.group(tag="automated_actions_group", show=False):
dpg.add_button(tag="btn_project_new_automated", callback=lambda s, a, u: self._cb_new_project_automated(u))
with dpg.tab_bar():
with dpg.tab(label="Projects"):
proj_meta = self.project.get("project", {})
@@ -2301,10 +2326,24 @@ class App:
dpg.set_value(item, val)
elif action == "click":
item = task.get("item")
args = task.get("args", [])
kwargs = task.get("kwargs", {})
user_data = task.get("user_data")
if item and dpg.does_item_exist(item):
cb = dpg.get_item_callback(item)
if cb:
cb()
try:
# DPG callbacks can have (sender, app_data, user_data)
# If we have specific args/kwargs we use them,
# otherwise we try to follow the DPG pattern.
if args or kwargs:
cb(*args, **kwargs)
elif user_data is not None:
cb(item, None, user_data)
else:
cb()
except Exception as e:
print(f"Error in GUI hook callback for {item}: {e}")
elif action == "select_tab":
tab_bar = task.get("tab_bar")
tab = task.get("tab")
@@ -2320,6 +2359,13 @@ class App:
# Dear PyGui callbacks for listbox usually receive (sender, app_data, user_data)
# app_data is the selected value.
cb(listbox, val)
elif action == "custom_callback":
cb = task.get("callback")
if cb:
try:
cb()
except Exception as e:
print(f"Error in custom GUI hook callback: {e}")
elif action == "refresh_api_metrics":
self._refresh_api_metrics(task.get("payload", {}))
except Exception as e: