From 8968c9b35c031c8f47043234593bf556a0bc0202 Mon Sep 17 00:00:00 2001 From: JeffreyChen Date: Sun, 5 Apr 2026 04:22:01 +0800 Subject: [PATCH] Update docs Update docs --- .idea/AutoControl.iml | 3 +- .idea/workspace.xml | 18 +- .readthedocs.yaml | 2 +- README.md | 1 + docs/requirements.txt | 3 +- docs/source/API/api_index.rst | 64 +- docs/source/API/special/keyboard_keys.rst | 708 +++++------------- docs/source/API/special/mouse_keys.rst | 116 ++- docs/source/API/utils/callback.rst | 55 +- docs/source/API/utils/critical_exit.rst | 94 +-- docs/source/API/utils/executor.rst | 84 ++- docs/source/API/utils/file.rst | 42 +- docs/source/API/utils/generate_report.rst | 95 ++- docs/source/API/utils/package_manager.rst | 157 ++-- docs/source/API/utils/scheduler.rst | 245 +++--- docs/source/API/utils/socket_server.rst | 105 ++- docs/source/API/wrapper/image.rst | 114 +-- docs/source/API/wrapper/keyboard.rst | 117 ++- docs/source/API/wrapper/mouse.rst | 203 +++-- docs/source/API/wrapper/record.rst | 36 +- docs/source/API/wrapper/screen.rst | 38 +- .../callback_function_doc.rst | 107 +-- docs/source/Eng/doc/cli/cli_doc.rst | 55 +- .../doc/create_project/create_project_doc.rst | 55 +- .../doc/critical_exit/critical_exit_doc.rst | 82 +- .../generate_report/generate_report_doc.rst | 216 ++---- docs/source/Eng/doc/image/image_doc.rst | 105 ++- .../Eng/doc/installation/installation_doc.rst | 75 +- docs/source/Eng/doc/keyboard/keyboard_doc.rst | 95 ++- .../keyword_and_executor_doc.rst | 114 ++- docs/source/Eng/doc/mouse/mouse_doc.rst | 81 +- docs/source/Eng/doc/record/record_doc.rst | 60 +- .../Eng/doc/scheduler/scheduler_doc.rst | 88 ++- docs/source/Eng/doc/screen/screen_doc.rst | 42 +- .../doc/socket_driver/socket_driver_doc.rst | 82 +- docs/source/Eng/eng_index.rst | 38 +- .../callback_function_doc.rst | 100 ++- docs/source/Zh/doc/cli/cli_doc.rst | 53 +- .../doc/create_project/create_project_doc.rst | 54 +- .../doc/critical_exit/critical_exit_doc.rst | 79 +- .../generate_report/generate_report_doc.rst | 214 ++---- docs/source/Zh/doc/image/image_doc.rst | 105 ++- .../Zh/doc/installation/installation_doc.rst | 75 +- docs/source/Zh/doc/keyboard/keyboard_doc.rst | 95 ++- .../keyword_and_executor_doc.rst | 113 ++- docs/source/Zh/doc/mouse/mouse_doc.rst | 80 +- docs/source/Zh/doc/record/record_doc.rst | 60 +- .../source/Zh/doc/scheduler/scheduler_doc.rst | 90 ++- docs/source/Zh/doc/screen/screen_doc.rst | 42 +- .../doc/socket_driver/socket_driver_doc.rst | 78 +- docs/source/Zh/zh_index.rst | 38 +- docs/source/conf.py | 46 +- docs/source/getting_started/installation.rst | 106 +++ docs/source/getting_started/quickstart.rst | 124 +++ docs/source/index.rst | 64 +- 55 files changed, 2994 insertions(+), 2217 deletions(-) create mode 100644 docs/source/getting_started/installation.rst create mode 100644 docs/source/getting_started/quickstart.rst diff --git a/.idea/AutoControl.iml b/.idea/AutoControl.iml index 0365335..d8b726a 100644 --- a/.idea/AutoControl.iml +++ b/.idea/AutoControl.iml @@ -4,8 +4,9 @@ + - + diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 6744968..3d60252 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,10 +5,7 @@ - - - - + diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 25cb707..d65bbef 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,7 +9,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.11" + python: "3.12" # Build documentation in the "docs/" directory with Sphinx sphinx: diff --git a/README.md b/README.md index 5c678c9..3f84e90 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![PyPI](https://img.shields.io/pypi/v/je_auto_control)](https://pypi.org/project/je_auto_control/) [![Python](https://img.shields.io/pypi/pyversions/je_auto_control)](https://pypi.org/project/je_auto_control/) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) +[![Documentation](https://readthedocs.org/projects/autocontrol/badge/?version=latest)](https://autocontrol.readthedocs.io/en/latest/?badge=latest) **AutoControl** is a cross-platform Python GUI automation framework providing mouse control, keyboard input, image recognition, screen capture, action scripting, and report generation — all through a unified API that works on Windows, macOS, and Linux (X11). diff --git a/docs/requirements.txt b/docs/requirements.txt index 4170c03..3884022 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1,2 @@ -sphinx-rtd-theme \ No newline at end of file +sphinx-rtd-theme +sphinx>=7.0 diff --git a/docs/source/API/api_index.rst b/docs/source/API/api_index.rst index ae4a946..042a5ff 100644 --- a/docs/source/API/api_index.rst +++ b/docs/source/API/api_index.rst @@ -1,21 +1,47 @@ -AutoControl API Documentation ----- +================= +API Reference +================= + +Complete API documentation for all AutoControl modules. + +Wrapper API +=========== + +Platform-agnostic high-level functions. + +.. toctree:: + :maxdepth: 2 + + wrapper/mouse + wrapper/keyboard + wrapper/image + wrapper/screen + wrapper/record + +Utils API +========= + +Utility modules for scripting, reporting, and more. .. toctree:: - :maxdepth: 4 - - wrapper/image.rst - wrapper/keyboard.rst - wrapper/mouse.rst - wrapper/record.rst - wrapper/screen.rst - utils/callback.rst - utils/critical_exit.rst - utils/executor.rst - utils/scheduler.rst - utils/file.rst - utils/generate_report.rst - utils/package_manager.rst - utils/socket_server.rst - special/keyboard_keys.rst - special/mouse_keys.rst + :maxdepth: 2 + + utils/executor + utils/callback + utils/generate_report + utils/scheduler + utils/file + utils/package_manager + utils/socket_server + utils/critical_exit + +Key Reference +============= + +Platform-specific key code tables. + +.. toctree:: + :maxdepth: 2 + + special/keyboard_keys + special/mouse_keys diff --git a/docs/source/API/special/keyboard_keys.rst b/docs/source/API/special/keyboard_keys.rst index cb1d6e4..dd7cc67 100644 --- a/docs/source/API/special/keyboard_keys.rst +++ b/docs/source/API/special/keyboard_keys.rst @@ -1,542 +1,184 @@ -Keyboard keys API ----- +============= +Keyboard Keys +============= -* Windows +Keyboard key names available per platform. Use these names as the ``keycode`` parameter +in keyboard functions. -.. code-block:: python +.. note:: - keyboard_keys_table = { - "absolute": win32_ABSOLUTE, - "eventf_extendedkey": win32_EventF_EXTENDEDKEY, - "eventf_keyup": win32_EventF_KEYUP, - "eventf_scancode": win32_EventF_SCANCODE, - "eventf_unicode": win32_EventF_UNICODE, - "hwheel": win32_HWHEEL, - "leftdown": win32_LEFTDOWN, - "leftup": win32_LEFTUP, - "middledown": win32_MIDDLEDOWN, - "middleup": win32_MIDDLEUP, - "move": win32_MOVE, - "rightdown": win32_RIGHTDOWN, - "rightup": win32_RIGHTUP, - "accept": win32_VK_ACCEPT, - "add": win32_VK_ADD, - "apps": win32_VK_APPS, - "back": win32_VK_BACK, - "browser_back": win32_VK_BROWSER_BACK, - "browser_favorites": win32_VK_BROWSER_FAVORITES, - "browser_forward": win32_VK_BROWSER_FORWARD, - "browser_refresh": win32_VK_BROWSER_REFRESH, - "browser_search": win32_VK_BROWSER_SEARCH, - "browser_stop": win32_VK_BROWSER_STOP, - "cancel": win32_VK_CANCEL, - "capital": win32_VK_CAPITAL, - "clear": win32_VK_CLEAR, - "control": win32_VK_CONTROL, - "convert": win32_VK_CONVERT, - "decimal": win32_VK_DECIMAL, - "delete": win32_VK_DELETE, - "divide": win32_VK_DIVIDE, - "vk_down": win32_VK_DOWN, - "end": win32_VK_END, - "escape": win32_VK_ESCAPE, - "execute": win32_VK_EXECUTE, - "f1": win32_VK_F1, - "f2": win32_VK_F2, - "f3": win32_VK_F3, - "f4": win32_VK_F4, - "f5": win32_VK_F5, - "f6": win32_VK_F6, - "f7": win32_VK_F7, - "f8": win32_VK_F8, - "f9": win32_VK_F9, - "f10": win32_VK_F10, - "f11": win32_VK_F11, - "f12": win32_VK_F12, - "f13": win32_VK_F13, - "f14": win32_VK_F14, - "f15": win32_VK_F15, - "f16": win32_VK_F16, - "f17": win32_VK_F17, - "f18": win32_VK_F18, - "f19": win32_VK_F19, - "f20": win32_VK_F20, - "f21": win32_VK_F21, - "f22": win32_VK_F22, - "f23": win32_VK_F23, - "f24": win32_VK_F24, - "final": win32_VK_FINAL, - "hanja": win32_VK_HANJA, - "help": win32_VK_HELP, - "home": win32_VK_HOME, - "ime_off": win32_VK_IME_OFF, - "ime_on": win32_VK_IME_ON, - "insert": win32_VK_INSERT, - "junja": win32_VK_JUNJA, - "kana": win32_VK_KANA, - "launch_app1": win32_VK_LAUNCH_APP1, - "LAUNCH_APP2": win32_VK_LAUNCH_APP2, - "launch_mail": win32_VK_LAUNCH_MAIL, - "launch_media_select": win32_VK_LAUNCH_MEDIA_SELECT, - "lbutton": win32_VK_LBUTTON, - "lcontrol": win32_VK_LCONTROL, - "left": win32_VK_LEFT, - "lmenu": win32_VK_LMENU, - "lshift": win32_VK_LSHIFT, - "lwin": win32_VK_LWIN, - "mbutton": win32_VK_MBUTTON, - "media_next_track": win32_VK_MEDIA_NEXT_TRACK, - "media_play_pause": win32_VK_MEDIA_PLAY_PAUSE, - "media_prev_track": win32_VK_MEDIA_PREV_TRACK, - "media_stop": win32_VK_MEDIA_STOP, - "modechange": win32_VK_MODECHANGE, - "multiply": win32_VK_MULTIPLY, - "menu": win32_VK_Menu, - "next": win32_VK_NEXT, - "nonconvert": win32_VK_NONCONVERT, - "numlock": win32_VK_NUMLOCK, - "num0": win32_VK_NUMPAD0, - "num1": win32_VK_NUMPAD1, - "num2": win32_VK_NUMPAD2, - "num3": win32_VK_NUMPAD3, - "num4": win32_VK_NUMPAD4, - "num5": win32_VK_NUMPAD5, - "num6": win32_VK_NUMPAD6, - "num7": win32_VK_NUMPAD7, - "num8": win32_VK_NUMPAD8, - "num9": win32_VK_NUMPAD9, - "pause": win32_VK_PAUSE, - "print": win32_VK_PRINT, - "prior": win32_VK_PRIOR, - "rbutton": win32_VK_RBUTTON, - "rcontrol": win32_VK_RCONTROL, - "return": win32_VK_RETURN, - "right": win32_VK_RIGHT, - "rmenu": win32_VK_RMENU, - "rshift": win32_VK_RSHIFT, - "rwin": win32_VK_RWIN, - "scroll": win32_VK_SCROLL, - "select": win32_VK_SELECT, - "separator": win32_VK_SEPARATOR, - "shift": win32_VK_SHIFT, - "sleep": win32_VK_SLEEP, - "snapshot": win32_VK_SNAPSHOT, - "space": win32_VK_SPACE, - "subtract": win32_VK_SUBTRACT, - "tab": win32_VK_TAB, - "up": win32_VK_UP, - "volume_down": win32_VK_VOLUME_DOWN, - "volume_mute": win32_VK_VOLUME_MUTE, - "volume_up": win32_VK_VOLUME_UP, - "vk_xbutton1": win32_VK_XBUTTON1, - "vk_xbutton2": win32_VK_XBUTTON2, - "xbutton1": win32_XBUTTON1, - "xbutton2": win32_XBUTTON2, - "vktovsc": win32_VkToVSC, - "wheel": win32_WHEEL, - "down": win32_DOWN, - "xup": win32_XUP, - "0": win32_key0, - "1": win32_key1, - "2": win32_key2, - "3": win32_key3, - "4": win32_key4, - "5": win32_key5, - "6": win32_key6, - "7": win32_key7, - "8": win32_key8, - "9": win32_key9, - "A": win32_keyA, - "a": win32_keyA, - "B": win32_keyB, - "b": win32_keyB, - "C": win32_keyC, - "c": win32_keyC, - "D": win32_keyD, - "d": win32_keyD, - "E": win32_keyE, - "e": win32_keyE, - "F": win32_keyF, - "f": win32_keyF, - "G": win32_keyG, - "g": win32_keyG, - "H": win32_keyH, - "h": win32_keyH, - "I": win32_keyI, - "i": win32_keyI, - "J": win32_keyJ, - "j": win32_keyJ, - "K": win32_keyK, - "k": win32_keyK, - "L": win32_keyL, - "l": win32_keyL, - "M": win32_keyM, - "m": win32_keyM, - "N": win32_keyN, - "n": win32_keyN, - "O": win32_keyO, - "o": win32_keyO, - "P": win32_keyP, - "p": win32_keyP, - "Q": win32_keyQ, - "q": win32_keyQ, - "R": win32_keyR, - "r": win32_keyR, - "S": win32_keyS, - "s": win32_keyS, - "T": win32_keyT, - "t": win32_keyT, - "U": win32_keyU, - "u": win32_keyU, - "V": win32_keyV, - "v": win32_keyV, - "W": win32_keyW, - "w": win32_keyW, - "X": win32_keyX, - "x": win32_keyX, - "Y": win32_keyY, - "y": win32_keyY, - "Z": win32_keyZ, - "z": win32_keyZ, - } + Key names are **platform-specific**. Always check the table for your target platform. + Use ``keys_table`` and ``get_special_table()`` at runtime to get the exact keys + available on the current system. -* Linux +Windows +======= -.. code-block:: python +Common keys: - keyboard_keys_table = { - "backspace": x11_linux_key_backspace, - "\b": x11_linux_key_slash_b, - "tab": x11_linux_key_tab, - "enter": x11_linux_key_enter, - "return": x11_linux_key_return, - "shift": x11_linux_key_shift, - "ctrl": x11_linux_key_ctrl, - "alt": x11_linux_key_alt, - "pause": x11_linux_key_pause, - "capslock": x11_linux_key_capslock, - "esc": x11_linux_key_esc, - "pgup": x11_linux_key_pgup, - "pgdn": x11_linux_key_pgdn, - "pageup": x11_linux_key_pageup, - "pagedown": x11_linux_key_pagedown, - "end": x11_linux_key_end, - "home": x11_linux_key_home, - "left": x11_linux_key_left, - "up": x11_linux_key_up, - "right": x11_linux_key_right, - "down": x11_linux_key_down, - "select": x11_linux_key_select, - "print": x11_linux_key_print, - "execute": x11_linux_key_execute, - "prtsc": x11_linux_key_prtsc, - "prtscr": x11_linux_key_prtscr, - "prntscrn": x11_linux_key_prntscrn, - "insert": x11_linux_key_insert, - "del": x11_linux_key_del, - "delete": x11_linux_key_delete, - "help": x11_linux_key_help, - "win": x11_linux_key_win, - "winleft": x11_linux_key_winleft, - "winright": x11_linux_key_winright, - "apps": x11_linux_key_apps, - "num0": x11_linux_key_num0, - "num1": x11_linux_key_num1, - "num2": x11_linux_key_num2, - "num3": x11_linux_key_num3, - "num4": x11_linux_key_num4, - "num5": x11_linux_key_num5, - "num6": x11_linux_key_num6, - "num7": x11_linux_key_num7, - "num8": x11_linux_key_num8, - "num9": x11_linux_key_num9, - "multiply": x11_linux_key_multiply, - "add": x11_linux_key_add, - "separator": x11_linux_key_separator, - "subtract": x11_linux_key_subtract, - "decimal": x11_linux_key_decimal, - "divide": x11_linux_key_divide, - "f1": x11_linux_key_f1, - "f2": x11_linux_key_f2, - "f3": x11_linux_key_f3, - "f4": x11_linux_key_f4, - "f5": x11_linux_key_f5, - "f6": x11_linux_key_f6, - "f7": x11_linux_key_f7, - "f8": x11_linux_key_f8, - "f9": x11_linux_key_f9, - "f10": x11_linux_key_f10, - "f11": x11_linux_key_f11, - "f12": x11_linux_key_f12, - "f13": x11_linux_key_f13, - "f14": x11_linux_key_f14, - "f15": x11_linux_key_f15, - "f16": x11_linux_key_f16, - "f17": x11_linux_key_f17, - "f18": x11_linux_key_f18, - "f19": x11_linux_key_f19, - "f20": x11_linux_key_f20, - "f21": x11_linux_key_f21, - "f22": x11_linux_key_f22, - "f23": x11_linux_key_f23, - "f24": x11_linux_key_f24, - "numlock": x11_linux_key_numlock, - "scrolllock": x11_linux_key_scrolllock, - "shiftleft": x11_linux_key_shiftleft, - "shiftright": x11_linux_key_shiftright, - "ctrlleft": x11_linux_key_ctrlleft, - "ctrlright": x11_linux_key_ctrlright, - "altleft": x11_linux_key_altleft, - "altright": x11_linux_key_altright, - "space": x11_linux_key_space, - "\n": x11_linux_key_newline_n, - "\r": x11_linux_key_newline_r, - "\t": x11_linux_key_newline_t, - "!": x11_linux_key_exclam, - "#": x11_linux_key_numbersign, - "%": x11_linux_key_percent, - "$": x11_linux_key_dollar, - "&": x11_linux_key_ampersand, - '"': x11_linux_key_quotedbl, - "'": x11_linux_key_apostrophe, - "(": x11_linux_key_parenleft, - ")": x11_linux_key_parenright, - "*": x11_linux_key_asterisk, - "=": x11_linux_key_equal, - "+": x11_linux_key_plus, - ",": x11_linux_key_comma, - "-": x11_linux_key_minus, - ".": x11_linux_key_period, - "/": x11_linux_key_slash, - ":": x11_linux_key_colon, - ";": x11_linux_key_semicolon, - "<": x11_linux_key_less, - ">": x11_linux_key_greater, - "?": x11_linux_key_question, - "@": x11_linux_key_at, - "[": x11_linux_key_bracketleft, - "]": x11_linux_key_bracketright, - "\\": x11_linux_key_backslash, - "^": x11_linux_key_asciicircum, - "_": x11_linux_key_underscore, - "`": x11_linux_key_grave, - "{": x11_linux_key_braceleft, - "|": x11_linux_key_bar, - "}": x11_linux_key_braceright, - "~": x11_linux_key_asciitilde, - "a": x11_linux_key_a, - "b": x11_linux_key_b, - "c": x11_linux_key_c, - "d": x11_linux_key_d, - "e": x11_linux_key_e, - "f": x11_linux_key_f, - "g": x11_linux_key_g, - "h": x11_linux_key_h, - "i": x11_linux_key_i, - "j": x11_linux_key_j, - "k": x11_linux_key_k, - "l": x11_linux_key_l, - "m": x11_linux_key_m, - "n": x11_linux_key_n, - "o": x11_linux_key_o, - "p": x11_linux_key_p, - "q": x11_linux_key_q, - "r": x11_linux_key_r, - "s": x11_linux_key_s, - "t": x11_linux_key_t, - "u": x11_linux_key_u, - "v": x11_linux_key_v, - "w": x11_linux_key_w, - "x": x11_linux_key_x, - "y": x11_linux_key_y, - "z": x11_linux_key_z, - "A": x11_linux_key_A, - "B": x11_linux_key_B, - "C": x11_linux_key_C, - "D": x11_linux_key_D, - "E": x11_linux_key_E, - "F": x11_linux_key_F, - "G": x11_linux_key_G, - "H": x11_linux_key_H, - "I": x11_linux_key_I, - "J": x11_linux_key_J, - "K": x11_linux_key_K, - "L": x11_linux_key_L, - "M": x11_linux_key_M, - "N": x11_linux_key_N, - "O": x11_linux_key_O, - "P": x11_linux_key_P, - "Q": x11_linux_key_Q, - "R": x11_linux_key_R, - "S": x11_linux_key_S, - "T": x11_linux_key_T, - "U": x11_linux_key_U, - "V": x11_linux_key_V, - "W": x11_linux_key_W, - "X": x11_linux_key_X, - "Y": x11_linux_key_Y, - "Z": x11_linux_key_Z, - "1": x11_linux_key_1, - "2": x11_linux_key_2, - "3": x11_linux_key_3, - "4": x11_linux_key_4, - "5": x11_linux_key_5, - "6": x11_linux_key_6, - "7": x11_linux_key_7, - "8": x11_linux_key_8, - "9": x11_linux_key_9, - "0": x11_linux_key_0, - } +.. list-table:: + :header-rows: 1 + :widths: 30 70 -* MacOS + * - Key Name + - Description + * - ``a`` - ``z`` + - Letter keys + * - ``0`` - ``9`` + - Number keys + * - ``f1`` - ``f24`` + - Function keys + * - ``enter``, ``return`` + - Enter / Return + * - ``tab`` + - Tab + * - ``space`` + - Space bar + * - ``back`` + - Backspace + * - ``escape`` + - Escape + * - ``lcontrol``, ``rcontrol`` + - Left / Right Control + * - ``lshift``, ``rshift`` + - Left / Right Shift + * - ``lalt``, ``ralt`` + - Left / Right Alt + * - ``lwin``, ``rwin`` + - Left / Right Windows key + * - ``up``, ``down``, ``left``, ``right`` + - Arrow keys + * - ``insert``, ``delete`` + - Insert / Delete + * - ``home``, ``end`` + - Home / End + * - ``pageup``, ``pagedown`` + - Page Up / Page Down + * - ``capslock`` + - Caps Lock + * - ``numlock`` + - Num Lock + * - ``print_screen`` + - Print Screen + * - ``numpad0`` - ``numpad9`` + - Numpad keys + * - ``add``, ``subtract``, ``multiply``, ``divide`` + - Numpad operators + * - ``apps`` + - Application / Menu key + * - ``browser_back``, ``browser_forward`` + - Browser navigation keys + * - ``volume_mute``, ``volume_up``, ``volume_down`` + - Volume control keys -.. code-block:: python +.. tip:: - keyboard_keys_table = { - "a": osx_key_a, - "A": osx_key_A, - "b": osx_key_b, - "B": osx_key_B, - "c": osx_key_c, - "C": osx_key_C, - "d": osx_key_d, - "D": osx_key_D, - "e": osx_key_e, - "E": osx_key_E, - "f": osx_key_f, - "F": osx_key_F, - "g": osx_key_g, - "G": osx_key_G, - "h": osx_key_h, - "H": osx_key_H, - "i": osx_key_i, - "I": osx_key_I, - "j": osx_key_j, - "J": osx_key_J, - "k": osx_key_k, - "K": osx_key_K, - "l": osx_key_l, - "L": osx_key_L, - "m": osx_key_m, - "M": osx_key_M, - "n": osx_key_n, - "N": osx_key_N, - "o": osx_key_o, - "O": osx_key_O, - "p": osx_key_p, - "P": osx_key_P, - "q": osx_key_q, - "Q": osx_key_Q, - "r": osx_key_r, - "R": osx_key_R, - "s": osx_key_s, - "S": osx_key_S, - "t": osx_key_t, - "T": osx_key_T, - "u": osx_key_u, - "U": osx_key_U, - "v": osx_key_v, - "V": osx_key_V, - "w": osx_key_w, - "W": osx_key_W, - "x": osx_key_x, - "X": osx_key_X, - "y": osx_key_y, - "Y": osx_key_Y, - "z": osx_key_z, - "Z": osx_key_Z, - "1": osx_key_1, - "!": osx_key_exclam, - "2": osx_key_2, - "@": osx_key_at, - "3": osx_key_3, - "#": osx_key_numbersign, - "4": osx_key_4, - "$": osx_key_money, - "5": osx_key_5, - "%": osx_key_percent, - "6": osx_key_6, - "^": osx_key_asciicircum, - "7": osx_key_7, - "&": osx_key_ampersand, - "8": osx_key_8, - "*": osx_key_asterisk, - "9": osx_key_9, - "(": osx_key_parenleft, - "0": osx_key_0, - ")": osx_key_parenright, - "=": osx_key_equal, - "+": osx_key_plus, - "-": osx_key_minus, - "_": osx_key_underscore, - "]": osx_key_bracketright, - "}": osx_key_braceright, - "[": osx_key_bracketleft, - "{": osx_key_braceleft, - "'": osx_key_apostrophe, - '"': osx_key_quotedbl, - ";": osx_key_semicolon, - ":": osx_key_colon, - "\\": osx_key_backslash, - "|": osx_key_bar, - ",": osx_key_comma, - "<": osx_key_less, - "/": osx_key_slash, - "?": osx_key_question, - ".": osx_key_period, - ">": osx_key_greater, - "`": osx_key_grave, - "~": osx_key_asciitilde, - "space": osx_key_space, - "return": osx_key_return, - "newline": osx_key_newline, - "enter": osx_key_enter, - "tab": osx_key_tab, - "backspace": osx_key_backspace, - "esc": osx_key_esc, - "command": osx_key_command, - "shift": osx_key_shift, - "caps_lock": osx_key_caps_lock, - "option": osx_key_option, - "alt": osx_key_alt, - "ctrl": osx_key_ctrl, - "shift_right": osx_key_shift_right, - "option_right": osx_key_option_right, - "control_right": osx_key_control_right, - "fn": osx_key_fn, - "volume_up": osx_key_volume_up, - "volume_down": osx_key_volume_down, - "volume_mute": osx_key_volume_mute, - "f1": osx_key_f1, - "f2": osx_key_f2, - "f3": osx_key_f3, - "f4": osx_key_f4, - "f5": osx_key_f5, - "f6": osx_key_f6, - "f7": osx_key_f7, - "f8": osx_key_f8, - "f9": osx_key_f9, - "f10": osx_key_f10, - "f11": osx_key_f11, - "f12": osx_key_f12, - "f13": osx_key_f13, - "f14": osx_key_f14, - "f15": osx_key_f15, - "f16": osx_key_f16, - "f17": osx_key_f17, - "f18": osx_key_f18, - "f19": osx_key_f19, - "f20": osx_key_f20, - "help": osx_key_help, - "home": osx_key_home, - "pageup": osx_key_pageup, - "end": osx_key_end, - "pagedown": osx_key_pagedown, - "left": osx_key_left, - "right": osx_key_right, - "down": osx_key_down, - "up": osx_key_up, - "yen": osx_key_yen, - "eisu": osx_key_eisu, - "kana": osx_key_kana, - } \ No newline at end of file + For the complete list of 200+ Windows keys, use ``keys_table`` at runtime: + + .. code-block:: python + + from je_auto_control import keys_table + for key_name in sorted(keys_table.keys()): + print(key_name) + +Linux (X11) +=========== + +Common keys: + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Key Name + - Description + * - ``a`` - ``z`` + - Letter keys + * - ``0`` - ``9`` + - Number keys + * - ``f1`` - ``f12`` + - Function keys + * - ``return`` + - Enter / Return + * - ``tab`` + - Tab + * - ``space`` + - Space bar + * - ``backspace`` + - Backspace + * - ``escape`` + - Escape + * - ``ctrl``, ``ctrl_r`` + - Left / Right Control + * - ``shift``, ``shift_r`` + - Left / Right Shift + * - ``alt``, ``alt_r`` + - Left / Right Alt + * - ``super``, ``super_r`` + - Left / Right Super (Windows) key + * - ``up``, ``down``, ``left``, ``right`` + - Arrow keys + * - ``insert``, ``delete`` + - Insert / Delete + * - ``home``, ``end`` + - Home / End + * - ``page_up``, ``page_down`` + - Page Up / Page Down + * - ``caps_lock`` + - Caps Lock + * - ``num_lock`` + - Num Lock + +.. tip:: + + Linux supports 380+ key codes. Use ``keys_table`` at runtime for the complete list. + +macOS +===== + +Common keys: + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Key Name + - Description + * - ``a`` - ``z`` + - Letter keys (mapped to macOS virtual key codes) + * - ``0`` - ``9`` + - Number keys + * - ``f1`` - ``f20`` + - Function keys + * - ``return`` + - Enter / Return + * - ``tab`` + - Tab + * - ``space`` + - Space bar + * - ``delete`` + - Backspace / Delete + * - ``escape`` + - Escape + * - ``command`` + - Command key + * - ``shift``, ``shift_r`` + - Left / Right Shift + * - ``option``, ``option_r`` + - Left / Right Option (Alt) + * - ``control`` + - Control + * - ``up``, ``down``, ``left``, ``right`` + - Arrow keys + * - ``home``, ``end`` + - Home / End + * - ``page_up``, ``page_down`` + - Page Up / Page Down + * - ``caps_lock`` + - Caps Lock + * - ``volume_up``, ``volume_down``, ``mute`` + - Volume control keys + +.. tip:: + + macOS supports 170+ key codes. Use ``keys_table`` at runtime for the complete list. diff --git a/docs/source/API/special/mouse_keys.rst b/docs/source/API/special/mouse_keys.rst index 76bbaaf..fe6fde5 100644 --- a/docs/source/API/special/mouse_keys.rst +++ b/docs/source/API/special/mouse_keys.rst @@ -1,40 +1,76 @@ -Mouse keys API ----- - -* Windows - -.. code-block:: python - - mouse_keys_table = { - "mouse_left": win32_mouse_left, - "mouse_middle": win32_mouse_middle, - "mouse_right": win32_mouse_right, - "mouse_x1": win32_mouse_x1, - "mouse_x2": win32_mouse_x2 - } - -* Linux - -.. code-block:: python - - mouse_keys_table = { - "mouse_left": x11_linux_mouse_left, - "mouse_middle": x11_linux_mouse_middle, - "mouse_right": x11_linux_mouse_right - } - special_mouse_keys_table = { - "scroll_up": x11_linux_scroll_direction_up, - "scroll_down": x11_linux_scroll_direction_down, - "scroll_left": x11_linux_scroll_direction_left, - "scroll_right": x11_linux_scroll_direction_right - } - -* MacOS - -.. code-block:: python - - mouse_keys_table = { - "mouse_left": osx_mouse_left, - "mouse_middle": osx_mouse_middle, - "mouse_right": osx_mouse_right, - } \ No newline at end of file +========== +Mouse Keys +========== + +Mouse button key names available per platform. + +Windows +======= + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Key Name + - Description + * - ``mouse_left`` + - Left mouse button + * - ``mouse_middle`` + - Middle mouse button (scroll wheel click) + * - ``mouse_right`` + - Right mouse button + * - ``mouse_x1`` + - Extra button 1 (back) + * - ``mouse_x2`` + - Extra button 2 (forward) + +Linux (X11) +=========== + +**Standard buttons:** + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Key Name + - Description + * - ``mouse_left`` + - Left mouse button + * - ``mouse_middle`` + - Middle mouse button + * - ``mouse_right`` + - Right mouse button + +**Scroll directions (special keys):** + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Key Name + - Description + * - ``scroll_up`` + - Scroll up + * - ``scroll_down`` + - Scroll down + * - ``scroll_left`` + - Scroll left + * - ``scroll_right`` + - Scroll right + +macOS +===== + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Key Name + - Description + * - ``mouse_left`` + - Left mouse button + * - ``mouse_middle`` + - Middle mouse button + * - ``mouse_right`` + - Right mouse button diff --git a/docs/source/API/utils/callback.rst b/docs/source/API/utils/callback.rst index 4b69dce..23999d6 100644 --- a/docs/source/API/utils/callback.rst +++ b/docs/source/API/utils/callback.rst @@ -1,21 +1,40 @@ +===================== Callback Function API +===================== + +The Callback Executor runs a trigger function and then invokes a callback. + ---- -.. code-block:: python - - def callback_function( - self, - trigger_function_name: str, - callback_function: typing.Callable, - callback_function_param: [dict, None] = None, - callback_param_method: str = "kwargs", - **kwargs - ): - """ - :param trigger_function_name: what function we want to trigger only accept function in event_dict - :param callback_function: what function we want to callback - :param callback_function_param: callback function's param only accept dict - :param callback_param_method: what type param will use on callback function only accept kwargs and args - :param kwargs: trigger_function's param - :return: trigger_function_name return value - """ \ No newline at end of file +callback_function +================= + +.. function:: callback_executor.callback_function(trigger_function_name, callback_function, callback_function_param=None, callback_param_method="kwargs", **kwargs) + + Executes a trigger function from the event dictionary, then calls a callback function. + + :param str trigger_function_name: Name of the function to trigger (must exist in ``event_dict``). + :param callable callback_function: Function to call after the trigger completes. + :param dict callback_function_param: Parameters for the callback function. Pass ``None`` for no parameters. + :param str callback_param_method: How to pass parameters to the callback: + + - ``"args"`` -- unpack as positional arguments + - ``"kwargs"`` -- unpack as keyword arguments + + :param kwargs: Keyword arguments passed to the trigger function. + :returns: Return value of the trigger function. + + **Example:** + + .. code-block:: python + + from je_auto_control import callback_executor + + def on_complete(width, height): + print(f"Screen size: {width}x{height}") + + result = callback_executor.callback_function( + trigger_function_name="screen_size", + callback_function=on_complete, + callback_param_method="args" + ) diff --git a/docs/source/API/utils/critical_exit.rst b/docs/source/API/utils/critical_exit.rst index b61621a..54ebdb3 100644 --- a/docs/source/API/utils/critical_exit.rst +++ b/docs/source/API/utils/critical_exit.rst @@ -1,47 +1,53 @@ +================= Critical Exit API +================= + +Safety mechanism to forcibly terminate automation scripts via a hotkey. + ---- -.. code-block:: python - - class CriticalExit(Thread): - """ - use to make program interrupt - """ - - def __init__(self, default_daemon: bool = True): - """ - default interrupt is keyboard F7 key - :param default_daemon bool thread setDaemon - """ - super().__init__() - self.daemon = default_daemon - self._exit_check_key: int = keyboard_keys_table.get("f7") - - def set_critical_key(self, keycode: [int, str] = None) -> None: - """ - set interrupt key - :param keycode interrupt key - """ - if isinstance(keycode, int): - self._exit_check_key = keycode - else: - self._exit_check_key = keyboard_keys_table.get(keycode) - - def run(self) -> None: - """ - listener keycode _exit_check_key to interrupt - """ - try: - while True: - if keyboard_check.check_key_is_press(self._exit_check_key): - _thread.interrupt_main() - except Exception as error: - print(repr(error), file=sys.stderr) - - def init_critical_exit(self) -> None: - """ - should only use this to start critical exit - may this function will add more - """ - critical_thread = self - critical_thread.start() \ No newline at end of file +CriticalExit +============ + +.. class:: CriticalExit(default_daemon=True) + + A daemon thread that monitors a keyboard key and interrupts the main thread when pressed. + Inherits from ``threading.Thread``. + + :param bool default_daemon: Whether the thread runs as a daemon. Defaults to ``True``. + + The default interrupt key is **F7**. + + .. method:: set_critical_key(keycode=None) + + Changes the hotkey used to trigger the critical exit. + + :param keycode: New key name or key code. If ``None``, no change is made. + :type keycode: int or str + + **Example:** + + .. code-block:: python + + critical = CriticalExit() + critical.set_critical_key("escape") + + .. method:: run() + + The thread's main loop. Continuously checks if the exit key is pressed and + calls ``_thread.interrupt_main()`` when detected. + + .. warning:: Do not call ``run()`` directly. Use :meth:`init_critical_exit` instead. + + .. method:: init_critical_exit() + + Starts the critical exit monitoring thread. This is the recommended way to + enable critical exit. + + **Example:** + + .. code-block:: python + + from je_auto_control import CriticalExit + + CriticalExit().init_critical_exit() diff --git a/docs/source/API/utils/executor.rst b/docs/source/API/utils/executor.rst index 48522f1..b64e744 100644 --- a/docs/source/API/utils/executor.rst +++ b/docs/source/API/utils/executor.rst @@ -1,26 +1,74 @@ +============ Executor API +============ + +The executor is the JSON action interpreter that parses and executes automation scripts. + ---- -.. code-block:: python +execute_action +============== + +.. function:: execute_action(action_list) + + Executes all actions in the given action list. + + :param action_list: A list of actions to execute. Each action is a list of + ``[function_name, {params}]``. + :type action_list: list or dict + :returns: Dictionary mapping each action to its return value. + :rtype: dict + + **Example:** + + .. code-block:: python + + from je_auto_control import execute_action + + result = execute_action([ + ["AC_set_mouse_position", {"x": 100, "y": 200}], + ["AC_click_mouse", {"mouse_keycode": "mouse_left"}] + ]) + +---- + +execute_files +============= + +.. function:: execute_files(execute_files_list) + + Executes all JSON action files in the given list. + + :param list execute_files_list: List of file paths to execute. + :returns: List of execution results for each file. + :rtype: list + + **Example:** + + .. code-block:: python + + from je_auto_control import execute_files, get_dir_files_as_list + + execute_files(get_dir_files_as_list("./actions/")) + +---- + +add_command_to_executor +======================= + +.. function:: add_command_to_executor(command_dict) + + Adds custom commands to the executor's event dictionary. + + :param dict command_dict: Dictionary of ``{"command_name": callable}`` to add. - def execute_action(self, action_list: [list, dict]) -> dict: - """ - use to execute all action on action list(action file or program list) - :param action_list the list include action - for loop the list and execute action - """ + **Example:** -.. code-block:: python + .. code-block:: python - def execute_files(self, execute_files_list: list) -> list: - """ - :param execute_files_list: list include execute files path - :return: every execute detail as list - """ + from je_auto_control import executor -.. code-block:: python + def my_custom_function(message): + print(f"Custom: {message}") - def add_command_to_executor(command_dict: dict): - """ - :param command_dict: dict include command we want to add to event_dict - """ \ No newline at end of file + executor.event_dict["my_func"] = my_custom_function diff --git a/docs/source/API/utils/file.rst b/docs/source/API/utils/file.rst index 1b1614a..ec6275a 100644 --- a/docs/source/API/utils/file.rst +++ b/docs/source/API/utils/file.rst @@ -1,14 +1,32 @@ -File process API +==================== +File Processing API +==================== + +Utility functions for working with action files. + ---- -.. code-block:: python - - def get_dir_files_as_list( - dir_path: str = getcwd(), - default_search_file_extension: str = ".json") -> List[str]: - """ - get dir file when end with default_search_file_extension - :param dir_path: which dir we want to walk and get file list - :param default_search_file_extension: which extension we want to search - :return: [] if nothing searched or [file1, file2.... files] file was searched - """ \ No newline at end of file +get_dir_files_as_list +===================== + +.. function:: get_dir_files_as_list(dir_path=os.getcwd(), default_search_file_extension=".json") + + Walks a directory and returns all files matching the given extension. + + :param str dir_path: Directory path to search. Defaults to the current working directory. + :param str default_search_file_extension: File extension to filter by (e.g., ``".json"``). + :returns: List of matching file paths. Empty list if no files found. + :rtype: list[str] + + **Example:** + + .. code-block:: python + + from je_auto_control import get_dir_files_as_list + + # Get all JSON files in a directory + files = get_dir_files_as_list("./actions/") + print(files) # ['./actions/step1.json', './actions/step2.json'] + + # Search for Python files instead + py_files = get_dir_files_as_list("./scripts/", ".py") diff --git a/docs/source/API/utils/generate_report.rst b/docs/source/API/utils/generate_report.rst index b534d80..1e65520 100644 --- a/docs/source/API/utils/generate_report.rst +++ b/docs/source/API/utils/generate_report.rst @@ -1,47 +1,74 @@ -Generate Report API +===================== +Report Generation API +===================== + +Functions for generating test reports from recorded automation actions. + +---- + +generate_html +============= + +.. function:: generate_html() + + Creates an HTML string from recorded test actions. + + :returns: HTML report content. + :rtype: str + +---- + +generate_html_report +==================== + +.. function:: generate_html_report(html_name="default_name") + + Saves an HTML report file. + + :param str html_name: Output file name (without extension). + +---- + +generate_json +============= + +.. function:: generate_json() + + Creates JSON data from recorded test actions. + + :returns: Tuple of ``(success_dict, failure_dict)``. + :rtype: tuple[dict, dict] + ---- -.. code-block:: python +generate_json_report +==================== + +.. function:: generate_json_report(json_file_name="default_name") - def generate_html() -> str: - """ - this function will create html string - :return: html_string - """ + Saves a JSON report file. -.. code-block:: python + :param str json_file_name: Output file name (without extension). - def generate_html_report(html_name: str = "default_name"): - """ - Output html report file - :param html_name: save html file name - """ +---- + +generate_xml +============ -.. code-block:: python +.. function:: generate_xml() - def generate_json(): - """ - :return: two dict {success_dict}, {failure_dict} - """ + Creates XML data from recorded test actions. -.. code-block:: python + :returns: Tuple of ``(success_dict, failure_dict)``. + :rtype: tuple[dict, dict] - def generate_json_report(json_file_name: str = "default_name"): - """ - Output json report file - :param json_file_name: save json file's name - """ +---- -.. code-block:: python +generate_xml_report +=================== - def generate_xml(): - """ - :return: two dict {success_dict}, {failure_dict} - """ +.. function:: generate_xml_report(xml_file_name="default_name") -.. code-block:: python + Saves an XML report file. - def generate_xml_report(xml_file_name: str = "default_name"): - """ - :param xml_file_name: save xml file name - """ \ No newline at end of file + :param str xml_file_name: Output file name (without extension). diff --git a/docs/source/API/utils/package_manager.rst b/docs/source/API/utils/package_manager.rst index d3276d1..f3fa093 100644 --- a/docs/source/API/utils/package_manager.rst +++ b/docs/source/API/utils/package_manager.rst @@ -1,101 +1,66 @@ +==================== Package Manager API +==================== + +The ``PackageManager`` class dynamically loads external Python packages into the +executor and callback executor at runtime. + ---- +PackageManager +============== + +.. class:: PackageManager + + .. method:: check_package(package) + + Checks if a package is installed and importable. + + :param str package: Package name to check. + :returns: The imported module if found, ``None`` otherwise. + + .. method:: add_package_to_executor(package) + + Loads all functions, built-ins, and classes from a package into the main executor's + ``event_dict``. + + :param str package: Package name to load. + + Functions are added with the naming convention ``package_function``. + For example, ``time.sleep`` becomes ``time_sleep``. + + .. method:: add_package_to_callback_executor(package) + + Loads all functions, built-ins, and classes from a package into the callback executor's + ``event_dict``. + + :param str package: Package name to load. + + .. method:: get_member(package, predicate, target) + + Retrieves members from a package matching the given predicate and adds them to the + target executor. + + :param str package: Package name. + :param predicate: Inspection predicate (e.g., ``isfunction``, ``isclass``). + :param target: Target executor whose ``event_dict`` will be updated. + + .. method:: add_package_to_target(package, target) + + Loads functions, built-ins, and classes from a package into the specified target executor. + + :param str package: Package name. + :param target: Target executor. + +**Example:** + .. code-block:: python - from importlib import import_module - from importlib.util import find_spec - from inspect import getmembers, isfunction, isbuiltin, isclass - from sys import stderr - - - class PackageManager(object): - - def __init__(self): - self.installed_package_dict = { - } - self.executor = None - self.callback_executor = None - - def check_package(self, package: str): - """ - :param package: package to check exists or not - :return: package if find else None - """ - if self.installed_package_dict.get(package, None) is None: - found_spec = find_spec(package) - if found_spec is not None: - try: - installed_package = import_module(found_spec.name) - self.installed_package_dict.update( - {found_spec.name: installed_package}) - except ModuleNotFoundError as error: - print(repr(error), file=stderr) - return self.installed_package_dict.get(package, None) - - def add_package_to_executor(self, package): - """ - :param package: package's function will add to executor - """ - self.add_package_to_target( - package=package, - target=self.executor - ) - - def add_package_to_callback_executor(self, package): - """ - :param package: package's function will add to callback_executor - """ - self.add_package_to_target( - package=package, - target=self.callback_executor - ) - - def get_member(self, package, predicate, target): - """ - :param package: package we want to get member - :param predicate: predicate - :param target: which event_dict will be added - """ - installed_package = self.check_package(package) - if installed_package is not None and target is not None: - for member in getmembers(installed_package, predicate): - target.event_dict.update( - {str(package) + "_" + str(member[0]): member[1]}) - elif installed_package is None: - print(repr(ModuleNotFoundError(f"Can't find package {package}")), - file=stderr) - else: - print(f"Executor error {self.executor}", file=stderr) - - def add_package_to_target(self, package, target): - """ - :param package: package we want to get member - :param target: which event_dict will be added - """ - try: - self.get_member( - package=package, - predicate=isfunction, - target=target - ) - self.get_member( - package=package, - predicate=isbuiltin, - target=target - ) - self.get_member( - package=package, - predicate=isfunction, - target=target - ) - self.get_member( - package=package, - predicate=isclass, - target=target - ) - except Exception as error: - print(repr(error), file=stderr) - - - package_manager = PackageManager() \ No newline at end of file + from je_auto_control import package_manager + + # Add 'os' module to the executor + package_manager.add_package_to_executor("os") + + # Now you can use os functions in JSON actions: + # ["os_getcwd", {}] + # ["os_listdir", {"path": "."}] diff --git a/docs/source/API/utils/scheduler.rst b/docs/source/API/utils/scheduler.rst index 7ee025f..9578e3f 100644 --- a/docs/source/API/utils/scheduler.rst +++ b/docs/source/API/utils/scheduler.rst @@ -1,192 +1,117 @@ +============= Scheduler API +============= + +The ``SchedulerManager`` class wraps APScheduler for scheduling automation tasks. + ---- -.. code-block:: python - - def add_blocking_job( - self, func: Callable, trigger: str = None, args: Union[list, tuple] = None, - kwargs: dict = None, id: str = None, name: str = None, - misfire_grace_time: int = undefined, coalesce: bool = undefined, max_instances: int = undefined, - next_run_time: datetime = undefined, jobstore: str = 'default', executor: str = 'default', - replace_existing: bool = False, **trigger_args: Any) -> Job: - """ - Just an apscheduler add job wrapper. - :param func: callable (or a textual reference to one) to run at the given time - :param str|apscheduler.triggers.base.BaseTrigger trigger: trigger that determines when - ``func`` is called - :param list|tuple args: list of positional arguments to call func with - :param dict kwargs: dict of keyword arguments to call func with - :param str|unicode id: explicit identifier for the job (for modifying it later) - :param str|unicode name: textual description of the job - :param int misfire_grace_time: seconds after the designated runtime that the job is still - allowed to be run (or ``None`` to allow the job to run no matter how late it is) - :param bool coalesce: run once instead of many times if the scheduler determines that the - job should be run more than once in succession - :param int max_instances: maximum number of concurrently running instances allowed for this - job - :param datetime next_run_time: when to first run the job, regardless of the trigger (pass - ``None`` to add the job as paused) - :param str|unicode jobstore: alias of the job store to store the job in - :param str|unicode executor: alias of the executor to run the job with - :param bool replace_existing: ``True`` to replace an existing job with the same ``id`` - (but retain the number of runs from the existing one) - :return: Job - """ - -.. code-block:: python - - def add_nonblocking_job( - self, func: Callable, trigger: str = None, args: Union[list, tuple] = None, - kwargs: dict = None, id: str = None, name: str = None, - misfire_grace_time: int = undefined, coalesce: bool = undefined, max_instances: int = undefined, - next_run_time: datetime = undefined, jobstore: str = 'default', executor: str = 'default', - replace_existing: bool = False, **trigger_args: Any) -> Job: - """ - Just an apscheduler add job wrapper. - :param func: callable (or a textual reference to one) to run at the given time - :param str|apscheduler.triggers.base.BaseTrigger trigger: trigger that determines when - ``func`` is called - :param list|tuple args: list of positional arguments to call func with - :param dict kwargs: dict of keyword arguments to call func with - :param str|unicode id: explicit identifier for the job (for modifying it later) - :param str|unicode name: textual description of the job - :param int misfire_grace_time: seconds after the designated runtime that the job is still - allowed to be run (or ``None`` to allow the job to run no matter how late it is) - :param bool coalesce: run once instead of many times if the scheduler determines that the - job should be run more than once in succession - :param int max_instances: maximum number of concurrently running instances allowed for this - job - :param datetime next_run_time: when to first run the job, regardless of the trigger (pass - ``None`` to add the job as paused) - :param str|unicode jobstore: alias of the job store to store the job in - :param str|unicode executor: alias of the executor to run the job with - :param bool replace_existing: ``True`` to replace an existing job with the same ``id`` - (but retain the number of runs from the existing one) - :return: Job - """ - -.. code-block:: python - - def get_blocking_scheduler(self) -> BlockingScheduler: - """ - Return self blocking scheduler - :return: BlockingScheduler - """ - -.. code-block:: python - - def get_nonblocking_scheduler(self) -> BackgroundScheduler: - """ - Return self background scheduler - :return: BackgroundScheduler - """ - -.. code-block:: python - - def start_block_scheduler(self, *args: Any, **kwargs: Any) -> None: - """ - Start blocking scheduler - :return: None - """ - -.. code-block:: python - - def start_nonblocking_scheduler(self, *args: Any, **kwargs: Any) -> None: - """ - Start background scheduler - :return: None - """ - -.. code-block:: python - - def start_all_scheduler(self, *args: Any, **kwargs: Any) -> None: - """ - Start background and blocking scheduler - :return: None - """ - -.. code-block:: python - - def add_interval_blocking_secondly( - self, function: Callable, id: str = None, args: Union[list, tuple] = None, - kwargs: dict = None, seconds: int = 1, **trigger_args: Any) -> Job: - -.. code-block:: python +SchedulerManager +================ + +.. class:: SchedulerManager + + Manages blocking and non-blocking schedulers. + + Adding Jobs + ----------- + + .. method:: add_blocking_job(func, trigger=None, args=None, kwargs=None, id=None, name=None, misfire_grace_time=undefined, coalesce=undefined, max_instances=undefined, next_run_time=undefined, jobstore='default', executor='default', replace_existing=False, **trigger_args) + + Adds a job to the blocking scheduler. Wraps APScheduler's ``add_job()``. + + :param callable func: Function to run. + :param str trigger: Trigger type (e.g., ``"interval"``, ``"cron"``). + :param str id: Unique job identifier. + :param str name: Human-readable job name. + :param bool replace_existing: If ``True``, replaces a job with the same ``id``. + :returns: The created Job instance. + :rtype: Job + + .. method:: add_nonblocking_job(func, trigger=None, args=None, kwargs=None, id=None, name=None, **trigger_args) + + Adds a job to the non-blocking (background) scheduler. Same parameters as ``add_blocking_job()``. + + Interval Scheduling + ------------------- + + Convenience methods for interval-based scheduling. All accept ``function``, ``id``, ``args``, ``kwargs``, and the interval parameter. + + **Blocking:** + + .. method:: add_interval_blocking_secondly(function, id=None, seconds=1, **trigger_args) + .. method:: add_interval_blocking_minutely(function, id=None, minutes=1, **trigger_args) + .. method:: add_interval_blocking_hourly(function, id=None, hours=1, **trigger_args) + .. method:: add_interval_blocking_daily(function, id=None, days=1, **trigger_args) + .. method:: add_interval_blocking_weekly(function, id=None, weeks=1, **trigger_args) + + **Non-blocking:** + + .. method:: add_interval_nonblocking_secondly(function, id=None, seconds=1, **trigger_args) + .. method:: add_interval_nonblocking_minutely(function, id=None, minutes=1, **trigger_args) + .. method:: add_interval_nonblocking_hourly(function, id=None, hours=1, **trigger_args) + .. method:: add_interval_nonblocking_daily(function, id=None, days=1, **trigger_args) + .. method:: add_interval_nonblocking_weekly(function, id=None, weeks=1, **trigger_args) - def add_interval_blocking_minutely( - self, function: Callable, id: str = None, args: Union[list, tuple] = None, - kwargs: dict = None, minutes: int = 1, **trigger_args: Any) -> Job: + Cron Scheduling + --------------- -.. code-block:: python + .. method:: add_cron_blocking(function, id=None, **trigger_args) - def add_interval_blocking_hourly( - self, function: Callable, id: str = None, args: Union[list, tuple] = None, - kwargs: dict = None, hours: int = 1, **trigger_args: Any) -> Job: + Adds a cron-triggered job to the blocking scheduler. -.. code-block:: python + .. method:: add_cron_nonblocking(function, id=None, **trigger_args) - def add_interval_blocking_daily( - self, function: Callable, id: str = None, args: Union[list, tuple] = None, - kwargs: dict = None, days: int = 1, **trigger_args: Any) -> Job: + Adds a cron-triggered job to the non-blocking scheduler. -.. code-block:: python + Scheduler Control + ----------------- - def add_interval_blocking_weekly( - self, function: Callable, id: str = None, args: Union[list, tuple] = None, - kwargs: dict = None, weeks: int = 1, **trigger_args: Any) -> Job: + .. method:: get_blocking_scheduler() -.. code-block:: python + :returns: The blocking scheduler instance. + :rtype: BlockingScheduler - def add_interval_nonblocking_secondly( - self, function: Callable, id: str = None, args: list = None, - kwargs: dict = None, seconds: int = 1, **trigger_args: Any) -> Job: + .. method:: get_nonblocking_scheduler() -.. code-block:: python + :returns: The background scheduler instance. + :rtype: BackgroundScheduler - def add_interval_nonblocking_minutely( - self, function: Callable, id: str = None, args: list = None, - kwargs: dict = None, minutes: int = 1, **trigger_args: Any) -> Job: + .. method:: start_block_scheduler(*args, **kwargs) -.. code-block:: python + Starts the blocking scheduler (blocks the current thread). - def add_interval_nonblocking_hourly( - self, function: Callable, id: str = None, args: Union[list, tuple] = None, - kwargs: dict = None, hours: int = 1, **trigger_args: Any) -> Job: + .. method:: start_nonblocking_scheduler(*args, **kwargs) -.. code-block:: python + Starts the non-blocking scheduler in a background thread. - def add_interval_nonblocking_daily( - self, function: Callable, id: str = None, args: Union[list, tuple] = None, - kwargs: dict = None, days: int = 1, **trigger_args: Any) -> Job: + .. method:: start_all_scheduler(*args, **kwargs) -.. code-block:: python + Starts both blocking and non-blocking schedulers. - def add_interval_nonblocking_weekly( - self, function: Callable, id: str = None, args: Union[list, tuple] = None, - kwargs: dict = None, weeks: int = 1, **trigger_args: Any) -> Job: + Job Management + -------------- -.. code-block:: python + .. method:: remove_blocking_job(id, jobstore='default') - def add_cron_blocking( - self, function: Callable, id: str = None, **trigger_args: Any) -> Job: + Removes a job from the blocking scheduler. -.. code-block:: python + :param str id: Job identifier. - def add_cron_nonblocking( - self, function: Callable, id: str = None, **trigger_args: Any) -> Job: + .. method:: remove_nonblocking_job(id, jobstore='default') -.. code-block:: python + Removes a job from the non-blocking scheduler. - def remove_blocking_job(self, id: str, jobstore: str = 'default') -> Any: + :param str id: Job identifier. -.. code-block:: python + .. method:: shutdown_blocking_scheduler(wait=False) - def remove_nonblocking_job(self, id: str, jobstore: str = 'default') -> Any: + Shuts down the blocking scheduler. -.. code-block:: python + :param bool wait: If ``True``, waits for running jobs to finish. - def shutdown_blocking_scheduler(self, wait: bool = False) -> None: + .. method:: shutdown_nonblocking_scheduler(wait=False) -.. code-block:: python + Shuts down the non-blocking scheduler. - def shutdown_nonblocking_scheduler(self, wait: bool = False) -> None: + :param bool wait: If ``True``, waits for running jobs to finish. diff --git a/docs/source/API/utils/socket_server.rst b/docs/source/API/utils/socket_server.rst index 2bae132..034ece8 100644 --- a/docs/source/API/utils/socket_server.rst +++ b/docs/source/API/utils/socket_server.rst @@ -1,60 +1,51 @@ +================= Socket Server API +================= + +TCP server for receiving and executing JSON automation commands remotely. + +---- + +start_autocontrol_socket_server +=============================== + +.. function:: start_autocontrol_socket_server(host="localhost", port=9938) + + Starts a threaded TCP server that accepts JSON automation commands. + + :param str host: Server hostname. Defaults to ``"localhost"``. + :param int port: Server port. Defaults to ``9938``. + :returns: The server instance. Check ``server.close_flag`` to detect shutdown. + :rtype: TCPServer + + The server can also read ``host`` and ``port`` from command-line arguments + (``sys.argv[1]`` and ``sys.argv[2]``). + +---- + +TCPServer +========= + +.. class:: TCPServer(server_address, RequestHandlerClass) + + Threaded TCP server (extends ``socketserver.ThreadingMixIn`` and ``socketserver.TCPServer``). + + .. attribute:: close_flag + :type: bool + + Set to ``True`` when the server receives a ``"quit_server"`` command. + ---- -.. code-block:: python - - import json - import socketserver - import sys - import threading - - from je_auto_control.utils.executor.action_executor import execute_action - - - class TCPServerHandler(socketserver.BaseRequestHandler): - - def handle(self): - command_string = str(self.request.recv(8192).strip(), encoding="utf-8") - socket = self.request - print("command is: " + command_string, flush=True) - if command_string == "quit_server": - self.server.shutdown() - self.server.close_flag = True - print("Now quit server", flush=True) - else: - try: - execute_str = json.loads(command_string) - for execute_function, execute_return in execute_action(execute_str).items(): - socket.sendto(str(execute_return).encode("utf-8"), self.client_address) - socket.sendto("\n".encode("utf-8"), self.client_address) - socket.sendto("Return_Data_Over_JE".encode("utf-8"), self.client_address) - socket.sendto("\n".encode("utf-8"), self.client_address) - except Exception as error: - print(repr(error), file=sys.stderr) - try: - socket.sendto(str(error).encode("utf-8"), self.client_address) - socket.sendto("\n".encode("utf-8"), self.client_address) - socket.sendto("Return_Data_Over_JE".encode("utf-8"), self.client_address) - socket.sendto("\n".encode("utf-8"), self.client_address) - except Exception as error: - print(repr(error)) - - - class TCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): - - def __init__(self, server_address, RequestHandlerClass): - super().__init__(server_address, RequestHandlerClass) - self.close_flag: bool = False - - - def start_autocontrol_socket_server(host: str = "localhost", port: int = 9938): - if len(sys.argv) == 2: - host = sys.argv[1] - elif len(sys.argv) == 3: - host = sys.argv[1] - port = int(sys.argv[2]) - server = TCPServer((host, port), TCPServerHandler) - server_thread = threading.Thread(target=server.serve_forever) - server_thread.daemon = True - server_thread.start() - return server \ No newline at end of file +TCPServerHandler +================ + +.. class:: TCPServerHandler + + Request handler for the TCP server. + + Receives up to 8192 bytes per request, decodes as UTF-8, and processes: + + - ``"quit_server"`` -- shuts down the server. + - Any other string -- parsed as JSON and passed to ``execute_action()``. + Results are sent back to the client, terminated by ``"Return_Data_Over_JE"``. diff --git a/docs/source/API/wrapper/image.rst b/docs/source/API/wrapper/image.rst index 3175a54..86a81c0 100644 --- a/docs/source/API/wrapper/image.rst +++ b/docs/source/API/wrapper/image.rst @@ -1,47 +1,73 @@ +========= Image API +========= + +.. module:: je_auto_control + +Functions for image recognition using OpenCV template matching. + +---- + +locate_all_image +================ + +.. function:: locate_all_image(image, detect_threshold=1, draw_image=False) + + Locates all occurrences of a template image on the screen. + + :param image: Template image to search for (file path or PIL image). + :type image: str or PIL.Image + :param float detect_threshold: Detection precision from ``0.0`` to ``1.0``. + ``1.0`` requires an exact match. + :param bool draw_image: If ``True``, marks detected areas on the returned image. + :returns: List of bounding boxes ``[[x1, y1, x2, y2], ...]``. + :rtype: list[list[int]] + +---- + +locate_image_center +=================== + +.. function:: locate_image_center(image, detect_threshold=1, draw_image=False) + + Locates a template image and returns its center position. + + :param image: Template image to search for (file path or PIL image). + :type image: str or PIL.Image + :param float detect_threshold: Detection precision from ``0.0`` to ``1.0``. + :param bool draw_image: If ``True``, marks detected areas on the returned image. + :returns: Center coordinates ``(x, y)``. + :rtype: list[int, int] + +---- + +locate_and_click +================ + +.. function:: locate_and_click(image, mouse_keycode, detect_threshold=1, draw_image=False) + + Locates a template image and clicks on its center position. + + :param image: Template image to search for (file path or PIL image). + :type image: str or PIL.Image + :param mouse_keycode: Mouse button to click (e.g., ``"mouse_left"``). + :type mouse_keycode: int or str + :param float detect_threshold: Detection precision from ``0.0`` to ``1.0``. + :param bool draw_image: If ``True``, marks detected areas on the returned image. + :returns: Center coordinates ``(x, y)`` of the clicked image. + :rtype: list[int, int] + ---- -.. code-block:: python - - def locate_all_image(image, detect_threshold: [float, int] = 1, - draw_image: bool = False) -> List[int]: - """ - use to locate all image that detected and then return detected images list - :param image which image we want to find on screen (png or PIL ImageGrab.grab()) - :param detect_threshold detect precision 0.0 ~ 1.0; 1 is absolute equal (float or int) - :param draw_image draw detect tag on return image (bool) - """ - - -.. code-block:: python - - def locate_image_center(image, detect_threshold: [float, int] = 1, draw_image: bool = False) -> List[Union[int, int]]: - """ - use to locate image and return image center position - :param image which image we want to find on screen (png or PIL ImageGrab.grab()) - :param detect_threshold detect precision 0.0 ~ 1.0; 1 is absolute equal (float or int) - :param draw_image draw detect tag on return image (bool) - """ - -.. code-block:: python - - def locate_and_click( - image, mouse_keycode: [int, str], - detect_threshold: [float, int] = 1, - draw_image: bool = False) -> List[Union[int, int]]: - """ - use to locate image and click image center position and the return image center position - :param image which image we want to find on screen (png or PIL ImageGrab.grab()) - :param mouse_keycode which mouse keycode we want to click - :param detect_threshold detect precision 0.0 ~ 1.0; 1 is absolute equal (float or int) - :param draw_image draw detect tag on return image (bool) - """ - -.. code-block:: python - - def screenshot(file_path: str = None, region: list = None) -> List[Union[int, int]]: - """ - use to get now screen image return image - :param file_path save screenshot path (None is no save) - :param region screenshot screen_region (screenshot screen_region on screen) - """ \ No newline at end of file +screenshot +========== + +.. function:: screenshot(file_path=None, region=None) + + Captures the current screen. + + :param str file_path: Path to save the screenshot. If ``None``, the image is not saved to disk. + :param list region: Screen region to capture as ``[x1, y1, x2, y2]``. + If ``None``, captures the full screen. + :returns: The captured screen image. + :rtype: PIL.Image diff --git a/docs/source/API/wrapper/keyboard.rst b/docs/source/API/wrapper/keyboard.rst index eca67f8..8850ca4 100644 --- a/docs/source/API/wrapper/keyboard.rst +++ b/docs/source/API/wrapper/keyboard.rst @@ -1,52 +1,93 @@ +============ Keyboard API +============ + +Functions for simulating keyboard input. + ---- -.. code-block:: python +get_special_table +================= - def get_special_table(): - return special_mouse_keys_table +.. function:: get_special_table() -.. code-block:: python + Returns the special keyboard keys table for the current platform. - def get_keyboard_keys_table(): - return keyboard_keys_table + :returns: Dictionary of special key names to key codes. + :rtype: dict -.. code-block:: python + .. note:: Not every platform has special keys. + +---- - def press_keyboard_key(keycode: [int, str], is_shift: bool = False, skip_record: bool = False) -> str: - """ - use to press a key still press to use release key - or use critical exit - return keycode - :param keycode which keycode we want to press - :param is_shift press shift True or False - :param skip_record skip record on record total list True or False - """ +get_keyboard_keys_table +======================= -.. code-block:: python +.. function:: get_keyboard_keys_table() - def release_keyboard_key(keycode: [int, str], is_shift: bool = False, skip_record: bool = False) -> str: - """ - use to release pressed key return keycode - :param keycode which keycode we want to release - :param is_shift press shift True or False - :param skip_record skip record on record total list True or False - """ + Returns the full keyboard keys table for the current platform. + + :returns: Dictionary of key names to key codes. + :rtype: dict + +---- + +press_keyboard_key +================== + +.. function:: press_keyboard_key(keycode, is_shift=False, skip_record=False) + + Presses and holds a keyboard key. Use :func:`release_keyboard_key` to release it. + + :param keycode: Key name or key code to press. + :type keycode: int or str + :param bool is_shift: Whether to press Shift simultaneously. + :param bool skip_record: If ``True``, this action will not be recorded. + :returns: The key code that was pressed. + :rtype: str + +---- + +release_keyboard_key +==================== + +.. function:: release_keyboard_key(keycode, is_shift=False, skip_record=False) + + Releases a previously pressed keyboard key. + + :param keycode: Key name or key code to release. + :type keycode: int or str + :param bool is_shift: Whether Shift was pressed. + :param bool skip_record: If ``True``, this action will not be recorded. + :returns: The key code that was released. + :rtype: str + +---- + +type_keyboard +============= + +.. function:: type_keyboard(keycode, is_shift=False, skip_record=False) + + Presses and immediately releases a keyboard key. + + :param keycode: Key name or key code to type. + :type keycode: int or str + :param bool is_shift: Whether to press Shift simultaneously. + :param bool skip_record: If ``True``, this action will not be recorded. + :returns: The key code that was typed. + :rtype: str + +---- -.. code-block:: python +check_key_is_press +================== - def type_keyboard(keycode: [int, str], is_shift: bool = False, skip_record: bool = False) -> str: - """ - press and release key return keycode - :param keycode which keycode we want to type - :param is_shift press shift True or False - :param skip_record skip record on record total list True or False - """ +.. function:: check_key_is_press(keycode) -.. code-block:: python + Checks whether a specific key is currently pressed. - def check_key_is_press(keycode: [int, str]) -> bool: - """ - use to check key is press return True or False - :param keycode check key is press or not - """ \ No newline at end of file + :param keycode: Key name or key code to check. + :type keycode: int or str + :returns: ``True`` if the key is pressed, ``False`` otherwise. + :rtype: bool diff --git a/docs/source/API/wrapper/mouse.rst b/docs/source/API/wrapper/mouse.rst index 5cda31e..2ec151d 100644 --- a/docs/source/API/wrapper/mouse.rst +++ b/docs/source/API/wrapper/mouse.rst @@ -1,84 +1,127 @@ +========= Mouse API +========= + +Functions for controlling the mouse cursor. + +---- + +get_mouse_table +=============== + +.. function:: get_mouse_table() + + Returns the mouse key table for the current platform. + + :returns: Dictionary mapping mouse button names to platform-specific key codes. + :rtype: dict + +---- + +mouse_preprocess +================ + +.. function:: mouse_preprocess(mouse_keycode, x, y) + + Validates the mouse key code and resolves the cursor position. + If ``x`` or ``y`` is ``None``, the current mouse position is used. + + :param mouse_keycode: Mouse button name or key code. + :type mouse_keycode: int or str + :param x: X coordinate. If ``None``, uses current position. + :type x: int + :param y: Y coordinate. If ``None``, uses current position. + :type y: int + :returns: Tuple of ``(keycode, x, y)``. + :rtype: tuple + +---- + +get_mouse_position +================== + +.. function:: get_mouse_position() + + Returns the current mouse cursor position. + + :returns: Tuple of ``(x, y)``. + :rtype: tuple[int, int] + +---- + +set_mouse_position +================== + +.. function:: set_mouse_position(x, y) + + Moves the mouse cursor to the specified coordinates. + + :param int x: Target X position. + :param int y: Target Y position. + :returns: Tuple of ``(x, y)``. + :rtype: tuple[int, int] + +---- + +press_mouse +=========== + +.. function:: press_mouse(mouse_keycode, x=None, y=None) + + Presses and holds a mouse button at the specified position. + + :param mouse_keycode: Mouse button name (e.g., ``"mouse_left"``). + :type mouse_keycode: int or str + :param int x: X position (default: current position). + :param int y: Y position (default: current position). + :returns: Tuple of ``(keycode, x, y)``. + :rtype: tuple + ---- -.. code-block:: python - - def get_mouse_table(): - return mouse_keys_table - -.. code-block:: python - - def mouse_preprocess(mouse_keycode: [int, str], x: int, y: int) -> Tuple[Union[int, str], int, int]: - """ - check mouse keycode is verified or not - and then check current mouse position - if x or y is None set x, y is current position - :param mouse_keycode which mouse keycode we want to click - :param x mouse click x position - :param y mouse click y position - """ - -.. code-block:: python - - def get_mouse_position() -> Tuple[int, int]: - """ - get mouse current position - return mouse_x, mouse_y - """ - -.. code-block:: python - - def set_mouse_position(x: int, y: int) -> Tuple[int, int]: - """ - :param x set mouse position x - :param y set mouse position y - return x, y - """ - -.. code-block:: python - - def press_mouse(mouse_keycode: [int, str], x: int = None, y: int = None) -> Tuple[Union[int, str], int, int]: - """ - press mouse keycode on x, y - return keycode, x, y - :param mouse_keycode which mouse keycode we want to press - :param x mouse click x position - :param y mouse click y position - """ - -.. code-block:: python - - def release_mouse(mouse_keycode: [int, str], x: int = None, y: int = None) -> Tuple[Union[int, str], int, int]: - """ - release mouse keycode on x, y - return keycode, x, y - :param mouse_keycode which mouse keycode we want to release - :param x mouse click x position - :param y mouse click y position - """ - -.. code-block:: python - - def click_mouse(mouse_keycode: [int, str], x: int = None, y: int = None) -> Tuple[Union[int, str], int, int]: - """ - press and release mouse keycode on x, y - return keycode, x, y - :param mouse_keycode which mouse keycode we want to click - :param x mouse click x position - :param y mouse click y position - """ - -.. code-block:: python - - def mouse_scroll(scroll_value: int, x: int = None, y: int = None, scroll_direction: str = "scroll_down") -> Tuple[int, str]: - """" - :param scroll_value scroll count - :param x mouse click x position - :param y mouse click y position - :param scroll_direction which direction we want to scroll (only linux) - scroll_direction = scroll_up : direction up - scroll_direction = scroll_down : direction down - scroll_direction = scroll_left : direction left - scroll_direction = scroll_right : direction right - """ +release_mouse +============= + +.. function:: release_mouse(mouse_keycode, x=None, y=None) + + Releases a previously pressed mouse button. + + :param mouse_keycode: Mouse button name (e.g., ``"mouse_left"``). + :type mouse_keycode: int or str + :param int x: X position (default: current position). + :param int y: Y position (default: current position). + :returns: Tuple of ``(keycode, x, y)``. + :rtype: tuple + +---- + +click_mouse +=========== + +.. function:: click_mouse(mouse_keycode, x=None, y=None) + + Presses and releases a mouse button at the specified position. + + :param mouse_keycode: Mouse button name (e.g., ``"mouse_left"``). + :type mouse_keycode: int or str + :param int x: X position (default: current position). + :param int y: Y position (default: current position). + :returns: Tuple of ``(keycode, x, y)``. + :rtype: tuple + +---- + +mouse_scroll +============ + +.. function:: mouse_scroll(scroll_value, x=None, y=None, scroll_direction="scroll_down") + + Scrolls the mouse wheel. + :param int scroll_value: Number of scroll units. + :param int x: X position (default: current position). + :param int y: Y position (default: current position). + :param str scroll_direction: Scroll direction (Linux only). One of: + ``"scroll_up"``, ``"scroll_down"``, ``"scroll_left"``, ``"scroll_right"``. + :returns: Tuple of ``(scroll_value, direction)``. + :rtype: tuple diff --git a/docs/source/API/wrapper/record.rst b/docs/source/API/wrapper/record.rst index 684ed79..29f0c57 100644 --- a/docs/source/API/wrapper/record.rst +++ b/docs/source/API/wrapper/record.rst @@ -1,16 +1,32 @@ +========== Record API +========== + +Functions for recording and replaying mouse/keyboard events. + ---- -.. code-block:: python +record +====== + +.. function:: record() + + Starts recording all keyboard and mouse events in the background. + Recording continues until :func:`stop_record` is called. + + :returns: None + +---- + +stop_record +=========== + +.. function:: stop_record() - def record() -> None: - """ - start record keyboard and mouse event until stop_record - """ + Stops the current recording session and returns the captured events. -.. code-block:: python + :returns: List of recorded actions in executor-compatible format. + :rtype: list - def stop_record() -> list: - """ - stop current record - """ \ No newline at end of file + The returned list can be passed directly to ``execute_action()`` for playback, + or saved to a JSON file for later use. diff --git a/docs/source/API/wrapper/screen.rst b/docs/source/API/wrapper/screen.rst index dd959a5..6d5ccd2 100644 --- a/docs/source/API/wrapper/screen.rst +++ b/docs/source/API/wrapper/screen.rst @@ -1,18 +1,32 @@ +========== Screen API +========== + +Functions for screen information and capture. + +---- + +screen_size +=========== + +.. function:: screen_size() + + Returns the current screen resolution. + + :returns: Screen dimensions as ``(width, height)``. + :rtype: tuple[int, int] + ---- -.. code-block:: python +screenshot +========== - def screen_size() -> Tuple[int, int]: - """ - get screen size - """ +.. function:: screenshot(file_path=None, screen_region=None) -.. code-block:: python + Captures the current screen image. - def screenshot(file_path: str = None, screen_region: list = None) -> List[int]: - """ - use to capture current screen image - :param file_path screenshot file save path - :param screen_region screenshot screen_region - """ \ No newline at end of file + :param str file_path: File path to save the screenshot. If ``None``, the image is not saved. + :param list screen_region: Region to capture as ``[x1, y1, x2, y2]``. + If ``None``, captures the full screen. + :returns: The captured screen image. + :rtype: list[int] diff --git a/docs/source/Eng/doc/callback_function/callback_function_doc.rst b/docs/source/Eng/doc/callback_function/callback_function_doc.rst index 1bca1c3..8572d95 100644 --- a/docs/source/Eng/doc/callback_function/callback_function_doc.rst +++ b/docs/source/Eng/doc/callback_function/callback_function_doc.rst @@ -1,56 +1,73 @@ -Callback Function ----- +================= +Callback Executor +================= -In AutoControl, callback functions are supported by the Callback Executor. -Below is a simple example of using the Callback Executor: +The Callback Executor allows you to execute an automation function and trigger a callback +function upon completion. + +Basic Usage +=========== .. code-block:: python - from je_auto_control import callback_executor - # trigger_function will first to execute, but return value need to wait everything done - # so this test will first print("test") then print(size_function_return_value) - print( - callback_executor.callback_function( - trigger_function_name="size", - callback_function=print, - callback_param_method="args", - callback_function_param={"": "test"} - ) - ) - -* Note that if the "name: function" pair in the callback_executor event_dict is different from the executor, it is a bug. -* Of course, like the executor, it can be expanded by adding external functions. Please see the example below. - -In this example, we use callback_executor to execute the "size" function defined in AutoControl. -After executing the "size" function, the function passed to callback_function will be executed. -The delivery method can be determined by the callback_param_method parameter. -If it is "args", please pass in {"value1", "value2", ...}. -Here, the ellipsis (...) represents multiple inputs. - If it is "kwargs", please pass in {"actually_param_name": value, ...}. -Here, the ellipsis (...) again represents multiple inputs. - If you want to use the return value, -since the return value will only be returned after all functions are executed, -you will actually see the "print" statement -before the "print(size_function_return_value)" statement in this example, -even though the order of size -> print is correct. -This is because the "size" function only returns the value itself without printing it. - -This code will load all built-in functions, methods, and classes of the time module into the callback executor. -To use the loaded functions, we need to use the package_function name, -for example, time.sleep will become time_sleep. - -If we want to add functions in the callback_executor, we can use the following code: - -This code will add all the functions of the time module to the executor (interpreter). + from je_auto_control import callback_executor + + result = callback_executor.callback_function( + trigger_function_name="screen_size", + callback_function=print, + callback_param_method="args", + callback_function_param={"": "Callback triggered!"} + ) + print(f"Return value: {result}") + +How It Works +============ + +1. The ``trigger_function_name`` function executes first. +2. After it completes, the ``callback_function`` is called. +3. The return value of the trigger function is returned after all callbacks finish. + +Parameters +========== + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Parameter + - Description + * - ``trigger_function_name`` + - Name of the function to execute (must exist in ``event_dict``) + * - ``callback_function`` + - The function to call after the trigger function completes + * - ``callback_function_param`` + - Parameters to pass to the callback function (dict) + * - ``callback_param_method`` + - ``"args"`` for positional or ``"kwargs"`` for keyword arguments + * - ``**kwargs`` + - Additional keyword arguments passed to the trigger function + +Extending the Callback Executor +================================ + +Load external package functions into the callback executor: .. code-block:: python - from je_auto_control import package_manager - package_manager.add_package_to_callback_executor("time") + from je_auto_control import package_manager + + # Add all functions from the 'time' module + package_manager.add_package_to_callback_executor("time") -If you need to check the updated event_dict, you can use: +To inspect the current event dictionary: .. code-block:: python - from je_auto_control import callback_executor - print(callback_executor.event_dict) \ No newline at end of file + from je_auto_control import callback_executor + + print(callback_executor.event_dict) + +.. note:: + + The callback executor's ``event_dict`` should contain the same function mappings as the + main executor. If they differ, it is a bug. diff --git a/docs/source/Eng/doc/cli/cli_doc.rst b/docs/source/Eng/doc/cli/cli_doc.rst index 8c2dbc9..a2a709e 100644 --- a/docs/source/Eng/doc/cli/cli_doc.rst +++ b/docs/source/Eng/doc/cli/cli_doc.rst @@ -1,19 +1,54 @@ -CLI ----- +======================= +Command-Line Interface +======================= -We can use the CLI mode to execute the keyword. -json file or execute the folder containing the Keyword.json files. +AutoControl can be used directly from the command line to execute automation scripts. -The following example is to execute the specified path of the keyword JSON file. +Execute a Single Action File +============================= -.. code-block:: +.. code-block:: bash - python je_auto_control --execute_file "your_file_path" + python -m je_auto_control --execute_file "path/to/actions.json" + # Short form + python -m je_auto_control -e "path/to/actions.json" +Execute All Files in a Directory +================================ -The following example is to run all keyword JSON files in a specified folder: +.. code-block:: bash -.. code-block:: + python -m je_auto_control --execute_dir "path/to/action_files/" - python je_auto_control --execute_dir "your_dir_path" \ No newline at end of file + # Short form + python -m je_auto_control -d "path/to/action_files/" + +Execute a JSON String Directly +============================== + +.. code-block:: bash + + python -m je_auto_control --execute_str '[["AC_screenshot", {"file_path": "test.png"}]]' + +Create a Project Template +========================= + +.. code-block:: bash + + python -m je_auto_control --create_project "path/to/my_project" + + # Short form + python -m je_auto_control -c "path/to/my_project" + +Launch the GUI +============== + +.. code-block:: bash + + python -m je_auto_control + +.. note:: + + Launching the GUI requires the ``[gui]`` extra to be installed: + ``pip install je_auto_control[gui]`` diff --git a/docs/source/Eng/doc/create_project/create_project_doc.rst b/docs/source/Eng/doc/create_project/create_project_doc.rst index 431815f..e911a42 100644 --- a/docs/source/Eng/doc/create_project/create_project_doc.rst +++ b/docs/source/Eng/doc/create_project/create_project_doc.rst @@ -1,23 +1,48 @@ -Create Project ----- +================== +Project Management +================== -In AutoControl, you can create a project which will automatically generate sample files once the project is created. -These sample files include a Python executor file and a keyword.json file. +AutoControl can scaffold a project directory with template files to help you get started quickly. -To create a project, you can use the following method: +Creating a Project +================== + +Using Python: .. code-block:: python - from je_auto_control import create_project_dir - # create on current workdir - create_project_dir() - # create project on project_path - create_project_dir("project_path") - # create project on project_path and dir name is My First Project - create_project_dir("project_path", "My First Project") + from je_auto_control import create_project_dir + + # Create in current working directory + create_project_dir() + + # Create at a specific path + create_project_dir("path/to/project") + + # Create with a custom directory name + create_project_dir("path/to/project", "My First Project") + +Using the CLI: + +.. code-block:: bash + + python -m je_auto_control --create_project "path/to/project" + +Generated Structure +=================== -Or using CLI, this will generate a project at the project_path location. +.. code-block:: text -.. code-block:: console + my_project/ + └── AutoControl/ + ├── keyword/ + │ ├── keyword1.json # Template action file + │ ├── keyword2.json # Template action file + │ └── bad_keyword_1.json # Error handling template + └── executor/ + ├── executor_one_file.py # Execute single file example + ├── executor_folder.py # Execute folder example + └── executor_bad_file.py # Error handling example - python -m je_auto_control --create_project project_path \ No newline at end of file +The ``keyword/`` directory contains JSON action files, and the ``executor/`` directory +contains Python scripts that demonstrate how to run them. diff --git a/docs/source/Eng/doc/critical_exit/critical_exit_doc.rst b/docs/source/Eng/doc/critical_exit/critical_exit_doc.rst index ba9c4f2..e5691d2 100644 --- a/docs/source/Eng/doc/critical_exit/critical_exit_doc.rst +++ b/docs/source/Eng/doc/critical_exit/critical_exit_doc.rst @@ -1,42 +1,62 @@ +============= Critical Exit ----- +============= -* Critical Exit is a mechanism that provides fault protection. -* Critical Exit is disabled by default. -* If enabled, the default hotkey is F7. -* To enable Critical Exit, call CriticalExit().init_critical_exit() -* (enabling it will consume additional system resources). +Critical Exit is a safety mechanism that allows you to forcibly stop an automation script +by pressing a hotkey (default: **F7**). -The following example is to make the mouse move uncontrollably and throw an exception. -When the exception is caught, initialize the Critical Exit and automatically press F7. -(Note! If you modify this example, you must be extremely careful. -You may lose control of your computer, such as the mouse being out of control.) +.. warning:: + + Critical Exit is **disabled by default**. Enabling it consumes additional system resources + as it runs a background thread that continuously monitors the keyboard. + +Enabling Critical Exit +====================== + +.. code-block:: python + + from je_auto_control import CriticalExit + + CriticalExit().init_critical_exit() + +After calling ``init_critical_exit()``, pressing **F7** will interrupt the main thread +and terminate the program. + +Changing the Hotkey +=================== .. code-block:: python - import sys + from je_auto_control import CriticalExit + + critical = CriticalExit() + critical.set_critical_key("escape") # Use Escape instead of F7 + critical.init_critical_exit() + +Example: Recovering from Runaway Mouse +======================================= + +.. code-block:: python - from je_auto_control import AutoControlMouseException - from je_auto_control import CriticalExit - from je_auto_control import press_key - from je_auto_control import set_position - from je_auto_control import size + import sys + from je_auto_control import ( + CriticalExit, AutoControlMouseException, + set_mouse_position, screen_size, press_keyboard_key + ) - # print your screen width and height + print(screen_size()) - print(size()) + try: + while True: + set_mouse_position(200, 400) + set_mouse_position(400, 600) + raise AutoControlMouseException + except Exception as error: + print(repr(error), file=sys.stderr) + CriticalExit().init_critical_exit() + press_keyboard_key("f7") - # simulate you can't use your mouse because you use while true to set mouse position +.. danger:: - try: - from time import sleep - # Or no sleep - sleep(3) - while True: - set_mouse_position(200, 400) - set_mouse_position(400, 600) - raise AutoControlMouseException - except Exception as error: - print(repr(error), file=sys.stderr) - CriticalExit().init_critical_exit() - press_key("f7") \ No newline at end of file + Be extremely careful when testing automation loops that move the mouse continuously. + Always have Critical Exit enabled or another way to regain control. diff --git a/docs/source/Eng/doc/generate_report/generate_report_doc.rst b/docs/source/Eng/doc/generate_report/generate_report_doc.rst index d4d8cb9..9ed362d 100644 --- a/docs/source/Eng/doc/generate_report/generate_report_doc.rst +++ b/docs/source/Eng/doc/generate_report/generate_report_doc.rst @@ -1,156 +1,84 @@ -Generate Report ----- +================= +Report Generation +================= -* Generate Report can generate reports in the following formats: - * HTML - * JSON - * XML +AutoControl can generate test reports in HTML, JSON, and XML formats. Reports record +which automation steps were executed and whether they succeeded or failed. -* Generate Report is mainly used to record and confirm which steps were executed and whether they were successful or not. -* If you want to use Generate Report, you need to set the recording to True by using test_record_instance.init_record = True. -* The following example is used with keywords and an executor. If you don't understand, please first take a look at the executor. +Setup +===== -Here's an example of generating an HTML report. +Before generating reports, enable test recording: .. code-block:: python - import sys - - from je_auto_control import execute_action - from je_auto_control import test_record_instance - - test_list = None - test_record_instance.init_record = True - if sys.platform in ["win32", "cygwin", "msys"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 65}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_html_report"], - ] - - elif sys.platform in ["linux", "linux2"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 38}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_html_report"], - ] - elif sys.platform in ["darwin"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 0x00}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_html_report"], - ] - print("\n\n") - execute_action(test_list) - - -Here's an example of generating an JSON report. + from je_auto_control import test_record_instance + + test_record_instance.init_record = True + +.. important:: + + Recording must be enabled **before** executing actions, otherwise no data will be captured. + +Generating Reports +================== + +HTML Report +----------- + +.. code-block:: python + + from je_auto_control import execute_action, generate_html_report, test_record_instance + + test_record_instance.init_record = True + + actions = [ + ["set_record_enable", {"set_enable": True}], + ["AC_set_mouse_position", {"x": 500, "y": 500}], + ["AC_click_mouse", {"mouse_keycode": "mouse_left"}], + ["generate_html_report"], + ] + execute_action(actions) + +This produces an HTML file where successful actions appear in **cyan** and failed actions in **red**. + +JSON Report +----------- .. code-block:: python - import sys - - from je_auto_control import execute_action - from je_auto_control import test_record_instance - - test_list = None - test_record_instance.init_record = True - if sys.platform in ["win32", "cygwin", "msys"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 65}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_json_report"], - ] - - elif sys.platform in ["linux", "linux2"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 38}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_json_report"], - ] - elif sys.platform in ["darwin"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 0x00}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_json_report"], - ] - print("\n\n") - execute_action(test_list) - -Here's an example of generating an XML report. + from je_auto_control import generate_json_report + + generate_json_report("test_report") # -> test_report.json + +XML Report +---------- + +.. code-block:: python + + from je_auto_control import generate_xml_report + + generate_xml_report("test_report") # -> test_report.xml + +Getting Report Content as String +================================ + +If you need the report content without saving to a file: .. code-block:: python - import sys - - from je_auto_control import execute_action - from je_auto_control import test_record_instance - - test_list = None - test_record_instance.init_record = True - if sys.platform in ["win32", "cygwin", "msys"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 65}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_xml_report"] - ] - - elif sys.platform in ["linux", "linux2"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 38}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_xml_report"] - ] - elif sys.platform in ["darwin"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 0x00}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_xml_report"] - ] - print("\n\n") - execute_action(test_list) + from je_auto_control import generate_html, generate_json, generate_xml + + html_string = generate_html() + json_data = generate_json() + xml_data = generate_xml() + +Report Contents +=============== + +Each report entry includes: + +* **Function name** -- the automation function that was called +* **Parameters** -- the arguments passed to the function +* **Timestamp** -- when the action was executed +* **Exception info** -- error details if the action failed diff --git a/docs/source/Eng/doc/image/image_doc.rst b/docs/source/Eng/doc/image/image_doc.rst index cdfd02f..9c04a1e 100644 --- a/docs/source/Eng/doc/image/image_doc.rst +++ b/docs/source/Eng/doc/image/image_doc.rst @@ -1,60 +1,85 @@ -Image detect ----- +================= +Image Recognition +================= -* Image detect provides functionalities related to image recognition. -* Locating the position of a single image on the screen. -* Locating the position of multiple images on the screen. -* Locating the position of an image on the screen and clicking on it. +AutoControl uses OpenCV template matching to locate UI elements on the screen. +This is useful for finding buttons, icons, or other visual elements and interacting with them. -This is mainly used to recognize images on the screen and perform clicks or determine if the image exists on the screen. +Locate All Matches +================== -The following example is to locate all images. +Find all occurrences of a template image on the screen: .. code-block:: python - import time + import time + from je_auto_control import locate_all_image, screenshot - from je_auto_control import locate_all_image - from je_auto_control import screenshot + time.sleep(2) - time.sleep(2) + # detect_threshold: 0.0 ~ 1.0 (1.0 = exact match) + image_data = locate_all_image( + screenshot(), + detect_threshold=0.9, + draw_image=False + ) + print(image_data) # [[x1, y1, x2, y2], ...] - # detect_threshold 0~1 , 1 is absolute equal - # draw_image, mark the find target +Locate Image Center +=================== - image_data = locate_all_image(screenshot(), detect_threshold=0.9, draw_image=False) - print(image_data) - -The following example is used to locate and click on an image. +Find a template image and return its center coordinates: .. code-block:: python - import time + import time + from je_auto_control import locate_image_center, screenshot - from je_auto_control import locate_and_click - from je_auto_control import screenshot + time.sleep(2) - time.sleep(2) + cx, cy = locate_image_center( + screenshot(), + detect_threshold=0.9, + draw_image=False + ) + print(f"Found at center: ({cx}, {cy})") - # detect_threshold 0~1 , 1 is absolute equal - # draw_image, mark the find target - image_data = locate_and_click(screenshot(), "mouse_left", detect_threshold=0.9, draw_image=False) - print(image_data) +Locate and Click +================ -The following example locates an image. +Find a template image and automatically click on its center: .. code-block:: python - import time - - from je_auto_control import locate_image_center - from je_auto_control import screenshot - - time.sleep(2) - - # detect_threshold 0~1 , 1 is absolute equal - # draw_image, mark the find target - - image_data = locate_image_center(screenshot(), detect_threshold=0.9, draw_image=False) - print(image_data) - + import time + from je_auto_control import locate_and_click, screenshot + + time.sleep(2) + + image_data = locate_and_click( + screenshot(), + "mouse_left", + detect_threshold=0.9, + draw_image=False + ) + print(image_data) + +Parameters +========== + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Type + - Description + * - ``image`` + - str / PIL Image + - The template image to search for (file path or PIL ``ImageGrab.grab()`` result) + * - ``detect_threshold`` + - float + - Detection precision from ``0.0`` to ``1.0``. ``1.0`` requires an exact match. + * - ``draw_image`` + - bool + - If ``True``, marks the detected area on the returned image. diff --git a/docs/source/Eng/doc/installation/installation_doc.rst b/docs/source/Eng/doc/installation/installation_doc.rst index 719caf7..dc27cc1 100644 --- a/docs/source/Eng/doc/installation/installation_doc.rst +++ b/docs/source/Eng/doc/installation/installation_doc.rst @@ -1,31 +1,60 @@ +============ Installation ----- +============ -.. code-block:: python +Install from PyPI +================= - pip install je_auto_control +.. code-block:: bash -* Python & pip require version - * Python 3.7 & up - * pip 19.3 & up + pip install je_auto_control -* Dev env - * windows 11 - * osx 11 big sur - * ubuntu 20.0.4 +Requirements +============ -| If you want install on raspberry pi +* Python 3.10 or higher +* pip 19.3 or higher -.. code-block:: python +With GUI Support +================ - sudo apt-get install python3 - pip3 install je_auto_control - sudo apt-get install libcblas-dev - sudo apt-get install libhdf5-dev - sudo apt-get install libhdf5-serial-dev - sudo apt-get install libatlas-base-dev - sudo apt-get install libjasper-dev - sudo apt-get install libqtgui4 - sudo apt-get install libqt4-test - pip3 install -U pillow - pip3 install -U numpy \ No newline at end of file +.. code-block:: bash + + pip install je_auto_control[gui] + +Linux Prerequisites +=================== + +.. code-block:: bash + + sudo apt-get install cmake libssl-dev + +Raspberry Pi +============ + +.. code-block:: bash + + sudo apt-get install python3 + pip3 install je_auto_control + sudo apt-get install libcblas-dev libhdf5-dev libhdf5-serial-dev + sudo apt-get install libatlas-base-dev libjasper-dev + sudo apt-get install libqtgui4 libqt4-test + pip3 install -U pillow numpy + +Development Environment +======================= + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Platform + - Tested Version + * - Windows + - Windows 11 + * - macOS + - macOS Big Sur (11) + * - Linux + - Ubuntu 20.04 + * - Raspberry Pi + - 3B / 4B diff --git a/docs/source/Eng/doc/keyboard/keyboard_doc.rst b/docs/source/Eng/doc/keyboard/keyboard_doc.rst index 7e6f4cc..3f2afb1 100644 --- a/docs/source/Eng/doc/keyboard/keyboard_doc.rst +++ b/docs/source/Eng/doc/keyboard/keyboard_doc.rst @@ -1,69 +1,96 @@ -Keyboard ----- +================ +Keyboard Control +================ -* Used to simulate keyboard control. -* Provides functions such as hotkeys, checking keyboard key status (whether pressed), and simulating keyboard control. -* The following example is to obtain information about the keyboard. +AutoControl provides functions for simulating keyboard input including key press/release, +typing strings, hotkey combinations, and key state detection. -* special_table is a table of special keyboard keys (Note! Not every platform has them). -* keys_table is a table of all available keys. +Getting Key Tables +================== + +Retrieve available key names: .. code-block:: python - from je_auto_control import keys_table, get_special_table + from je_auto_control import keys_table, get_special_table + + # All available keys for your platform + print(keys_table) + + # Special keys (platform-specific) + print(get_special_table()) - print(keys_table) - print(get_special_table()) +.. tip:: + See :doc:`/API/special/keyboard_keys` for the full list of available keyboard keys per platform. -The following example presses a certain key on the keyboard and releases it after one second. +Press and Release +================= + +Hold a key down and release it after a delay: .. code-block:: python - from time import sleep - from je_auto_control import press_keyboard_key, release_keyboard_key + from time import sleep + from je_auto_control import press_keyboard_key, release_keyboard_key + + press_keyboard_key("a") + sleep(1) + release_keyboard_key("a") - press_key("a") - sleep(1) - release_key("a") +Type a Single Key +================= -The following example presses and releases a key. +Press and immediately release a key: .. code-block:: python - from je_auto_control import type_keyboard + from je_auto_control import type_keyboard - type_keyboard("a") + type_keyboard("a") -The following example checks whether the 'a' key on the keyboard is being pressed. +Check Key State +=============== + +Check whether a specific key is currently pressed: .. code-block:: python - from je_auto_control import check_key_is_press + from je_auto_control import check_key_is_press + + is_pressed = check_key_is_press("a") + print(f"Key 'a' is pressed: {is_pressed}") - check_key_is_press("a") +Type a String +============= -The following example presses and releases a series of keys. +Type a sequence of characters one by one: .. code-block:: python - from je_auto_control import write + from je_auto_control import write - write("abcdefg") + write("Hello World") -The following example uses a hotkey to press and then release the passed-in key in reverse. +Hotkey Combinations +=================== + +Press multiple keys in sequence, then release them in reverse order: .. code-block:: python - import sys + import sys + from je_auto_control import hotkey + + if sys.platform in ["win32", "cygwin", "msys"]: + hotkey(["lcontrol", "a", "lcontrol", "c", "lcontrol", "v"]) - from je_auto_control import hotkey + elif sys.platform == "darwin": + hotkey(["command", "a", "command", "c", "command", "v"]) - if sys.platform in ["win32", "cygwin", "msys"]: - hotkey(["lcontrol", "a", "lcontrol", "c", "lcontrol", "v", "lcontrol", "v"]) + elif sys.platform in ["linux", "linux2"]: + hotkey(["ctrl", "a", "ctrl", "c", "ctrl", "v"]) - elif sys.platform in ["darwin"]: - hotkey(["command", "a", "command", "c", "command", "v", "command", "v"]) +.. warning:: - elif sys.platform in ["linux", "linux2"]: - hotkey(["ctrl", "a", "ctrl", "c", "ctrl", "v", "ctrl", "v"]) \ No newline at end of file + Key names differ across platforms. Always check the key table for your target platform. diff --git a/docs/source/Eng/doc/keyword_and_executor/keyword_and_executor_doc.rst b/docs/source/Eng/doc/keyword_and_executor/keyword_and_executor_doc.rst index 34c8c82..05386f1 100644 --- a/docs/source/Eng/doc/keyword_and_executor/keyword_and_executor_doc.rst +++ b/docs/source/Eng/doc/keyword_and_executor/keyword_and_executor_doc.rst @@ -1,53 +1,103 @@ -Keyword & Executor ----- - -Keyword is a JSON file that contains many custom keywords and parameters. -Keywords are used in conjunction with the Executor. -The format of a keyword is as shown in the following example, and the same format is used in the JSON file. +==================== +Keywords & Executor +==================== + +The Keyword/Executor system is AutoControl's JSON-based scripting engine. You define automation +steps as JSON arrays (keywords), and the executor interprets and runs them. + +Keyword Format +============== + +Keywords are JSON arrays where each element is an action: + +.. code-block:: json + + [ + ["function_name", {"param_name": "param_value"}], + ["function_name", {"param_name": "param_value"}] + ] + +For example: + +.. code-block:: json + + [ + ["AC_set_mouse_position", {"x": 500, "y": 300}], + ["AC_click_mouse", {"mouse_keycode": "mouse_left"}], + ["AC_write", {"write_string": "Hello"}] + ] + +Available Action Commands +========================= + +.. list-table:: + :header-rows: 1 + :widths: 20 80 + + * - Category + - Commands + * - Mouse + - ``AC_click_mouse``, ``AC_set_mouse_position``, ``AC_get_mouse_position``, ``AC_press_mouse``, ``AC_release_mouse``, ``AC_mouse_scroll`` + * - Keyboard + - ``AC_type_keyboard``, ``AC_press_keyboard_key``, ``AC_release_keyboard_key``, ``AC_write``, ``AC_hotkey``, ``AC_check_key_is_press`` + * - Image + - ``AC_locate_all_image``, ``AC_locate_image_center``, ``AC_locate_and_click`` + * - Screen + - ``AC_screen_size``, ``AC_screenshot`` + * - Record + - ``AC_record``, ``AC_stop_record`` + * - Report + - ``AC_generate_html``, ``AC_generate_json``, ``AC_generate_xml``, ``AC_generate_html_report``, ``AC_generate_json_report``, ``AC_generate_xml_report`` + * - Project + - ``AC_create_project`` + * - Shell + - ``AC_shell_command`` + * - Executor + - ``AC_execute_action``, ``AC_execute_files`` + +Executing a JSON File +===================== .. code-block:: python - [ - ["function_name_in_event_dict": {"param_name": param_value}], - ["function_name_in_event_dict": {"param_name": param_value}], - ["function_name_in_event_dict": {"param_name": param_value}], - # many.... - # If you are using position param - ["function_name_in_event_dict": {param_value1, param_value2....}] - ] + from je_auto_control import execute_action, read_action_json -The executor is an interpreter that can parse JSON files and execute automation scripts. -It can be easily transferred over the network to a remote server or computer, -which can then execute the automation scripts using the executor. + execute_action(read_action_json("actions.json")) -If we want to add a function to the executor, we can use the following code snippet. -This code will load all the built-in functions, methods, and classes of the time module into the executor. -To use the loaded functions, we need to use the package_function name, for example, time.sleep will become time_sleep. +Executing All JSON Files in a Directory +======================================= .. code-block:: python - from je_auto_control import package_manager - package_manager.add_package_to_executor("time") + from je_auto_control import execute_files, get_dir_files_as_list + execute_files(get_dir_files_as_list("./action_files/")) +Extending the Executor +====================== -If you need to check the updated event_dict, you can use: +You can dynamically load external Python packages into the executor: .. code-block:: python - from je_auto_control import executor - print(executor.event_dict) + from je_auto_control import package_manager -If we want to execute a JSON file + # Load all functions from the 'time' module + package_manager.add_package_to_executor("time") -.. code-block:: python +After loading, functions are available with the ``package_function`` naming convention. +For example, ``time.sleep`` becomes ``time_sleep``: - from je_auto_control import execute_action, read_action_json - execute_action(read_action_json(file_path)) +.. code-block:: json -If we want to execute all JSON files in a folder, we can use the following code snippet: + [ + ["time_sleep", {"secs": 2}] + ] + +To inspect the current executor command dictionary: .. code-block:: python - from je_auto_control import execute_files, get_dir_files_as_list - execute_files(get_dir_files_as_list(dir_path)) \ No newline at end of file + from je_auto_control import executor + + print(executor.event_dict) diff --git a/docs/source/Eng/doc/mouse/mouse_doc.rst b/docs/source/Eng/doc/mouse/mouse_doc.rst index e26f62f..6f16543 100644 --- a/docs/source/Eng/doc/mouse/mouse_doc.rst +++ b/docs/source/Eng/doc/mouse/mouse_doc.rst @@ -1,54 +1,83 @@ -Mouse ----- +============= +Mouse Control +============= -* This module is used for simulating mouse control. -* It provides functions for simulating clicks, setting positions, etc. +AutoControl provides functions for simulating mouse actions including clicking, +positioning, scrolling, and drag operations. -The following example is to obtain information about mouse clicks: -mouse_table contains all the available mouse buttons. +Getting Mouse Button Table +========================== + +Retrieve all available mouse button key names: .. code-block:: python - from je_auto_control import mouse_table + from je_auto_control import mouse_table + + print(mouse_table) + +.. tip:: - print(mouse_table) + See :doc:`/API/special/mouse_keys` for the full list of available mouse keys per platform. -The following example is holding down the mouse button and releasing it after one second. +Press and Release +================= + +Hold down a mouse button and release it after a delay: .. code-block:: python - from time import sleep + from time import sleep + from je_auto_control import press_mouse, release_mouse - from je_auto_control import press_mouse, release_mouse + press_mouse("mouse_right") + sleep(1) + release_mouse("mouse_right") - press_mouse("mouse_right") - sleep(1) - release_mouse("mouse_right") +Click +===== -The following example is clicking and releasing the mouse. +Press and immediately release a mouse button: .. code-block:: python - from je_auto_control import click_mouse + from je_auto_control import click_mouse + + # Right click at current position + click_mouse("mouse_right") - click_mouse("mouse_right") + # Left click at specific coordinates + click_mouse("mouse_left", x=500, y=300) -The following example is to check the mouse position and change the mouse position. +Position +======== + +Get and set the mouse cursor position: .. code-block:: python - from je_auto_control import get_mouse_position, set_mouse_position + from je_auto_control import get_mouse_position, set_mouse_position + + # Get current position + x, y = get_mouse_position() + print(f"Mouse at: ({x}, {y})") - print(get_mouse_position()) - set_mouse_position(100, 100) + # Move mouse to (100, 100) + set_mouse_position(100, 100) -Here's an example where the mouse will scroll up after 3 seconds: +Scroll +====== + +Scroll the mouse wheel: .. code-block:: python - from time import sleep - from je_auto_control import scroll + from je_auto_control import mouse_scroll + + # Scroll down by 5 units + mouse_scroll(scroll_value=5) - sleep(3) +.. note:: - scroll(100) \ No newline at end of file + On Linux, you can specify the scroll direction using the ``scroll_direction`` parameter: + ``"scroll_up"``, ``"scroll_down"``, ``"scroll_left"``, ``"scroll_right"``. diff --git a/docs/source/Eng/doc/record/record_doc.rst b/docs/source/Eng/doc/record/record_doc.rst index bcd3f35..8c379be 100644 --- a/docs/source/Eng/doc/record/record_doc.rst +++ b/docs/source/Eng/doc/record/record_doc.rst @@ -1,30 +1,38 @@ -Record ----- +===================== +Recording & Playback +===================== -* Record is mainly used to record keyboard and mouse actions. -* It can be used with an executor to replay keyboard and mouse actions. -* The following example shows how to use it. +AutoControl can record mouse and keyboard events and replay them using the executor. + +Record and Replay +================= .. code-block:: python - from time import sleep - - from je_auto_control import execute_action - from je_auto_control import record - from je_auto_control import stop_record - from je_auto_control import type_keyboard - - # this program will type test two time - # one time is type key one time is test_record - - record() - sleep(1) - print(type_keyboard("t")) - print(type_keyboard("e")) - print(type_keyboard("s")) - print(type_keyboard("t")) - sleep(2) - record_result = stop_record() - print(record_result) - execute_action(record_result) - sleep(5) \ No newline at end of file + from time import sleep + from je_auto_control import record, stop_record, execute_action + + # Start recording all mouse and keyboard events + record() + + sleep(5) # Record for 5 seconds + + # Stop and get the recorded action list + actions = stop_record() + print(actions) + + # Replay the recorded actions + execute_action(actions) + +.. note:: + + Action recording is **not available on macOS**. See :doc:`/getting_started/installation` for platform support details. + +How It Works +============ + +1. ``record()`` starts a background listener that captures all mouse and keyboard events. +2. ``stop_record()`` stops the listener and returns a list of actions in the executor-compatible format. +3. ``execute_action(actions)`` replays the captured actions through the built-in executor. + +The recorded actions are in the same JSON format used by the :doc:`/Eng/doc/keyword_and_executor/keyword_and_executor_doc`, so you can save them to a file and replay later. diff --git a/docs/source/Eng/doc/scheduler/scheduler_doc.rst b/docs/source/Eng/doc/scheduler/scheduler_doc.rst index 7f5d68b..2ef3337 100644 --- a/docs/source/Eng/doc/scheduler/scheduler_doc.rst +++ b/docs/source/Eng/doc/scheduler/scheduler_doc.rst @@ -1,19 +1,87 @@ +========= Scheduler ----- +========= -You can use scheduling to perform repetitive tasks, either by using a simple wrapper for APScheduler or by consulting the API documentation to use it yourself. +AutoControl provides a wrapper around `APScheduler `_ for +scheduling repetitive automation tasks. + +Basic Example +============= + +.. code-block:: python + + from je_auto_control import SchedulerManager + + def my_task(): + print("Task executed!") + scheduler.remove_blocking_job(id="my_job") + scheduler.shutdown_blocking_scheduler() + + scheduler = SchedulerManager() + scheduler.add_interval_blocking_secondly(function=my_task, id="my_job") + scheduler.start_block_scheduler() + +Blocking vs Non-Blocking +========================= + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Mode + - Description + * - Blocking + - ``start_block_scheduler()`` blocks the current thread. Use for standalone scheduler scripts. + * - Non-blocking + - ``start_nonblocking_scheduler()`` runs in a background thread. Use when you need the main thread for other work. + +Interval Scheduling +=================== + +Schedule a function to run at fixed intervals: + +.. code-block:: python + + # Every second + scheduler.add_interval_blocking_secondly(function=my_task, id="job1") + + # Every minute + scheduler.add_interval_blocking_minutely(function=my_task, id="job2") + + # Every hour + scheduler.add_interval_blocking_hourly(function=my_task, id="job3") + + # Every day + scheduler.add_interval_blocking_daily(function=my_task, id="job4") + + # Every week + scheduler.add_interval_blocking_weekly(function=my_task, id="job5") + +Non-blocking equivalents are available with ``add_interval_nonblocking_*`` methods. + +Cron Scheduling +=============== .. code-block:: python - from je_auto_control import SchedulerManager + scheduler.add_cron_blocking(function=my_task, id="cron_job", hour=9, minute=30) +Removing Jobs +============= + +.. code-block:: python + + scheduler.remove_blocking_job(id="job1") + scheduler.remove_nonblocking_job(id="job2") + +Shutting Down +============= + +.. code-block:: python - def test_scheduler(): - print("Test Scheduler") - scheduler.remove_blocking_job(id="test") - scheduler.shutdown_blocking_scheduler() + scheduler.shutdown_blocking_scheduler() + scheduler.shutdown_nonblocking_scheduler() +.. tip:: - scheduler = SchedulerManager() - scheduler.add_interval_blocking_secondly(function=test_scheduler, id="test") - scheduler.start_block_scheduler() + See the :doc:`/API/utils/scheduler` for the complete API reference. diff --git a/docs/source/Eng/doc/screen/screen_doc.rst b/docs/source/Eng/doc/screen/screen_doc.rst index c37814f..8a2f995 100644 --- a/docs/source/Eng/doc/screen/screen_doc.rst +++ b/docs/source/Eng/doc/screen/screen_doc.rst @@ -1,20 +1,44 @@ -Screen ----- +================= +Screen Operations +================= -* Screen is used to get the screen size and take screenshots. +AutoControl provides functions for capturing screenshots and retrieving screen dimensions. -The following example shows how to take a screenshot. +Screenshot +========== + +Capture the current screen and save to a file: .. code-block:: python - from je_auto_control import screenshot + from je_auto_control import screenshot + + # Save a full-screen screenshot + screenshot("my_screenshot.png") + + # Capture a specific region [x1, y1, x2, y2] + screenshot("region.png", screen_region=[100, 100, 500, 400]) + +Screen Size +=========== + +Get the current screen resolution: + +.. code-block:: python + + from je_auto_control import screen_size + + width, height = screen_size() + print(f"Screen resolution: {width} x {height}") - screenshot() +Get Pixel Color +=============== -The following example shows how to get a screen size. +Retrieve the color of a pixel at specific coordinates: .. code-block:: python - from je_auto_control import size + from je_auto_control import get_pixel - print(size()) \ No newline at end of file + color = get_pixel(500, 300) + print(f"Pixel color at (500, 300): {color}") diff --git a/docs/source/Eng/doc/socket_driver/socket_driver_doc.rst b/docs/source/Eng/doc/socket_driver/socket_driver_doc.rst index 5aef2d8..7e731d5 100644 --- a/docs/source/Eng/doc/socket_driver/socket_driver_doc.rst +++ b/docs/source/Eng/doc/socket_driver/socket_driver_doc.rst @@ -1,25 +1,67 @@ -Socket Driver ----- - -* This is an experimental feature. -* The Socket Server is mainly used to allow other programming languages to use AutoControl. -* It processes received strings and performs actions through the underlying executor. -* Tests can be performed remotely through this feature. -* Currently, Java and C# support are experimental. -* Return_Data_Over_JE should be transmitted at the end of each paragraph. -* UTF-8 encoding is used. -* Sending quit_server will shut down the server. +========================== +Socket Server (Remote API) +========================== + +.. warning:: + + This is an **experimental** feature. + +The Socket Server allows other programming languages (or remote machines) to use AutoControl +by sending JSON commands over TCP. + +Starting the Server +=================== + +.. code-block:: python + + import sys + from je_auto_control import start_autocontrol_socket_server + + try: + server = start_autocontrol_socket_server(host="localhost", port=9938) + while not server.close_flag: + pass + sys.exit(0) + except Exception as error: + print(repr(error)) + +The server runs in a background thread and listens for JSON action commands. + +Sending Commands (Client) +========================= .. code-block:: python - import sys + import socket + import json + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(("localhost", 9938)) + + command = json.dumps([ + ["AC_set_mouse_position", {"x": 500, "y": 300}], + ["AC_click_mouse", {"mouse_keycode": "mouse_left"}] + ]) + sock.sendall(command.encode("utf-8")) + + response = sock.recv(8192).decode("utf-8") + print(response) + sock.close() + +Protocol Details +================ - from je_auto_control import start_autocontrol_socket_server +.. list-table:: + :header-rows: 1 + :widths: 30 70 - try: - server = start_autocontrol_socket_server() - while not server.close_flag: - pass - sys.exit(0) - except Exception as error: - print(repr(error)) \ No newline at end of file + * - Property + - Value + * - Encoding + - UTF-8 + * - End-of-response marker + - ``Return_Data_Over_JE`` + * - Shutdown command + - Send ``"quit_server"`` to stop the server + * - Default port + - 9938 diff --git a/docs/source/Eng/eng_index.rst b/docs/source/Eng/eng_index.rst index b8c1d01..6e3b5b4 100644 --- a/docs/source/Eng/eng_index.rst +++ b/docs/source/Eng/eng_index.rst @@ -1,20 +1,24 @@ -AutoControl English Documentation ----- +============================== +English Documentation +============================== + +Comprehensive guides for all AutoControl features. .. toctree:: - :maxdepth: 4 + :maxdepth: 2 + :caption: User Guide - doc/installation/installation_doc.rst - doc/create_project/create_project_doc.rst - doc/keyboard/keyboard_doc.rst - doc/mouse/mouse_doc.rst - doc/screen/screen_doc.rst - doc/image/image_doc.rst - doc/record/record_doc.rst - doc/generate_report/generate_report_doc.rst - doc/callback_function/callback_function_doc.rst - doc/keyword_and_executor/keyword_and_executor_doc.rst - doc/critical_exit/critical_exit_doc.rst - doc/cli/cli_doc.rst - doc/scheduler/scheduler_doc.rst - doc/socket_driver/socket_driver_doc.rst \ No newline at end of file + doc/installation/installation_doc + doc/mouse/mouse_doc + doc/keyboard/keyboard_doc + doc/screen/screen_doc + doc/image/image_doc + doc/record/record_doc + doc/keyword_and_executor/keyword_and_executor_doc + doc/generate_report/generate_report_doc + doc/callback_function/callback_function_doc + doc/scheduler/scheduler_doc + doc/socket_driver/socket_driver_doc + doc/critical_exit/critical_exit_doc + doc/cli/cli_doc + doc/create_project/create_project_doc diff --git a/docs/source/Zh/doc/callback_function/callback_function_doc.rst b/docs/source/Zh/doc/callback_function/callback_function_doc.rst index b58ca20..9470157 100644 --- a/docs/source/Zh/doc/callback_function/callback_function_doc.rst +++ b/docs/source/Zh/doc/callback_function/callback_function_doc.rst @@ -1,50 +1,72 @@ -回調函數 ----- +================ +回調函數執行器 +================ -在 AutoControl 裡,Callback function 是由 Callback Executor 提供支援, -以下是簡易的使用 Callback Executor 的範例, +回調函數執行器允許你執行一個自動化函式,並在完成後觸發回調函式。 + +基本用法 +======== .. code-block:: python - from je_auto_control import callback_executor - # trigger_function will first to execute, but return value need to wait everything done - # so this test will first print("test") then print(size_function_return_value) - print( - callback_executor.callback_function( - trigger_function_name="size", - callback_function=print, - callback_param_method="args", - callback_function_param={"": "test"} - ) - ) - -* ( 注意!,如果 callback_executor event_dict 裡面包含的 name: function 需與 executor 一樣,不一樣則是 Bug) -* (當然跟 executor 一樣可以藉由添加外部 function 來擴充,請看下面例子) - -在這個範例裡,我們使用 callback_executor 執行定義在 AutoControl 的 size function, -然後執行完 size function 後,會去執行傳遞給 callback_function 的 function, -可以由 callback_param_method 參數來決定要使用的傳遞方法, -如果是 "args" 請傳入 {"value1", "value2", ...} 這裡 ... 代表可以複數傳入, -如果是 "kwargs" 請傳入 {"actually_param_name": value, ...} 這裡 ... 代表可以複數傳入, -然後如果要使用回傳值的話,由於回傳值會在所有 function 執行完後才回傳, -實際上 size -> print 順序沒錯,但此例會先看到 print 之後才是 print(size_function_return_value), -因為 size function 只有回傳值本身沒有 print 的動作。 - -如果我們想要在 callback_executor 裡面添加 function,可以使用如下: -這段程式碼會把所有 time module 的 builtin, function, method, class -載入到 callback executor,然後要使用被載入的 function 需要使用 package_function 名稱, -例如 time.sleep 會變成 time_sleep - -這段程式碼會把 time 所有的 function 加入 executor(直譯器裡)。 + from je_auto_control import callback_executor + + result = callback_executor.callback_function( + trigger_function_name="screen_size", + callback_function=print, + callback_param_method="args", + callback_function_param={"": "回調已觸發!"} + ) + print(f"回傳值: {result}") + +運作方式 +======== + +1. ``trigger_function_name`` 指定的函式首先執行。 +2. 完成後,呼叫 ``callback_function``。 +3. 觸發函式的回傳值在所有回調完成後回傳。 + +參數說明 +======== + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - 參數 + - 說明 + * - ``trigger_function_name`` + - 要執行的函式名稱(必須存在於 ``event_dict`` 中) + * - ``callback_function`` + - 觸發函式完成後要呼叫的函式 + * - ``callback_function_param`` + - 傳遞給回調函式的參數(dict) + * - ``callback_param_method`` + - ``"args"`` 表示位置引數,``"kwargs"`` 表示關鍵字引數 + * - ``**kwargs`` + - 傳遞給觸發函式的額外關鍵字引數 + +擴充回調執行器 +============== + +載入外部套件函式到回調執行器: .. code-block:: python - from je_auto_control import package_manager - package_manager.add_package_to_callback_executor("time") + from je_auto_control import package_manager + + # 載入 time 模組的所有函式 + package_manager.add_package_to_callback_executor("time") -如果你需要查看被更新的 event_dict 可以使用 +查看目前的事件字典: .. code-block:: python - from je_auto_control import callback_executor - print(callback_executor.event_dict) \ No newline at end of file + from je_auto_control import callback_executor + + print(callback_executor.event_dict) + +.. note:: + + 回調執行器的 ``event_dict`` 應該包含與主執行器相同的函式對應。 + 若不一致,則為 Bug。 diff --git a/docs/source/Zh/doc/cli/cli_doc.rst b/docs/source/Zh/doc/cli/cli_doc.rst index 10503aa..96135ed 100644 --- a/docs/source/Zh/doc/cli/cli_doc.rst +++ b/docs/source/Zh/doc/cli/cli_doc.rst @@ -1,17 +1,54 @@ +============ 命令列介面 ----- +============ -我們可以使用 CLI 模式去執行 keyword.json 檔案或執行包含 Keyword.json files 的資料夾, -以下這個範例是去執行指定路徑的關鍵字 json 檔 +AutoControl 可以直接從命令列執行自動化腳本。 -.. code-block:: +執行單一動作檔案 +================ - python je_auto_control --execute_file "your_file_path" +.. code-block:: bash + python -m je_auto_control --execute_file "path/to/actions.json" + # 簡寫 + python -m je_auto_control -e "path/to/actions.json" -以下這個範例是去執行指定路徑資料夾下所有的 keyword json 檔 +執行資料夾內所有檔案 +==================== -.. code-block:: +.. code-block:: bash - python je_auto_control --execute_dir "your_dir_path" \ No newline at end of file + python -m je_auto_control --execute_dir "path/to/action_files/" + + # 簡寫 + python -m je_auto_control -d "path/to/action_files/" + +直接執行 JSON 字串 +================== + +.. code-block:: bash + + python -m je_auto_control --execute_str '[["AC_screenshot", {"file_path": "test.png"}]]' + +建立專案範本 +============ + +.. code-block:: bash + + python -m je_auto_control --create_project "path/to/my_project" + + # 簡寫 + python -m je_auto_control -c "path/to/my_project" + +啟動 GUI +======== + +.. code-block:: bash + + python -m je_auto_control + +.. note:: + + 啟動 GUI 需要安裝 ``[gui]`` 額外套件: + ``pip install je_auto_control[gui]`` diff --git a/docs/source/Zh/doc/create_project/create_project_doc.rst b/docs/source/Zh/doc/create_project/create_project_doc.rst index 29aebfc..449a3c6 100644 --- a/docs/source/Zh/doc/create_project/create_project_doc.rst +++ b/docs/source/Zh/doc/create_project/create_project_doc.rst @@ -1,23 +1,47 @@ -創建專案 ----- +======== +專案管理 +======== -在 AutoControl 裡可以創建專案,創建專案後將會自動生成範例文件, -範例文件包含 python executor 檔案以及 keyword.json 檔案。 +AutoControl 可以建立專案目錄架構與範本檔案,幫助你快速開始。 -要創建專案可以用以下方式: +建立專案 +======== + +使用 Python: .. code-block:: python - from je_auto_control import create_project_dir - # create on current workdir - create_project_dir() - # create project on project_path - create_project_dir("project_path") - # create project on project_path and dir name is My First Project - create_project_dir("project_path", "My First Project") + from je_auto_control import create_project_dir + + # 在目前工作目錄建立 + create_project_dir() + + # 在指定路徑建立 + create_project_dir("path/to/project") + + # 在指定路徑建立,並自訂目錄名稱 + create_project_dir("path/to/project", "My First Project") + +使用 CLI: + +.. code-block:: bash + + python -m je_auto_control --create_project "path/to/project" + +產生的目錄結構 +============== -或是這個方式將會在 project_path 路徑產生專案 +.. code-block:: text -.. code-block:: console + my_project/ + └── AutoControl/ + ├── keyword/ + │ ├── keyword1.json # 動作範本檔案 + │ ├── keyword2.json # 動作範本檔案 + │ └── bad_keyword_1.json # 錯誤處理範本 + └── executor/ + ├── executor_one_file.py # 執行單一檔案範例 + ├── executor_folder.py # 執行資料夾範例 + └── executor_bad_file.py # 錯誤處理範例 - python -m je_auto_control --create_project project_path \ No newline at end of file +``keyword/`` 目錄包含 JSON 動作檔案,``executor/`` 目錄包含示範如何執行的 Python 腳本。 diff --git a/docs/source/Zh/doc/critical_exit/critical_exit_doc.rst b/docs/source/Zh/doc/critical_exit/critical_exit_doc.rst index 250131b..5b0ff96 100644 --- a/docs/source/Zh/doc/critical_exit/critical_exit_doc.rst +++ b/docs/source/Zh/doc/critical_exit/critical_exit_doc.rst @@ -1,41 +1,60 @@ +======== 緊急退出 ----- +======== -* Critical Exit 是提供故障保護的機制。 -* Critical Exit 預設是關閉的。 -* 如果開啟,預設按鍵是 F7。 -* 開啟的方法是 CriticalExit().init_critical_exit() (開啟後會額外消耗系統資源) +緊急退出是一種安全機制,允許你透過按下熱鍵(預設:**F7**)來強制停止自動化腳本。 -以下這個範例是讓滑鼠不受控制的移動並拋出例外, -當接收到例外,初始化 Critical Exit 並自動按下 F7, -( 注意! 如果修改這個範例必須極度小心。 ) -(你可能會失去對你電腦的控制,例如滑鼠不受控制) +.. warning:: + + 緊急退出 **預設為關閉**。啟用後會額外消耗系統資源, + 因為會在背景執行緒中持續監控鍵盤。 + +啟用緊急退出 +============ + +.. code-block:: python + + from je_auto_control import CriticalExit + + CriticalExit().init_critical_exit() + +呼叫 ``init_critical_exit()`` 後,按下 **F7** 會中斷主執行緒並終止程式。 + +更改熱鍵 +======== .. code-block:: python - import sys + from je_auto_control import CriticalExit + + critical = CriticalExit() + critical.set_critical_key("escape") # 使用 Escape 取代 F7 + critical.init_critical_exit() + +範例:從失控的滑鼠恢復 +====================== + +.. code-block:: python - from je_auto_control import AutoControlMouseException - from je_auto_control import CriticalExit - from je_auto_control import press_key - from je_auto_control import set_position - from je_auto_control import size + import sys + from je_auto_control import ( + CriticalExit, AutoControlMouseException, + set_mouse_position, screen_size, press_keyboard_key + ) - # print your screen width and height + print(screen_size()) - print(size()) + try: + while True: + set_mouse_position(200, 400) + set_mouse_position(400, 600) + raise AutoControlMouseException + except Exception as error: + print(repr(error), file=sys.stderr) + CriticalExit().init_critical_exit() + press_keyboard_key("f7") - # simulate you can't use your mouse because you use while true to set mouse position +.. danger:: - try: - from time import sleep - # Or no sleep - sleep(3) - while True: - set_mouse_position(200, 400) - set_mouse_position(400, 600) - raise AutoControlMouseException - except Exception as error: - print(repr(error), file=sys.stderr) - CriticalExit().init_critical_exit() - press_key("f7") \ No newline at end of file + 測試持續移動滑鼠的自動化迴圈時請極度小心。 + 務必啟用緊急退出或準備其他方式來重新取得控制。 diff --git a/docs/source/Zh/doc/generate_report/generate_report_doc.rst b/docs/source/Zh/doc/generate_report/generate_report_doc.rst index d27d83d..7567799 100644 --- a/docs/source/Zh/doc/generate_report/generate_report_doc.rst +++ b/docs/source/Zh/doc/generate_report/generate_report_doc.rst @@ -1,156 +1,84 @@ +======== 報告產生 ----- +======== -Generate Report 可以生成以下格式的報告 +AutoControl 可以產生 HTML、JSON 和 XML 格式的測試報告。報告會記錄哪些自動化步驟被執行, +以及是否成功。 -* HTML -* JSON -* XML -* Generate Report 主要用來記錄與確認有哪些步驟執行,執行是否成功, -* 如果要使用 Generate Report 需要先設定紀錄為 True,使用 test_record_instance.init_record = True -* 下面的範例有搭配 keyword and executor 如果看不懂可以先去看看 executor +設定 +==== -以下是產生 HTML 的範例。 +在產生報告之前,需先啟用測試記錄: .. code-block:: python - import sys - - from je_auto_control import execute_action - from je_auto_control import test_record_instance - - test_list = None - test_record_instance.init_record = True - if sys.platform in ["win32", "cygwin", "msys"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 65}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_html_report"], - ] - - elif sys.platform in ["linux", "linux2"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 38}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_html_report"], - ] - elif sys.platform in ["darwin"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 0x00}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_html_report"], - ] - print("\n\n") - execute_action(test_list) - - -以下是產生 JSON 的範例。 + from je_auto_control import test_record_instance + + test_record_instance.init_record = True + +.. important:: + + 記錄必須在執行動作 **之前** 啟用,否則不會擷取到任何資料。 + +產生報告 +======== + +HTML 報告 +--------- + +.. code-block:: python + + from je_auto_control import execute_action, generate_html_report, test_record_instance + + test_record_instance.init_record = True + + actions = [ + ["set_record_enable", {"set_enable": True}], + ["AC_set_mouse_position", {"x": 500, "y": 500}], + ["AC_click_mouse", {"mouse_keycode": "mouse_left"}], + ["generate_html_report"], + ] + execute_action(actions) + +產生的 HTML 報告中,成功的動作以 **青色** 顯示,失敗的動作以 **紅色** 顯示。 + +JSON 報告 +--------- .. code-block:: python - import sys - - from je_auto_control import execute_action - from je_auto_control import test_record_instance - - test_list = None - test_record_instance.init_record = True - if sys.platform in ["win32", "cygwin", "msys"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 65}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_json_report"], - ] - - elif sys.platform in ["linux", "linux2"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 38}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_json_report"], - ] - elif sys.platform in ["darwin"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 0x00}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_json_report"], - ] - print("\n\n") - execute_action(test_list) - -以下是產生 XML 的範例。 + from je_auto_control import generate_json_report + + generate_json_report("test_report") # -> test_report.json + +XML 報告 +-------- + +.. code-block:: python + + from je_auto_control import generate_xml_report + + generate_xml_report("test_report") # -> test_report.xml + +取得報告內容為字串 +================== + +如果需要報告內容而不儲存為檔案: .. code-block:: python - import sys - - from je_auto_control import execute_action - from je_auto_control import test_record_instance - - test_list = None - test_record_instance.init_record = True - if sys.platform in ["win32", "cygwin", "msys"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 65}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_xml_report"] - ] - - elif sys.platform in ["linux", "linux2"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 38}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_xml_report"] - ] - elif sys.platform in ["darwin"]: - test_list = [ - ["set_record_enable", {"set_enable": True}], - ["type_keyboard", {"keycode": 0x00}], - ["mouse_left", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["get_mouse_position"], - ["press_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["release_mouse", {"mouse_keycode": "mouse_left", "x": 500, "y": 500}], - ["type_keyboard", {"mouse_keycode": "dwadwawda", "dwadwad": 500, "wdawddwawad": 500}], - ["generate_xml_report"] - ] - print("\n\n") - execute_action(test_list) + from je_auto_control import generate_html, generate_json, generate_xml + + html_string = generate_html() + json_data = generate_json() + xml_data = generate_xml() + +報告內容 +======== + +每筆報告記錄包含: + +* **函式名稱** -- 被呼叫的自動化函式 +* **參數** -- 傳遞給函式的引數 +* **時間戳記** -- 動作執行的時間 +* **例外資訊** -- 動作失敗時的錯誤詳情 diff --git a/docs/source/Zh/doc/image/image_doc.rst b/docs/source/Zh/doc/image/image_doc.rst index ec57f14..aa41d4c 100644 --- a/docs/source/Zh/doc/image/image_doc.rst +++ b/docs/source/Zh/doc/image/image_doc.rst @@ -1,60 +1,85 @@ -圖片偵測 ----- +======== +圖片辨識 +======== -* Image 提供了關於圖像辨識的功能。 -* 定位一張圖片在螢幕中的位置。 -* 定位多張圖片在螢幕中的位置。 -* 定位圖片在螢幕中的位置並點擊。 +AutoControl 使用 OpenCV 模板匹配技術在螢幕上定位 UI 元素。 +可用於尋找按鈕、圖示或其他視覺元素並與之互動。 -主要用來在螢幕上辨識圖片並進行點擊,或是判斷圖片是否存在螢幕上。 +定位所有匹配 +============ -以下範例是定位所有圖片 +尋找螢幕上所有符合模板圖片的位置: .. code-block:: python - import time + import time + from je_auto_control import locate_all_image, screenshot - from je_auto_control import locate_all_image - from je_auto_control import screenshot + time.sleep(2) - time.sleep(2) + # detect_threshold: 0.0 ~ 1.0(1.0 = 完全匹配) + image_data = locate_all_image( + screenshot(), + detect_threshold=0.9, + draw_image=False + ) + print(image_data) # [[x1, y1, x2, y2], ...] - # detect_threshold 0~1 , 1 is absolute equal - # draw_image, mark the find target +定位圖片中心 +============ - image_data = locate_all_image(screenshot(), detect_threshold=0.9, draw_image=False) - print(image_data) - -以下範例是定位並點擊圖片 +尋找模板圖片並回傳其中心座標: .. code-block:: python - import time + import time + from je_auto_control import locate_image_center, screenshot - from je_auto_control import locate_and_click - from je_auto_control import screenshot + time.sleep(2) - time.sleep(2) + cx, cy = locate_image_center( + screenshot(), + detect_threshold=0.9, + draw_image=False + ) + print(f"找到位置: ({cx}, {cy})") - # detect_threshold 0~1 , 1 is absolute equal - # draw_image, mark the find target - image_data = locate_and_click(screenshot(), "mouse_left", detect_threshold=0.9, draw_image=False) - print(image_data) +定位並點擊 +========== -以下範例是定位圖片 +尋找模板圖片並自動點擊其中心: .. code-block:: python - import time - - from je_auto_control import locate_image_center - from je_auto_control import screenshot - - time.sleep(2) - - # detect_threshold 0~1 , 1 is absolute equal - # draw_image, mark the find target - - image_data = locate_image_center(screenshot(), detect_threshold=0.9, draw_image=False) - print(image_data) - + import time + from je_auto_control import locate_and_click, screenshot + + time.sleep(2) + + image_data = locate_and_click( + screenshot(), + "mouse_left", + detect_threshold=0.9, + draw_image=False + ) + print(image_data) + +參數說明 +======== + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 參數 + - 型別 + - 說明 + * - ``image`` + - str / PIL Image + - 要搜尋的模板圖片(檔案路徑或 PIL ``ImageGrab.grab()`` 結果) + * - ``detect_threshold`` + - float + - 偵測精確度,範圍 ``0.0`` 到 ``1.0``。``1.0`` 要求完全匹配。 + * - ``draw_image`` + - bool + - 若為 ``True``,會在回傳的圖片上標記偵測到的區域。 diff --git a/docs/source/Zh/doc/installation/installation_doc.rst b/docs/source/Zh/doc/installation/installation_doc.rst index 06543f8..0d1fb50 100644 --- a/docs/source/Zh/doc/installation/installation_doc.rst +++ b/docs/source/Zh/doc/installation/installation_doc.rst @@ -1,31 +1,60 @@ +====== 安裝 ----- +====== -.. code-block:: python +從 PyPI 安裝 +============ - pip install je_auto_control +.. code-block:: bash -* Python & pip require version - * Python 3.7 & up - * pip 19.3 & up + pip install je_auto_control -* Dev env - * windows 11 - * osx 11 big sur - * ubuntu 20.0.4 +系統需求 +======== -| 如果想要在樹莓派使用 +* Python 3.10 以上 +* pip 19.3 以上 -.. code-block:: python +安裝 GUI 支援 +============= - sudo apt-get install python3 - pip3 install je_auto_control - sudo apt-get install libcblas-dev - sudo apt-get install libhdf5-dev - sudo apt-get install libhdf5-serial-dev - sudo apt-get install libatlas-base-dev - sudo apt-get install libjasper-dev - sudo apt-get install libqtgui4 - sudo apt-get install libqt4-test - pip3 install -U pillow - pip3 install -U numpy \ No newline at end of file +.. code-block:: bash + + pip install je_auto_control[gui] + +Linux 前置套件 +============== + +.. code-block:: bash + + sudo apt-get install cmake libssl-dev + +樹莓派 +====== + +.. code-block:: bash + + sudo apt-get install python3 + pip3 install je_auto_control + sudo apt-get install libcblas-dev libhdf5-dev libhdf5-serial-dev + sudo apt-get install libatlas-base-dev libjasper-dev + sudo apt-get install libqtgui4 libqt4-test + pip3 install -U pillow numpy + +開發環境 +======== + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - 平台 + - 測試版本 + * - Windows + - Windows 11 + * - macOS + - macOS Big Sur (11) + * - Linux + - Ubuntu 20.04 + * - Raspberry Pi + - 3B / 4B diff --git a/docs/source/Zh/doc/keyboard/keyboard_doc.rst b/docs/source/Zh/doc/keyboard/keyboard_doc.rst index 05c5276..0c15091 100644 --- a/docs/source/Zh/doc/keyboard/keyboard_doc.rst +++ b/docs/source/Zh/doc/keyboard/keyboard_doc.rst @@ -1,69 +1,96 @@ -鍵盤 ----- +======== +鍵盤控制 +======== -* 用來模擬鍵盤的控制。 -* 提供熱鍵、檢盤鍵盤按鍵狀態(是否按下)、模擬鍵盤控制等功能。 +AutoControl 提供模擬鍵盤輸入的功能,包括按鍵按下/釋放、輸入字串、 +熱鍵組合及按鍵狀態偵測。 -以下範例是取得鍵盤的資訊, -* special_table 是特定的鍵盤按鍵 ( 注意! 不是每個平台都有 ) -* keys_table 是所有可以使用的按鍵 +取得按鍵表 +========== + +取得可用的按鍵名稱: .. code-block:: python - from je_auto_control import keys_table, get_special_table + from je_auto_control import keys_table, get_special_table + + # 取得所有可用按鍵 + print(keys_table) + + # 取得特殊按鍵(因平台而異) + print(get_special_table()) - print(keys_table) - print(get_special_table()) +.. tip:: + 完整的鍵盤按鍵列表請參考 :doc:`/API/special/keyboard_keys`。 -以下範例是按著鍵盤的傳入的按鍵,並在一秒後釋放 +按下與釋放 +========== + +按住按鍵,延遲後釋放: .. code-block:: python - from time import sleep - from je_auto_control import press_keyboard_key, release_keyboard_key + from time import sleep + from je_auto_control import press_keyboard_key, release_keyboard_key + + press_keyboard_key("a") + sleep(1) + release_keyboard_key("a") - press_key("a") - sleep(1) - release_key("a") +按下單一按鍵 +============ -以下範例會幫你完成按下與釋放傳入的按鍵 +按下並立即釋放一個按鍵: .. code-block:: python - from je_auto_control import type_keyboard + from je_auto_control import type_keyboard - type_keyboard("a") + type_keyboard("a") -以下範例是檢查鍵盤 a 鍵是否按著 +檢查按鍵狀態 +============= + +檢查某個按鍵是否正被按住: .. code-block:: python - from je_auto_control import check_key_is_press + from je_auto_control import check_key_is_press + + is_pressed = check_key_is_press("a") + print(f"按鍵 'a' 被按住: {is_pressed}") - check_key_is_press("a") +輸入字串 +======== -以下範例是按下與放開一串按鍵 +逐字輸入一串字元: .. code-block:: python - from je_auto_control import write + from je_auto_control import write - write("abcdefg") + write("Hello World") -以下範例使用熱鍵,會按下傳入的按鍵並相反的放開 +熱鍵組合 +======== + +依序按下多個按鍵,再反向釋放: .. code-block:: python - import sys + import sys + from je_auto_control import hotkey + + if sys.platform in ["win32", "cygwin", "msys"]: + hotkey(["lcontrol", "a", "lcontrol", "c", "lcontrol", "v"]) - from je_auto_control import hotkey + elif sys.platform == "darwin": + hotkey(["command", "a", "command", "c", "command", "v"]) - if sys.platform in ["win32", "cygwin", "msys"]: - hotkey(["lcontrol", "a", "lcontrol", "c", "lcontrol", "v", "lcontrol", "v"]) + elif sys.platform in ["linux", "linux2"]: + hotkey(["ctrl", "a", "ctrl", "c", "ctrl", "v"]) - elif sys.platform in ["darwin"]: - hotkey(["command", "a", "command", "c", "command", "v", "command", "v"]) +.. warning:: - elif sys.platform in ["linux", "linux2"]: - hotkey(["ctrl", "a", "ctrl", "c", "ctrl", "v", "ctrl", "v"]) \ No newline at end of file + 按鍵名稱在不同平台上有所不同,請務必查閱目標平台的按鍵表。 diff --git a/docs/source/Zh/doc/keyword_and_executor/keyword_and_executor_doc.rst b/docs/source/Zh/doc/keyword_and_executor/keyword_and_executor_doc.rst index f30b143..396807f 100644 --- a/docs/source/Zh/doc/keyword_and_executor/keyword_and_executor_doc.rst +++ b/docs/source/Zh/doc/keyword_and_executor/keyword_and_executor_doc.rst @@ -1,54 +1,103 @@ +================ 關鍵字與執行者 ----- - -* Keyword 是一個 JSON 檔案裏面包含許多自定義的關鍵字與參數。 -* Keyword 會與 Executor 搭配使用。 -* Keyword 的格式是以下範例,且在 JSON 檔案裡面使用一樣格式。 +================ + +關鍵字/執行者系統是 AutoControl 的 JSON 腳本引擎。你可以將自動化步驟定義為 +JSON 陣列(關鍵字),由執行者解析並執行。 + +關鍵字格式 +========== + +關鍵字是 JSON 陣列,每個元素代表一個動作: + +.. code-block:: json + + [ + ["function_name", {"param_name": "param_value"}], + ["function_name", {"param_name": "param_value"}] + ] + +範例: + +.. code-block:: json + + [ + ["AC_set_mouse_position", {"x": 500, "y": 300}], + ["AC_click_mouse", {"mouse_keycode": "mouse_left"}], + ["AC_write", {"write_string": "Hello"}] + ] + +可用的動作指令 +============== + +.. list-table:: + :header-rows: 1 + :widths: 20 80 + + * - 分類 + - 指令 + * - 滑鼠 + - ``AC_click_mouse``, ``AC_set_mouse_position``, ``AC_get_mouse_position``, ``AC_press_mouse``, ``AC_release_mouse``, ``AC_mouse_scroll`` + * - 鍵盤 + - ``AC_type_keyboard``, ``AC_press_keyboard_key``, ``AC_release_keyboard_key``, ``AC_write``, ``AC_hotkey``, ``AC_check_key_is_press`` + * - 圖片 + - ``AC_locate_all_image``, ``AC_locate_image_center``, ``AC_locate_and_click`` + * - 螢幕 + - ``AC_screen_size``, ``AC_screenshot`` + * - 錄製 + - ``AC_record``, ``AC_stop_record`` + * - 報告 + - ``AC_generate_html``, ``AC_generate_json``, ``AC_generate_xml``, ``AC_generate_html_report``, ``AC_generate_json_report``, ``AC_generate_xml_report`` + * - 專案 + - ``AC_create_project`` + * - Shell + - ``AC_shell_command`` + * - 執行器 + - ``AC_execute_action``, ``AC_execute_files`` + +執行 JSON 檔案 +=============== .. code-block:: python - [ - ["function_name_in_event_dict": {"param_name": param_value}], - ["function_name_in_event_dict": {"param_name": param_value}], - ["function_name_in_event_dict": {"param_name": param_value}], - # many.... - # If you are using position param - ["function_name_in_event_dict": {param_value1, param_value2....}] - ] + from je_auto_control import execute_action, read_action_json -executor 是一個會解析 JSON 檔案的直譯器, -可以簡單地透過網路傳輸到遠端伺服器或電腦, -再藉由遠端伺服器或電腦的 executor 執行自動化。 + execute_action(read_action_json("actions.json")) -如果我們想要在 executor 裡面添加 function,可以使用如下: -這段程式碼會把所有 time module 的 builtin, function, method, class -載入到 executor,然後要使用被載入的 function 需要使用 package_function 名稱, -例如 time.sleep 會變成 time_sleep +執行資料夾內所有 JSON 檔案 +=========================== .. code-block:: python - from je_auto_control import package_manager - package_manager.add_package_to_executor("time") + from je_auto_control import execute_files, get_dir_files_as_list + execute_files(get_dir_files_as_list("./action_files/")) +擴充執行者 +========== -如果你需要查看被更新的 event_dict 可以使用 +你可以動態載入外部 Python 套件到執行者中: .. code-block:: python - from je_auto_control import executor - print(executor.event_dict) + from je_auto_control import package_manager -如果我們想要執行 JSON 檔案 + # 載入 time 模組的所有函式 + package_manager.add_package_to_executor("time") -.. code-block:: python +載入後,函式可透過 ``套件_函式名`` 的命名方式使用。 +例如 ``time.sleep`` 會變成 ``time_sleep``: - from je_auto_control import execute_action, read_action_json - execute_action(read_action_json(file_path)) +.. code-block:: json -如果我們想要執行資料夾裡所有 JSON 檔案 + [ + ["time_sleep", {"secs": 2}] + ] + +查看目前執行者的指令字典: .. code-block:: python - from je_auto_control import execute_files, get_dir_files_as_list - execute_files(get_dir_files_as_list(dir_path)) \ No newline at end of file + from je_auto_control import executor + + print(executor.event_dict) diff --git a/docs/source/Zh/doc/mouse/mouse_doc.rst b/docs/source/Zh/doc/mouse/mouse_doc.rst index d65f9e6..f4a680b 100644 --- a/docs/source/Zh/doc/mouse/mouse_doc.rst +++ b/docs/source/Zh/doc/mouse/mouse_doc.rst @@ -1,54 +1,82 @@ -滑鼠 ----- +======== +滑鼠控制 +======== -* 主要用來模擬滑鼠的控制。 -* 提供模擬點擊、設定位置等功能。 +AutoControl 提供模擬滑鼠操作的功能,包括點擊、定位、捲動及拖曳操作。 -以下範例是取得鍵盤的資訊, -* mouse_table 是所有可以使用的按鍵 +取得滑鼠按鍵表 +============== + +取得所有可用的滑鼠按鍵名稱: .. code-block:: python - from je_auto_control import mouse_table + from je_auto_control import mouse_table + + print(mouse_table) + +.. tip:: - print(mouse_table) + 完整的滑鼠按鍵列表請參考 :doc:`/API/special/mouse_keys`。 -以下範例是按著滑鼠,一秒後釋放滑鼠 +按下與釋放 +========== + +按住滑鼠按鍵,延遲後釋放: .. code-block:: python - from time import sleep + from time import sleep + from je_auto_control import press_mouse, release_mouse - from je_auto_control import press_mouse, release_mouse + press_mouse("mouse_right") + sleep(1) + release_mouse("mouse_right") - press_mouse("mouse_right") - sleep(1) - release_mouse("mouse_right") +點擊 +==== -以下範例是點擊並放開滑鼠 +按下並立即釋放滑鼠按鍵: .. code-block:: python - from je_auto_control import click_mouse + from je_auto_control import click_mouse + + # 在目前位置右鍵點擊 + click_mouse("mouse_right") - click_mouse("mouse_right") + # 在指定座標左鍵點擊 + click_mouse("mouse_left", x=500, y=300) -以下範例是檢查滑鼠位置並改變滑鼠位置 +游標位置 +======== + +取得及設定滑鼠游標位置: .. code-block:: python - from je_auto_control import get_mouse_position, set_mouse_position + from je_auto_control import get_mouse_position, set_mouse_position + + # 取得目前位置 + x, y = get_mouse_position() + print(f"滑鼠位置: ({x}, {y})") - print(get_mouse_position()) - set_mouse_position(100, 100) + # 移動滑鼠到 (100, 100) + set_mouse_position(100, 100) -以下範例是3秒後滑鼠會往上 scroll +捲動 +==== + +捲動滑鼠滾輪: .. code-block:: python - from time import sleep - from je_auto_control import scroll + from je_auto_control import mouse_scroll + + # 向下捲動 5 個單位 + mouse_scroll(scroll_value=5) - sleep(3) +.. note:: - scroll(100) \ No newline at end of file + 在 Linux 上,可以使用 ``scroll_direction`` 參數指定捲動方向: + ``"scroll_up"``、``"scroll_down"``、``"scroll_left"``、``"scroll_right"``。 diff --git a/docs/source/Zh/doc/record/record_doc.rst b/docs/source/Zh/doc/record/record_doc.rst index d56c1df..aa4ff0e 100644 --- a/docs/source/Zh/doc/record/record_doc.rst +++ b/docs/source/Zh/doc/record/record_doc.rst @@ -1,31 +1,39 @@ -記錄 ----- +============ +錄製與回放 +============ -* Record 主要用來錄製鍵盤跟滑鼠的動作。 -* 可以搭配 executor 使用來重播鍵盤跟滑鼠動作。 +AutoControl 可以錄製滑鼠與鍵盤事件,並透過執行器回放。 -以下是範例如何使用 +使用範例 +======== .. code-block:: python - from time import sleep - - from je_auto_control import execute_action - from je_auto_control import record - from je_auto_control import stop_record - from je_auto_control import type_keyboard - - # this program will type test two time - # one time is type key one time is test_record - - record() - sleep(1) - print(type_keyboard("t")) - print(type_keyboard("e")) - print(type_keyboard("s")) - print(type_keyboard("t")) - sleep(2) - record_result = stop_record() - print(record_result) - execute_action(record_result) - sleep(5) \ No newline at end of file + from time import sleep + from je_auto_control import record, stop_record, execute_action + + # 開始錄製所有滑鼠與鍵盤事件 + record() + + sleep(5) # 錄製 5 秒 + + # 停止錄製並取得動作列表 + actions = stop_record() + print(actions) + + # 回放錄製的動作 + execute_action(actions) + +.. note:: + + macOS **不支援** 動作錄製功能。請參考 :doc:`/getting_started/installation` 了解平台支援詳情。 + +運作方式 +======== + +1. ``record()`` 啟動背景監聽器,擷取所有滑鼠與鍵盤事件。 +2. ``stop_record()`` 停止監聯器並回傳與執行器相容的動作列表。 +3. ``execute_action(actions)`` 透過內建執行器回放擷取的動作。 + +錄製的動作格式與 :doc:`/Zh/doc/keyword_and_executor/keyword_and_executor_doc` 使用的 JSON 格式相同, +可以儲存為檔案供日後回放。 diff --git a/docs/source/Zh/doc/scheduler/scheduler_doc.rst b/docs/source/Zh/doc/scheduler/scheduler_doc.rst index 9b66156..cb4d8ee 100644 --- a/docs/source/Zh/doc/scheduler/scheduler_doc.rst +++ b/docs/source/Zh/doc/scheduler/scheduler_doc.rst @@ -1,19 +1,87 @@ -Scheduler ----- +====== +排程器 +====== -可以使用排程來執行重複的任務,可以使用對 APScheduler 的簡易包裝或是觀看 API 文件自行使用 +AutoControl 提供 `APScheduler `_ 的包裝, +用於排程重複性的自動化任務。 + +基本範例 +======== + +.. code-block:: python + + from je_auto_control import SchedulerManager + + def my_task(): + print("任務已執行!") + scheduler.remove_blocking_job(id="my_job") + scheduler.shutdown_blocking_scheduler() + + scheduler = SchedulerManager() + scheduler.add_interval_blocking_secondly(function=my_task, id="my_job") + scheduler.start_block_scheduler() + +阻塞與非阻塞模式 +================= + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - 模式 + - 說明 + * - 阻塞 (Blocking) + - ``start_block_scheduler()`` 會阻塞目前的執行緒。適用於獨立的排程腳本。 + * - 非阻塞 (Non-blocking) + - ``start_nonblocking_scheduler()`` 在背景執行緒中執行。適用於主執行緒需要處理其他工作的情況。 + +間隔排程 +======== + +以固定間隔執行函式: + +.. code-block:: python + + # 每秒 + scheduler.add_interval_blocking_secondly(function=my_task, id="job1") + + # 每分鐘 + scheduler.add_interval_blocking_minutely(function=my_task, id="job2") + + # 每小時 + scheduler.add_interval_blocking_hourly(function=my_task, id="job3") + + # 每天 + scheduler.add_interval_blocking_daily(function=my_task, id="job4") + + # 每週 + scheduler.add_interval_blocking_weekly(function=my_task, id="job5") + +非阻塞版本可使用 ``add_interval_nonblocking_*`` 系列方法。 + +Cron 排程 +========= .. code-block:: python - from je_auto_control import SchedulerManager + scheduler.add_cron_blocking(function=my_task, id="cron_job", hour=9, minute=30) +移除任務 +======== + +.. code-block:: python + + scheduler.remove_blocking_job(id="job1") + scheduler.remove_nonblocking_job(id="job2") + +關閉排程器 +========== + +.. code-block:: python - def test_scheduler(): - print("Test Scheduler") - scheduler.remove_blocking_job(id="test") - scheduler.shutdown_blocking_scheduler() + scheduler.shutdown_blocking_scheduler() + scheduler.shutdown_nonblocking_scheduler() +.. tip:: - scheduler = SchedulerManager() - scheduler.add_interval_blocking_secondly(function=test_scheduler, id="test") - scheduler.start_block_scheduler() + 完整 API 參考請見 :doc:`/API/utils/scheduler`。 diff --git a/docs/source/Zh/doc/screen/screen_doc.rst b/docs/source/Zh/doc/screen/screen_doc.rst index fb92983..5b0ddff 100644 --- a/docs/source/Zh/doc/screen/screen_doc.rst +++ b/docs/source/Zh/doc/screen/screen_doc.rst @@ -1,20 +1,44 @@ -螢幕 ----- +======== +螢幕操作 +======== -* Screen 用來取得螢幕尺寸與截圖。 +AutoControl 提供截圖與取得螢幕資訊的功能。 -以下範例是截圖 +截圖 +==== + +擷取目前螢幕畫面並儲存為檔案: .. code-block:: python - from je_auto_control import screenshot + from je_auto_control import screenshot + + # 全螢幕截圖 + screenshot("my_screenshot.png") + + # 擷取特定區域 [x1, y1, x2, y2] + screenshot("region.png", screen_region=[100, 100, 500, 400]) + +螢幕尺寸 +======== + +取得目前螢幕解析度: + +.. code-block:: python + + from je_auto_control import screen_size + + width, height = screen_size() + print(f"螢幕解析度: {width} x {height}") - screenshot() +取得像素顏色 +============ -以下範例是取得螢幕尺寸 +取得指定座標的像素顏色: .. code-block:: python - from je_auto_control import size + from je_auto_control import get_pixel - print(size()) \ No newline at end of file + color = get_pixel(500, 300) + print(f"(500, 300) 的像素顏色: {color}") diff --git a/docs/source/Zh/doc/socket_driver/socket_driver_doc.rst b/docs/source/Zh/doc/socket_driver/socket_driver_doc.rst index f0784a7..8c957b6 100644 --- a/docs/source/Zh/doc/socket_driver/socket_driver_doc.rst +++ b/docs/source/Zh/doc/socket_driver/socket_driver_doc.rst @@ -1,26 +1,66 @@ -Socket Driver ----- +============================== +Socket 伺服器(遠端 API) +============================== -* 實驗性的功能。 -* Socket Server 主要用來讓其他程式語言也可以使用 AutoControl。 -* 透過底層的 executor 處理接收到字串並進行執行動作。 -* 可以透過遠端來執行測試的動作。 +.. warning:: -* 目前有實驗性的 Java 與 C# 支援。 -* 每個段落結束都應該傳輸 Return_Data_Over_JE。 -* 使用 UTF-8 encoding。 -* 傳送 quit_server 將會關閉伺服器。 + 這是 **實驗性** 功能。 + +Socket 伺服器允許其他程式語言(或遠端機器)透過 TCP 傳送 JSON 指令來使用 AutoControl。 + +啟動伺服器 +========== + +.. code-block:: python + + import sys + from je_auto_control import start_autocontrol_socket_server + + try: + server = start_autocontrol_socket_server(host="localhost", port=9938) + while not server.close_flag: + pass + sys.exit(0) + except Exception as error: + print(repr(error)) + +伺服器在背景執行緒中執行,監聽 JSON 自動化指令。 + +傳送指令(客戶端) +================== .. code-block:: python - import sys + import socket + import json + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(("localhost", 9938)) + + command = json.dumps([ + ["AC_set_mouse_position", {"x": 500, "y": 300}], + ["AC_click_mouse", {"mouse_keycode": "mouse_left"}] + ]) + sock.sendall(command.encode("utf-8")) + + response = sock.recv(8192).decode("utf-8") + print(response) + sock.close() + +協定細節 +======== - from je_auto_control import start_autocontrol_socket_server +.. list-table:: + :header-rows: 1 + :widths: 30 70 - try: - server = start_autocontrol_socket_server() - while not server.close_flag: - pass - sys.exit(0) - except Exception as error: - print(repr(error)) \ No newline at end of file + * - 屬性 + - 值 + * - 編碼 + - UTF-8 + * - 回應結束標記 + - ``Return_Data_Over_JE`` + * - 關閉伺服器指令 + - 傳送 ``"quit_server"`` 以停止伺服器 + * - 預設連接埠 + - 9938 diff --git a/docs/source/Zh/zh_index.rst b/docs/source/Zh/zh_index.rst index 4576cc3..4b5a22a 100644 --- a/docs/source/Zh/zh_index.rst +++ b/docs/source/Zh/zh_index.rst @@ -1,20 +1,24 @@ -AutoControl 繁體中文 文件 ----- +==================== +繁體中文文件 +==================== + +AutoControl 所有功能的完整使用指南。 .. toctree:: - :maxdepth: 4 + :maxdepth: 2 + :caption: 使用者指南 - doc/installation/installation_doc.rst - doc/create_project/create_project_doc.rst - doc/keyboard/keyboard_doc.rst - doc/mouse/mouse_doc.rst - doc/screen/screen_doc.rst - doc/image/image_doc.rst - doc/record/record_doc.rst - doc/generate_report/generate_report_doc.rst - doc/callback_function/callback_function_doc.rst - doc/keyword_and_executor/keyword_and_executor_doc.rst - doc/critical_exit/critical_exit_doc.rst - doc/cli/cli_doc.rst - doc/scheduler/scheduler_doc.rst - doc/socket_driver/socket_driver_doc.rst + doc/installation/installation_doc + doc/mouse/mouse_doc + doc/keyboard/keyboard_doc + doc/screen/screen_doc + doc/image/image_doc + doc/record/record_doc + doc/keyword_and_executor/keyword_and_executor_doc + doc/generate_report/generate_report_doc + doc/callback_function/callback_function_doc + doc/scheduler/scheduler_doc + doc/socket_driver/socket_driver_doc + doc/critical_exit/critical_exit_doc + doc/cli/cli_doc + doc/create_project/create_project_doc diff --git a/docs/source/conf.py b/docs/source/conf.py index c1b47ff..b5e1f29 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,51 +1,49 @@ # Configuration file for the Sphinx documentation builder. # -# This file only contains a selection of the most common options. For a full -# list see the documentation: +# For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# import os import sys -sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('../..')) # -- Project information ----------------------------------------------------- project = 'AutoControl' copyright = '2020 ~ Now, JE-Chen' author = 'JE-Chen' +release = '0.0.179' # -- General configuration --------------------------------------------------- -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. extensions = [ - "sphinx.ext.autosectionlabel" + 'sphinx.ext.autosectionlabel', + 'sphinx.ext.autodoc', + 'sphinx.ext.viewcode', ] -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +autosectionlabel_prefix_document = True -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. +templates_path = ['_templates'] exclude_patterns = [] # -- Options for HTML output ------------------------------------------------- -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# html_theme = 'sphinx_rtd_theme' -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". +html_theme_options = { + 'navigation_depth': 4, + 'collapse_navigation': False, + 'sticky_navigation': True, + 'includehidden': True, + 'titles_only': False, +} + html_static_path = ['_static'] + +# -- Options for language ---------------------------------------------------- + +language = 'en' +locale_dirs = ['locale/'] +gettext_compact = False diff --git a/docs/source/getting_started/installation.rst b/docs/source/getting_started/installation.rst new file mode 100644 index 0000000..0bfb61d --- /dev/null +++ b/docs/source/getting_started/installation.rst @@ -0,0 +1,106 @@ +============ +Installation +============ + +Requirements +============ + +* **Python** >= 3.10 +* **pip** >= 19.3 + +Basic Installation +================== + +Install AutoControl from PyPI: + +.. code-block:: bash + + pip install je_auto_control + +With GUI Support +================ + +To use the built-in PySide6 graphical interface: + +.. code-block:: bash + + pip install je_auto_control[gui] + +Linux Prerequisites +=================== + +On Linux, install the following system packages **before** installing AutoControl: + +.. code-block:: bash + + sudo apt-get install cmake libssl-dev + +Raspberry Pi +============ + +On Raspberry Pi (3B / 4B), install the following: + +.. code-block:: bash + + sudo apt-get install python3 + pip3 install je_auto_control + sudo apt-get install libcblas-dev libhdf5-dev libhdf5-serial-dev + sudo apt-get install libatlas-base-dev libjasper-dev + sudo apt-get install libqtgui4 libqt4-test + pip3 install -U pillow numpy + +Dependencies +============ + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - Package + - Purpose + * - ``je_open_cv`` + - Image recognition (OpenCV template matching) + * - ``pillow`` + - Screenshot capture + * - ``mss`` + - Fast multi-monitor screenshot + * - ``pyobjc`` + - macOS backend (auto-installed on macOS) + * - ``python-Xlib`` + - Linux X11 backend (auto-installed on Linux) + * - ``PySide6`` + - GUI application (optional, install with ``[gui]``) + * - ``qt-material`` + - GUI theme (optional, install with ``[gui]``) + +Platform Support +================ + +.. list-table:: + :header-rows: 1 + :widths: 20 15 25 40 + + * - Platform + - Status + - Backend + - Notes + * - Windows 10 / 11 + - Supported + - Win32 API (ctypes) + - Full feature support + * - macOS 10.15+ + - Supported + - pyobjc / Quartz + - Action recording not available; ``send_key_event_to_window`` / ``send_mouse_event_to_window`` not supported + * - Linux (X11) + - Supported + - python-Xlib + - Full feature support + * - Linux (Wayland) + - Not supported + - -- + - May be added in a future release + * - Raspberry Pi 3B / 4B + - Supported + - python-Xlib + - Runs on X11 diff --git a/docs/source/getting_started/quickstart.rst b/docs/source/getting_started/quickstart.rst new file mode 100644 index 0000000..eb86f41 --- /dev/null +++ b/docs/source/getting_started/quickstart.rst @@ -0,0 +1,124 @@ +=========== +Quick Start +=========== + +This guide walks you through the most common AutoControl features with minimal examples. + +Mouse Control +============= + +.. code-block:: python + + import je_auto_control + + # Get current mouse position + x, y = je_auto_control.get_mouse_position() + print(f"Mouse at: ({x}, {y})") + + # Move mouse to coordinates + je_auto_control.set_mouse_position(500, 300) + + # Left click at current position + je_auto_control.click_mouse("mouse_left") + + # Right click at specific coordinates + je_auto_control.click_mouse("mouse_right", x=800, y=400) + + # Scroll down + je_auto_control.mouse_scroll(scroll_value=5) + +Keyboard Control +================ + +.. code-block:: python + + import je_auto_control + + # Press and release a single key + je_auto_control.type_keyboard("a") + + # Type a whole string character by character + je_auto_control.write("Hello World") + + # Hotkey combination (e.g., Ctrl+C) + je_auto_control.hotkey(["ctrl_l", "c"]) + + # Check if a key is currently pressed + is_pressed = je_auto_control.check_key_is_press("shift_l") + +Image Recognition +================= + +.. code-block:: python + + import je_auto_control + + # Find all occurrences of an image on screen + positions = je_auto_control.locate_all_image("button.png", detect_threshold=0.9) + + # Find a single image and get its center coordinates + cx, cy = je_auto_control.locate_image_center("icon.png", detect_threshold=0.85) + + # Find an image and automatically click it + je_auto_control.locate_and_click("submit_button.png", mouse_keycode="mouse_left") + +Screenshot +========== + +.. code-block:: python + + import je_auto_control + + # Full-screen screenshot + je_auto_control.pil_screenshot("screenshot.png") + + # Screenshot of a specific region [x1, y1, x2, y2] + je_auto_control.pil_screenshot("region.png", screen_region=[100, 100, 500, 400]) + + # Get screen resolution + width, height = je_auto_control.screen_size() + +Action Recording & Playback +============================ + +.. code-block:: python + + import je_auto_control + import time + + je_auto_control.record() + time.sleep(10) # Record for 10 seconds + actions = je_auto_control.stop_record() + + # Replay the recorded actions + je_auto_control.execute_action(actions) + +JSON Action Scripting +===================== + +Create a JSON action file (``actions.json``): + +.. code-block:: json + + [ + ["AC_set_mouse_position", {"x": 500, "y": 300}], + ["AC_click_mouse", {"mouse_keycode": "mouse_left"}], + ["AC_write", {"write_string": "Hello from AutoControl"}], + ["AC_hotkey", {"key_code_list": ["ctrl_l", "s"]}] + ] + +Execute it: + +.. code-block:: python + + import je_auto_control + + je_auto_control.execute_action( + je_auto_control.read_action_json("actions.json") + ) + +What's Next? +============ + +* See the :doc:`../Eng/eng_index` for detailed guides on each feature. +* See the :doc:`../API/api_index` for complete API reference. diff --git a/docs/source/index.rst b/docs/source/index.rst index 5a74b08..1564138 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,29 +1,65 @@ +=========== AutoControl +=========== + +**AutoControl** is a cross-platform Python GUI automation framework providing mouse control, +keyboard input, image recognition, screen capture, action scripting, and report generation +-- all through a unified API that works on Windows, macOS, and Linux (X11). + +.. note:: + + AutoControl does not currently support Linux Wayland. This may be added in a future release. + ---- +Getting Started +=============== + .. toctree:: - :maxdepth: 4 + :maxdepth: 2 + :caption: Getting Started - Eng/eng_index.rst - Zh/zh_index.rst - API/api_index.rst + getting_started/installation + getting_started/quickstart ----- +User Guide (English) +==================== -RoadMap +.. toctree:: + :maxdepth: 2 + :caption: User Guide (English) ----- + Eng/eng_index -* Project Kanban -* https://github.com/orgs/Integration-Automation/projects/2/views/1 +User Guide (繁體中文) +===================== ----- +.. toctree:: + :maxdepth: 2 + :caption: 使用者指南 (繁體中文) -Notice + Zh/zh_index ----- +API Reference +============= + +.. toctree:: + :maxdepth: 2 + :caption: API Reference -* We don't support Unix/Linux Wayland GUI Now \ -* May be future feature + API/api_index ---- + +Links +===== + +* `PyPI `_ +* `GitHub `_ +* `Project Kanban `_ + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search`