robotframework-desktop 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,171 @@
1
+ import time
2
+
3
+ import pyautogui
4
+ import pyperclip
5
+ from robot.api.deco import keyword
6
+
7
+ from .base import KeywordGroup, busy_action
8
+
9
+ _KEY_ALIASES = {
10
+ 'ESCAPE': 'esc',
11
+ 'ESC': 'esc',
12
+ 'RETURN': 'enter',
13
+ 'ENTER': 'enter',
14
+ 'BACKSPACE': 'backspace',
15
+ 'DELETE': 'delete',
16
+ 'DEL': 'delete',
17
+ 'PAGE_UP': 'pageup',
18
+ 'PAGEUP': 'pageup',
19
+ 'PAGE_DOWN': 'pagedown',
20
+ 'PAGEDOWN': 'pagedown',
21
+ 'CTRL': 'ctrl',
22
+ 'CONTROL': 'ctrl',
23
+ 'ALT': 'alt',
24
+ 'SHIFT': 'shift',
25
+ 'WIN': 'win',
26
+ 'META': 'win',
27
+ 'CMD': 'win',
28
+ 'WINDOWS': 'win',
29
+ 'CAPS_LOCK': 'capslock',
30
+ 'NUM_LOCK': 'numlock',
31
+ 'SCROLL_LOCK': 'scrolllock',
32
+ 'PRINTSCREEN': 'printscreen',
33
+ 'PRINT': 'printscreen',
34
+ 'INSERT': 'insert',
35
+ 'SPACE': 'space',
36
+ 'TAB': 'tab',
37
+ 'HOME': 'home',
38
+ 'END': 'end',
39
+ 'UP': 'up',
40
+ 'DOWN': 'down',
41
+ 'LEFT': 'left',
42
+ 'RIGHT': 'right',
43
+ 'PAUSE': 'pause',
44
+ 'ALTGR': 'altright',
45
+ }
46
+
47
+
48
+ def to_key(name: str) -> str:
49
+ text = str(name).strip()
50
+ if len(text) == 1:
51
+ return text.lower()
52
+ upper = text.upper().replace(' ', '_')
53
+ if upper in _KEY_ALIASES:
54
+ return _KEY_ALIASES[upper]
55
+ key = upper.replace('_', '').lower()
56
+ if key in pyautogui.KEYBOARD_KEYS:
57
+ return key
58
+ raise ValueError(f"Unknown key '{name}'.")
59
+
60
+
61
+ class KeyboardKeywords(KeywordGroup):
62
+ def _focus(self, element):
63
+ if element not in (None, ''):
64
+ self._click_at(self._locate(element))
65
+ time.sleep(0.05)
66
+
67
+ def _write(self, text: str):
68
+ if text.isascii():
69
+ pyautogui.write(text, interval=0.01)
70
+ else:
71
+ self._paste(text)
72
+
73
+ @staticmethod
74
+ def _paste(text: str):
75
+ try:
76
+ previous = pyperclip.paste()
77
+ except Exception:
78
+ previous = None
79
+ pyperclip.copy(text)
80
+ pyautogui.hotkey('ctrl', 'v')
81
+ time.sleep(0.1)
82
+ if previous is not None:
83
+ pyperclip.copy(previous)
84
+
85
+ @keyword
86
+ @busy_action
87
+ def input_text(self, element, text: str):
88
+ """Clicks ``element`` (if given) and types ``text``.
89
+
90
+ ``element`` can be empty to type into the currently focused control.
91
+ Non-ASCII text is typed through the clipboard.
92
+
93
+ Examples:
94
+ | Input Text | username_field.png | john |
95
+ | Input Text | automation_id:txtName | John Doe |
96
+ | Input Text | ${EMPTY} | text typed into the focused field |
97
+ """
98
+ self._focus(element)
99
+ self._ready()
100
+ self._write(str(text))
101
+
102
+ @keyword
103
+ @busy_action
104
+ def paste_text(self, element, text: str):
105
+ """Clicks ``element`` (if given) and pastes ``text`` via the clipboard.
106
+
107
+ Examples:
108
+ | Paste Text | notes.png | Olá, ação! |
109
+ | Paste Text | ${EMPTY} | pasted into the focused control |
110
+ """
111
+ self._focus(element)
112
+ self._ready()
113
+ self._paste(str(text))
114
+
115
+ @keyword
116
+ @busy_action
117
+ def press_special_key(self, key_constant: str):
118
+ """Presses a special key such as ``ENTER``, ``TAB``, ``ESC``, ``DELETE``,
119
+ ``F1``...``F12``, ``UP``, ``DOWN``, ``PAGE_UP``, ``HOME``.
120
+
121
+ Examples:
122
+ | Double Click | field.png |
123
+ | Press Special Key | DELETE |
124
+ | Press Special Key | ENTER |
125
+ """
126
+ self._ready()
127
+ pyautogui.press(to_key(key_constant))
128
+
129
+ @keyword
130
+ @busy_action
131
+ def key_down(self, key_constant: str):
132
+ """Presses and holds a keyboard key.
133
+
134
+ Example:
135
+ | Key Down | CTRL |
136
+ | Click | item1.png |
137
+ | Click | item2.png |
138
+ | Key Up | CTRL |
139
+ """
140
+ self._ready()
141
+ pyautogui.keyDown(to_key(key_constant))
142
+
143
+ @keyword
144
+ @busy_action
145
+ def key_up(self, key_constant: str):
146
+ """Releases a keyboard key held by `Key Down`.
147
+
148
+ Example:
149
+ | Key Up | CTRL |
150
+ """
151
+ pyautogui.keyUp(to_key(key_constant))
152
+
153
+ @keyword
154
+ @busy_action
155
+ def type_with_modifiers(self, text: str, *modifiers):
156
+ """Types ``text`` while holding ``modifiers`` (``CTRL``, ``ALT``, ``SHIFT``, ``WIN``).
157
+
158
+ Examples:
159
+ | Type With Modifiers | a | CTRL | | # select all |
160
+ | Type With Modifiers | s | CTRL | SHIFT |
161
+ """
162
+ mods = [to_key(m) for m in modifiers]
163
+ self._ready()
164
+ for m in mods:
165
+ pyautogui.keyDown(m)
166
+ try:
167
+ for ch in str(text):
168
+ pyautogui.press(to_key(ch) if len(ch) == 1 else ch)
169
+ finally:
170
+ for m in reversed(mods):
171
+ pyautogui.keyUp(m)
@@ -0,0 +1,357 @@
1
+ import time
2
+
3
+ import pyautogui
4
+ from robot.api.deco import keyword
5
+
6
+ from ..geometry import Region, sort_matches, to_region
7
+ from ..locator import parse_locator
8
+ from .base import KeywordGroup, busy_action
9
+
10
+ _BUTTONS = {'LEFT': 'left', 'MIDDLE': 'middle', 'RIGHT': 'right'}
11
+
12
+
13
+ class MouseKeywords(KeywordGroup):
14
+ def __init__(self, ctx):
15
+ super().__init__(ctx)
16
+ self._held: set[str] = set()
17
+
18
+ # ---- clicks -------------------------------------------------------------
19
+ @keyword
20
+ @busy_action
21
+ def click(self, element, x_offset: int = 0, y_offset: int = 0):
22
+ """Clicks the given ``element``.
23
+
24
+ ``element`` can be any of the formats described in `Element locators`
25
+ (image path, dictionary, UI Automation locator, region or match).
26
+ ``x_offset``/``y_offset`` are added to the offset defined in the locator.
27
+
28
+ Examples:
29
+ | Click | buttons/save.png |
30
+ | Click | save.png=0.9 | | | # custom similarity |
31
+ | Click | save.png | 10 | -5 | # click 10px right and 5px above the center |
32
+ | Click | automation_id:btnSave |
33
+ | Click | name:OK AND control_type:Button |
34
+ | ${btn}= | Create Dictionary | path=ok.png | similarity=0.8 |
35
+ | Click | ${btn} |
36
+ | Click | ${{ {"path": ["ok.png", "ok_dark.png"], "offset": {"x": 5, "y": 0}} }} |
37
+ """
38
+ self._click_at(self._locate(element, x_offset, y_offset))
39
+
40
+ @keyword
41
+ @busy_action
42
+ def double_click(self, element, x_offset: int = 0, y_offset: int = 0):
43
+ """Double clicks the given ``element``. See `Click` for arguments.
44
+
45
+ Examples:
46
+ | Double Click | file_icon.png |
47
+ | Double Click | name:report.txt |
48
+ """
49
+ self._click_at(self._locate(element, x_offset, y_offset), clicks=2)
50
+
51
+ @keyword
52
+ @busy_action
53
+ def right_click(self, element, x_offset: int = 0, y_offset: int = 0):
54
+ """Right clicks the given ``element``. See `Click` for arguments.
55
+
56
+ Examples:
57
+ | Right Click | desktop_icon.png |
58
+ | Right Click | automation_id:listItems | 0 | 20 |
59
+ """
60
+ self._click_at(
61
+ self._locate(element, x_offset, y_offset), button='right'
62
+ )
63
+
64
+ def _click_in(self, area, target, clicks=1, button='left'):
65
+ area_match = self._locate(area)
66
+ locator = parse_locator(target).with_region(
67
+ Region(*area_match.to_tuple())
68
+ )
69
+ self._click_at(self._locate(locator), clicks, button)
70
+
71
+ @keyword
72
+ @busy_action
73
+ def click_in(self, area_element, target_element):
74
+ """Clicks ``target_element`` searched only inside ``area_element``.
75
+
76
+ Examples:
77
+ | Click In | dialog.png | ok.png |
78
+ | Click In | name:Settings | checkbox.png |
79
+ """
80
+ self._click_in(area_element, target_element)
81
+
82
+ @keyword
83
+ @busy_action
84
+ def double_click_in(self, area_element, target_element):
85
+ """Double clicks ``target_element`` inside ``area_element``.
86
+
87
+ Example:
88
+ | Double Click In | file_list.png | report_icon.png |
89
+ """
90
+ self._click_in(area_element, target_element, clicks=2)
91
+
92
+ @keyword
93
+ @busy_action
94
+ def right_click_in(self, area_element, target_element):
95
+ """Right clicks ``target_element`` inside ``area_element``.
96
+
97
+ Example:
98
+ | Right Click In | file_list.png | report_icon.png |
99
+ """
100
+ self._click_in(area_element, target_element, button='right')
101
+
102
+ @keyword
103
+ @busy_action
104
+ def click_nth(self, element, index: int, sort_by_column: bool = True):
105
+ """Clicks the ``index``-th (0 based) occurrence of ``element``.
106
+
107
+ Matches are sorted by column (left to right, then top to bottom) when
108
+ ``sort_by_column`` is true, otherwise by row (top to bottom, then left
109
+ to right).
110
+
111
+ Examples:
112
+ | Click Nth | checkbox.png | 0 |
113
+ | Click Nth | checkbox.png | 2 | ${FALSE} |
114
+ """
115
+ matches = self.ctx.find_all(
116
+ element, self.ctx.timeout, wait_cursor=True
117
+ )
118
+ if not matches:
119
+ raise AssertionError(
120
+ f"Element '{parse_locator(element).describe()}' not found."
121
+ )
122
+ matches = sort_matches(matches, 'column' if sort_by_column else 'row')
123
+ index = int(index)
124
+ if not 0 <= index < len(matches):
125
+ raise AssertionError(
126
+ f'Index {index} out of range: {len(matches)} matches found.'
127
+ )
128
+ self.ctx.show_action(matches[index])
129
+ self._click_at(matches[index])
130
+
131
+ @keyword
132
+ @busy_action
133
+ def click_on_match(self, match):
134
+ """Clicks a match returned by `Return Match From Region` (no offset).
135
+
136
+ Example:
137
+ | ${match}= | Return Match From Region | ${region} | item.png |
138
+ | Click On Match | ${match} |
139
+ """
140
+ self._click_at(self._locate(match))
141
+
142
+ @keyword
143
+ @busy_action
144
+ def double_click_on_match(self, match):
145
+ """Double clicks a match returned by `Return Match From Region`.
146
+
147
+ Example:
148
+ | Double Click On Match | ${match} |
149
+ """
150
+ self._click_at(self._locate(match), clicks=2)
151
+
152
+ @keyword
153
+ @busy_action
154
+ def click_on_region(self, region):
155
+ """Clicks the center of ``region`` (e.g. from `Get Extended Region From`).
156
+
157
+ Example:
158
+ | ${region}= | Get Extended Region From | label.png | below | 1 |
159
+ | Click On Region | ${region} |
160
+ """
161
+ self._click_at(self._locate(to_region(region)))
162
+
163
+ @keyword
164
+ @busy_action
165
+ def double_click_on_region(self, region):
166
+ """Double clicks the center of ``region``.
167
+
168
+ Example:
169
+ | Double Click On Region | ${region} |
170
+ """
171
+ self._click_at(self._locate(to_region(region)), clicks=2)
172
+
173
+ @keyword
174
+ @busy_action
175
+ def click_region(
176
+ self, coordinates, wait_change: float = 0, timeout: float = 0
177
+ ):
178
+ """Clicks the center of the region ``coordinates`` ``[x, y, w, h]``.
179
+
180
+ If ``wait_change`` is greater than 0, waits up to that many seconds for
181
+ the region content to change after the click and fails otherwise.
182
+ If ``timeout`` is greater than 0 the region is highlighted for that
183
+ many seconds before clicking.
184
+
185
+ Examples:
186
+ | ${coor}= | Create List | 0 | 0 | 100 | 100 |
187
+ | Click Region | ${coor} |
188
+ | Click Region | ${coor} | 2 | | # wait up to 2s for the region to change |
189
+ | Click Region | ${coor} | 0 | 1 | # highlight 1s before clicking |
190
+ """
191
+ region = to_region(coordinates)
192
+ timeout = float(timeout)
193
+ if timeout > 0:
194
+ self.ctx.overlay.highlight(region, timeout)
195
+ time.sleep(timeout)
196
+ before = (
197
+ self.ctx.image.grab(region) if float(wait_change) > 0 else None
198
+ )
199
+ self._click_at(self._locate(region))
200
+ if before is not None:
201
+ changed = self.ctx._poll(
202
+ lambda: self.ctx.image.grab(region).tobytes()
203
+ != before.tobytes(),
204
+ float(wait_change),
205
+ )
206
+ if not changed:
207
+ raise AssertionError(
208
+ f'{region} did not change in {wait_change} seconds.'
209
+ )
210
+
211
+ # ---- drag and drop --------------------------------------------------------
212
+ def _drag(self, sx, sy, tx, ty):
213
+ self._move(sx, sy)
214
+ pyautogui.mouseDown(sx, sy)
215
+ time.sleep(0.1)
216
+ pyautogui.moveTo(tx, ty, duration=max(0.3, self.ctx.move_mouse_delay))
217
+ time.sleep(0.1)
218
+ pyautogui.mouseUp(tx, ty)
219
+
220
+ @keyword
221
+ @busy_action
222
+ def drag_and_drop(self, source_element, target_element):
223
+ """Drags ``source_element`` and drops it on ``target_element``.
224
+
225
+ If ``source_element`` is empty, the last match is dragged.
226
+
227
+ Examples:
228
+ | Drag And Drop | file.png | trash.png |
229
+ | Drag And Drop | name:Item 1 | name:Drop Area |
230
+ | Drag And Drop | ${EMPTY} | trash.png |
231
+ """
232
+ src = self.ctx.resolve_or_last(source_element)
233
+ dst = self._locate(target_element)
234
+ self._drag(*src.target, *dst.target)
235
+
236
+ @keyword
237
+ @busy_action
238
+ def drag_and_drop_by_offset(
239
+ self, source_element, x_offset: int, y_offset: int
240
+ ):
241
+ """Drags ``source_element`` and drops it ``x_offset``/``y_offset`` pixels away.
242
+
243
+ If ``source_element`` is empty, the last match is dragged.
244
+
245
+ Examples:
246
+ | Drag And Drop By Offset | slider.png | 100 | 0 |
247
+ | Drag And Drop By Offset | ${EMPTY} | 0 | -50 |
248
+ """
249
+ src = self.ctx.resolve_or_last(source_element)
250
+ sx, sy = src.target
251
+ self._drag(sx, sy, sx + int(x_offset), sy + int(y_offset))
252
+
253
+ # ---- move / buttons / wheel -------------------------------------------------
254
+ @keyword
255
+ def mouse_move(self, element=''):
256
+ """Moves the mouse pointer to ``element``.
257
+
258
+ If ``element`` is empty, moves to the last match.
259
+
260
+ Examples:
261
+ | Mouse Move | menu.png |
262
+ | Mouse Move | automation_id:btnHelp |
263
+ | Mouse Move | |
264
+ """
265
+ match = self.ctx.resolve_or_last(element)
266
+ self._move(*match.target)
267
+
268
+ @keyword
269
+ def mouse_move_location(self, x: int = 0, y: int = 0):
270
+ """Moves the mouse pointer to the absolute screen coordinate ``x``, ``y``.
271
+
272
+ Example:
273
+ | Mouse Move Location | 20 | 20 |
274
+ """
275
+ self._move(int(x), int(y))
276
+
277
+ @keyword
278
+ def mouse_move_region(self, coordinates, highlight_timeout: float = 0):
279
+ """Moves the mouse pointer to the center of region ``coordinates``.
280
+
281
+ Optionally highlights the region for ``highlight_timeout`` seconds.
282
+
283
+ Example:
284
+ | Mouse Move Region | [20, 20, 20, 20] |
285
+ | Mouse Move Region | ${coordinates} | 2 |
286
+ """
287
+ region = to_region(coordinates)
288
+ if float(highlight_timeout) > 0:
289
+ self.ctx.overlay.highlight(region, float(highlight_timeout))
290
+ self._move(*region.center)
291
+
292
+ @keyword
293
+ @busy_action
294
+ def mouse_down(self, *mouse_buttons):
295
+ """Presses and holds the given mouse buttons (``LEFT``, ``MIDDLE``, ``RIGHT``).
296
+
297
+ Defaults to ``LEFT`` when no button is given.
298
+
299
+ Example:
300
+ | Mouse Move | item.png |
301
+ | Mouse Down | LEFT |
302
+ | Mouse Move | target.png |
303
+ | Mouse Up |
304
+ """
305
+ self._ready()
306
+ for b in mouse_buttons or ('LEFT',):
307
+ btn = _BUTTONS[str(b).upper()]
308
+ pyautogui.mouseDown(button=btn)
309
+ self._held.add(btn)
310
+
311
+ @keyword
312
+ @busy_action
313
+ def mouse_up(self, *mouse_buttons):
314
+ """Releases the given mouse buttons. If empty, releases all held buttons.
315
+
316
+ Example:
317
+ | Mouse Up | LEFT | RIGHT |
318
+ | Mouse Up |
319
+ """
320
+ buttons = (
321
+ [_BUTTONS[str(b).upper()] for b in mouse_buttons]
322
+ or list(self._held)
323
+ or ['left']
324
+ )
325
+ for btn in buttons:
326
+ pyautogui.mouseUp(button=btn)
327
+ self._held.discard(btn)
328
+
329
+ def _wheel(self, steps, element, direction):
330
+ if element not in (None, ''):
331
+ self._move(*self._locate(element).target)
332
+ self._ready()
333
+ for _ in range(int(steps)):
334
+ pyautogui.scroll(direction * 120)
335
+ time.sleep(0.02)
336
+
337
+ @keyword
338
+ @busy_action
339
+ def wheel_down(self, steps: int, element=''):
340
+ """Moves the mouse to ``element`` (optional) and scrolls down ``steps`` notches.
341
+
342
+ Examples:
343
+ | Wheel Down | 5 |
344
+ | Wheel Down | 5 | list.png |
345
+ """
346
+ self._wheel(steps, element, -1)
347
+
348
+ @keyword
349
+ @busy_action
350
+ def wheel_up(self, steps: int, element=''):
351
+ """Moves the mouse to ``element`` (optional) and scrolls up ``steps`` notches.
352
+
353
+ Examples:
354
+ | Wheel Up | 5 |
355
+ | Wheel Up | 5 | automation_id:lstItems |
356
+ """
357
+ self._wheel(steps, element, 1)
@@ -0,0 +1,166 @@
1
+ import unicodedata
2
+
3
+ from robot.api.deco import keyword
4
+
5
+ from ..geometry import Match, Region, to_region
6
+ from .base import KeywordGroup, busy_action
7
+
8
+ _ENGINE = None
9
+
10
+
11
+ def _engine():
12
+ global _ENGINE
13
+ if _ENGINE is None:
14
+ try:
15
+ from rapidocr import RapidOCR
16
+ except ImportError as err:
17
+ raise RuntimeError(
18
+ "OCR requires 'rapidocr' and 'onnxruntime': "
19
+ 'pip install robotframework-desktop[ocr]'
20
+ ) from err
21
+ _ENGINE = RapidOCR()
22
+ return _ENGINE
23
+
24
+
25
+ def _normalize(text: str) -> str:
26
+ decomposed = unicodedata.normalize('NFKD', str(text))
27
+ plain = ''.join(c for c in decomposed if not unicodedata.combining(c))
28
+ return ' '.join(plain.lower().split())
29
+
30
+
31
+ def _ocr_lines(image):
32
+ """Runs OCR and returns ``[(text, x1, y1, x2, y2), ...]`` in reading order."""
33
+ import cv2
34
+ import numpy as np
35
+
36
+ array = cv2.cvtColor(np.array(image.convert('RGB')), cv2.COLOR_RGB2BGR)
37
+ result = _engine()(array)
38
+ boxes = getattr(result, 'boxes', None)
39
+ texts = getattr(result, 'txts', None)
40
+ if boxes is None or texts is None:
41
+ return []
42
+ lines = []
43
+ for box, text in zip(boxes, texts):
44
+ if not str(text).strip():
45
+ continue
46
+ xs = [float(p[0]) for p in box]
47
+ ys = [float(p[1]) for p in box]
48
+ lines.append((str(text).strip(), min(xs), min(ys), max(xs), max(ys)))
49
+ if not lines:
50
+ return []
51
+ height = sorted(l[4] - l[2] for l in lines)[len(lines) // 2] or 1
52
+ return sorted(lines, key=lambda l: (round(l[2] / (height * 0.6)), l[1]))
53
+
54
+
55
+ class OcrKeywords(KeywordGroup):
56
+ def _check(self):
57
+ if not self.ctx.ocr_enabled:
58
+ raise RuntimeError(
59
+ "OCR is disabled. Use 'Set Ocr Text Read true'."
60
+ )
61
+
62
+ def _read(self, region: Region) -> str:
63
+ self._check()
64
+ lines = _ocr_lines(self.ctx.image.grab(region))
65
+ rows, last_y, height = [], None, None
66
+ for text, x1, y1, x2, y2 in lines:
67
+ if last_y is not None and abs(y1 - last_y) <= (height or 0) * 0.6:
68
+ rows[-1].append(text)
69
+ else:
70
+ rows.append([text])
71
+ last_y, height = y1, y2 - y1
72
+ return '\n'.join(' '.join(row) for row in rows).strip()
73
+
74
+ def _find_text(self, text: str, region: Region) -> Match:
75
+ self._check()
76
+ wanted = _normalize(text)
77
+ if not wanted:
78
+ raise ValueError('Text cannot be empty.')
79
+
80
+ def search():
81
+ for line, x1, y1, x2, y2 in _ocr_lines(
82
+ self.ctx.image.grab(region)
83
+ ):
84
+ normalized = _normalize(line)
85
+ start = normalized.find(wanted)
86
+ if start < 0:
87
+ continue
88
+ char_w = (x2 - x1) / max(len(normalized), 1)
89
+ left = x1 + start * char_w
90
+ width = max(len(wanted) * char_w, 1)
91
+ return Match(
92
+ int(region.x + left),
93
+ int(region.y + y1),
94
+ int(round(width)),
95
+ int(round(y2 - y1)),
96
+ source=f'text:{text}',
97
+ )
98
+ return None
99
+
100
+ match = self.ctx._poll(search, self.ctx.timeout)
101
+ if not match:
102
+ raise AssertionError(f"Text '{text}' not found on screen.")
103
+ self.ctx.last_match = match
104
+ return match
105
+
106
+ @keyword
107
+ def set_ocr_text_read(self, ocr_text_read: bool):
108
+ """Enables or disables OCR keywords (enabled by default).
109
+
110
+ Example:
111
+ | Set Ocr Text Read | true |
112
+ """
113
+ self.ctx.ocr_enabled = str(ocr_text_read).lower() in (
114
+ 'true',
115
+ '1',
116
+ 'yes',
117
+ 'on',
118
+ )
119
+
120
+ @keyword
121
+ def get_text(self, element='') -> str:
122
+ """Reads text with OCR (RapidOCR) from ``element`` area or from the
123
+ whole screen.
124
+
125
+ Examples:
126
+ | ${all}= | Get Text |
127
+ | ${txt}= | Get Text | status_area.png |
128
+ | ${txt}= | Get Text | automation_id:lblStatus |
129
+ """
130
+ if element in (None, ''):
131
+ return self._read(self.ctx.search_region())
132
+ match = self.ctx.find(element)
133
+ return self._read(Region(*match.to_tuple()))
134
+
135
+ @keyword
136
+ def read_text_from_region(self, region) -> str:
137
+ """Reads text with OCR from ``region`` ``[x, y, w, h]``.
138
+
139
+ Example:
140
+ | ${region}= | Get Extended Region From Image | label.png | right | 3 |
141
+ | ${text}= | Read Text From Region | ${region} |
142
+ """
143
+ return self._read(to_region(region))
144
+
145
+ @keyword
146
+ @busy_action
147
+ def click_text(self, text: str):
148
+ """Clicks ``text`` found with OCR on the current screen. Matching is
149
+ case and accent insensitive.
150
+
151
+ Example:
152
+ | Click Text | Hello |
153
+ """
154
+ self._click_at(self._find_text(text, self.ctx.screen()))
155
+
156
+ @keyword
157
+ @busy_action
158
+ def region_click_text(self, text: str):
159
+ """Clicks ``text`` found with OCR inside the region of interest
160
+ (see `Set ROI`).
161
+
162
+ Example:
163
+ | Set ROI | [0, 0, 600, 400] |
164
+ | Region Click Text | Hello |
165
+ """
166
+ self._click_at(self._find_text(text, self.ctx.search_region()))