app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge: multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
← Commit history
0.9.340: land the shipped runtime source for handlers/open_footprint_editor.py (tour fixes 0.9.318-0.9.339 never reached master, issue #79)
1 file changed
+64−2
handlers/open_footprint_editor.py+64−2@@ -524,7 +524,7 @@ def _background_navigate_win32_fp(fp_hwnd: int, filter_text: str, verify: str, fixed the Symbol Editor (0.9.86). WM_SETTEXT into the LIB_TREE filter, Down through the filter box to select the match, Enter posted to the tree candidates + PostMessage dblclick fallback. Zero focus, no plugin needed."""- edit = _find_child_by_class_and_parent(fp_hwnd, "Edit", "searchCtrl")+ edit = _ensure_tree_search(fp_hwnd) if not edit: return False WM_SETTEXT = 0x000C@@ -583,6 +583,61 @@ def _background_navigate_win32_fp(fp_hwnd: int, filter_text: str, verify: str, return False ++_TREE_TOGGLED: dict = {} # fp editor hwnd -> monotonic time we toggled its Library Tree pane on+++def _ensure_tree_search(fp_hwnd: int):+ """The Library Tree's search control, showing the pane through the frame's+ own View menu when the user has it hidden (arav-rog 2026-09-05: every+ background load typed into a control that did not exist). Toggle at most+ once per editor per minute, then WAIT for the control instead of toggling+ again (a second toggle hid it right back)."""+ def _visible(h) -> bool:+ # wx HIDES a closed pane: the control still exists, so "found" is not+ # enough (arav-rog 2026-09-05: the loader typed into a hidden filter box+ # for three runs). Visible means the control and its ancestors are shown.+ try:+ return bool(h) and bool(user32.IsWindowVisible(h))+ except Exception: # pylint: disable=broad-except+ return bool(h)+ edit = _find_child_by_class_and_parent(fp_hwnd, "Edit", "searchCtrl")+ if _visible(edit):+ return edit+ import time as _t+ now = _t.monotonic()+ if now - _TREE_TOGGLED.get(int(fp_hwnd), 0) > 60 and _show_library_tree_pane():+ _TREE_TOGGLED[int(fp_hwnd)] = now+ for _ in range(16):+ edit = _find_child_by_class_and_parent(fp_hwnd, "Edit", "searchCtrl")+ if _visible(edit):+ return edit+ _t.sleep(0.25)+ return edit if _visible(edit) else None+++def _show_library_tree_pane() -> bool:+ """Tick View > Panels > Library Tree on the Footprint Editor frame via the+ pcbnew plugin (menu id resolved live; the FP editor is not frame 0)."""+ try:+ from handlers.bridge_client import _resolve_plugin, call as _rpc+ meta, err = _resolve_plugin("pcbnew")+ if err or not meta:+ return False+ rpc = _rpc(meta["port"], "get_menu_ids", {}, timeout=8.0)+ frames = ((rpc.get("result") or {}).get("frames")) or []+ for idx, fr in enumerate(frames):+ if "footprint editor" not in str(fr.get("frameTitle") or "").lower():+ continue+ for m in fr.get("menuItems") or []:+ if str(m.get("label") or "").strip().lower() == "library tree":+ r = _rpc(meta["port"], "wm_command", {"frame_index": idx, "menu_id": int(m["id"])}, timeout=8.0)+ return bool((r.get("result") or {}).get("posted"))+ return False+ except Exception: # pylint: disable=broad-except+ return False++ def _navigate_to_footprint(fp_hwnd: int, footprint_name: str, load_after: bool = True) -> dict: """Call the PowerShell script to navigate to a specific footprint. @@ -593,7 +648,7 @@ def _navigate_to_footprint(fp_hwnd: int, footprint_name: str, load_after: bool = if not _NAVIGATE_SCRIPT.exists(): return {"status": "error", "error": f"Script not found: {_NAVIGATE_SCRIPT}"} - search_edit = _find_child_by_class_and_parent(fp_hwnd, "Edit", "searchCtrl")+ search_edit = _ensure_tree_search(fp_hwnd) if not search_edit: return {"status": "error", "error": "Search edit control not found in Footprint Editor"} @@ -1035,6 +1090,13 @@ def handle_open_footprint_editor(kicad_info: dict, args: dict) -> dict: _on_disk = bool(_ls.get("existsInLibrary")) except Exception: _ls = {}+ # arav-rog 2026-09-05: with no pcbnew running (plugin_not_running on every+ # tier) the editor never OPENED, yet this block read "on disk but not+ # loaded" as a stale cache and restarted KiCad, twice, 128 s each. A cache+ # verdict needs an open editor that failed to load the part; without one+ # there is no evidence, so fall through to the plain failure and its hint.+ if not fp_hwnd:+ _on_disk = False if _on_disk and not args.get("_autoRestarted") and not args.get("_treeRefreshed"): # First resort: reload the tree in place (#51). Cheap, no restart, # and it keeps every window and the user's other work untouched.