140 lines
4.8 KiB
Python
140 lines
4.8 KiB
Python
"""绘图区最大化验证:`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())
|