ms-vite-plugin 1.4.40 → 1.4.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/api/dotocr.md +4 -4
- package/docs/api/node.md +242 -266
- package/docs/api/opencv.md +46 -356
- package/docs/apicn/dotocr.md +2 -2
- package/docs/apicn/node.md +245 -284
- package/docs/apipython/dotocr.md +2 -2
- package/docs/apipython/node.md +193 -215
- package/package.json +1 -1
package/docs/apipython/node.md
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
# 节点模块 (Node)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
用于查找和操作当前界面上的控件节点。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**注意**
|
|
6
6
|
|
|
7
|
-
-
|
|
7
|
+
- 仅支持快点 Agent 模式
|
|
8
|
+
- 通过 `from kuaijs import node` 使用
|
|
9
|
+
|
|
10
|
+
**基本流程**
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
createNodeSelector → 设置筛选条件 → getOneNodeInfo / getNodeInfo
|
|
14
|
+
→ 点击 / 输入 / 设值等
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
8
18
|
|
|
9
19
|
## 类型定义
|
|
10
20
|
|
|
11
21
|
```python
|
|
12
|
-
from typing import List,
|
|
22
|
+
from typing import List, Optional, TypedDict
|
|
13
23
|
|
|
14
24
|
class NodeBounds:
|
|
15
25
|
x: int
|
|
@@ -37,6 +47,13 @@ class NodeInfo:
|
|
|
37
47
|
def clickCenter(self) -> bool: ...
|
|
38
48
|
def clickRandom(self) -> bool: ...
|
|
39
49
|
def hittable(self) -> bool: ...
|
|
50
|
+
def tap(self) -> bool: ...
|
|
51
|
+
def doubleTap(self) -> bool: ...
|
|
52
|
+
def press(self, duration: int = 500) -> bool: ...
|
|
53
|
+
def scrollToVisible(self) -> bool: ...
|
|
54
|
+
def clearInput(self) -> bool: ...
|
|
55
|
+
def input(self, text: str) -> bool: ...
|
|
56
|
+
def setValue(self, text: str) -> bool: ...
|
|
40
57
|
def parent(self) -> Optional["NodeInfo"]: ...
|
|
41
58
|
def child(self, index: int) -> Optional["NodeInfo"]: ...
|
|
42
59
|
def allChildren(self) -> List["NodeInfo"]: ...
|
|
@@ -45,286 +62,247 @@ class NodeInfo:
|
|
|
45
62
|
def nextSiblings(self) -> List["NodeInfo"]: ...
|
|
46
63
|
|
|
47
64
|
class NodeSelectorOptions(TypedDict, total=False):
|
|
48
|
-
maxDepth: int #
|
|
49
|
-
mode: int
|
|
65
|
+
maxDepth: int # 最大层级,默认 50
|
|
66
|
+
mode: int # 抓取模式 1 | 2 | 3,默认 1
|
|
50
67
|
```
|
|
51
68
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
节点选择器是访问 UI 元素的核心工具,提供了灵活的筛选和查找机制。
|
|
69
|
+
---
|
|
55
70
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
#### `createNodeSelector` - 创建节点选择器
|
|
59
|
-
|
|
60
|
-
创建一个新的节点选择器实例。
|
|
71
|
+
## 创建选择器
|
|
61
72
|
|
|
62
73
|
```python
|
|
63
74
|
def createNodeSelector(params: Optional[NodeSelectorOptions] = None) -> NodeSelector
|
|
64
75
|
```
|
|
65
76
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
| `maxDepth` | int | 否 | 50 | 最大层级深度,默认 50 |
|
|
71
|
-
| `mode` | int | 否 | 1 | 抓取模式。支持模式 1、模式 2、模式 3,默认模式 1 |
|
|
72
|
-
|
|
73
|
-
**返回值:** `NodeSelector`
|
|
74
|
-
|
|
75
|
-
**示例:**
|
|
77
|
+
| 参数 | 默认 | 说明 |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| `maxDepth` | `50` | 抓取深度,深层控件抓不到时可调大 |
|
|
80
|
+
| `mode` | `1` | 抓取模式,可选 `1` / `2` / `3`,默认 `1` |
|
|
76
81
|
|
|
77
82
|
```python
|
|
78
83
|
from kuaijs import node
|
|
79
84
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"mode": 1 # 默认模式 1
|
|
84
|
-
})
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### 选择器方法
|
|
88
|
-
|
|
89
|
-
#### 获取节点
|
|
90
|
-
|
|
91
|
-
```python
|
|
92
|
-
# 获取当前页面的 XML 字符串
|
|
93
|
-
def xml(timeout: int = 5000) -> Optional[str]
|
|
94
|
-
|
|
95
|
-
# 获取多个节点
|
|
96
|
-
def getNodeInfo(timeout: int = 5000) -> List[NodeInfo]
|
|
97
|
-
|
|
98
|
-
# 获取单个节点
|
|
99
|
-
def getOneNodeInfo(timeout: int = 5000) -> Optional[NodeInfo]
|
|
100
|
-
|
|
101
|
-
# 加载节点数据(通常内部自动调用)
|
|
102
|
-
def loadNode(timeout: int = 5000) -> NodeSelector
|
|
103
|
-
|
|
104
|
-
# 清除所有选择条件
|
|
105
|
-
def clearSelector() -> NodeSelector
|
|
85
|
+
selector = node.createNodeSelector()
|
|
86
|
+
shallow = node.createNodeSelector({"maxDepth": 15})
|
|
87
|
+
list_page = node.createNodeSelector({"mode": 2})
|
|
106
88
|
```
|
|
107
89
|
|
|
108
|
-
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 节点信息
|
|
93
|
+
|
|
94
|
+
### 属性
|
|
95
|
+
|
|
96
|
+
| 字段 | 说明 |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `id` | 节点 ID(内部定位用,不要改) |
|
|
99
|
+
| `identifier` | 控件标识符 |
|
|
100
|
+
| `label` | 标签文案 |
|
|
101
|
+
| `type` | 类型,如 `XCUIElementTypeButton` |
|
|
102
|
+
| `value` | 当前值 |
|
|
103
|
+
| `placeholderValue` | 输入框占位文字 |
|
|
104
|
+
| `title` | 标题 |
|
|
105
|
+
| `enabled` | 是否启用 |
|
|
106
|
+
| `bounds` | 位置大小 |
|
|
107
|
+
| `depth` / `index` | 层级 / 序号 |
|
|
108
|
+
| `parentId` / `childCount` | 父节点 ID / 子节点数 |
|
|
109
|
+
|
|
110
|
+
### 方法
|
|
111
|
+
|
|
112
|
+
| 方法 | 说明 |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| `hittable()` | 当前是否可点击 |
|
|
115
|
+
| `scrollToVisible()` | 滚动到可见区域 |
|
|
116
|
+
| `tap()` | 单击(不可见时会先尝试滚到可见) |
|
|
117
|
+
| `doubleTap()` | 双击 |
|
|
118
|
+
| `press(duration=500)` | 长按,毫秒,默认 500 |
|
|
119
|
+
| `clickCenter()` | 点击中心 |
|
|
120
|
+
| `clickRandom()` | 范围内随机点击 |
|
|
121
|
+
| `clearInput()` | 清空输入 |
|
|
122
|
+
| `input(text)` | 追加输入(不先清空) |
|
|
123
|
+
| `setValue(text)` | 设置值(见下方) |
|
|
124
|
+
| `parent()` / `child(i)` / `allChildren()` | 父 / 子 / 全部子节点 |
|
|
125
|
+
| `siblings()` / `previousSiblings()` / `nextSiblings()` | 兄弟节点 |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 选择器方法
|
|
130
|
+
|
|
131
|
+
### 加载与结果
|
|
132
|
+
|
|
133
|
+
| 方法 | 说明 |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| `loadNode(timeout=5000)` | 重新加载界面节点 |
|
|
136
|
+
| `clearSelector()` | 清除筛选条件 |
|
|
137
|
+
| `xml(timeout=5000)` | 获取界面 XML,失败返回 `None` |
|
|
138
|
+
| `getNodeInfo(timeout=5000)` | 所有匹配节点 |
|
|
139
|
+
| `getOneNodeInfo(timeout=5000)` | 第一个匹配节点,没有则 `None` |
|
|
140
|
+
|
|
141
|
+
`timeout` 单位毫秒。
|
|
109
142
|
|
|
110
143
|
```python
|
|
111
144
|
from kuaijs import node
|
|
112
145
|
|
|
113
146
|
selector = node.createNodeSelector()
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
print(f"找到 {len(all_nodes)} 个节点")
|
|
118
|
-
|
|
119
|
-
# 获取第一个匹配的节点
|
|
120
|
-
first_node = selector.getOneNodeInfo(3000)
|
|
121
|
-
if first_node:
|
|
122
|
-
print(f"节点标签: {first_node.label}")
|
|
147
|
+
nodes = selector.label("确定").getNodeInfo(5000)
|
|
148
|
+
one = selector.getOneNodeInfo(3000)
|
|
149
|
+
xml = selector.xml()
|
|
123
150
|
```
|
|
124
151
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
##### 文本相关
|
|
128
|
-
|
|
129
|
-
```python
|
|
130
|
-
# 精确匹配标签
|
|
131
|
-
def label(text: str) -> NodeSelector
|
|
132
|
-
|
|
133
|
-
# 标签模糊匹配
|
|
134
|
-
def labelMatch(pattern: str) -> NodeSelector
|
|
135
|
-
|
|
136
|
-
# 精确匹配名称
|
|
137
|
-
def title(text: str) -> NodeSelector
|
|
152
|
+
### 筛选条件
|
|
138
153
|
|
|
139
|
-
|
|
140
|
-
def titleMatch(pattern: str) -> NodeSelector
|
|
154
|
+
可链式调用,多个条件同时满足。带 `Match` 的为模糊匹配。
|
|
141
155
|
|
|
142
|
-
|
|
143
|
-
|
|
156
|
+
| 分类 | 方法 |
|
|
157
|
+
| --- | --- |
|
|
158
|
+
| 文本 | `label` / `labelMatch`、`title` / `titleMatch`、`identifier` / `identifierMatch`、`value` / `valueMatch`、`placeholderValue` / `placeholderValueMatch` |
|
|
159
|
+
| 类型状态 | `type` / `typeMatch`、`enabled` |
|
|
160
|
+
| 结构位置 | `index`、`depth`、`childCount`(支持 `">0"`)、`bounds`、`xpath`、`id` |
|
|
144
161
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def value(text: str) -> NodeSelector
|
|
150
|
-
|
|
151
|
-
# 值模糊匹配
|
|
152
|
-
def valueMatch(pattern: str) -> NodeSelector
|
|
162
|
+
```python
|
|
163
|
+
selector.label("确定").type("XCUIElementTypeButton").getOneNodeInfo(3000)
|
|
164
|
+
selector.labelMatch(".*登录.*").getNodeInfo(3000)
|
|
165
|
+
selector.type("XCUIElementTypeTextField").enabled(True).getOneNodeInfo(3000)
|
|
153
166
|
```
|
|
154
167
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
```python
|
|
158
|
-
# 查找标签为"确定"的按钮
|
|
159
|
-
confirm_button = selector.label("确定").getOneNodeInfo(3000)
|
|
168
|
+
常见类型:`Button`、`StaticText`、`TextField`、`SecureTextField`、`TextView`、`SearchField`、`Cell`、`Table`、`CollectionView`、`PickerWheel`、`Switch`、`Slider` 等(完整名为 `XCUIElementType...`)。
|
|
160
169
|
|
|
161
|
-
|
|
162
|
-
login_elements = selector.labelMatch(".*登录.*").getNodeInfo(3000)
|
|
170
|
+
---
|
|
163
171
|
|
|
164
|
-
|
|
165
|
-
buttons = selector.titleMatch("button").getNodeInfo(3000)
|
|
172
|
+
## 节点操作说明
|
|
166
173
|
|
|
167
|
-
|
|
168
|
-
submit_element = selector.value("submit").getOneNodeInfo(2000)
|
|
174
|
+
### 是否可点 / 滚到可见
|
|
169
175
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
176
|
+
```python
|
|
177
|
+
if node.hittable():
|
|
178
|
+
node.tap()
|
|
179
|
+
else:
|
|
180
|
+
node.scrollToVisible()
|
|
181
|
+
node.tap()
|
|
173
182
|
```
|
|
174
183
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
# 精确匹配类型
|
|
179
|
-
def type(type_title: str) -> NodeSelector
|
|
184
|
+
- `hittable()`:当前能否点到
|
|
185
|
+
- `scrollToVisible()`:手动滚到可见
|
|
186
|
+
- `tap` / `doubleTap` / `press`:不可见时会自动尝试滚到可见再操作
|
|
180
187
|
|
|
181
|
-
|
|
182
|
-
def typeMatch(pattern: str) -> NodeSelector
|
|
188
|
+
### 点击
|
|
183
189
|
|
|
184
|
-
|
|
185
|
-
|
|
190
|
+
```python
|
|
191
|
+
btn = (
|
|
192
|
+
selector.label("确定")
|
|
193
|
+
.type("XCUIElementTypeButton")
|
|
194
|
+
.getOneNodeInfo(3000)
|
|
195
|
+
)
|
|
196
|
+
if btn:
|
|
197
|
+
btn.tap()
|
|
198
|
+
btn.doubleTap()
|
|
199
|
+
btn.press(800)
|
|
200
|
+
# btn.clickCenter()
|
|
201
|
+
# btn.clickRandom()
|
|
186
202
|
```
|
|
187
203
|
|
|
188
|
-
|
|
204
|
+
### 输入与设值
|
|
189
205
|
|
|
190
|
-
|
|
191
|
-
|
|
206
|
+
| 方法 | 说明 |
|
|
207
|
+
| --- | --- |
|
|
208
|
+
| `clearInput()` | 清空 |
|
|
209
|
+
| `input(text)` | 追加输入 |
|
|
210
|
+
| `setValue(text)` | 按类型设置 |
|
|
192
211
|
|
|
193
|
-
|
|
212
|
+
| 控件 | 传参 | 效果 |
|
|
213
|
+
| --- | --- | --- |
|
|
214
|
+
| `PickerWheel` | 选项文案,如 `"1小时"` | 滚到该选项 |
|
|
215
|
+
| 文本框类 | 字符串 | 追加输入,不先清空 |
|
|
194
216
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
# 查找所有文本框
|
|
199
|
-
text_fields = selector.typeMatch("XCUIElementTypeTextField").getNodeInfo(3000)
|
|
217
|
+
```python
|
|
218
|
+
field.clearInput()
|
|
219
|
+
field.setValue("hello")
|
|
200
220
|
|
|
201
|
-
|
|
202
|
-
|
|
221
|
+
wheel = selector.type("XCUIElementTypePickerWheel").getOneNodeInfo(3000)
|
|
222
|
+
if wheel:
|
|
223
|
+
wheel.setValue("1小时")
|
|
203
224
|
```
|
|
204
225
|
|
|
205
|
-
|
|
226
|
+
### 树导航
|
|
206
227
|
|
|
207
228
|
```python
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
# 子节点数量(支持表达式,如 ">0")
|
|
215
|
-
def childCount(expr: Union[int, str]) -> NodeSelector
|
|
216
|
-
|
|
217
|
-
# 边界范围
|
|
218
|
-
def bounds(x: int, y: int, width: int, height: int) -> NodeSelector
|
|
219
|
-
|
|
220
|
-
# XPath 路径
|
|
221
|
-
def xpath(path: str) -> NodeSelector
|
|
229
|
+
field = selector.type("XCUIElementTypeTextField").getOneNodeInfo(3000)
|
|
230
|
+
if field:
|
|
231
|
+
parent = field.parent()
|
|
232
|
+
children = parent.allChildren() if parent else []
|
|
233
|
+
prev = field.previousSiblings()
|
|
234
|
+
next_ = field.nextSiblings()
|
|
222
235
|
```
|
|
223
236
|
|
|
224
|
-
|
|
237
|
+
---
|
|
225
238
|
|
|
226
|
-
|
|
239
|
+
## 示例
|
|
240
|
+
|
|
241
|
+
### 登录
|
|
227
242
|
|
|
228
243
|
```python
|
|
229
244
|
from kuaijs import node
|
|
230
245
|
|
|
231
246
|
selector = node.createNodeSelector()
|
|
232
247
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
248
|
+
user = selector.type("XCUIElementTypeTextField").getOneNodeInfo(5000)
|
|
249
|
+
if user:
|
|
250
|
+
user.clearInput()
|
|
251
|
+
user.setValue("demo_user")
|
|
252
|
+
|
|
253
|
+
password = (
|
|
254
|
+
selector.clearSelector()
|
|
255
|
+
.type("XCUIElementTypeSecureTextField")
|
|
256
|
+
.getOneNodeInfo(3000)
|
|
257
|
+
)
|
|
258
|
+
if password:
|
|
259
|
+
password.clearInput()
|
|
260
|
+
password.setValue("password")
|
|
261
|
+
|
|
262
|
+
login = (
|
|
263
|
+
selector.clearSelector()
|
|
264
|
+
.labelMatch(".*登录.*")
|
|
265
|
+
.type("XCUIElementTypeButton")
|
|
266
|
+
.getOneNodeInfo(3000)
|
|
267
|
+
)
|
|
268
|
+
if login:
|
|
269
|
+
login.tap()
|
|
247
270
|
```
|
|
248
271
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
`NodeSelector` 查找得到的 `NodeInfo` 支持直接调用操作方法(点击、无障碍手势等)。
|
|
252
|
-
|
|
253
|
-
### 点击操作
|
|
272
|
+
### 列表里靠后的项
|
|
254
273
|
|
|
255
274
|
```python
|
|
256
|
-
|
|
257
|
-
def clickCenter() -> bool
|
|
258
|
-
|
|
259
|
-
# 随机点击节点区域
|
|
260
|
-
def clickRandom() -> bool
|
|
275
|
+
from kuaijs import node
|
|
261
276
|
|
|
262
|
-
|
|
263
|
-
|
|
277
|
+
selector = node.createNodeSelector({"mode": 2})
|
|
278
|
+
item = selector.label("第 30 项").getOneNodeInfo(5000)
|
|
279
|
+
if item:
|
|
280
|
+
item.tap() # 不在可见区域时会自动滚动
|
|
264
281
|
```
|
|
265
282
|
|
|
266
|
-
|
|
283
|
+
### 查看界面结构
|
|
267
284
|
|
|
268
285
|
```python
|
|
269
286
|
from kuaijs import node
|
|
270
287
|
|
|
271
288
|
selector = node.createNodeSelector()
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if confirm_btn:
|
|
277
|
-
print(f"是否可接收事件: {confirm_btn.hittable()}")
|
|
278
|
-
|
|
279
|
-
clicked = confirm_btn.clickCenter()
|
|
280
|
-
if clicked:
|
|
281
|
-
print("成功点击确定按钮")
|
|
282
|
-
|
|
283
|
-
# 随机点击避免检测
|
|
284
|
-
confirm_btn.clickRandom()
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
### 节点导航
|
|
288
|
-
|
|
289
|
-
```python
|
|
290
|
-
# 获取父节点
|
|
291
|
-
def parent() -> Optional[NodeInfo]
|
|
292
|
-
|
|
293
|
-
# 获取指定索引的子节点
|
|
294
|
-
def child(index: int) -> Optional[NodeInfo]
|
|
295
|
-
|
|
296
|
-
# 获取所有子节点
|
|
297
|
-
def allChildren() -> List[NodeInfo]
|
|
298
|
-
|
|
299
|
-
# 获取兄弟节点
|
|
300
|
-
def siblings() -> List[NodeInfo]
|
|
301
|
-
|
|
302
|
-
# 获取前面的兄弟节点
|
|
303
|
-
def previousSiblings() -> List[NodeInfo]
|
|
304
|
-
|
|
305
|
-
# 获取后面的兄弟节点
|
|
306
|
-
def nextSiblings() -> List[NodeInfo]
|
|
289
|
+
xml = selector.xml(8000)
|
|
290
|
+
if xml:
|
|
291
|
+
print(xml)
|
|
307
292
|
```
|
|
308
293
|
|
|
309
|
-
|
|
294
|
+
---
|
|
310
295
|
|
|
311
|
-
|
|
312
|
-
from kuaijs import node
|
|
313
|
-
|
|
314
|
-
selector = node.createNodeSelector()
|
|
296
|
+
## 常见问题
|
|
315
297
|
|
|
316
|
-
|
|
317
|
-
|
|
298
|
+
**找不到节点**
|
|
299
|
+
加大超时;减少条件或改用 `*Match`;换 `mode`;用 `xml()` 看实际文案和类型。
|
|
318
300
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if container:
|
|
322
|
-
print(f"容器类型: {container.type}")
|
|
301
|
+
**点了没反应**
|
|
302
|
+
先看 `hittable()`;确认点到的是目标控件;可对比 `clickCenter()`。
|
|
323
303
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
print(f"容器包含 {len(children)} 个子元素")
|
|
304
|
+
**输入叠在旧文字后面**
|
|
305
|
+
`input` / 文本类 `setValue` 是追加,先 `clearInput()`。
|
|
327
306
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
```
|
|
307
|
+
**滚轮设不中**
|
|
308
|
+
`setValue` 要传界面上显示的选项文案;确认类型是 `PickerWheel`;多列时选对那一列。
|