2026-07-17 23:50:03 +08:00
|
|
|
|
"""L2 Locator 抽象层基类:GeomSpec + RoiSpec + Selector + ElementHandle。
|
2026-07-16 01:19:47 +08:00
|
|
|
|
|
|
|
|
|
|
GeomSpec:几何定位规格(relative_to / ratio / offset)
|
2026-07-17 23:50:03 +08:00
|
|
|
|
RoiSpec:图像匹配感兴趣区域(相对窗口的比例矩形,缩小 OpenCV 搜索范围)
|
|
|
|
|
|
Selector:UI 元素选择器(kind / by_image / by_geom / roi / threshold / require_image)
|
2026-07-16 01:19:47 +08:00
|
|
|
|
ElementHandle:定位结果句柄(绝对坐标 + 置信度 + 策略)
|
|
|
|
|
|
|
|
|
|
|
|
L3 Actions 通过 Selector 描述要找的元素,LocatorRegistry 解析为
|
|
|
|
|
|
ElementHandle(图像命中或几何兜底),Actions 据此调 Backend 执行点击。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
|
from typing import Optional
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
|
|
class GeomSpec:
|
|
|
|
|
|
"""几何定位规格。
|
|
|
|
|
|
|
|
|
|
|
|
relative_to 取值:window / window_top_left / window_bottom_right(语义占位,
|
|
|
|
|
|
实际计算统一用 ratio + offset,offset 已含正负号区分方向)。
|
|
|
|
|
|
|
|
|
|
|
|
所有字段带默认值,避免构造时必填字段顺序问题。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
relative_to: str = "window"
|
|
|
|
|
|
x_ratio: float = 0.0
|
|
|
|
|
|
y_ratio: float = 0.0
|
|
|
|
|
|
x_offset: int = 0
|
|
|
|
|
|
y_offset: int = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-07-17 23:50:03 +08:00
|
|
|
|
@dataclass
|
|
|
|
|
|
class RoiSpec:
|
|
|
|
|
|
"""图像匹配感兴趣区域(ROI),相对窗口的比例矩形。
|
|
|
|
|
|
|
|
|
|
|
|
用于将 OpenCV 模板匹配限制在窗口的某个子区域,避免误匹配到
|
|
|
|
|
|
窗口内其他视觉相似的 UI 元素。例如 input_box 只在聊天区右下角查找,
|
|
|
|
|
|
即使模板图像有缺陷也不会匹配到窗口中部。
|
|
|
|
|
|
|
|
|
|
|
|
所有字段为相对窗口宽/高的比例(0.0~1.0),由 Actions 按当前窗口几何
|
|
|
|
|
|
转换为绝对像素 ROI 传给 OpenCVBackend.find_template。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
x_ratio: ROI 左上角相对窗口左上角的 X 比例
|
|
|
|
|
|
y_ratio: ROI 左上角相对窗口左上角的 Y 比例
|
|
|
|
|
|
w_ratio: ROI 宽度相对窗口宽度的比例
|
|
|
|
|
|
h_ratio: ROI 高度相对窗口高度的比例
|
|
|
|
|
|
|
|
|
|
|
|
边界由 Actions/opencv.py 做裁剪保护,YAML 中可略微宽松。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
x_ratio: float = 0.0
|
|
|
|
|
|
y_ratio: float = 0.0
|
|
|
|
|
|
w_ratio: float = 1.0
|
|
|
|
|
|
h_ratio: float = 1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-07-16 01:19:47 +08:00
|
|
|
|
@dataclass
|
|
|
|
|
|
class Selector:
|
|
|
|
|
|
"""UI 元素选择器。
|
|
|
|
|
|
|
|
|
|
|
|
kind:元素类别键(如 search_box / send_button / input_box)
|
|
|
|
|
|
by_image:图像模板名(P2 启用),非空时优先图像匹配
|
|
|
|
|
|
by_geom:几何规格,非空时图像失败或 P1 阶段走几何兜底
|
2026-07-17 23:50:03 +08:00
|
|
|
|
roi:图像匹配感兴趣区域(可选)。非空时限制 OpenCV 搜索范围;
|
|
|
|
|
|
为空时使用全窗口。用于防止模板误匹配到窗口其他相似元素。
|
2026-07-16 01:19:47 +08:00
|
|
|
|
threshold:图像匹配置信度阈值(默认 0.80)
|
|
|
|
|
|
require_image:高风险操作为 True 时,图像失败即拒绝执行
|
|
|
|
|
|
description:人类可读描述
|
|
|
|
|
|
|
|
|
|
|
|
所有字段带默认值。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
kind: str = ""
|
|
|
|
|
|
by_image: Optional[str] = None
|
|
|
|
|
|
by_geom: Optional[GeomSpec] = None
|
2026-07-17 23:50:03 +08:00
|
|
|
|
roi: Optional[RoiSpec] = None
|
2026-07-16 01:19:47 +08:00
|
|
|
|
threshold: float = 0.80
|
|
|
|
|
|
require_image: bool = False
|
|
|
|
|
|
description: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
|
|
class ElementHandle:
|
|
|
|
|
|
"""定位结果句柄。
|
|
|
|
|
|
|
|
|
|
|
|
selector:来源 Selector(回溯用)
|
|
|
|
|
|
x / y:元素中心绝对坐标(点击点)
|
|
|
|
|
|
w / h:元素宽高(图像匹配时返回,几何兜底为 0)
|
|
|
|
|
|
confidence:匹配置信度(图像命中时为模型分数,几何兜底为 1.0)
|
|
|
|
|
|
strategy:定位策略 image / geom
|
|
|
|
|
|
|
|
|
|
|
|
所有字段带默认值。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
selector: Optional[Selector] = None
|
|
|
|
|
|
x: int = 0
|
|
|
|
|
|
y: int = 0
|
|
|
|
|
|
w: int = 0
|
|
|
|
|
|
h: int = 0
|
|
|
|
|
|
confidence: float = 1.0
|
|
|
|
|
|
strategy: str = "geom"
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
|
def center(self) -> tuple[int, int]:
|
|
|
|
|
|
"""返回元素中心点(x, y)。"""
|
|
|
|
|
|
return (self.x, self.y)
|