AutoMonkey 0.4.1__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,133 @@
1
+ from time import sleep
2
+
3
+ from tkinter import Tk
4
+ from tkinter import Canvas
5
+
6
+ from pyautogui import size
7
+ from pyautogui import position
8
+
9
+ def track_mouse():
10
+ """Tracks the mouse position and when the mouse stops moving
11
+ for 1 second it prints the position in the terminal
12
+ """
13
+
14
+ cur_pos = ""
15
+ print("Tracking mouse position started")
16
+ print("Press ctrl+c anytime to end tracking")
17
+ try:
18
+ while True:
19
+ if cur_pos != position():
20
+ cur_pos = position()
21
+ print(position())
22
+ sleep(1)
23
+ except KeyboardInterrupt:
24
+ print("Tracking mouse position stopped")
25
+
26
+
27
+ class PositionTracker: # was Toplevel
28
+ """PositionTracker
29
+ """
30
+ def __init__(self, follow_mouse: bool = False):
31
+ self.follow_mouse = follow_mouse
32
+ self.window = Tk() # Was Tk()
33
+ self.window.canvas = None
34
+ self.coords = None
35
+ self.get_coords = False
36
+ self.after = None
37
+
38
+ def start(self, get_coords: bool = False):
39
+ """Take the screenshot
40
+ """
41
+ if self.after:
42
+ self.window.after_cancel(self.after)
43
+ if get_coords:
44
+ self.get_coords = True
45
+ self.window.bind('<Control-Button-1>', lambda e: self.destroy_())
46
+ else:
47
+ self.window.bind('<Escape>', lambda e: self.destroy_())
48
+ self.window.attributes('-fullscreen', True, '-alpha', 0.3)
49
+ self.window.configure(bg='black')
50
+
51
+ self.window.canvas = Canvas(
52
+ self.window,
53
+ width=self.window.winfo_screenwidth(),
54
+ height=self.window.winfo_screenheight(),
55
+ cursor="crosshair"
56
+ )
57
+ self.window.canvas.configure(highlightthickness=0, bg='black')
58
+ self.window.canvas.pack()
59
+
60
+ self.after = self.window.after(1, self._crosshair, None)
61
+ self.window.mainloop()
62
+ if get_coords:
63
+ return self.coords
64
+ return None
65
+
66
+ def _crosshair(self, coords):
67
+ if self.get_coords:
68
+ self.window.canvas.create_text(
69
+ 400,
70
+ 20,
71
+ text="CTRL+Left Click to get the cursor coordinates",
72
+ fill='red',
73
+ font=("Helvetica", 30),
74
+ )
75
+ self.coords = position()
76
+ else:
77
+ x_point, y_point = position()
78
+
79
+ self.window.canvas.delete(coords)
80
+ coords = self._make_coords(coords, x_point, y_point)
81
+
82
+ self.window.after(1, self._crosshair, coords)
83
+
84
+ def _make_coords(self, coords, x_point: int, y_point: int) -> any:
85
+ if coords is None:
86
+ self.window.canvas.create_text(
87
+ 180,
88
+ 20,
89
+ text="Press ESC to exit.",
90
+ fill='red',
91
+ font=("Helvetica", 30),
92
+ )
93
+ if self.follow_mouse:
94
+ coords = self._make_coords_1(x_point, y_point)
95
+ else:
96
+ coords = self._make_coords_2(x_point, y_point)
97
+ return coords
98
+
99
+ def _make_coords_1(self, x_point: int, y_point: int) -> any:
100
+ return self.window.canvas.create_text(
101
+ self._make_coords_1_x(x_point),
102
+ self._make_coords_1_y(x_point, y_point),
103
+ text=f"x={x_point}, y={y_point}",
104
+ fill='red',
105
+ font=("Helvetica", 20) if x_point < size()[0] + 100 else ("Helvetica", 40),
106
+ )
107
+
108
+ def _make_coords_2(self, x_point: int, y_point: int) -> any:
109
+ return self.window.canvas.create_text(
110
+ size()[0] / 2,
111
+ size()[1] / 2,
112
+ text=f"x={x_point}, y={y_point}",
113
+ fill='red',
114
+ font=("Helvetica", 40),
115
+ )
116
+
117
+ @staticmethod
118
+ def _make_coords_1_x(x_point: int) -> int:
119
+ return x_point + 100 if x_point < size()[0] - 200 else x_point - 100 if x_point < size()[0] + 100 else size()[0] / 2
120
+
121
+ @staticmethod
122
+ def _make_coords_1_y(x_point: int, y_point: int) -> int:
123
+ if x_point > size()[0] + 100:
124
+ return size()[1] / 2
125
+ if (y_point < 70 and x_point < 300):
126
+ return y_point + 100
127
+ if y_point < size()[1] - 200:
128
+ return y_point + 20
129
+ return y_point - 100
130
+
131
+ def destroy_(self):
132
+ self.window.after_cancel(self.after)
133
+ self.window.destroy()
@@ -0,0 +1,343 @@
1
+ """Control-level Windows automation based on pywinauto.
2
+
3
+ Unlike the mouse/image functions, these find controls (buttons, edits, menus, ...)
4
+ through the accessibility tree, so they do not depend on screen resolution,
5
+ theme or window position.
6
+
7
+ Every function takes:
8
+ window: title of the top-level window (regex, matched anywhere in the title,
9
+ like focus/close/minimize) or a pywinauto window object.
10
+ control: what to act on inside the window:
11
+ - None: the window itself
12
+ - str: pywinauto "best match" name, e.g. "Save", "SaveButton", "Edit"
13
+ - dict: pywinauto search criteria, e.g. {"title": "Save", "control_type": "Button"}
14
+ backend: "uia" (default, modern apps) or "win32" (old MFC/WinForms apps).
15
+ timeout: seconds to wait for the window/control to appear.
16
+
17
+ Use list_controls(window) to see the names of the controls in a window.
18
+ pywinauto is only available on Windows: pip install pywinauto
19
+ """
20
+ from io import StringIO
21
+ from contextlib import redirect_stdout
22
+
23
+ from .utils import copy
24
+
25
+ _TYPE_KEYS_SPECIAL = "{}+^%~()"
26
+
27
+ _VALID_BACKENDS = ("uia", "win32", None)
28
+
29
+
30
+ def _check_backend(backend: str):
31
+ if backend not in _VALID_BACKENDS:
32
+ raise ValueError(
33
+ f"backend={backend!r} is not valid on Windows. Use backend='uia' (the default) "
34
+ "or 'win32' here; 'atspi' is the Linux backend (automonkey/atspi_funcs.py)."
35
+ )
36
+
37
+
38
+ def _pywinauto():
39
+ try:
40
+ import pywinauto # pylint: disable=import-outside-toplevel
41
+ except ImportError as err:
42
+ raise ImportError(
43
+ "This function needs pywinauto (Windows only): pip install pywinauto"
44
+ ) from err
45
+ return pywinauto
46
+
47
+
48
+ def get_window(window, backend: str = "uia", timeout: float = 10):
49
+ """Find a top-level window and wait until it is visible.
50
+
51
+ Args:
52
+ window (str): title of the window, regex matched anywhere in the title.
53
+ A pywinauto window object is returned as it is.
54
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
55
+ timeout (float, optional): seconds to wait for the window. Defaults to 10.
56
+
57
+ Returns:
58
+ pywinauto WindowSpecification, for anything not covered by these functions.
59
+ """
60
+ _check_backend(backend)
61
+ if not isinstance(window, str):
62
+ return window
63
+ spec = _pywinauto().Desktop(backend=backend).window(title_re=f".*?{window}.*?", found_index=0)
64
+ spec.wait("exists visible", timeout=timeout)
65
+ return spec
66
+
67
+
68
+ def _control_spec(win, control):
69
+ if control is None:
70
+ return win
71
+ if isinstance(control, dict):
72
+ return win.child_window(**control)
73
+ return win.child_window(best_match=control)
74
+
75
+
76
+ def get_control(window, control=None, backend: str = "uia", timeout: float = 10):
77
+ """Find a control inside a window and wait until it exists.
78
+
79
+ Args:
80
+ window (str): title of the window (regex) or a pywinauto window object.
81
+ control (str | dict, optional): best-match name or search criteria.
82
+ None returns the window itself.
83
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
84
+ timeout (float, optional): seconds to wait. Defaults to 10.
85
+
86
+ Returns:
87
+ pywinauto control wrapper
88
+ """
89
+ return _control_spec(get_window(window, backend, timeout), control).wait("exists", timeout=timeout)
90
+
91
+
92
+ def start_app(command: str, backend: str = "uia", timeout: float = 10):
93
+ """Start an application with pywinauto.
94
+
95
+ Args:
96
+ command (str): command line / path of the executable, e.g. "notepad.exe"
97
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
98
+ timeout (float, optional): seconds to wait for the app to be ready. Defaults to 10.
99
+
100
+ Returns:
101
+ pywinauto Application
102
+ """
103
+ _check_backend(backend)
104
+ return _pywinauto().Application(backend=backend).start(command, timeout=timeout)
105
+
106
+
107
+ def click_control(window, control=None, button: str = "left", double: bool = False,
108
+ invoke: bool = False, backend: str = "uia", timeout: float = 10):
109
+ """Click a control.
110
+
111
+ Args:
112
+ window (str): title of the window (regex) or a pywinauto window object.
113
+ control (str | dict, optional): the control to click. None clicks the window.
114
+ button (str, optional): "left", "right" or "middle". Defaults to "left".
115
+ double (bool, optional): double click. Defaults to False.
116
+ invoke (bool, optional): press the control through the accessibility API
117
+ instead of moving the mouse. Works for buttons/menu items even when
118
+ the window is covered. Defaults to False.
119
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
120
+ timeout (float, optional): seconds to wait for the control. Defaults to 10.
121
+ """
122
+ ctrl = get_control(window, control, backend, timeout)
123
+ if invoke:
124
+ if hasattr(ctrl, "invoke"):
125
+ ctrl.invoke()
126
+ else:
127
+ ctrl.click()
128
+ return
129
+ ctrl.click_input(button=button, double=double)
130
+
131
+
132
+ def double_click_control(window, control=None, backend: str = "uia", timeout: float = 10):
133
+ """Double click a control. See click_control."""
134
+ click_control(window, control, double=True, backend=backend, timeout=timeout)
135
+
136
+
137
+ def right_click_control(window, control=None, backend: str = "uia", timeout: float = 10):
138
+ """Right click a control. See click_control."""
139
+ click_control(window, control, button="right", backend=backend, timeout=timeout)
140
+
141
+
142
+ def _escape_keys(text: str) -> str:
143
+ return "".join(f"{{{char}}}" if char in _TYPE_KEYS_SPECIAL else char for char in text)
144
+
145
+
146
+ def type_in_control(window, control=None, text: str = "", literal: bool = True, pause: float = None,
147
+ backend: str = "uia", timeout: float = 10):
148
+ """Focus a control and type text into it with simulated key presses.
149
+
150
+ Args:
151
+ window (str): title of the window (regex) or a pywinauto window object.
152
+ control (str | dict, optional): the control to type into. None types into the window.
153
+ text (str): the text to type.
154
+ literal (bool, optional): type the text as it is. If False the text uses
155
+ pywinauto key syntax: {ENTER}, {TAB}, ^a (ctrl+a), %f (alt+f), +x (shift+x).
156
+ Defaults to True.
157
+ pause (float, optional): seconds between key presses. Increase it if a slow
158
+ or just started app loses characters. Defaults to pywinauto's 0.01.
159
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
160
+ timeout (float, optional): seconds to wait for the control. Defaults to 10.
161
+ """
162
+ ctrl = get_control(window, control, backend, timeout)
163
+ keys = _escape_keys(text) if literal else text
164
+ ctrl.type_keys(keys, pause=pause, with_spaces=True, with_tabs=True, with_newlines=True, set_foreground=True)
165
+
166
+
167
+ def set_control_text(window, control=None, text: str = "", backend: str = "uia", timeout: float = 10):
168
+ """Replace the text of an edit control directly, without typing.
169
+
170
+ Args:
171
+ window (str): title of the window (regex) or a pywinauto window object.
172
+ control (str | dict, optional): the edit control.
173
+ text (str): the new text.
174
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
175
+ timeout (float, optional): seconds to wait for the control. Defaults to 10.
176
+ """
177
+ ctrl = get_control(window, control, backend, timeout)
178
+ if hasattr(ctrl, "set_edit_text"):
179
+ ctrl.set_edit_text(text)
180
+ else: # generic uia control, e.g. a Document: use the Value pattern
181
+ ctrl.iface_value.SetValue(text)
182
+
183
+
184
+ def get_control_text(window, control=None, backend: str = "uia", timeout: float = 10) -> str:
185
+ """Read the text of a control. The text is also copied to the clipboard.
186
+
187
+ Args:
188
+ window (str): title of the window (regex) or a pywinauto window object.
189
+ control (str | dict, optional): the control. None reads the window title.
190
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
191
+ timeout (float, optional): seconds to wait for the control. Defaults to 10.
192
+
193
+ Returns:
194
+ str: the text of the control
195
+ """
196
+ ctrl = get_control(window, control, backend, timeout)
197
+ text = None
198
+ if hasattr(ctrl, "get_value"): # uia edits: window_text() is only the label
199
+ try:
200
+ text = ctrl.get_value()
201
+ except Exception: # pylint: disable=broad-except
202
+ text = None # control has no Value pattern
203
+ if text is None:
204
+ texts = [_ for _ in ctrl.texts() if _]
205
+ text = "\n".join(texts) if texts else ctrl.window_text()
206
+ copy(text)
207
+ return text
208
+
209
+
210
+ def select_menu(window, path: str, backend: str = "uia", timeout: float = 10):
211
+ """Select a menu item.
212
+
213
+ Args:
214
+ window (str): title of the window (regex) or a pywinauto window object.
215
+ path (str): menu path separated by "->", e.g. "File->Save As..."
216
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
217
+ timeout (float, optional): seconds to wait for the window. Defaults to 10.
218
+ """
219
+ win = get_control(window, None, backend, timeout)
220
+ try:
221
+ win.menu_select(path)
222
+ except AttributeError: # uia finds only a menu bar that is a direct child of the window
223
+ _walk_menu(win, path, timeout)
224
+
225
+
226
+ def _find_menu_item(win, name: str):
227
+ for item in win.descendants(control_type="MenuItem"):
228
+ if item.window_text().replace("&", "").strip().lower() == name.lower():
229
+ return item
230
+ raise LookupError(f"Menu item not found: {name}")
231
+
232
+
233
+ def _walk_menu(win, path: str, timeout: float):
234
+ """Open each menu of the path in turn; submenus show up as descendants of the window"""
235
+ wait_until_passes = _pywinauto().timings.wait_until_passes
236
+ names = [_.strip() for _ in path.split("->")]
237
+ for i, name in enumerate(names):
238
+ item = wait_until_passes(timeout, 0.1, _find_menu_item, LookupError, win, name)
239
+ try:
240
+ if i == len(names) - 1:
241
+ item.invoke()
242
+ else:
243
+ item.expand()
244
+ except Exception: # pylint: disable=broad-except
245
+ item.click_input() # item without Invoke / ExpandCollapse pattern
246
+
247
+
248
+ def select_item(window, control, item, backend: str = "uia", timeout: float = 10):
249
+ """Select an item in a combo box, list box, list view or tab control.
250
+
251
+ Args:
252
+ window (str): title of the window (regex) or a pywinauto window object.
253
+ control (str | dict): the combo box / list control.
254
+ item (str | int): text or index of the item.
255
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
256
+ timeout (float, optional): seconds to wait for the control. Defaults to 10.
257
+ """
258
+ get_control(window, control, backend, timeout).select(item)
259
+
260
+
261
+ def check_control(window, control, checked: bool = True, backend: str = "uia", timeout: float = 10):
262
+ """Check or uncheck a check box / toggle button.
263
+
264
+ Args:
265
+ window (str): title of the window (regex) or a pywinauto window object.
266
+ control (str | dict): the check box.
267
+ checked (bool, optional): wanted state. Defaults to True.
268
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
269
+ timeout (float, optional): seconds to wait for the control. Defaults to 10.
270
+ """
271
+ ctrl = get_control(window, control, backend, timeout)
272
+ if hasattr(ctrl, "get_toggle_state"): # uia
273
+ if bool(ctrl.get_toggle_state()) != checked:
274
+ ctrl.toggle()
275
+ elif checked:
276
+ ctrl.check()
277
+ else:
278
+ ctrl.uncheck()
279
+
280
+
281
+ def uncheck_control(window, control, backend: str = "uia", timeout: float = 10):
282
+ """Uncheck a check box / toggle button. See check_control."""
283
+ check_control(window, control, False, backend, timeout)
284
+
285
+
286
+ def wait_control(window, control=None, state: str = "exists visible",
287
+ backend: str = "uia", timeout: float = 10):
288
+ """Wait until a control reaches a state.
289
+
290
+ Args:
291
+ window (str): title of the window (regex) or a pywinauto window object.
292
+ control (str | dict, optional): the control. None waits for the window.
293
+ state (str, optional): space separated: exists, visible, enabled, ready, active.
294
+ Defaults to "exists visible".
295
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
296
+ timeout (float, optional): seconds to wait. Defaults to 10.
297
+
298
+ Raises:
299
+ pywinauto.timings.TimeoutError: if the state is not reached in time.
300
+ """
301
+ return _control_spec(get_window(window, backend, timeout), control).wait(state, timeout=timeout)
302
+
303
+
304
+ def control_exists(window, control=None, backend: str = "uia", timeout: float = 0) -> bool:
305
+ """Check if a window / control exists.
306
+
307
+ Args:
308
+ window (str): title of the window (regex).
309
+ control (str | dict, optional): the control. None checks only the window.
310
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
311
+ timeout (float, optional): seconds to keep looking. Defaults to 0.
312
+
313
+ Returns:
314
+ bool: True if found
315
+ """
316
+ pywinauto = _pywinauto()
317
+ try:
318
+ get_control(window, control, backend, timeout)
319
+ except (pywinauto.timings.TimeoutError, pywinauto.findwindows.ElementNotFoundError):
320
+ return False
321
+ return True
322
+
323
+
324
+ def list_controls(window, depth: int = None, backend: str = "uia", timeout: float = 10) -> str:
325
+ """Print the controls of a window with the names usable as `control`.
326
+
327
+ Args:
328
+ window (str): title of the window (regex) or a pywinauto window object.
329
+ depth (int, optional): how many levels deep to go. Defaults to all.
330
+ backend (str, optional): "uia" or "win32". Defaults to "uia".
331
+ timeout (float, optional): seconds to wait for the window. Defaults to 10.
332
+
333
+ Returns:
334
+ str: the printed control tree
335
+ """
336
+ win = get_window(window, backend, timeout)
337
+ out = StringIO()
338
+ with redirect_stdout(out):
339
+ win.print_control_identifiers(depth=depth)
340
+ tree = out.getvalue()
341
+ print(tree)
342
+ return tree
343
+
automonkey/utils.py ADDED
@@ -0,0 +1,169 @@
1
+ from time import sleep
2
+
3
+ from clipboard import copy as copy1
4
+ from pyperclip import copy as copy2
5
+
6
+ from clipboard import paste
7
+
8
+ from pyautogui import click
9
+ from pyautogui import moveTo
10
+ from pyautogui import mouseUp
11
+ from pyautogui import mouseDown
12
+ from pyautogui import scroll as scrollup
13
+ from pyautogui import hscroll as scrollright
14
+ from .key_funcs import hotkey as keys2 # keyboard-layout aware
15
+
16
+ from .img_funcs import is_on_screen
17
+
18
+ def copy(text: str):
19
+ """Copy text to clipboard using two functions, falling back to the next one on
20
+ failure - e.g. pyperclip's auto-detected "gtk" backend raises AttributeError
21
+ (not ImportError, so pyperclip's own guard doesn't catch it) once gi.repository
22
+ has been imported in-process, which the AT-SPI backend (atspi_funcs.py) does.
23
+
24
+ Args:
25
+ text (str): text to be copied to clipboard
26
+ """
27
+ for copy_fn in (copy1, copy2):
28
+ try:
29
+ copy_fn(text)
30
+ except Exception: # pylint: disable=broad-except
31
+ pass
32
+
33
+
34
+ def clear_clipboard():
35
+ """Try to clear the clipboard by copying an empty string
36
+ Might not work in all cases, for example: in environments
37
+ with shared/multiple clipboards.
38
+ """
39
+ for _ in range(0, 10):
40
+ copy('')
41
+
42
+
43
+ def copy_from_to(*args) -> str:
44
+ """This function will copy text from one point to another.
45
+ Args:
46
+ point1 (PyAutoGUI point): PyAutoGUI start point (from)
47
+ point2 (PyAutoGUI point): PyAutoGUI end point (to)
48
+
49
+ Returns:
50
+ str: The copied text
51
+ """
52
+ if len(args) == 1:
53
+ assert isinstance(args[0][0], tuple), "The argument must be a tuple. First point tuple"
54
+ assert isinstance(args[0][1], tuple), "The argument must be a tuple. Second point tuple"
55
+ assert isinstance(args[0][0][0], int), "The argument must be an integer. First point X"
56
+ assert isinstance(args[0][0][1], int), "The argument must be an integer. First point Y"
57
+ assert isinstance(args[0][1][0], int), "The argument must be an integer. Second point X"
58
+ assert isinstance(args[0][1][1], int), "The argument must be an integer. Second point Y"
59
+ point1 = (args[0][0][0], args[0][0][1])
60
+ point2 = (args[0][1][0], args[0][1][1])
61
+ elif len(args) == 2:
62
+ assert isinstance(args[0], tuple), "The argument must be a tuple. First point tuple"
63
+ assert isinstance(args[1], tuple), "The argument must be a tuple. Second point tuple"
64
+ assert isinstance(args[0][0], int), "The argument must be an integer. First point X"
65
+ assert isinstance(args[0][1], int), "The argument must be an integer. First point Y"
66
+ assert isinstance(args[1][0], int), "The argument must be an integer. Second point X"
67
+ assert isinstance(args[1][1], int), "The argument must be an integer. Second point Y"
68
+ point1 = args[0]
69
+ point2 = args[1]
70
+
71
+ mouseDown(point1)
72
+ moveTo(point2)
73
+ mouseUp()
74
+ clear_clipboard()
75
+
76
+ while str(paste()) == '':
77
+ keys2('ctrl', 'c')
78
+
79
+ copied = paste()
80
+ print(copied)
81
+ copy(copied)
82
+ return copied
83
+
84
+
85
+ def copy_from(point) -> str:
86
+ """This function will copy text from one point to the end of line.
87
+ This function uses select all functionality (ctrl+a) and as such
88
+ it should be used only when you are sure ctrl+a will select only
89
+ the content you want.
90
+ Args:
91
+ point (PyAutoGUI point): PyAutoGUI start point (from)
92
+ Returns:
93
+ [string]: The copied text.
94
+ """
95
+
96
+ clear_clipboard()
97
+ click(point)
98
+ sleep(0.2)
99
+ keys2('ctrl', 'a')
100
+ sleep(0.2)
101
+
102
+ while str(paste()) == '':
103
+ keys2('ctrl', 'c')
104
+
105
+ copied = paste()
106
+ copy(copied)
107
+
108
+ return copied
109
+
110
+ def pastetext(text: str):
111
+ """Copy the text and paste it in the applicable place
112
+ This type of text output can be used when write or type are not capable
113
+ of writting special characters. For example German or Chinese characters.
114
+ In those cases a simple solution would be to copy the text and just paste it in the
115
+ where needed.
116
+
117
+ Args:
118
+ text (str): text to be output
119
+ """
120
+
121
+ temp_clipboard = paste()
122
+ while paste() != text:
123
+ copy(text)
124
+ keys2('ctrl', 'v')
125
+ copy(temp_clipboard)
126
+
127
+
128
+ def scrolldown(clicks: int):
129
+ """Scroll down a given number of clicks
130
+ Note: You have to select the scrollable area first
131
+
132
+ Args:
133
+ clicks (int): number of clicks
134
+ """
135
+ scrollup(-clicks)
136
+
137
+
138
+ def scrollleft(clicks: int):
139
+ """Scroll left a given number of clicks
140
+ Note: You have to select the scrollable area first
141
+
142
+ Args:
143
+ clicks (int): number of clicks
144
+ """
145
+ scrollright(-clicks)
146
+
147
+
148
+ def waitwhile(img: str):
149
+ """While an image is on screen wait
150
+ For example a loading window.
151
+
152
+ Args:
153
+ img (str): image location path + name
154
+ """
155
+
156
+ while is_on_screen(img):
157
+ sleep(0.1)
158
+
159
+
160
+ def waituntil(img: str):
161
+ """Wait until an image appears on screen
162
+ For example wait for a software to start
163
+
164
+ Args:
165
+ img (str): image location path + name
166
+ """
167
+
168
+ while not is_on_screen(img):
169
+ sleep(0.1)