"""Inspektera Windows-fönster för att hitta rätt selectors till desktop_*-stegen
(motsvarigheten till rpa/discover.py fast för skrivbordsappar).
python -m rpa.desktop_inspect # listar synliga toppfönster
python -m rpa.desktop_inspect "Anteckningar" # dumpar kontroll-identifierare
Kör på Windows-maskinen med målappen igång. Använd 'name'/'auto_id'/
'control_type' från utskriften i webui:ts desktop-steg (webui:t har samma
funktion under ⚙ → Inspektera fönster).
"""
from __future__ import annotations
import sys
class InspectUnavailable(RuntimeError):
"""pywinauto kunde inte laddas (icke-Windows?)."""
def _desktop():
try:
from pywinauto import Desktop
except Exception as exc: # noqa: BLE001
raise InspectUnavailable(
f"pywinauto kunde inte laddas (Windows-only): {exc}"
) from exc
return Desktop(backend="uia")
def list_windows() -> list[str]:
"""Titlarna på alla synliga toppfönster."""
out: list[str] = []
for w in _desktop().windows():
try:
if w.is_visible() and w.window_text():
out.append(w.window_text())
except Exception: # noqa: BLE001
continue
# unika, i ordning
seen: set[str] = set()
return [t for t in out if not (t in seen or seen.add(t))]
def inspect_window(title: str, max_depth: int = 6, limit: int = 800) -> list[dict]:
"""Plattad lista över kontrollerna i första fönstret vars titel matchar
`title` (regex). Varje rad: {depth, control_type, name, auto_id, class_name}."""
desktop = _desktop()
matches = desktop.windows(title_re=title, visible_only=True)
if not matches:
raise ValueError(f"Hittade inget synligt fönster med titel som matchar {title!r}")
win = desktop.window(handle=matches[0].handle)
win.wait("exists", timeout=10)
rows: list[dict] = []
def walk(ctrl, depth: int) -> None:
if len(rows) >= limit or depth > max_depth:
return
try:
info = ctrl.element_info
rows.append({
"depth": depth,
"control_type": getattr(info, "control_type", "") or "",
"name": (ctrl.window_text() or "").strip(),
"auto_id": getattr(info, "automation_id", "") or "",
"class_name": getattr(info, "class_name", "") or "",
})
except Exception: # noqa: BLE001
return
try:
for child in ctrl.children():
walk(child, depth + 1)
except Exception: # noqa: BLE001
pass
walk(win, 0)
return rows
def main() -> None:
try:
if len(sys.argv) < 2:
wins = list_windows()
print("Synliga toppfönster:\n")
for t in wins:
print(f" {t!r}")
print('\nKör igen med en titel (regex), t.ex.:\n python -m rpa.desktop_inspect "Anteckningar"')
return
for row in inspect_window(sys.argv[1]):
indent = " " * row["depth"]
bits = [f'{indent}{row["control_type"] or "?"}']
if row["name"]:
bits.append(f'name={row["name"]!r}')
if row["auto_id"]:
bits.append(f'auto_id={row["auto_id"]!r}')
if row["class_name"]:
bits.append(f'class={row["class_name"]!r}')
print(" ".join(bits))
except (InspectUnavailable, ValueError) as exc:
sys.exit(str(exc))
if __name__ == "__main__":
main()