Files
cad-agent/verify_drawing_area.py
T

140 lines
4.8 KiB
Python
Raw Normal View History

"""绘图区最大化验证:`set_drawing_area(max)` 到底把绘图区放大了多少,`restore` 能否保真还原。
不消耗 VLM token(只调截图工具 + 解码 PNG 量尺寸)。
用法:
python verify_drawing_area.py [--wait 3.0] [--restore-wait 1.5]
前置:CAD 在线(127.0.0.1:8080 监听)且已加载最新插件。不需要 DEEPSEEK_API_KEY。
判定:
1. max 后绘图区面积应显著大于基线(实测约 3.7x);
2. restore 后窗口矩形 / 最大化状态 / 绘图区尺寸应回到基线(容差 ±2px)。
注意:脚本会改动 CAD 窗口位置与干净屏幕状态,结束时一定还原(finally)。
"""
import argparse
import ctypes
import sys
import time
from ctypes import wintypes
from dwg_review.llm_client import image_size_b64
from dwg_review.mcp_client import McpClient, result_images, result_json
_TOL = 2 # 还原容差(像素)
# --------------------------------------------------------------------------
# Win32:定位 CAD 主窗口并读取其状态
# --------------------------------------------------------------------------
def _user32():
u = ctypes.windll.user32
u.GetWindowTextW.argtypes = [wintypes.HWND, wintypes.LPWSTR, ctypes.c_int]
u.IsWindowVisible.argtypes = [wintypes.HWND]
u.GetWindowRect.argtypes = [wintypes.HWND, ctypes.POINTER(wintypes.RECT)]
u.IsZoomed.argtypes = [wintypes.HWND]
return u
def find_main_hwnd(u):
"""枚举可见顶层窗口,取标题含 AutoCAD 且带 [图纸名] 的那个。"""
found = []
proc_type = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
def cb(hwnd, _):
if not u.IsWindowVisible(hwnd):
return True
n = u.GetWindowTextLengthW(hwnd)
if n == 0:
return True
buf = ctypes.create_unicode_buffer(n + 1)
u.GetWindowTextW(hwnd, buf, n + 1)
if "AutoCAD" in buf.value and "[" in buf.value:
found.append(hwnd)
return False
return True
u.EnumWindows(proc_type(cb), 0)
return found[0] if found else None
def win_state(u, hwnd):
rc = wintypes.RECT()
u.GetWindowRect(hwnd, ctypes.byref(rc))
return (rc.left, rc.top, rc.right, rc.bottom), bool(u.IsZoomed(hwnd))
# --------------------------------------------------------------------------
# 测量
# --------------------------------------------------------------------------
def shot_size(client):
"""截一张整图,返回 (宽, 高)。"""
resp = client.call_tool("get_viewport_screenshot", {})
imgs = result_images(resp)
if not imgs:
return None
return image_size_b64(imgs[0][0])
def _area(sz):
return (sz[0] * sz[1]) if sz else 0
def main(argv=None):
ap = argparse.ArgumentParser()
ap.add_argument("--wait", type=float, default=3.0, help="max 后等待渲染秒数")
ap.add_argument("--restore-wait", type=float, default=1.5,
help="restore 后等待秒数(须 > 插件内 700ms 延迟还原)")
args = ap.parse_args(argv)
u = _user32()
hwnd = find_main_hwnd(u)
if not hwnd:
print("找不到 AutoCAD 主窗口,请先启动 CAD", file=sys.stderr)
return 1
print(f"CAD 主窗口: {hwnd:#x}")
client = McpClient()
rc0, zoom0 = win_state(u, hwnd)
base = shot_size(client)
print(f"[基线] 窗口矩形={list(rc0)} 最大化={zoom0} 绘图区={base}")
ok = True
try:
info = result_json(client.call_tool("set_drawing_area", {"mode": "max"}), {})
time.sleep(args.wait)
big = shot_size(client)
rc1, zoom1 = win_state(u, hwnd)
ratio = _area(big) / _area(base) if _area(base) else 0.0
print(f"[max] 窗口矩形={list(rc1)} 最大化={zoom1} 绘图区={big} "
f"面积={ratio:.2f}x 工具返回={info.get('main_rect')}")
if ratio < 1.5:
print(" !! 绘图区放大不足 1.5x,请检查窗口是否被其它窗口遮挡")
ok = False
finally:
client.call_tool("set_drawing_area", {"mode": "restore"})
time.sleep(args.restore_wait)
rc2, zoom2 = win_state(u, hwnd)
back = shot_size(client)
print(f"[restore] 窗口矩形={list(rc2)} 最大化={zoom2} 绘图区={back}")
# ---- 还原保真度判定 ----
if zoom2 != zoom0:
print(f" !! 最大化状态未还原: {zoom0} -> {zoom2}")
ok = False
if any(abs(a - b) > _TOL for a, b in zip(rc0, rc2)):
print(f" !! 窗口矩形未还原(容差 {_TOL}px)")
ok = False
if base and back and any(abs(a - b) > _TOL for a, b in zip(base, back)):
print(f" !! 绘图区尺寸未还原: {base} -> {back}")
ok = False
print("\n结果:", "PASS" if ok else "FAIL")
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())