Skip to content

Commit e6c16c6

Browse files
Edited the inspector_ui to serve the elemenet data locally and added a no ui flag for onboarding (#683)
1 parent f1952dd commit e6c16c6

1 file changed

Lines changed: 99 additions & 3 deletions

File tree

Apps/Windows/inspector_ui.py

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,97 @@
1+
import argparse
2+
import json
13
import threading
4+
import time
25
import xml.etree.ElementTree as ET
6+
from http.server import HTTPServer, BaseHTTPRequestHandler
7+
38
import tkinter as tk
49
from tkinter import ttk, scrolledtext
510
import os
611
import pyperclip # Add this import for clipboard functionality
712

813
from inspector import WindowsInspector, Authenticate, inspect
914

15+
# Server-only mode: in-memory latest inspection for GET /latest
16+
LATEST_RESULT = {"xml": "", "paths": [], "window_name": "", "log": [], "captured_at": 0}
17+
LATEST_LOCK = threading.Lock()
18+
HTTP_PORT = 8765
19+
20+
21+
def _update_latest(xml_str: str, paths: list, window_name: str, log: list):
22+
with LATEST_LOCK:
23+
LATEST_RESULT["xml"] = xml_str or ""
24+
LATEST_RESULT["paths"] = list(paths) if paths else []
25+
LATEST_RESULT["window_name"] = window_name or ""
26+
LATEST_RESULT["log"] = list(log) if log else []
27+
LATEST_RESULT["captured_at"] = time.time()
28+
29+
30+
def _get_latest():
31+
with LATEST_LOCK:
32+
return {
33+
"xml": LATEST_RESULT["xml"],
34+
"paths": LATEST_RESULT["paths"],
35+
"window_name": LATEST_RESULT["window_name"],
36+
"log": LATEST_RESULT["log"],
37+
"captured_at": LATEST_RESULT["captured_at"],
38+
}
39+
40+
41+
class InspectorHTTPHandler(BaseHTTPRequestHandler):
42+
"""Serves GET /latest with CORS for the web Inspector panel."""
43+
44+
def do_GET(self):
45+
if self.path == "/latest" or self.path == "/latest/":
46+
body = json.dumps(_get_latest()).encode("utf-8")
47+
self.send_response(200)
48+
self.send_header("Content-Type", "application/json; charset=utf-8")
49+
self.send_header("Content-Length", str(len(body)))
50+
self.send_header("Access-Control-Allow-Origin", "*")
51+
self.end_headers()
52+
self.wfile.write(body)
53+
else:
54+
self.send_response(404)
55+
self.end_headers()
56+
57+
def log_message(self, format, *args):
58+
pass # quiet by default in server mode
59+
60+
61+
def run_server_only():
62+
"""Run without Tkinter: HTTP server on port 8765 + hover+Ctrl inspection loop."""
63+
inspector = WindowsInspector()
64+
log_lines = []
65+
66+
def log(msg: str):
67+
log_lines.append(msg)
68+
print(msg)
69+
70+
def inspection_loop():
71+
while True:
72+
log("Hover over the element you want to inspect and press Ctrl...")
73+
try:
74+
x, y = inspect()
75+
log(f"Captured at x={x}, y={y}")
76+
cleanup = inspector.inspect_element(x, y)
77+
try:
78+
root = ET.fromstring(inspector.xml_str)
79+
path_list = [{"path": p["path"], "area": p.get("area", "")} for p in inspector.paths]
80+
_update_latest(inspector.xml_str, path_list, inspector.window_name, list(log_lines))
81+
except ET.ParseError:
82+
log("Failed to parse XML from inspector.")
83+
finally:
84+
cleanup()
85+
except Exception as e:
86+
log(f"Inspection error: {e}")
87+
88+
server = HTTPServer(("", HTTP_PORT), InspectorHTTPHandler)
89+
server_thread = threading.Thread(target=server.serve_forever, daemon=True)
90+
server_thread.start()
91+
log(f"Inspector server listening on http://localhost:{HTTP_PORT}/latest (no UI mode)")
92+
93+
inspection_loop()
94+
1095

1196
class InspectorGUI(tk.Tk):
1297
def __init__(self):
@@ -204,6 +289,17 @@ def _inspect_element(self):
204289
self.create_path_section(text, path["path"])
205290

206291

207-
if __name__ == '__main__':
208-
app = InspectorGUI()
209-
app.mainloop()
292+
if __name__ == "__main__":
293+
parser = argparse.ArgumentParser(description="ZeuZ Windows Inspector")
294+
parser.add_argument(
295+
"--no-ui",
296+
action="store_true",
297+
help="Run without GUI: start HTTP server on port 8765 and serve GET /latest for the web Inspector panel. Use hover+Ctrl in the terminal to capture.",
298+
)
299+
args = parser.parse_args()
300+
301+
if args.no_ui:
302+
run_server_only()
303+
else:
304+
app = InspectorGUI()
305+
app.mainloop()

0 commit comments

Comments
 (0)