Skip to content

Commit e7f3772

Browse files
committed
Add grid/table cell addressing by row/col
1 parent 649c8b8 commit e7f3772

15 files changed

Lines changed: 292 additions & 1 deletion

File tree

‎README/WHATS_NEW_zh-CN.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# 本次更新 — AutoControl
22

3+
## 本次更新 (2026-06-23) — 表格 / 网格单元格定位
4+
5+
依(行、列)从单元格边界框定位表格单元格。完整参考:[`docs/source/Zh/doc/new_features/v125_features_doc.rst`](../docs/source/Zh/doc/new_features/v125_features_doc.rst)。
6+
7+
- **`cluster_grid` / `locate_cell`**(`AC_grid_cell`):`anchor_locator` 处理成对关系,但无法定位二维网格。给定单元格边界框(来自 `locate_all_image` / `find_text_matches`),本功能将其分群为行(依中心 y 在 `row_tolerance` 内)与列(依中心 x),并返回 0 起算 `(row, col)` 单元格的中心——可直接点击。纯分群、完全无头可测。
8+
39
## 本次更新 (2026-06-23) — 锚点序数与全部定位
410

511
挑选第 N 个锚点相对匹配,或列举全部。完整参考:[`docs/source/Zh/doc/new_features/v124_features_doc.rst`](../docs/source/Zh/doc/new_features/v124_features_doc.rst)。

‎README/WHATS_NEW_zh-TW.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# 本次更新 — AutoControl
22

3+
## 本次更新 (2026-06-23) — 表格 / 格線儲存格定位
4+
5+
依(列、欄)從儲存格邊界框定位表格儲存格。完整參考:[`docs/source/Zh/doc/new_features/v125_features_doc.rst`](../docs/source/Zh/doc/new_features/v125_features_doc.rst)。
6+
7+
- **`cluster_grid` / `locate_cell`**(`AC_grid_cell`):`anchor_locator` 處理成對關係,但無法定位二維格線。給定儲存格邊界框(來自 `locate_all_image` / `find_text_matches`),本功能將其分群為列(依中心 y 在 `row_tolerance` 內)與欄(依中心 x),並回傳 0 起算 `(row, col)` 儲存格的中心——可直接點擊。純分群、完全無頭可測。
8+
39
## 本次更新 (2026-06-23) — 錨點序數與全部定位
410

511
挑選第 N 個錨點相對比對,或列舉全部。完整參考:[`docs/source/Zh/doc/new_features/v124_features_doc.rst`](../docs/source/Zh/doc/new_features/v124_features_doc.rst)。

‎WHATS_NEW.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# What's New — AutoControl
22

3+
## What's new (2026-06-23) — Grid / Table Cell Addressing
4+
5+
Address a table cell by (row, column) from cell bounding boxes. Full reference: [`docs/source/Eng/doc/new_features/v125_features_doc.rst`](docs/source/Eng/doc/new_features/v125_features_doc.rst).
6+
7+
- **`cluster_grid` / `locate_cell`** (`AC_grid_cell`): `anchor_locator` does pairwise relations but nothing addresses a 2-D grid. Given the cell bounding boxes (from `locate_all_image` / `find_text_matches`), this clusters them into rows (by centre-y within `row_tolerance`) and columns (by centre-x) and returns the centre of the 0-based `(row, col)` cell — ready to click. Pure clustering, fully headless-testable.
8+
39
## What's new (2026-06-23) — Anchor Ordinal & Locate-All
410

511
Pick the Nth anchor-relative match, or enumerate them all. Full reference: [`docs/source/Eng/doc/new_features/v124_features_doc.rst`](docs/source/Eng/doc/new_features/v124_features_doc.rst).
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
Grid / Table Cell Addressing
2+
============================
3+
4+
``anchor_locator`` does pairwise spatial relations (target *near* / *below* an
5+
anchor) but nothing addresses a 2-D grid — "the cell at row 3, column 2" of a
6+
table. Given the bounding boxes of the cells (from an image or OCR enumeration,
7+
e.g. ``locate_all_image`` / ``find_text_matches``), this clusters them into rows
8+
and columns and returns the requested cell's centre.
9+
10+
The clustering and lookup are pure (boxes in, grid / cell out) and fully
11+
unit-testable; the box enumeration stays the caller's job, so nothing here needs a
12+
real screen. Imports no ``PySide6``.
13+
14+
Headless API
15+
------------
16+
17+
.. code-block:: python
18+
19+
from je_auto_control import cluster_grid, locate_cell
20+
21+
boxes = [(10, 100, 20, 10), (110, 100, 20, 10), (210, 100, 20, 10),
22+
(10, 200, 20, 10), (110, 200, 20, 10), (210, 200, 20, 10)]
23+
24+
locate_cell(boxes, row=1, col=2)
25+
# {'found': True, 'center': [220, 205], 'box': [210, 200, 20, 10],
26+
# 'row': 1, 'col': 2, 'rows': 2, 'cols': 3}
27+
28+
cluster_grid(boxes) # rows top-to-bottom, cells left-to-right
29+
30+
``cluster_grid`` sorts the boxes by centre-y, starts a new row when the gap
31+
exceeds ``row_tolerance``, and orders each row's cells by centre-x.
32+
``locate_cell`` returns the centre (ready to click) of the 0-based ``(row, col)``
33+
cell, or ``{found: False, reason}`` when the index is out of range.
34+
35+
Executor commands
36+
-----------------
37+
38+
``AC_grid_cell`` takes ``boxes`` (a JSON ``[[x, y, w, h], ...]`` list, e.g. from a
39+
prior ``AC_locate_all_image`` step) plus ``row`` / ``col`` / ``row_tolerance`` and
40+
returns the cell dict. It is exposed as the MCP tool ``ac_grid_cell`` and as a
41+
Script Builder command under **Mouse**.

‎docs/source/Eng/eng_index.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ Comprehensive guides for all AutoControl features.
147147
doc/new_features/v122_features_doc
148148
doc/new_features/v123_features_doc
149149
doc/new_features/v124_features_doc
150+
doc/new_features/v125_features_doc
150151
doc/ocr_backends/ocr_backends_doc
151152
doc/observability/observability_doc
152153
doc/operations_layer/operations_layer_doc
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
表格 / 格線儲存格定位
2+
====================
3+
4+
``anchor_locator`` 處理成對的空間關係(目標在錨點*附近* / *下方*),但無法定位二維格線——表格中「第 3 列、
5+
第 2 欄的儲存格」。給定各儲存格的邊界框(來自影像或 OCR 列舉,例如 ``locate_all_image`` / ``find_text_matches``),
6+
本功能將其分群為列與欄,並回傳所求儲存格的中心。
7+
8+
分群與查詢皆為純函式(框進、格線 / 儲存格出),完全可單元測試;框的列舉仍由呼叫端負責,因此此處不需真實螢幕。
9+
不匯入 ``PySide6``。
10+
11+
無頭 API
12+
--------
13+
14+
.. code-block:: python
15+
16+
from je_auto_control import cluster_grid, locate_cell
17+
18+
boxes = [(10, 100, 20, 10), (110, 100, 20, 10), (210, 100, 20, 10),
19+
(10, 200, 20, 10), (110, 200, 20, 10), (210, 200, 20, 10)]
20+
21+
locate_cell(boxes, row=1, col=2)
22+
# {'found': True, 'center': [220, 205], 'box': [210, 200, 20, 10],
23+
# 'row': 1, 'col': 2, 'rows': 2, 'cols': 3}
24+
25+
cluster_grid(boxes) # 列由上到下、儲存格由左到右
26+
27+
``cluster_grid`` 依中心 y 排序框,當間距超過 ``row_tolerance`` 時開始新的一列,並將每列的儲存格依中心 x 排序。
28+
``locate_cell`` 回傳 0 起算 ``(row, col)`` 儲存格的中心(可直接點擊),索引超出範圍時回傳
29+
``{found: False, reason}``。
30+
31+
執行器命令
32+
----------
33+
34+
``AC_grid_cell`` 接受 ``boxes``(JSON ``[[x, y, w, h], ...]`` 清單,例如來自前一個 ``AC_locate_all_image`` 步驟)
35+
以及 ``row`` / ``col`` / ``row_tolerance``,並回傳儲存格 dict。它以 MCP 工具 ``ac_grid_cell`` 以及 Script Builder
36+
中 **Mouse** 分類下的命令提供。

‎docs/source/Zh/zh_index.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ AutoControl 所有功能的完整使用指南。
147147
doc/new_features/v122_features_doc
148148
doc/new_features/v123_features_doc
149149
doc/new_features/v124_features_doc
150+
doc/new_features/v125_features_doc
150151
doc/ocr_backends/ocr_backends_doc
151152
doc/observability/observability_doc
152153
doc/operations_layer/operations_layer_doc

‎je_auto_control/__init__.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@
271271
from je_auto_control.utils.modifier_state import (
272272
hold_modifiers, plan_with_modifiers,
273273
)
274+
# Address a table / grid cell by (row, column) from bounding boxes
275+
from je_auto_control.utils.grid_locator import cluster_grid, locate_cell
274276
# CI workflow annotations (GitHub Actions)
275277
from je_auto_control.utils.ci_annotations import (
276278
emit_annotations, format_annotation,
@@ -1058,6 +1060,8 @@ def start_autocontrol_gui(*args, **kwargs):
10581060
"unicode_code_units",
10591061
"hold_modifiers",
10601062
"plan_with_modifiers",
1063+
"cluster_grid",
1064+
"locate_cell",
10611065
"emit_annotations", "format_annotation",
10621066
"ClipboardHistory", "default_clipboard_history",
10631067
"analyze_heal_log", "heal_stats", "scan_secrets",

‎je_auto_control/gui/script_builder/command_schema.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,17 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
800800
),
801801
description="Move the pointer by (dx, dy) from its current position.",
802802
))
803+
specs.append(CommandSpec(
804+
"AC_grid_cell", "Mouse", "Grid Cell (row / col)",
805+
fields=(
806+
FieldSpec("boxes", FieldType.STRING,
807+
placeholder="[[10,100,20,10],[110,100,20,10], ...]"),
808+
FieldSpec("row", FieldType.INT, placeholder="0"),
809+
FieldSpec("col", FieldType.INT, placeholder="0"),
810+
FieldSpec("row_tolerance", FieldType.INT, optional=True, default=10),
811+
),
812+
description="Resolve a table cell centre by row/col from cell boxes.",
813+
))
803814
specs.append(CommandSpec(
804815
"AC_list_plugins", "Tools", "List Plugin Commands",
805816
fields=(FieldSpec("group", FieldType.STRING, optional=True,

‎je_auto_control/utils/executor/action_executor.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3175,6 +3175,17 @@ def _type_unicode(text: str, modifier: str = "ctrl") -> Dict[str, Any]:
31753175
return type_unicode(text, modifier=modifier)
31763176

31773177

3178+
def _grid_cell(boxes: Any, row: Any, col: Any,
3179+
row_tolerance: Any = 10) -> Dict[str, Any]:
3180+
"""Adapter: address a grid cell by (row, col) from a JSON list of boxes."""
3181+
import json
3182+
from je_auto_control.utils.grid_locator import locate_cell
3183+
if isinstance(boxes, str):
3184+
boxes = json.loads(boxes)
3185+
return locate_cell(list(boxes), int(row), int(col),
3186+
row_tolerance=int(row_tolerance))
3187+
3188+
31783189
def _with_modifiers(modifiers: Any, actions: Any) -> Dict[str, Any]:
31793190
"""Adapter: run nested actions while modifier keys are held down."""
31803191
import json
@@ -4891,6 +4902,7 @@ def __init__(self):
48914902
"AC_move_mouse_relative": _move_mouse_relative,
48924903
"AC_type_unicode": _type_unicode,
48934904
"AC_with_modifiers": _with_modifiers,
4905+
"AC_grid_cell": _grid_cell,
48944906
"AC_detect_drift": _detect_drift,
48954907
"AC_categorical_drift": _categorical_drift,
48964908
"AC_diff_rows": _diff_rows,

0 commit comments

Comments
 (0)