Skip to content

Commit 8a213d4

Browse files
committed
Add AC_read_table: read grid/table/list controls as rows
Complete the native control driver with table reading — the agent research's other top desktop gap (data scraping). Adds read_table to the backend interface, a Windows UIAutomation Grid-pattern implementation, read_control_table in the facade, the AC_read_table executor command, the ac_read_table MCP tool, a Script Builder entry, fake-backend tests, and the v7 docs / README sections.
1 parent eeeca9b commit 8a213d4

15 files changed

Lines changed: 135 additions & 10 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Object-level desktop automation: read and drive native controls through the OS a
6666

6767
- **Read / set value**`control_get_value` / `control_set_value` (`AC_control_get_value` / `AC_control_set_value`): read a textbox/combo value (no OCR) and set it in one call (no per-key typing).
6868
- **Invoke / toggle**`control_invoke` / `control_toggle` (`AC_control_invoke` / `AC_control_toggle`): press a button or flip a checkbox via its control pattern.
69+
- **Read a table/grid**`read_control_table` (`AC_read_table`): scrape a grid/list/table control into rows of cell strings — desktop data extraction without OCR.
6970
- Targets a control by `name` / `role` / `app_name` / `automation_id` (the stable Windows identifier), so it survives layout/localization changes.
7071

7172
## What's new (2026-06-19)

README/README_zh-CN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565

6666
- **读取 / 设置值**`control_get_value` / `control_set_value`(`AC_control_get_value` / `AC_control_set_value`):读 textbox/combo 值(不用 OCR),一次设置值(不必逐键输入)。
6767
- **调用 / 切换**`control_invoke` / `control_toggle`(`AC_control_invoke` / `AC_control_toggle`):通过控件模式按按钮或切换复选框。
68+
- **读取表格/列表**`read_control_table`(`AC_read_table`):把 grid/list/table 控件抓成逐行单元格字符串——不用 OCR 的桌面数据提取。
6869
-`name` / `role` / `app_name` / `automation_id`(Windows 稳定标识符)定位,版面/本地化改变也不坏。
6970

7071
## 本次更新 (2026-06-19)

README/README_zh-TW.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565

6666
- **讀取 / 設定值**`control_get_value` / `control_set_value`(`AC_control_get_value` / `AC_control_set_value`):讀 textbox/combo 值(不用 OCR),一次設定值(不必逐鍵輸入)。
6767
- **呼叫 / 切換**`control_invoke` / `control_toggle`(`AC_control_invoke` / `AC_control_toggle`):透過控制模式按按鈕或切換核取方塊。
68+
- **讀取表格/清單**`read_control_table`(`AC_read_table`):把 grid/list/table 控制項抓成逐列儲存格字串——不用 OCR 的桌面資料擷取。
6869
-`name` / `role` / `app_name` / `automation_id`(Windows 穩定識別碼)定位,版面/在地化改變也不壞。
6970

7071
## 本次更新 (2026-06-19)

docs/source/Eng/doc/new_features/v7_features_doc.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,22 @@ Invoking and toggling
5656
Executor commands: ``AC_control_invoke``, ``AC_control_toggle``.
5757

5858

59+
Reading tables / grids
60+
====================
61+
62+
::
63+
64+
from je_auto_control import read_control_table
65+
66+
rows = read_control_table(name="Results", app_name="myapp.exe")
67+
# -> [["Sam", "30"], ["Lee", "25"], ...]
68+
69+
``read_control_table`` reads a grid/table/list control into rows of cell
70+
strings via the Grid pattern — reliable desktop data scraping without OCR.
71+
72+
Executor command: ``AC_read_table``.
73+
74+
5975
Targeting controls
6076
=================
6177

docs/source/Zh/doc/new_features/v7_features_doc.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ Builder),並提供 Windows UIAutomation 後端;無法執行該動作的後端會
5252
執行器指令:``AC_control_invoke``、``AC_control_toggle``。
5353

5454

55+
讀取表格 / 清單
56+
================
57+
58+
::
59+
60+
from je_auto_control import read_control_table
61+
62+
rows = read_control_table(name="Results", app_name="myapp.exe")
63+
# -> [["Sam", "30"], ["Lee", "25"], ...]
64+
65+
``read_control_table`` 透過 Grid pattern 把 grid/table/list 控制項讀成
66+
逐列的儲存格字串——不用 OCR 的可靠桌面資料抓取。
67+
68+
執行器指令:``AC_read_table``。
69+
70+
5571
定位控制項
5672
==========
5773

je_auto_control/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
click_accessibility_element, control_get_value, control_invoke,
4747
control_set_value, control_toggle, dump_accessibility_tree,
4848
find_accessibility_element, list_accessibility_elements,
49+
read_control_table,
4950
)
5051
# VLM element locator (headless)
5152
from je_auto_control.utils.vision import (
@@ -546,7 +547,7 @@ def start_autocontrol_gui(*args, **kwargs):
546547
"click_accessibility_element", "dump_accessibility_tree",
547548
"find_accessibility_element", "list_accessibility_elements",
548549
"control_get_value", "control_set_value", "control_invoke",
549-
"control_toggle",
550+
"control_toggle", "read_control_table",
550551
# VLM locator
551552
"VLMNotAvailableError", "locate_by_description", "click_by_description",
552553
"verify_description",

je_auto_control/gui/script_builder/command_schema.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,11 @@ def _add_native_control_specs(specs: List[CommandSpec]) -> None:
596596
fields=fields,
597597
description="Toggle a native control (e.g. a checkbox).",
598598
))
599+
specs.append(CommandSpec(
600+
"AC_read_table", "Native UI", "Read Table / Grid",
601+
fields=fields,
602+
description="Read a grid/table/list control as rows of cell strings.",
603+
))
599604

600605

601606
def _add_misc_specs(specs: List[CommandSpec]) -> None:

je_auto_control/utils/accessibility/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
click_accessibility_element, control_get_value, control_invoke,
55
control_set_value, control_toggle, dump_accessibility_tree,
66
find_accessibility_element, list_accessibility_elements,
7+
read_control_table,
78
)
89
from je_auto_control.utils.accessibility.recorder import (
910
AXRecorderEvent, AccessibilityRecorder,
@@ -20,5 +21,5 @@
2021
"dump_accessibility_tree", "find_accessibility_element",
2122
"list_accessibility_elements", "max_depth",
2223
"control_get_value", "control_set_value", "control_invoke",
23-
"control_toggle",
24+
"control_toggle", "read_control_table",
2425
]

je_auto_control/utils/accessibility/accessibility_api.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,20 @@ def control_toggle(name: Optional[str] = None, role: Optional[str] = None,
126126
name=name, role=role, app_name=app_name, automation_id=automation_id)
127127

128128

129+
def read_control_table(name: Optional[str] = None, role: Optional[str] = None,
130+
app_name: Optional[str] = None,
131+
automation_id: Optional[str] = None,
132+
) -> List[List[str]]:
133+
"""Read a grid/table/list control as rows of cell strings."""
134+
return get_backend().read_table(
135+
name=name, role=role, app_name=app_name, automation_id=automation_id)
136+
137+
129138
__all__ = [
130139
"AccessibilityElement", "AccessibilityNotAvailableError",
131140
"AXTreeNode",
132141
"click_accessibility_element", "dump_accessibility_tree",
133142
"find_accessibility_element", "list_accessibility_elements",
134143
"control_get_value", "control_set_value", "control_invoke",
135-
"control_toggle",
144+
"control_toggle", "read_control_table",
136145
]

je_auto_control/utils/accessibility/backends/base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ def toggle(self, name: Optional[str] = None, role: Optional[str] = None,
4949
"""Toggle the matched control (e.g. a checkbox)."""
5050
self._unsupported("toggle")
5151

52+
def read_table(self, name: Optional[str] = None, role: Optional[str] = None,
53+
app_name: Optional[str] = None,
54+
automation_id: Optional[str] = None,
55+
) -> List[List[str]]:
56+
"""Read a grid/table/list control as rows of cell strings."""
57+
self._unsupported("read_table")
58+
5259
def _unsupported(self, operation: str):
5360
"""Raise a clear error for an action this backend can't perform."""
5461
raise AccessibilityNotAvailableError(

0 commit comments

Comments
 (0)