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.
Files changed (177) hide show
  1. ascript/__init__.py +8 -0
  2. ascript/hooks/__init__.py +0 -0
  3. ascript/hooks/hook-ascript.py +2 -0
  4. ascript/windows/__init__.py +29 -0
  5. ascript/windows/daos/__init__.py +0 -0
  6. ascript/windows/daos/screen.py +143 -0
  7. ascript/windows/hardware/__init__.py +5 -0
  8. ascript/windows/hardware/keyboard.py +279 -0
  9. ascript/windows/hardware/logitech_driver.py +179 -0
  10. ascript/windows/hardware/mouse.py +308 -0
  11. ascript/windows/license/__init__.py +16 -0
  12. ascript/windows/license/achttp.cp310-win_amd64.pyd +0 -0
  13. ascript/windows/license/achttp.cp311-win_amd64.pyd +0 -0
  14. ascript/windows/license/achttp.cp312-win_amd64.pyd +0 -0
  15. ascript/windows/license/achttp.cp313-win_amd64.pyd +0 -0
  16. ascript/windows/license/achttp.cp314-win_amd64.pyd +0 -0
  17. ascript/windows/license/achttp.cp38-win_amd64.pyd +0 -0
  18. ascript/windows/license/achttp.cp39-win_amd64.pyd +0 -0
  19. ascript/windows/media/__init__.py +0 -0
  20. ascript/windows/resources/__init__.py +0 -0
  21. ascript/windows/resources/libs/logitech/x64/LogitechGkeyEnginesWrapper.dll +0 -0
  22. ascript/windows/resources/libs/logitech/x86/LogitechGkeyEnginesWrapper.dll +0 -0
  23. ascript/windows/resources/templates/__init__.py +0 -0
  24. ascript/windows/resources/templates/__init__py.win.template +51 -0
  25. ascript/windows/resources/templates/build.as.win.template +14 -0
  26. ascript/windows/resources/templates/build.py +71 -0
  27. ascript/windows/resources/templates/logo_windows.ico +0 -0
  28. ascript/windows/resources/templates/windows/main.py +50 -0
  29. ascript/windows/resources/templates/windows/pyproject.toml +44 -0
  30. ascript/windows/resources/templates/windows/res/img/logo.ico +0 -0
  31. ascript/windows/screen/__init__.py +13 -0
  32. ascript/windows/screen/as_cv.py +382 -0
  33. ascript/windows/screen/canny.py +128 -0
  34. ascript/windows/screen/code_scanner.py +72 -0
  35. ascript/windows/screen/color_algorithms.py +277 -0
  36. ascript/windows/screen/find_colors.py +251 -0
  37. ascript/windows/screen/find_images.py +217 -0
  38. ascript/windows/screen/ocr/__init__.py +4 -0
  39. ascript/windows/screen/ocr/ocr.py +223 -0
  40. ascript/windows/screen/ocr/rapidocr_hook.py +301 -0
  41. ascript/windows/screen/ocrx.py +169 -0
  42. ascript/windows/screen/screen_core.py +139 -0
  43. ascript/windows/store/__init__.py +21 -0
  44. ascript/windows/system/__init__.py +8 -0
  45. ascript/windows/system/device.py +355 -0
  46. ascript/windows/system/easy_print.py +115 -0
  47. ascript/windows/system/env.py +492 -0
  48. ascript/windows/system/exception/__init__.py +1 -0
  49. ascript/windows/system/key_value.py +145 -0
  50. ascript/windows/system/pip.py +175 -0
  51. ascript/windows/system/r.py +165 -0
  52. ascript/windows/system/runtime.py +62 -0
  53. ascript/windows/tools/__init__.py +27 -0
  54. ascript/windows/tools/__main__.py +21 -0
  55. ascript/windows/tools/action.json +33 -0
  56. ascript/windows/tools/action.py +168 -0
  57. ascript/windows/tools/colors_tool.py +391 -0
  58. ascript/windows/tools/config.py +26 -0
  59. ascript/windows/tools/dumptool.py +18 -0
  60. ascript/windows/tools/eel_api.py +495 -0
  61. ascript/windows/tools/gpplug/__init__.py +0 -0
  62. ascript/windows/tools/gpplug/plug.py +70 -0
  63. ascript/windows/tools/mouse_catch.py +168 -0
  64. ascript/windows/tools/plug_manager.py +38 -0
  65. ascript/windows/tools/tool_api.py +906 -0
  66. ascript/windows/tools/tool_window.py +56 -0
  67. ascript/windows/tools/uia_worker.py +365 -0
  68. ascript/windows/tools/web/app_store_window.html +481 -0
  69. ascript/windows/tools/web/colors.html +532 -0
  70. ascript/windows/tools/web/css/screen.css +1342 -0
  71. ascript/windows/tools/web/css/viewtree.css +357 -0
  72. ascript/windows/tools/web/flot_control.html +195 -0
  73. ascript/windows/tools/web/img/ico/favicon.png +0 -0
  74. ascript/windows/tools/web/img/ico/paintpalette.png +0 -0
  75. ascript/windows/tools/web/img/svg/find_colors.svg +1 -0
  76. ascript/windows/tools/web/img/svg/find_images.svg +1 -0
  77. ascript/windows/tools/web/img/svg/illustration-6.svg +89 -0
  78. ascript/windows/tools/web/img/svg/img_default.svg +1 -0
  79. ascript/windows/tools/web/img/svg/ocr.svg +1 -0
  80. ascript/windows/tools/web/js/screen/colors.js +466 -0
  81. ascript/windows/tools/web/js/screen/gp_plug.js +108 -0
  82. ascript/windows/tools/web/js/screen/image_mouse.js +464 -0
  83. ascript/windows/tools/web/js/screen/img_view.js +558 -0
  84. ascript/windows/tools/web/js/screen/plug.js +246 -0
  85. ascript/windows/tools/web/js/screen/screen.js +690 -0
  86. ascript/windows/tools/web/js/screen/screen_api.js +778 -0
  87. ascript/windows/tools/web/js/screen/screen_maker.js +10 -0
  88. ascript/windows/tools/web/js/utils/map.js +73 -0
  89. ascript/windows/tools/web/js/utils/upload.js +157 -0
  90. ascript/windows/tools/web/js/viewtree/niv_js.js +27 -0
  91. ascript/windows/tools/web/js/viewtree/vtree.js +1510 -0
  92. ascript/windows/tools/web/libs/bootstrap/css/bootstrap.min.css +6 -0
  93. ascript/windows/tools/web/libs/bootstrap/js/bootstrap.bundle.min.js +7 -0
  94. ascript/windows/tools/web/libs/bootstrap-icons/0-circle.svg +4 -0
  95. ascript/windows/tools/web/libs/bootstrap-icons/0-square.svg +4 -0
  96. ascript/windows/tools/web/libs/bootstrap-icons/font/bootstrap-icons.css +2078 -0
  97. ascript/windows/tools/web/libs/bootstrap-icons/font/bootstrap-icons.json +2052 -0
  98. ascript/windows/tools/web/libs/bootstrap-icons/font/bootstrap-icons.min.css +5 -0
  99. ascript/windows/tools/web/libs/bootstrap-icons/font/bootstrap-icons.scss +2090 -0
  100. ascript/windows/tools/web/libs/bootstrap-icons/font/fonts/bootstrap-icons.woff +0 -0
  101. ascript/windows/tools/web/libs/bootstrap-icons/font/fonts/bootstrap-icons.woff2 +0 -0
  102. ascript/windows/tools/web/libs/feather-icons/dist/feather.min.js +13 -0
  103. ascript/windows/tools/web/libs/feather-icons/dist/icons.json +1 -0
  104. ascript/windows/tools/web/libs/jquery/core.js +399 -0
  105. ascript/windows/tools/web/libs/jquery/jquery.js +10598 -0
  106. ascript/windows/tools/web/libs/jquery/jquery.min.js +4 -0
  107. ascript/windows/tools/web/libs/jquery/jquery.min.js.zip +0 -0
  108. ascript/windows/tools/web/libs/jquery/jquery.slim.js +8495 -0
  109. ascript/windows/tools/web/libs/jquery/jquery.slim.min.js +2 -0
  110. ascript/windows/tools/web/libs/jquery-menu/jquery.contextMenu.js +2134 -0
  111. ascript/windows/tools/web/libs/jquery-menu/jquery.contextMenu.min.css +16 -0
  112. ascript/windows/tools/web/libs/jquery-zTree/css/zTreeStyle.css +97 -0
  113. ascript/windows/tools/web/libs/jquery-zTree/img/diy/1_close.png +0 -0
  114. ascript/windows/tools/web/libs/jquery-zTree/img/diy/1_open.png +0 -0
  115. ascript/windows/tools/web/libs/jquery-zTree/img/diy/2.png +0 -0
  116. ascript/windows/tools/web/libs/jquery-zTree/img/diy/3.png +0 -0
  117. ascript/windows/tools/web/libs/jquery-zTree/img/diy/4.png +0 -0
  118. ascript/windows/tools/web/libs/jquery-zTree/img/diy/5.png +0 -0
  119. ascript/windows/tools/web/libs/jquery-zTree/img/diy/6.png +0 -0
  120. ascript/windows/tools/web/libs/jquery-zTree/img/diy/7.png +0 -0
  121. ascript/windows/tools/web/libs/jquery-zTree/img/diy/8.png +0 -0
  122. ascript/windows/tools/web/libs/jquery-zTree/img/diy/9.png +0 -0
  123. ascript/windows/tools/web/libs/jquery-zTree/img/line_conn.gif +0 -0
  124. ascript/windows/tools/web/libs/jquery-zTree/img/loading.gif +0 -0
  125. ascript/windows/tools/web/libs/jquery-zTree/img/tree-node-btn.png +0 -0
  126. ascript/windows/tools/web/libs/jquery-zTree/img/zTreeStandard.gif +0 -0
  127. ascript/windows/tools/web/libs/jquery-zTree/img/zTreeStandard.png +0 -0
  128. ascript/windows/tools/web/libs/jquery-zTree/js/jquery.ztree.all.min.js +3 -0
  129. ascript/windows/tools/web/libs/treeview/css/jquery.treeview.css +79 -0
  130. ascript/windows/tools/web/libs/treeview/img/file.png +0 -0
  131. ascript/windows/tools/web/libs/treeview/img/ico_loading.png +0 -0
  132. ascript/windows/tools/web/libs/treeview/img/icon4.png +0 -0
  133. ascript/windows/tools/web/libs/treeview/img/icon5.png +0 -0
  134. ascript/windows/tools/web/libs/treeview/img/icon6.png +0 -0
  135. ascript/windows/tools/web/libs/treeview/img/py.png +0 -0
  136. ascript/windows/tools/web/libs/treeview/img/treeview-black-line.gif +0 -0
  137. ascript/windows/tools/web/libs/treeview/img/treeview-black.gif +0 -0
  138. ascript/windows/tools/web/libs/treeview/img/treeview-default-line.gif +0 -0
  139. ascript/windows/tools/web/libs/treeview/img/treeview-default.gif +0 -0
  140. ascript/windows/tools/web/libs/treeview/img/treeview-famfamfam-line.gif +0 -0
  141. ascript/windows/tools/web/libs/treeview/img/treeview-famfamfam.gif +0 -0
  142. ascript/windows/tools/web/libs/treeview/img/treeview-gray-line.gif +0 -0
  143. ascript/windows/tools/web/libs/treeview/img/treeview-gray.gif +0 -0
  144. ascript/windows/tools/web/libs/treeview/img/treeview-red-line.gif +0 -0
  145. ascript/windows/tools/web/libs/treeview/img/treeview-red.gif +0 -0
  146. ascript/windows/tools/web/libs/treeview/js/jquery.treeview.edit.js +37 -0
  147. ascript/windows/tools/web/libs/treeview/js/jquery.treeview.js +250 -0
  148. ascript/windows/tools/web/license_window.html +255 -0
  149. ascript/windows/tools/web/plugs/a.html +653 -0
  150. ascript/windows/tools/web/plugs/demo.html +52 -0
  151. ascript/windows/tools/web/plugs/find_colors.html +629 -0
  152. ascript/windows/tools/web/plugs/find_images.html +699 -0
  153. ascript/windows/tools/web/plugs/rapidocr.html +954 -0
  154. ascript/windows/tools/web/vtree.html +496 -0
  155. ascript/windows/tools/webview/assets/index-HymBPl-I.css +1 -0
  156. ascript/windows/tools/webview/assets/index-PqA_m8tL.js +17 -0
  157. ascript/windows/tools/webview/index.html +13 -0
  158. ascript/windows/ui/__init__.py +6 -0
  159. ascript/windows/ui/app_store_window.py +272 -0
  160. ascript/windows/ui/draw_window.py +231 -0
  161. ascript/windows/ui/flot_test.py +297 -0
  162. ascript/windows/ui/flot_window.py +488 -0
  163. ascript/windows/ui/license_window.py +111 -0
  164. ascript/windows/ui/web_window.py +466 -0
  165. ascript/windows/utils/__init__.py +0 -0
  166. ascript/windows/utils/node_utils.py +20 -0
  167. ascript/windows/utils/screen_utils.py +280 -0
  168. ascript/windows/window/Selector.py +1015 -0
  169. ascript/windows/window/Window.py +747 -0
  170. ascript/windows/window/__init__.py +5 -0
  171. ascript/windows/window/launcher.py +213 -0
  172. ascript/windows/window/uia_runtime.py +26 -0
  173. ascript-1.3.27.dist-info/METADATA +41 -0
  174. ascript-1.3.27.dist-info/RECORD +177 -0
  175. ascript-1.3.27.dist-info/WHEEL +5 -0
  176. ascript-1.3.27.dist-info/entry_points.txt +2 -0
  177. ascript-1.3.27.dist-info/top_level.txt +1 -0
@@ -0,0 +1,301 @@
1
+ import sys
2
+ import re
3
+ import importlib.abc
4
+ import importlib.util
5
+ import importlib
6
+ from dataclasses import dataclass, asdict
7
+ from typing import List, Optional, Tuple, Union, TYPE_CHECKING
8
+
9
+ # 假设你的硬件操作模块路径如下
10
+ from ...hardware.mouse import Mouse
11
+
12
+ # --- 1. 类型存根 (仅用于 PyCharm 等 IDE 的代码补全提示) ---
13
+ if TYPE_CHECKING:
14
+ try:
15
+ from rapidocr.utils.output import RapidOCROutput as BaseOutput
16
+ except ImportError:
17
+ class BaseOutput:
18
+ pass
19
+
20
+
21
+ class RapidOCROutput(BaseOutput):
22
+ """RapidOCR 输出结果的类型存根(仅用于 IDE 代码补全提示)。
23
+
24
+ 声明 find 和 find_all 方法签名,使 IDE 能正确提示动态注入的方法。
25
+ """
26
+
27
+ def find(self, text_re: str = None, score: float = 0.85) -> 'OCRItem': ...
28
+
29
+ def find_all(self, text_re: str = None, score: float = 0.85) -> List['OCRItem']: ...
30
+
31
+
32
+ # --- 2. 核心结果对象定义 ---
33
+
34
+ @dataclass
35
+ class OCRItem:
36
+ """OCR 单条识别结果的数据封装类。
37
+
38
+ 封装一条文字识别结果的完整信息,支持直接点击识别到的文字位置。
39
+
40
+ Attributes:
41
+ txt: 识别出的文本内容。
42
+ box: 文字区域的四个顶点坐标,格式为 [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]。
43
+ score: 识别置信度,范围 0.0 ~ 1.0。
44
+ center: 文字区域的中心点坐标 (x, y)。
45
+ rect: 文字区域的外接矩形 [left, top, right, bottom]。
46
+ """
47
+
48
+ txt: str
49
+ box: List[List[float]]
50
+ score: float
51
+ center: Tuple[int, int]
52
+ rect: List[int]
53
+
54
+ def click(self, window=None):
55
+ """点击该识别结果所在的位置。
56
+
57
+ Args:
58
+ window: 可选的 Window 对象。传入时调用 window.click() 进行窗口内相对坐标点击;
59
+ 不传时调用全局 Mouse.click() 进行屏幕绝对坐标点击。
60
+
61
+ Returns:
62
+ OCRItem: 返回自身,支持链式调用。
63
+ """
64
+ x, y = self.center
65
+ if window:
66
+ # 让窗口对象处理相对坐标点击
67
+ window.click(x, y)
68
+ else:
69
+ # 全屏绝对点击
70
+ Mouse.click(x, y)
71
+ return self
72
+
73
+ def to_dict(self) -> dict:
74
+ """将识别结果转换为字典格式。
75
+
76
+ Returns:
77
+ dict: 包含 txt、box、score、center、rect 等字段的字典。
78
+ """
79
+ return {
80
+ **asdict(self),
81
+ "text": self.txt,
82
+ "confidence": self.score,
83
+ "center_x": int(self.center[0]),
84
+ "center_y": int(self.center[1]),
85
+ }
86
+
87
+ @property
88
+ def text(self):
89
+ return self.txt
90
+
91
+ @property
92
+ def confidence(self):
93
+ return self.score
94
+
95
+ def __bool__(self):
96
+ """支持布尔判断,OCRItem 实例始终为 True。
97
+
98
+ Returns:
99
+ bool: 始终返回 True,表示存在有效识别结果。
100
+ """
101
+ return True
102
+
103
+ def __repr__(self):
104
+ """返回 OCRItem 的可读字符串表示。
105
+
106
+ Returns:
107
+ str: 包含文本、置信度和中心坐标的描述字符串。
108
+ """
109
+ return f"<OCRItem txt='{self.txt}' score={self.score:.2f} center={self.center}>"
110
+
111
+
112
+ class NullOCRItem:
113
+ """空 OCR 结果对象(空对象模式)。
114
+
115
+ 当 find 方法未找到匹配结果时返回此对象,防止链式调用因 None 而报错。
116
+ 所有属性均为默认空值,布尔值为 False。
117
+ """
118
+
119
+ def __init__(self):
120
+ """初始化空 OCR 结果,所有属性设为默认空值。"""
121
+ self.txt, self.box, self.score = "", [], 0.0
122
+ self.center, self.rect = (0, 0), [0, 0, 0, 0]
123
+
124
+ def click(self, window=None):
125
+ """静默处理点击操作,不执行任何动作。
126
+
127
+ Args:
128
+ window: 忽略此参数。
129
+
130
+ Returns:
131
+ NullOCRItem: 返回自身,支持链式调用。
132
+ """
133
+ return self
134
+
135
+ def to_dict(self) -> dict:
136
+ """转换为空字典。
137
+
138
+ Returns:
139
+ dict: 空字典。
140
+ """
141
+ return {}
142
+
143
+ @property
144
+ def text(self):
145
+ return ""
146
+
147
+ @property
148
+ def confidence(self):
149
+ return 0.0
150
+
151
+ def __bool__(self):
152
+ """支持布尔判断,NullOCRItem 始终为 False。
153
+
154
+ Returns:
155
+ bool: 始终返回 False,表示未找到结果。
156
+ """
157
+ return False
158
+
159
+ def __repr__(self):
160
+ """返回 NullOCRItem 的可读字符串表示。
161
+
162
+ Returns:
163
+ str: 空结果的描述字符串。
164
+ """
165
+ return "<NullOCRItem (Not Found)>"
166
+
167
+
168
+ # --- 3. 运行时 Hook 逻辑 ---
169
+
170
+ class RapidOCRHook(importlib.abc.MetaPathFinder):
171
+ """RapidOCR 运行时 Hook,动态注入 find/find_all 扩展方法。
172
+
173
+ 通过 Python 的 MetaPathFinder 机制拦截 rapidocr.utils.output 模块的导入,
174
+ 在其 RapidOCROutput 类上注入 find() 和 find_all() 方法,
175
+ 使 OCR 结果对象直接支持文字搜索和定位功能。
176
+ """
177
+
178
+ def find_spec(self, fullname, path, target=None):
179
+ """拦截模块导入,在目标模块加载时注入扩展方法。
180
+
181
+ Args:
182
+ fullname: 正在导入的模块全名。
183
+ path: 模块搜索路径。
184
+ target: 目标模块对象(可选)。
185
+
186
+ Returns:
187
+ ModuleSpec: 当拦截到目标模块时返回其 spec,否则返回 None。
188
+ """
189
+ # 监听 RapidOCR 的核心输出模块
190
+ if fullname == "rapidocr.utils.output":
191
+ importlib.invalidate_caches() # 动态感知新安装的包
192
+ try:
193
+ spec = importlib.util.find_spec(fullname)
194
+ if spec is None: return None
195
+
196
+ if self in sys.meta_path:
197
+ sys.meta_path.remove(self)
198
+
199
+ module = importlib.util.module_from_spec(spec)
200
+ spec.loader.exec_module(module)
201
+ self._patch(module)
202
+ sys.modules[fullname] = module
203
+ return spec
204
+ except Exception:
205
+ return None
206
+ return None
207
+
208
+ def _patch(self, module):
209
+ """向目标模块的 RapidOCROutput 类动态注入 find 和 find_all 方法。
210
+
211
+ Args:
212
+ module: 已加载的 rapidocr.utils.output 模块对象。
213
+ """
214
+
215
+ def _build_item(txt, box, score) -> OCRItem:
216
+ """根据原始 OCR 数据构建 OCRItem 结果对象。
217
+
218
+ Args:
219
+ txt: 识别出的文本内容。
220
+ box: 文字区域的四个顶点坐标数组。
221
+ score: 识别置信度。
222
+
223
+ Returns:
224
+ OCRItem: 封装好的识别结果对象。
225
+ """
226
+ l, t = float(box[0][0]), float(box[0][1])
227
+ r, b = float(box[2][0]), float(box[2][1])
228
+ center = (int((l + r) / 2), int((t + b) / 2))
229
+ rect = [int(l), int(t), int(r), int(b)]
230
+ return OCRItem(
231
+ txt=str(txt),
232
+ box=box.tolist() if hasattr(box, 'tolist') else box,
233
+ score=float(score),
234
+ center=center,
235
+ rect=rect
236
+ )
237
+
238
+ def find(self, text_re: str = None, score: float = 0.85) -> Union[OCRItem, NullOCRItem]:
239
+ """在 OCR 识别结果中查找第一个匹配的文本。
240
+
241
+ Args:
242
+ text_re: 正则表达式模式,用于匹配文本内容。为 None 时匹配所有文本。
243
+ score: 最低置信度阈值,默认为 0.85。
244
+
245
+ Returns:
246
+ Union[OCRItem, NullOCRItem]: 匹配到的结果对象,未找到时返回 NullOCRItem。
247
+ """
248
+ txts, boxes, scores = getattr(self, 'txts', None), getattr(self, 'boxes', None), getattr(self, 'scores',
249
+ None)
250
+ if txts is not None and boxes is not None:
251
+ for i in range(len(txts)):
252
+ if scores[i] >= score:
253
+ if text_re is None or re.search(text_re, str(txts[i])):
254
+ return _build_item(txts[i], boxes[i], scores[i])
255
+ return NullOCRItem()
256
+
257
+ def find_all(self, text_re: str = None, score: float = 0.85) -> List[OCRItem]:
258
+ """在 OCR 识别结果中查找所有匹配的文本,按从上到下、从左到右排序。
259
+
260
+ Args:
261
+ text_re: 正则表达式模式,用于匹配文本内容。为 None 时匹配所有文本。
262
+ score: 最低置信度阈值,默认为 0.85。
263
+
264
+ Returns:
265
+ List[OCRItem]: 匹配结果列表,按坐标排序(从上到下、从左到右)。
266
+ """
267
+ results = []
268
+ txts, boxes, scores = getattr(self, 'txts', None), getattr(self, 'boxes', None), getattr(self, 'scores',
269
+ None)
270
+ if txts is not None and boxes is not None:
271
+ for i in range(len(txts)):
272
+ if scores[i] >= score:
273
+ if text_re is None or re.search(text_re, str(txts[i])):
274
+ results.append(_build_item(txts[i], boxes[i], scores[i]))
275
+
276
+ # 按从上到下、从左到右排序
277
+ results.sort(key=lambda x: (x.center[1], x.center[0]))
278
+ return results
279
+
280
+ # 将方法绑定到目标类
281
+ target_class = "RapidOCROutput"
282
+ if hasattr(module, target_class):
283
+ cls = getattr(module, target_class)
284
+ if not hasattr(cls, "find"):
285
+ cls.find = find
286
+ cls.find_all = find_all
287
+ # print(f"✅ [RapidOCR-Hook] 成功注入 find/find_all 扩展")
288
+
289
+
290
+ def install():
291
+ """注册 RapidOCR Hook 的入口函数。
292
+
293
+ 如果 rapidocr.utils.output 已加载,直接对其进行补丁;
294
+ 否则将 RapidOCRHook 注册到 sys.meta_path,等待模块导入时自动注入。
295
+ """
296
+ target_mod = "rapidocr.utils.output"
297
+ if target_mod in sys.modules:
298
+ RapidOCRHook()._patch(sys.modules[target_mod])
299
+ return
300
+ if not any(isinstance(h, RapidOCRHook) for h in sys.meta_path):
301
+ sys.meta_path.insert(0, RapidOCRHook())
@@ -0,0 +1,169 @@
1
+ from dataclasses import dataclass
2
+ from pathlib import Path
3
+ from typing import List
4
+
5
+ import cv2
6
+ import numpy as np
7
+
8
+ from .color_algorithms import color_mask
9
+
10
+
11
+ _RADIX_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@}{[])(*?><~`$^"
12
+
13
+
14
+ def _to_radix(value: int) -> str:
15
+ if value == 0:
16
+ return "0"
17
+ base = len(_RADIX_ALPHABET)
18
+ output = []
19
+ while value:
20
+ value, remainder = divmod(value, base)
21
+ output.append(_RADIX_ALPHABET[remainder])
22
+ return "".join(reversed(output))
23
+
24
+
25
+ def _from_radix(value: str) -> int:
26
+ base = len(_RADIX_ALPHABET)
27
+ result = 0
28
+ for char in value:
29
+ index = _RADIX_ALPHABET.find(char)
30
+ if index < 0:
31
+ raise ValueError(f"字库包含无效字符: {char}")
32
+ result = result * base + index
33
+ return result
34
+
35
+
36
+ @dataclass
37
+ class OcrXResult:
38
+ text: str
39
+ fonts: List[dict]
40
+
41
+ def getText(self):
42
+ return self.text
43
+
44
+ def to_dict(self):
45
+ return {"text": self.text, "fonts": self.fonts}
46
+
47
+
48
+ class OcrX:
49
+ """兼容 Android ``.t`` 点阵字库格式的轻量识字实现。"""
50
+
51
+ @staticmethod
52
+ def append_to_lib(lib_path, content):
53
+ path = Path(lib_path)
54
+ path.parent.mkdir(parents=True, exist_ok=True)
55
+ prefix = "\n" if path.exists() and path.stat().st_size else ""
56
+ with path.open("a", encoding="utf-8") as stream:
57
+ stream.write(prefix + str(content).strip())
58
+
59
+ @staticmethod
60
+ def _trim_mask(mask):
61
+ ys, xs = np.where(mask)
62
+ if not len(xs):
63
+ return mask[:0, :0], 0, 0
64
+ left, right = int(xs.min()), int(xs.max()) + 1
65
+ top, bottom = int(ys.min()), int(ys.max()) + 1
66
+ return mask[top:bottom, left:right], left, top
67
+
68
+ @staticmethod
69
+ def make_font(image, colors: str, name: str = "字") -> str:
70
+ mask, _, _, _ = color_mask(image, colors, similarity=1.0)
71
+ mask, _, _ = OcrX._trim_mask(mask)
72
+ if mask.size == 0:
73
+ raise ValueError("选定颜色在图像中不存在")
74
+ rows = [_to_radix(int("".join("1" if value else "0" for value in row), 2)) for row in mask]
75
+ return f"{name}|{colors}|{int(mask.sum())}|{mask.shape[1]}|{'&'.join(rows)}"
76
+
77
+ @staticmethod
78
+ def _load(font_lib):
79
+ content = str(font_lib or "")
80
+ if isinstance(font_lib, (str, Path)):
81
+ try:
82
+ path = Path(font_lib)
83
+ if path.exists() and path.is_file():
84
+ content = path.read_text(encoding="utf-8")
85
+ except OSError:
86
+ pass
87
+ fonts = []
88
+ for line_number, line in enumerate(content.splitlines(), 1):
89
+ line = line.strip()
90
+ if not line:
91
+ continue
92
+ parts = line.split("|", 4)
93
+ if len(parts) != 5:
94
+ raise ValueError(f"字库第 {line_number} 行格式错误")
95
+ name, colors, count, width, encoded = parts
96
+ width = int(width)
97
+ rows = []
98
+ for row in encoded.split("&"):
99
+ bits = bin(_from_radix(row))[2:].zfill(width)[-width:]
100
+ rows.append([1 if char == "1" else 0 for char in bits])
101
+ matrix = np.asarray(rows, dtype=np.float32)
102
+ fonts.append({"text": name, "colors": colors, "count": max(1, int(count)), "matrix": matrix})
103
+ if not fonts:
104
+ raise ValueError("字库为空")
105
+ return fonts
106
+
107
+ @staticmethod
108
+ def _iou(first, second):
109
+ left = max(first[0], second[0])
110
+ top = max(first[1], second[1])
111
+ right = min(first[2], second[2])
112
+ bottom = min(first[3], second[3])
113
+ if right <= left or bottom <= top:
114
+ return 0.0
115
+ intersection = (right - left) * (bottom - top)
116
+ first_area = (first[2] - first[0]) * (first[3] - first[1])
117
+ second_area = (second[2] - second[0]) * (second[3] - second[1])
118
+ return intersection / max(1, first_area + second_area - intersection)
119
+
120
+ @staticmethod
121
+ def recognize(font_lib, rect=None, region=0.9, image=None) -> OcrXResult:
122
+ fonts = OcrX._load(font_lib)
123
+ candidates = []
124
+ for font in fonts:
125
+ mask, _, offset_x, offset_y = color_mask(image, font["colors"], rect=rect, similarity=1.0)
126
+ mask, trim_x, trim_y = OcrX._trim_mask(mask)
127
+ template = font["matrix"]
128
+ if mask.size == 0 or mask.shape[0] < template.shape[0] or mask.shape[1] < template.shape[1]:
129
+ continue
130
+ scores = cv2.matchTemplate(mask.astype(np.float32), template, cv2.TM_CCORR) / float(font["count"])
131
+ local_max = scores >= cv2.dilate(scores, np.ones((3, 3), np.uint8))
132
+ ys, xs = np.where((scores >= float(region)) & local_max)
133
+ for x, y in zip(xs, ys):
134
+ width, height = template.shape[1], template.shape[0]
135
+ left, top = int(x + trim_x + offset_x), int(y + trim_y + offset_y)
136
+ candidates.append({
137
+ "text": font["text"], "score": round(float(scores[y, x]), 4),
138
+ "rect": [left, top, left + width, top + height],
139
+ "x": left, "y": top, "width": width, "height": height,
140
+ })
141
+
142
+ selected = []
143
+ for candidate in sorted(candidates, key=lambda item: (-item["score"], item["y"], item["x"])):
144
+ if any(OcrX._iou(candidate["rect"], old["rect"]) > 0.3 for old in selected):
145
+ continue
146
+ selected.append(candidate)
147
+ selected.sort(key=lambda item: (item["y"] + item["height"] // 2, item["x"]))
148
+
149
+ lines = []
150
+ for item in selected:
151
+ center_y = item["y"] + item["height"] // 2
152
+ line = next((line for line in lines if abs(line[0] - center_y) <= max(3, item["height"] // 2)), None)
153
+ if line is None:
154
+ line = [center_y, []]
155
+ lines.append(line)
156
+ line[1].append(item)
157
+ lines.sort(key=lambda line: line[0])
158
+ text_lines = []
159
+ ordered = []
160
+ for _, items in lines:
161
+ items.sort(key=lambda item: item["x"])
162
+ ordered.extend(items)
163
+ text_lines.append("".join(item["text"] for item in items))
164
+ return OcrXResult("\n".join(text_lines), ordered)
165
+
166
+ @staticmethod
167
+ def find_all(font_lib: str, rect=None, region=0.9, image=None):
168
+ result = OcrX.recognize(font_lib, rect=rect, region=region, image=image)
169
+ return result.text or None
@@ -0,0 +1,139 @@
1
+ import os
2
+ import base64
3
+ import ctypes
4
+ from io import BytesIO
5
+ from typing import Literal, Union, Optional, List
6
+ import numpy as np
7
+ from PIL import Image
8
+ from mss import mss
9
+
10
+ # 解决 Windows 高分屏缩放导致的截图坐标偏差
11
+ try:
12
+ ctypes.windll.shcore.SetProcessDpiAwareness(1)
13
+ except Exception:
14
+ try:
15
+ ctypes.windll.user32.SetProcessDPIAware()
16
+ except Exception:
17
+ pass
18
+
19
+ class Screen:
20
+ """屏幕截图与显示器信息工具类。
21
+
22
+ 基于 mss 库实现高性能屏幕截图,支持 Windows 高分屏 DPI 自适应。
23
+ 提供多种输出格式(PIL、NumPy、OpenCV、Base64)及区域截取功能。
24
+ """
25
+
26
+ JPEG_QUALITY = 80
27
+
28
+ @staticmethod
29
+ def encode_image(image: Image.Image, image_format: str = "JPEG") -> bytes:
30
+ """Encode a PIL image using the same settings as capture()."""
31
+ image_format = image_format.upper()
32
+ output = BytesIO()
33
+ save_args = {"format": image_format}
34
+ if image_format == "JPEG":
35
+ if image.mode != "RGB":
36
+ image = image.convert("RGB")
37
+ save_args["quality"] = Screen.JPEG_QUALITY
38
+ image.save(output, **save_args)
39
+ return output.getvalue()
40
+
41
+ @staticmethod
42
+ def size() -> List[int]:
43
+ """获取主显示器的分辨率尺寸。
44
+
45
+ Returns:
46
+ List[int]: [宽度, 高度],单位为像素。
47
+ """
48
+ with mss() as sct:
49
+ monitor = sct.monitors[1]
50
+ return [monitor["width"], monitor["height"]]
51
+
52
+
53
+
54
+ @staticmethod
55
+ def capture(
56
+ rect: Optional[List[int]] = None,
57
+ format: str = "pillow",
58
+ save_path: Optional[str] = None
59
+ ) -> Union[Image.Image, np.ndarray, str, None]:
60
+ """截取屏幕图像。
61
+
62
+ 支持全屏或指定区域截图,可输出多种格式并可选保存到文件。
63
+ 已处理 Windows 高分屏 DPI 缩放问题。
64
+
65
+ Args:
66
+ rect: 截图区域 [left, top, right, bottom],为 None 时截取整个主显示器。
67
+ format: 输出格式,可选值:
68
+ - 'pillow': 返回 PIL.Image 对象(默认)。
69
+ - 'numpy': 返回 RGB 格式的 NumPy 数组。
70
+ - 'opencv': 返回 BGR 格式的 NumPy 数组。
71
+ - 'base64': 返回 JPEG 格式的 Base64 数据 URI 字符串。
72
+ - 'base64:png': 返回 PNG 格式的 Base64 数据 URI 字符串。
73
+ save_path: 文件保存路径,自动根据后缀名选择存储格式(支持 jpg/png),
74
+ 为 None 时不保存。
75
+
76
+ Returns:
77
+ Union[Image.Image, numpy.ndarray, str, None]: 根据 format 参数返回对应格式的截图,
78
+ 截图失败时返回 None。
79
+ """
80
+ with mss() as sct:
81
+ try:
82
+ # 1. 区域计算与合法性校验
83
+ if rect and len(rect) == 4:
84
+ left, top, right, bottom = rect
85
+ # 确保 width 和 height 为正数
86
+ width, height = max(0, right - left), max(0, bottom - top)
87
+ if width == 0 or height == 0:
88
+ return None
89
+ monitor = {"left": left, "top": top, "width": width, "height": height}
90
+ else:
91
+ monitor = sct.monitors[1]
92
+
93
+ # 2. 执行截图
94
+ sct_img = sct.grab(monitor)
95
+
96
+ # 3. 构造 PIL 对象 (核心中转)
97
+ img = Image.frombytes("RGB", sct_img.size, sct_img.bgra, "raw", "BGRX")
98
+
99
+ # 4. 磁盘保存逻辑
100
+ if save_path:
101
+ directory = os.path.dirname(save_path)
102
+ if directory and not os.path.exists(directory):
103
+ os.makedirs(directory, exist_ok=True)
104
+
105
+ _, ext = os.path.splitext(save_path)
106
+ ext = ext.lower().strip('.')
107
+ # 自动匹配存储格式
108
+ save_fmt = "JPEG" if ext in ["jpg", "jpeg"] else ("PNG" if ext == "png" else "PNG")
109
+
110
+ # 针对不同格式设置最优参数
111
+ save_args = {"format": save_fmt}
112
+ if save_fmt == "JPEG":
113
+ save_args["quality"] = 95
114
+ save_args["subsampling"] = 0 # 提升色彩保持度
115
+
116
+ img.save(save_path, **save_args)
117
+
118
+ # 5. 返回格式化处理
119
+ if format.startswith("base64"):
120
+ # 默认 jpeg,除非明确指定 base64:png
121
+ sub_format = "PNG" if (":" in format and format.split(":")[1].lower() == "png") else "JPEG"
122
+ image_bytes = Screen.encode_image(img, sub_format)
123
+ img_str = base64.b64encode(image_bytes).decode("utf-8")
124
+ return f"data:image/{sub_format.lower()};base64,{img_str}"
125
+
126
+ elif format == "pillow":
127
+ return img
128
+
129
+ elif format == "numpy":
130
+ return np.array(img)
131
+
132
+ elif format == "opencv":
133
+ return np.array(img)[:, :, ::-1]
134
+
135
+ return img
136
+
137
+ except Exception as e:
138
+ print(f"Screenshot Error: {e}")
139
+ return None
@@ -0,0 +1,21 @@
1
+
2
+ def main():
3
+ from ascript.windows.ui.app_store_window import AppStoreWindow
4
+ import os
5
+
6
+ def on_start(app_info, license_data):
7
+ print(app_info)
8
+ print(license_data)
9
+ # package_name = "AScriptProject2"
10
+ user_id = str(app_info["id"])
11
+ file_md5 = app_info['fileMd5']
12
+
13
+ is_ready = app_store.env_manager.smart_deploy(user_id, app_store.target_path, file_md5)
14
+ if not is_ready:
15
+ return False
16
+ return app_store.env_manager.run_app(user_id) is not None
17
+
18
+ root_path = os.path.join(os.getcwd(), "apps")
19
+ default_id = os.environ.get("ASCRIPT_APP_ID") or None
20
+ app_store = AppStoreWindow(default_id=default_id, on_start=on_start, env_path=root_path)
21
+ app_store.show()
@@ -0,0 +1,8 @@
1
+ from .r import R
2
+ from .key_value import KeyValue
3
+ from .runtime import Runtime
4
+ from .device import Device
5
+ from .pip import Pip
6
+ from .env import EnvManager
7
+
8
+ __all__ = ['R', 'KeyValue','Runtime',"Device","Pip","EnvManager"]