ascript 1.3.27__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ascript/__init__.py +8 -0
- ascript/hooks/__init__.py +0 -0
- ascript/hooks/hook-ascript.py +2 -0
- ascript/windows/__init__.py +29 -0
- ascript/windows/daos/__init__.py +0 -0
- ascript/windows/daos/screen.py +143 -0
- ascript/windows/hardware/__init__.py +5 -0
- ascript/windows/hardware/keyboard.py +279 -0
- ascript/windows/hardware/logitech_driver.py +179 -0
- ascript/windows/hardware/mouse.py +308 -0
- ascript/windows/license/__init__.py +16 -0
- ascript/windows/license/achttp.cp310-win_amd64.pyd +0 -0
- ascript/windows/license/achttp.cp311-win_amd64.pyd +0 -0
- ascript/windows/license/achttp.cp312-win_amd64.pyd +0 -0
- ascript/windows/license/achttp.cp313-win_amd64.pyd +0 -0
- ascript/windows/license/achttp.cp314-win_amd64.pyd +0 -0
- ascript/windows/license/achttp.cp38-win_amd64.pyd +0 -0
- ascript/windows/license/achttp.cp39-win_amd64.pyd +0 -0
- ascript/windows/media/__init__.py +0 -0
- ascript/windows/resources/__init__.py +0 -0
- ascript/windows/resources/libs/logitech/x64/LogitechGkeyEnginesWrapper.dll +0 -0
- ascript/windows/resources/libs/logitech/x86/LogitechGkeyEnginesWrapper.dll +0 -0
- ascript/windows/resources/templates/__init__.py +0 -0
- ascript/windows/resources/templates/__init__py.win.template +51 -0
- ascript/windows/resources/templates/build.as.win.template +14 -0
- ascript/windows/resources/templates/build.py +71 -0
- ascript/windows/resources/templates/logo_windows.ico +0 -0
- ascript/windows/resources/templates/windows/main.py +50 -0
- ascript/windows/resources/templates/windows/pyproject.toml +44 -0
- ascript/windows/resources/templates/windows/res/img/logo.ico +0 -0
- ascript/windows/screen/__init__.py +13 -0
- ascript/windows/screen/as_cv.py +382 -0
- ascript/windows/screen/canny.py +128 -0
- ascript/windows/screen/code_scanner.py +72 -0
- ascript/windows/screen/color_algorithms.py +277 -0
- ascript/windows/screen/find_colors.py +251 -0
- ascript/windows/screen/find_images.py +217 -0
- ascript/windows/screen/ocr/__init__.py +4 -0
- ascript/windows/screen/ocr/ocr.py +223 -0
- ascript/windows/screen/ocr/rapidocr_hook.py +301 -0
- ascript/windows/screen/ocrx.py +169 -0
- ascript/windows/screen/screen_core.py +139 -0
- ascript/windows/store/__init__.py +21 -0
- ascript/windows/system/__init__.py +8 -0
- ascript/windows/system/device.py +355 -0
- ascript/windows/system/easy_print.py +115 -0
- ascript/windows/system/env.py +492 -0
- ascript/windows/system/exception/__init__.py +1 -0
- ascript/windows/system/key_value.py +145 -0
- ascript/windows/system/pip.py +175 -0
- ascript/windows/system/r.py +165 -0
- ascript/windows/system/runtime.py +62 -0
- ascript/windows/tools/__init__.py +27 -0
- ascript/windows/tools/__main__.py +21 -0
- ascript/windows/tools/action.json +33 -0
- ascript/windows/tools/action.py +168 -0
- ascript/windows/tools/colors_tool.py +391 -0
- ascript/windows/tools/config.py +26 -0
- ascript/windows/tools/dumptool.py +18 -0
- ascript/windows/tools/eel_api.py +495 -0
- ascript/windows/tools/gpplug/__init__.py +0 -0
- ascript/windows/tools/gpplug/plug.py +70 -0
- ascript/windows/tools/mouse_catch.py +168 -0
- ascript/windows/tools/plug_manager.py +38 -0
- ascript/windows/tools/tool_api.py +906 -0
- ascript/windows/tools/tool_window.py +56 -0
- ascript/windows/tools/uia_worker.py +365 -0
- ascript/windows/tools/web/app_store_window.html +481 -0
- ascript/windows/tools/web/colors.html +532 -0
- ascript/windows/tools/web/css/screen.css +1342 -0
- ascript/windows/tools/web/css/viewtree.css +357 -0
- ascript/windows/tools/web/flot_control.html +195 -0
- ascript/windows/tools/web/img/ico/favicon.png +0 -0
- ascript/windows/tools/web/img/ico/paintpalette.png +0 -0
- ascript/windows/tools/web/img/svg/find_colors.svg +1 -0
- ascript/windows/tools/web/img/svg/find_images.svg +1 -0
- ascript/windows/tools/web/img/svg/illustration-6.svg +89 -0
- ascript/windows/tools/web/img/svg/img_default.svg +1 -0
- ascript/windows/tools/web/img/svg/ocr.svg +1 -0
- ascript/windows/tools/web/js/screen/colors.js +466 -0
- ascript/windows/tools/web/js/screen/gp_plug.js +108 -0
- ascript/windows/tools/web/js/screen/image_mouse.js +464 -0
- ascript/windows/tools/web/js/screen/img_view.js +558 -0
- ascript/windows/tools/web/js/screen/plug.js +246 -0
- ascript/windows/tools/web/js/screen/screen.js +690 -0
- ascript/windows/tools/web/js/screen/screen_api.js +778 -0
- ascript/windows/tools/web/js/screen/screen_maker.js +10 -0
- ascript/windows/tools/web/js/utils/map.js +73 -0
- ascript/windows/tools/web/js/utils/upload.js +157 -0
- ascript/windows/tools/web/js/viewtree/niv_js.js +27 -0
- ascript/windows/tools/web/js/viewtree/vtree.js +1510 -0
- ascript/windows/tools/web/libs/bootstrap/css/bootstrap.min.css +6 -0
- ascript/windows/tools/web/libs/bootstrap/js/bootstrap.bundle.min.js +7 -0
- ascript/windows/tools/web/libs/bootstrap-icons/0-circle.svg +4 -0
- ascript/windows/tools/web/libs/bootstrap-icons/0-square.svg +4 -0
- ascript/windows/tools/web/libs/bootstrap-icons/font/bootstrap-icons.css +2078 -0
- ascript/windows/tools/web/libs/bootstrap-icons/font/bootstrap-icons.json +2052 -0
- ascript/windows/tools/web/libs/bootstrap-icons/font/bootstrap-icons.min.css +5 -0
- ascript/windows/tools/web/libs/bootstrap-icons/font/bootstrap-icons.scss +2090 -0
- ascript/windows/tools/web/libs/bootstrap-icons/font/fonts/bootstrap-icons.woff +0 -0
- ascript/windows/tools/web/libs/bootstrap-icons/font/fonts/bootstrap-icons.woff2 +0 -0
- ascript/windows/tools/web/libs/feather-icons/dist/feather.min.js +13 -0
- ascript/windows/tools/web/libs/feather-icons/dist/icons.json +1 -0
- ascript/windows/tools/web/libs/jquery/core.js +399 -0
- ascript/windows/tools/web/libs/jquery/jquery.js +10598 -0
- ascript/windows/tools/web/libs/jquery/jquery.min.js +4 -0
- ascript/windows/tools/web/libs/jquery/jquery.min.js.zip +0 -0
- ascript/windows/tools/web/libs/jquery/jquery.slim.js +8495 -0
- ascript/windows/tools/web/libs/jquery/jquery.slim.min.js +2 -0
- ascript/windows/tools/web/libs/jquery-menu/jquery.contextMenu.js +2134 -0
- ascript/windows/tools/web/libs/jquery-menu/jquery.contextMenu.min.css +16 -0
- ascript/windows/tools/web/libs/jquery-zTree/css/zTreeStyle.css +97 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/diy/1_close.png +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/diy/1_open.png +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/diy/2.png +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/diy/3.png +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/diy/4.png +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/diy/5.png +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/diy/6.png +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/diy/7.png +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/diy/8.png +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/diy/9.png +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/line_conn.gif +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/loading.gif +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/tree-node-btn.png +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/zTreeStandard.gif +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/img/zTreeStandard.png +0 -0
- ascript/windows/tools/web/libs/jquery-zTree/js/jquery.ztree.all.min.js +3 -0
- ascript/windows/tools/web/libs/treeview/css/jquery.treeview.css +79 -0
- ascript/windows/tools/web/libs/treeview/img/file.png +0 -0
- ascript/windows/tools/web/libs/treeview/img/ico_loading.png +0 -0
- ascript/windows/tools/web/libs/treeview/img/icon4.png +0 -0
- ascript/windows/tools/web/libs/treeview/img/icon5.png +0 -0
- ascript/windows/tools/web/libs/treeview/img/icon6.png +0 -0
- ascript/windows/tools/web/libs/treeview/img/py.png +0 -0
- ascript/windows/tools/web/libs/treeview/img/treeview-black-line.gif +0 -0
- ascript/windows/tools/web/libs/treeview/img/treeview-black.gif +0 -0
- ascript/windows/tools/web/libs/treeview/img/treeview-default-line.gif +0 -0
- ascript/windows/tools/web/libs/treeview/img/treeview-default.gif +0 -0
- ascript/windows/tools/web/libs/treeview/img/treeview-famfamfam-line.gif +0 -0
- ascript/windows/tools/web/libs/treeview/img/treeview-famfamfam.gif +0 -0
- ascript/windows/tools/web/libs/treeview/img/treeview-gray-line.gif +0 -0
- ascript/windows/tools/web/libs/treeview/img/treeview-gray.gif +0 -0
- ascript/windows/tools/web/libs/treeview/img/treeview-red-line.gif +0 -0
- ascript/windows/tools/web/libs/treeview/img/treeview-red.gif +0 -0
- ascript/windows/tools/web/libs/treeview/js/jquery.treeview.edit.js +37 -0
- ascript/windows/tools/web/libs/treeview/js/jquery.treeview.js +250 -0
- ascript/windows/tools/web/license_window.html +255 -0
- ascript/windows/tools/web/plugs/a.html +653 -0
- ascript/windows/tools/web/plugs/demo.html +52 -0
- ascript/windows/tools/web/plugs/find_colors.html +629 -0
- ascript/windows/tools/web/plugs/find_images.html +699 -0
- ascript/windows/tools/web/plugs/rapidocr.html +954 -0
- ascript/windows/tools/web/vtree.html +496 -0
- ascript/windows/tools/webview/assets/index-HymBPl-I.css +1 -0
- ascript/windows/tools/webview/assets/index-PqA_m8tL.js +17 -0
- ascript/windows/tools/webview/index.html +13 -0
- ascript/windows/ui/__init__.py +6 -0
- ascript/windows/ui/app_store_window.py +272 -0
- ascript/windows/ui/draw_window.py +231 -0
- ascript/windows/ui/flot_test.py +297 -0
- ascript/windows/ui/flot_window.py +488 -0
- ascript/windows/ui/license_window.py +111 -0
- ascript/windows/ui/web_window.py +466 -0
- ascript/windows/utils/__init__.py +0 -0
- ascript/windows/utils/node_utils.py +20 -0
- ascript/windows/utils/screen_utils.py +280 -0
- ascript/windows/window/Selector.py +1015 -0
- ascript/windows/window/Window.py +747 -0
- ascript/windows/window/__init__.py +5 -0
- ascript/windows/window/launcher.py +213 -0
- ascript/windows/window/uia_runtime.py +26 -0
- ascript-1.3.27.dist-info/METADATA +41 -0
- ascript-1.3.27.dist-info/RECORD +177 -0
- ascript-1.3.27.dist-info/WHEEL +5 -0
- ascript-1.3.27.dist-info/entry_points.txt +2 -0
- ascript-1.3.27.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"""Native Windows mouse input based on the standard-library ctypes module."""
|
|
2
|
+
|
|
3
|
+
import ctypes
|
|
4
|
+
import math
|
|
5
|
+
import random
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
from ctypes import wintypes
|
|
9
|
+
from typing import Callable, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
_INPUT_MOUSE = 0
|
|
12
|
+
_MOUSEEVENTF_MOVE = 0x0001
|
|
13
|
+
_MOUSEEVENTF_LEFTDOWN = 0x0002
|
|
14
|
+
_MOUSEEVENTF_LEFTUP = 0x0004
|
|
15
|
+
_MOUSEEVENTF_RIGHTDOWN = 0x0008
|
|
16
|
+
_MOUSEEVENTF_RIGHTUP = 0x0010
|
|
17
|
+
_MOUSEEVENTF_MIDDLEDOWN = 0x0020
|
|
18
|
+
_MOUSEEVENTF_MIDDLEUP = 0x0040
|
|
19
|
+
_MOUSEEVENTF_WHEEL = 0x0800
|
|
20
|
+
_MOUSEEVENTF_HWHEEL = 0x1000
|
|
21
|
+
_MOUSEEVENTF_VIRTUALDESK = 0x4000
|
|
22
|
+
_MOUSEEVENTF_ABSOLUTE = 0x8000
|
|
23
|
+
_SM_SWAPBUTTON = 23
|
|
24
|
+
_SM_XVIRTUALSCREEN = 76
|
|
25
|
+
_SM_YVIRTUALSCREEN = 77
|
|
26
|
+
_SM_CXVIRTUALSCREEN = 78
|
|
27
|
+
_SM_CYVIRTUALSCREEN = 79
|
|
28
|
+
_WHEEL_DELTA = 120
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class MouseFailSafeError(RuntimeError):
|
|
32
|
+
"""Raised when the pointer reaches a virtual-screen corner."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class _MOUSEINPUT(ctypes.Structure):
|
|
36
|
+
_fields_ = [("dx", wintypes.LONG), ("dy", wintypes.LONG),
|
|
37
|
+
("mouseData", wintypes.DWORD), ("dwFlags", wintypes.DWORD),
|
|
38
|
+
("time", wintypes.DWORD), ("dwExtraInfo", ctypes.c_size_t)]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class _INPUTUNION(ctypes.Union):
|
|
42
|
+
_fields_ = [("mi", _MOUSEINPUT)]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class _INPUT(ctypes.Structure):
|
|
46
|
+
_anonymous_ = ("union",)
|
|
47
|
+
_fields_ = [("type", wintypes.DWORD), ("union", _INPUTUNION)]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class _POINT(ctypes.Structure):
|
|
51
|
+
_fields_ = [("x", wintypes.LONG), ("y", wintypes.LONG)]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
_user32 = ctypes.windll.user32
|
|
55
|
+
_send_input = _user32.SendInput
|
|
56
|
+
_send_input.argtypes = (wintypes.UINT, ctypes.c_void_p, ctypes.c_int)
|
|
57
|
+
_send_input.restype = wintypes.UINT
|
|
58
|
+
_get_cursor_pos = _user32.GetCursorPos
|
|
59
|
+
_get_cursor_pos.argtypes = (ctypes.POINTER(_POINT),)
|
|
60
|
+
_get_cursor_pos.restype = wintypes.BOOL
|
|
61
|
+
_get_system_metrics = _user32.GetSystemMetrics
|
|
62
|
+
_get_system_metrics.argtypes = (ctypes.c_int,)
|
|
63
|
+
_get_system_metrics.restype = ctypes.c_int
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _linear(value: float) -> float:
|
|
67
|
+
return value
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class Mouse:
|
|
71
|
+
"""Chainable mouse controller using the same SendInput path as PyDirectInput."""
|
|
72
|
+
|
|
73
|
+
LEFT = "left"
|
|
74
|
+
MIDDLE = "middle"
|
|
75
|
+
RIGHT = "right"
|
|
76
|
+
PRIMARY = "primary"
|
|
77
|
+
SECONDARY = "secondary"
|
|
78
|
+
DEFAULT_MOVE_DURATION = 0.1
|
|
79
|
+
FAILSAFE = True
|
|
80
|
+
MINIMUM_SLEEP = 1.0 / 120.0
|
|
81
|
+
_lock = threading.RLock()
|
|
82
|
+
_dpi_initialized = False
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def _initialize_dpi(cls) -> None:
|
|
86
|
+
if cls._dpi_initialized:
|
|
87
|
+
return
|
|
88
|
+
cls._dpi_initialized = True
|
|
89
|
+
try:
|
|
90
|
+
setter = _user32.SetProcessDpiAwarenessContext
|
|
91
|
+
setter.argtypes = (ctypes.c_void_p,)
|
|
92
|
+
setter.restype = wintypes.BOOL
|
|
93
|
+
setter(ctypes.c_void_p(-4)) # DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
|
|
94
|
+
return
|
|
95
|
+
except (AttributeError, OSError):
|
|
96
|
+
pass
|
|
97
|
+
try:
|
|
98
|
+
ctypes.windll.shcore.SetProcessDpiAwareness(2)
|
|
99
|
+
except (AttributeError, OSError):
|
|
100
|
+
try:
|
|
101
|
+
_user32.SetProcessDPIAware()
|
|
102
|
+
except (AttributeError, OSError):
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def _validate_pair(x: Optional[int], y: Optional[int], name: str = "coordinates") -> None:
|
|
107
|
+
if (x is None) != (y is None):
|
|
108
|
+
raise ValueError(f"{name}: x and y must be provided together")
|
|
109
|
+
if x is not None and (not isinstance(x, int) or isinstance(x, bool) or
|
|
110
|
+
not isinstance(y, int) or isinstance(y, bool)):
|
|
111
|
+
raise TypeError(f"{name}: x and y must be integers")
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def _duration(value: float, name: str) -> float:
|
|
115
|
+
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
116
|
+
raise TypeError(f"{name} must be a number")
|
|
117
|
+
value = float(value)
|
|
118
|
+
if value < 0 or not math.isfinite(value):
|
|
119
|
+
raise ValueError(f"{name} must be finite and non-negative")
|
|
120
|
+
return value
|
|
121
|
+
|
|
122
|
+
@classmethod
|
|
123
|
+
def _virtual_screen(cls) -> Tuple[int, int, int, int]:
|
|
124
|
+
cls._initialize_dpi()
|
|
125
|
+
result = (_get_system_metrics(_SM_XVIRTUALSCREEN),
|
|
126
|
+
_get_system_metrics(_SM_YVIRTUALSCREEN),
|
|
127
|
+
_get_system_metrics(_SM_CXVIRTUALSCREEN),
|
|
128
|
+
_get_system_metrics(_SM_CYVIRTUALSCREEN))
|
|
129
|
+
if result[2] <= 0 or result[3] <= 0:
|
|
130
|
+
raise OSError("Windows did not report a valid virtual screen")
|
|
131
|
+
return result
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def position(cls) -> Tuple[int, int]:
|
|
135
|
+
cls._initialize_dpi()
|
|
136
|
+
point = _POINT()
|
|
137
|
+
if not _get_cursor_pos(ctypes.byref(point)):
|
|
138
|
+
raise ctypes.WinError(ctypes.get_last_error())
|
|
139
|
+
return int(point.x), int(point.y)
|
|
140
|
+
|
|
141
|
+
@classmethod
|
|
142
|
+
def _fail_safe_check(cls) -> None:
|
|
143
|
+
if not cls.FAILSAFE:
|
|
144
|
+
return
|
|
145
|
+
left, top, width, height = cls._virtual_screen()
|
|
146
|
+
right, bottom = left + width - 1, top + height - 1
|
|
147
|
+
if cls.position() in ((left, top), (right, top), (left, bottom), (right, bottom)):
|
|
148
|
+
raise MouseFailSafeError("Mouse fail-safe triggered at a virtual-screen corner")
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def _emit(cls, flags: int, data: int = 0, dx: int = 0, dy: int = 0, check: bool = True) -> None:
|
|
152
|
+
if check:
|
|
153
|
+
cls._fail_safe_check()
|
|
154
|
+
event = _INPUT(type=_INPUT_MOUSE, mi=_MOUSEINPUT(
|
|
155
|
+
dx=dx, dy=dy, mouseData=ctypes.c_uint32(data).value,
|
|
156
|
+
dwFlags=flags, time=0, dwExtraInfo=0))
|
|
157
|
+
if _send_input(1, ctypes.byref(event), ctypes.sizeof(_INPUT)) != 1:
|
|
158
|
+
raise ctypes.WinError(ctypes.get_last_error())
|
|
159
|
+
|
|
160
|
+
@classmethod
|
|
161
|
+
def _button_flags(cls, button: str) -> Tuple[int, int]:
|
|
162
|
+
if not isinstance(button, str):
|
|
163
|
+
raise TypeError("button must be a string")
|
|
164
|
+
button = button.lower()
|
|
165
|
+
swapped = bool(_get_system_metrics(_SM_SWAPBUTTON))
|
|
166
|
+
if button == cls.PRIMARY:
|
|
167
|
+
button = cls.RIGHT if swapped else cls.LEFT
|
|
168
|
+
elif button == cls.SECONDARY:
|
|
169
|
+
button = cls.LEFT if swapped else cls.RIGHT
|
|
170
|
+
mapping = {cls.LEFT: (_MOUSEEVENTF_LEFTDOWN, _MOUSEEVENTF_LEFTUP),
|
|
171
|
+
cls.MIDDLE: (_MOUSEEVENTF_MIDDLEDOWN, _MOUSEEVENTF_MIDDLEUP),
|
|
172
|
+
cls.RIGHT: (_MOUSEEVENTF_RIGHTDOWN, _MOUSEEVENTF_RIGHTUP)}
|
|
173
|
+
if button not in mapping:
|
|
174
|
+
raise ValueError("button must be left, middle, right, primary or secondary")
|
|
175
|
+
return mapping[button]
|
|
176
|
+
|
|
177
|
+
@classmethod
|
|
178
|
+
def _move_immediately(cls, x: int, y: int, check: bool = True) -> None:
|
|
179
|
+
left, top, width, height = cls._virtual_screen()
|
|
180
|
+
x = min(max(x, left), left + width - 1)
|
|
181
|
+
y = min(max(y, top), top + height - 1)
|
|
182
|
+
normalized_x = round((x - left) * 65535 / max(1, width - 1))
|
|
183
|
+
normalized_y = round((y - top) * 65535 / max(1, height - 1))
|
|
184
|
+
cls._emit(_MOUSEEVENTF_MOVE | _MOUSEEVENTF_ABSOLUTE | _MOUSEEVENTF_VIRTUALDESK,
|
|
185
|
+
dx=normalized_x, dy=normalized_y, check=check)
|
|
186
|
+
|
|
187
|
+
@classmethod
|
|
188
|
+
def mouse_move(cls, x: int, y: int, move_duration: float = DEFAULT_MOVE_DURATION,
|
|
189
|
+
tween: Callable[[float], float] = _linear):
|
|
190
|
+
cls._validate_pair(x, y)
|
|
191
|
+
duration = cls._duration(move_duration, "move_duration")
|
|
192
|
+
if not callable(tween):
|
|
193
|
+
raise TypeError("tween must be callable")
|
|
194
|
+
with cls._lock:
|
|
195
|
+
cls._fail_safe_check()
|
|
196
|
+
if duration == 0:
|
|
197
|
+
cls._move_immediately(x, y, check=False)
|
|
198
|
+
return cls
|
|
199
|
+
start_x, start_y = cls.position()
|
|
200
|
+
steps = max(1, int(duration / cls.MINIMUM_SLEEP))
|
|
201
|
+
started = time.perf_counter()
|
|
202
|
+
for step in range(1, steps + 1):
|
|
203
|
+
progress = min(1.0, max(0.0, float(tween(step / steps))))
|
|
204
|
+
cls._fail_safe_check()
|
|
205
|
+
cls._move_immediately(round(start_x + (x - start_x) * progress),
|
|
206
|
+
round(start_y + (y - start_y) * progress), check=False)
|
|
207
|
+
delay = started + duration * step / steps - time.perf_counter()
|
|
208
|
+
if delay > 0:
|
|
209
|
+
time.sleep(delay)
|
|
210
|
+
cls._move_immediately(x, y, check=False)
|
|
211
|
+
return cls
|
|
212
|
+
|
|
213
|
+
@classmethod
|
|
214
|
+
def mouse_down(cls, x: Optional[int] = None, y: Optional[int] = None,
|
|
215
|
+
button: str = PRIMARY, move_duration: float = 0.0):
|
|
216
|
+
cls._validate_pair(x, y)
|
|
217
|
+
with cls._lock:
|
|
218
|
+
if x is not None:
|
|
219
|
+
cls.mouse_move(x, y, move_duration)
|
|
220
|
+
cls._emit(cls._button_flags(button)[0])
|
|
221
|
+
return cls
|
|
222
|
+
|
|
223
|
+
@classmethod
|
|
224
|
+
def mouse_up(cls, x: Optional[int] = None, y: Optional[int] = None,
|
|
225
|
+
button: str = PRIMARY, move_duration: float = 0.0):
|
|
226
|
+
cls._validate_pair(x, y)
|
|
227
|
+
with cls._lock:
|
|
228
|
+
if x is not None:
|
|
229
|
+
cls.mouse_move(x, y, move_duration)
|
|
230
|
+
cls._emit(cls._button_flags(button)[1])
|
|
231
|
+
return cls
|
|
232
|
+
|
|
233
|
+
@classmethod
|
|
234
|
+
def click(cls, x: Optional[int] = None, y: Optional[int] = None, clicks: int = 1,
|
|
235
|
+
interval: float = 0.1, button: str = PRIMARY, move_duration: float = 0.0,
|
|
236
|
+
duration: float = 0.0, offset: int = 0):
|
|
237
|
+
cls._validate_pair(x, y)
|
|
238
|
+
if not isinstance(clicks, int) or isinstance(clicks, bool):
|
|
239
|
+
raise TypeError("clicks must be an integer")
|
|
240
|
+
if clicks < 1:
|
|
241
|
+
raise ValueError("clicks must be at least one")
|
|
242
|
+
interval = cls._duration(interval, "interval")
|
|
243
|
+
duration = cls._duration(duration, "duration")
|
|
244
|
+
if not isinstance(offset, int) or isinstance(offset, bool):
|
|
245
|
+
raise TypeError("offset must be an integer")
|
|
246
|
+
if offset < 0:
|
|
247
|
+
raise ValueError("offset must be non-negative")
|
|
248
|
+
with cls._lock:
|
|
249
|
+
if x is not None:
|
|
250
|
+
if offset:
|
|
251
|
+
left, top, width, height = cls._virtual_screen()
|
|
252
|
+
x = min(max(x + random.randint(-offset, offset), left), left + width - 1)
|
|
253
|
+
y = min(max(y + random.randint(-offset, offset), top), top + height - 1)
|
|
254
|
+
cls.mouse_move(x, y, move_duration)
|
|
255
|
+
down, up = cls._button_flags(button)
|
|
256
|
+
for index in range(clicks):
|
|
257
|
+
cls._emit(down)
|
|
258
|
+
try:
|
|
259
|
+
if duration:
|
|
260
|
+
time.sleep(duration)
|
|
261
|
+
finally:
|
|
262
|
+
cls._emit(up, check=False)
|
|
263
|
+
if index + 1 < clicks and interval:
|
|
264
|
+
time.sleep(interval)
|
|
265
|
+
return cls
|
|
266
|
+
|
|
267
|
+
@classmethod
|
|
268
|
+
def long_press(cls, x: Optional[int] = None, y: Optional[int] = None,
|
|
269
|
+
press_duration: float = 2.0, button: str = PRIMARY,
|
|
270
|
+
move_duration: float = 0.0):
|
|
271
|
+
return cls.click(x, y, button=button, move_duration=move_duration, duration=press_duration)
|
|
272
|
+
|
|
273
|
+
@classmethod
|
|
274
|
+
def scroll(cls, lines: int, x: Optional[int] = None, y: Optional[int] = None,
|
|
275
|
+
direction: str = "v"):
|
|
276
|
+
cls._validate_pair(x, y)
|
|
277
|
+
if not isinstance(lines, int) or isinstance(lines, bool):
|
|
278
|
+
raise TypeError("lines must be an integer")
|
|
279
|
+
if direction not in ("v", "h"):
|
|
280
|
+
raise ValueError("direction must be 'v' or 'h'")
|
|
281
|
+
with cls._lock:
|
|
282
|
+
if x is not None:
|
|
283
|
+
cls.mouse_move(x, y, 0.0)
|
|
284
|
+
cls._emit(_MOUSEEVENTF_WHEEL if direction == "v" else _MOUSEEVENTF_HWHEEL,
|
|
285
|
+
lines * _WHEEL_DELTA)
|
|
286
|
+
return cls
|
|
287
|
+
|
|
288
|
+
@classmethod
|
|
289
|
+
def drag(cls, to_x: int, to_y: int, from_x: Optional[int] = None,
|
|
290
|
+
from_y: Optional[int] = None, move_duration: float = 0.5,
|
|
291
|
+
button: str = PRIMARY, tween: Callable[[float], float] = _linear):
|
|
292
|
+
cls._validate_pair(to_x, to_y, "destination")
|
|
293
|
+
cls._validate_pair(from_x, from_y, "origin")
|
|
294
|
+
with cls._lock:
|
|
295
|
+
if from_x is not None:
|
|
296
|
+
cls.mouse_move(from_x, from_y, 0.0)
|
|
297
|
+
down, up = cls._button_flags(button)
|
|
298
|
+
cls._emit(down)
|
|
299
|
+
try:
|
|
300
|
+
cls.mouse_move(to_x, to_y, move_duration, tween)
|
|
301
|
+
finally:
|
|
302
|
+
cls._emit(up, check=False)
|
|
303
|
+
return cls
|
|
304
|
+
|
|
305
|
+
@classmethod
|
|
306
|
+
def wait(cls, seconds: float):
|
|
307
|
+
time.sleep(cls._duration(seconds, "seconds"))
|
|
308
|
+
return cls
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""许可证加密模块。
|
|
2
|
+
|
|
3
|
+
提供基于 AES 加密的许可证验证功能,包括加密通信和数据填充工具。
|
|
4
|
+
|
|
5
|
+
Exports:
|
|
6
|
+
achttp: 加密 HTTP 通信模块。
|
|
7
|
+
AES: AES 加密算法。
|
|
8
|
+
pad: PKCS7 数据填充函数。
|
|
9
|
+
unpad: PKCS7 数据去填充函数。
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from Crypto.Cipher import AES
|
|
13
|
+
from Crypto.Util.Padding import pad, unpad
|
|
14
|
+
from . import achttp
|
|
15
|
+
|
|
16
|
+
__all__ = ['achttp', "AES", "pad", "unpad"]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import sys
|
|
3
|
+
from ascript.windows.system import R # 程序上下文与资源路由
|
|
4
|
+
from ascript.windows.ui import LicenseWindow # 安全验证组件
|
|
5
|
+
from ascript.windows.ui import FloatScriptWindow # 脚本控制台组件
|
|
6
|
+
|
|
7
|
+
# 初始化控制台:支持自启动 (auto_start) 与 多进程隔离 (use_process)
|
|
8
|
+
app = FloatScriptWindow(R.img("logo.ico"), title="AScript 自动化助手", auto_start=True)
|
|
9
|
+
|
|
10
|
+
@app.on('start')
|
|
11
|
+
def task_start():
|
|
12
|
+
# ---------------------------------------------------------
|
|
13
|
+
# 入口程序 (Entry Point)
|
|
14
|
+
# ---------------------------------------------------------
|
|
15
|
+
"""
|
|
16
|
+
[业务逻辑入口]
|
|
17
|
+
当用户点击启动按钮或触发自启动时,此函数将在独立的子进程中运行。
|
|
18
|
+
"""
|
|
19
|
+
print(">>> [系统] 业务进程已建立,开始执行自动化任务...")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@app.on('stop')
|
|
23
|
+
def task_stop():
|
|
24
|
+
"""
|
|
25
|
+
[善后处理回调]
|
|
26
|
+
当用户手动停止或任务结束时触发。
|
|
27
|
+
注意:您有约 3 秒的时间进行资源释放(如关闭文件、数据库断开等)。
|
|
28
|
+
即使此处存在死循环,系统也会在超时后强制回收进程资源。
|
|
29
|
+
"""
|
|
30
|
+
print(">>> [系统] 接收到停止信号,正在释放系统资源...")
|
|
31
|
+
# 模拟保存数据逻辑
|
|
32
|
+
time.sleep(1)
|
|
33
|
+
print(">>> [系统] 资源释放完毕,安全退出。")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if __name__ == '__main__':
|
|
39
|
+
# 1. 软件授权验证 (前往开发者后台获取程序 ID)
|
|
40
|
+
# 验证窗口会阻塞主线程,直到验证通过或用户关闭
|
|
41
|
+
license_manager = LicenseWindow("1085")
|
|
42
|
+
|
|
43
|
+
if license_manager.show():
|
|
44
|
+
print("✅ [认证] 授权验证成功")
|
|
45
|
+
|
|
46
|
+
# 2. 启动悬浮控制台
|
|
47
|
+
# 参数说明:is_debug=True 可开启调试模式,width/height 可自定义窗口尺寸
|
|
48
|
+
app.show(title="AScript_Control_Panel")
|
|
49
|
+
else:
|
|
50
|
+
print("❌ [认证] 验证被取消或失败,程序即将退出")
|
|
51
|
+
sys.exit()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "AScriptProject",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"entry": "__init__.py",
|
|
5
|
+
"parameters": [
|
|
6
|
+
"-m", "PyInstaller",
|
|
7
|
+
"--onefile",
|
|
8
|
+
"--clean",
|
|
9
|
+
"--noconsole",
|
|
10
|
+
"--icon", "res/img/logo.ico",
|
|
11
|
+
"--collect-all", "ascript",
|
|
12
|
+
"--add-data", "res;res"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def auto_build(entry_file, project_name):
|
|
8
|
+
"""使用 Nuitka 将 Python 项目打包为独立的 Windows 可执行文件。
|
|
9
|
+
|
|
10
|
+
自动构造 Nuitka 编译命令,实时输出编译日志到控制台,
|
|
11
|
+
并在完成后报告编译结果。
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
entry_file: Python 入口文件路径(如 'main.py')
|
|
15
|
+
project_name: 输出的可执行文件名称(不含 .exe 后缀)
|
|
16
|
+
"""
|
|
17
|
+
# 1. 确保入口文件存在
|
|
18
|
+
if not os.path.exists(entry_file):
|
|
19
|
+
print(f"❌ 错误: 找不到入口文件 {entry_file}")
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
# 2. 构造 Nuitka 命令
|
|
23
|
+
build_cmd = [
|
|
24
|
+
sys.executable, "-m", "nuitka",
|
|
25
|
+
"--standalone",
|
|
26
|
+
"--onefile",
|
|
27
|
+
"--enable-plugin=tk-inter",
|
|
28
|
+
"--include-package=Crypto", # 强制包含 Crypto 模块
|
|
29
|
+
# "--windows-disable-console", # 先注掉,我们要看控制台报错
|
|
30
|
+
"--include-package=ascript",
|
|
31
|
+
"--output-dir=out",
|
|
32
|
+
f"--output-filename={project_name}",
|
|
33
|
+
entry_file
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
# 3. 针对 Windows 下可能存在的路径空格问题进行处理
|
|
37
|
+
# Popen 传入 list 格式在 Windows 下通常能很好处理空格
|
|
38
|
+
|
|
39
|
+
print(f"🚀 Nuitka 开始编译 [{project_name}],这可能需要几分钟...")
|
|
40
|
+
|
|
41
|
+
# 执行打包
|
|
42
|
+
process = subprocess.Popen(
|
|
43
|
+
build_cmd,
|
|
44
|
+
stdout=subprocess.PIPE,
|
|
45
|
+
stderr=subprocess.STDOUT,
|
|
46
|
+
text=True,
|
|
47
|
+
encoding='utf-8',
|
|
48
|
+
bufsize=1 # 行缓冲,实时获取输出
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# 4. 实时输出给 IDE 的控制台
|
|
52
|
+
try:
|
|
53
|
+
for line in process.stdout:
|
|
54
|
+
# 过滤掉一些极其冗长的编译信息,只留关键进度
|
|
55
|
+
clean_line = line.strip()
|
|
56
|
+
if clean_line:
|
|
57
|
+
print(f"[Nuitka]: {clean_line}")
|
|
58
|
+
except Exception as e:
|
|
59
|
+
print(f"读取日志异常: {e}")
|
|
60
|
+
|
|
61
|
+
process.wait()
|
|
62
|
+
|
|
63
|
+
if process.returncode == 0:
|
|
64
|
+
print(f"✨ 打包成功!文件位于: out/{project_name}.exe")
|
|
65
|
+
else:
|
|
66
|
+
print(f"💥 打包失败,退出码: {process.returncode}")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
# 假设开发者的主程序是 main.py
|
|
71
|
+
auto_build("windows/main.py", "AScriptApp")
|
|
Binary file
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""AScript Windows 应用模板的入口程序。
|
|
2
|
+
|
|
3
|
+
演示如何使用 FloatScriptWindow 创建带有启动/停止控制的自动化脚本应用。
|
|
4
|
+
"""
|
|
5
|
+
import multiprocessing
|
|
6
|
+
import time
|
|
7
|
+
import sys
|
|
8
|
+
from ascript.windows.system import R # 程序上下文与资源路由
|
|
9
|
+
from ascript.windows.ui import LicenseWindow # 安全验证组件
|
|
10
|
+
from ascript.windows.ui import FloatScriptWindow # 脚本控制台组件
|
|
11
|
+
|
|
12
|
+
# 初始化控制台:支持自启动 (auto_start) 与 多进程隔离 (use_process)
|
|
13
|
+
app = FloatScriptWindow(R.img("logo.ico"), title="我的程序", auto_start=True)
|
|
14
|
+
|
|
15
|
+
@app.on('start')
|
|
16
|
+
def task_start():
|
|
17
|
+
"""业务逻辑入口函数。
|
|
18
|
+
|
|
19
|
+
当用户点击启动按钮或触发自启动时,此函数将在独立的子进程中运行。
|
|
20
|
+
在此函数中编写自动化任务的核心业务逻辑。
|
|
21
|
+
"""
|
|
22
|
+
print(">>> [系统] 业务进程已建立,开始执行自动化任务...")
|
|
23
|
+
i= 0
|
|
24
|
+
|
|
25
|
+
# 模拟耗时逻辑操作
|
|
26
|
+
while True:
|
|
27
|
+
print(i)
|
|
28
|
+
time.sleep(0.5)
|
|
29
|
+
i += 1
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@app.on('stop')
|
|
33
|
+
def task_stop():
|
|
34
|
+
"""任务停止时的善后处理回调。
|
|
35
|
+
|
|
36
|
+
当用户手动停止或任务结束时触发。可在此执行资源释放操作,
|
|
37
|
+
如关闭文件句柄、断开数据库连接等。注意约有 3 秒的执行时限,
|
|
38
|
+
超时后系统会强制回收进程资源。
|
|
39
|
+
"""
|
|
40
|
+
print(">>> [系统] 接收到停止信号,正在释放系统资源...")
|
|
41
|
+
# 模拟保存数据逻辑
|
|
42
|
+
time.sleep(1)
|
|
43
|
+
print(">>> [系统] 资源释放完毕,安全退出。")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if __name__ == '__main__':
|
|
49
|
+
# 解决打包后子进程重复启动问题
|
|
50
|
+
app.show()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "_project_name_"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Ascript Project Template"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
dependencies = [
|
|
13
|
+
"ascript",
|
|
14
|
+
# "rapidocr==3.5.0",
|
|
15
|
+
# "onnxruntime==1.23.2"
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[tool.setuptools]
|
|
19
|
+
# 【硬映射核心】
|
|
20
|
+
# 这里的含义是:创建一个名为 "_project_name_" 的虚拟包,
|
|
21
|
+
# 并告诉系统它的源代码就在当前根目录 "."
|
|
22
|
+
package-dir = {"_project_name_" = "."}
|
|
23
|
+
packages = ["_project_name_"]
|
|
24
|
+
|
|
25
|
+
include-package-data = true
|
|
26
|
+
|
|
27
|
+
[tool.setuptools.package-data]
|
|
28
|
+
# 这里的 key 必须和上面 packages 里的名字一致
|
|
29
|
+
"_project_name_" = ["res/**/*", "config.json", "build.as","pyarmor_runtime_000000/**/*"]
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.exclude-package-data]
|
|
32
|
+
"_project_name_" = ["sercert/**/*"]
|
|
33
|
+
|
|
34
|
+
[tool.ascript.pack]
|
|
35
|
+
# 文件夹排除列表:这些文件夹及其内容不会被打包
|
|
36
|
+
exclude-dirs = [
|
|
37
|
+
".venv", "venv", ".idea", ".vscode", "__pycache__",
|
|
38
|
+
"build", "dist", ".git", ".pytest_cache"
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
# 文件后缀排除列表:匹配这些后缀的文件不会被打包
|
|
42
|
+
exclude-exts = [
|
|
43
|
+
".pyc", ".pyo", ".pyd", ".DS_Store", ".log", "Thumbs.db"
|
|
44
|
+
]
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .screen_core import Screen
|
|
2
|
+
from .find_images import FindImages
|
|
3
|
+
from .find_colors import FindColors
|
|
4
|
+
from .color_algorithms import Colors, CompareColors, FindBlock
|
|
5
|
+
from .canny import Canny
|
|
6
|
+
from .code_scanner import CodeScanner
|
|
7
|
+
from .ocr import Ocr, OCRItem, NullOCRItem
|
|
8
|
+
from .ocrx import OcrX, OcrXResult
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
'Screen', 'FindImages', 'FindColors', 'CompareColors', 'Colors', 'FindBlock',
|
|
12
|
+
'Ocr', 'OCRItem', 'NullOCRItem', 'OcrX', 'OcrXResult', 'Canny', 'CodeScanner',
|
|
13
|
+
]
|