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,495 @@
1
+ # 1. 暴露函数建议放在函数外(或者确保在函数内时,函数被调用)
2
+ import os
3
+ import platform
4
+ import shutil
5
+ import stat
6
+ import subprocess
7
+
8
+ import eel,io,base64
9
+ from ..window import Window,Selector
10
+ import uiautomation as auto
11
+ import time
12
+
13
+
14
+ @eel.expose
15
+ def get_online_windows():
16
+ """获取当前系统中所有可见窗口的信息列表。
17
+
18
+ 遍历所有可见窗口,过滤掉无标题的后台窗口,返回包含窗口标题和句柄的字典列表。
19
+
20
+ Returns:
21
+ dict: 包含以下字段的字典:
22
+ - status (str): "success" 或 "error"
23
+ - data (list[dict]): 窗口信息列表,每项包含 display、hwnd、title
24
+ - message (str): 错误信息(仅失败时包含)
25
+ """
26
+ try:
27
+ # 调用你的类方法
28
+ windows = Window.find_all(visible_only=True)
29
+
30
+ # 转换为前端易读的格式:名称 [句柄]
31
+ # 同时保留 hwnd 以便后续探测指定窗口
32
+ output = []
33
+ for win in windows:
34
+ if win.title: # 过滤掉无标题的背景窗口
35
+ output.append({
36
+ "display": f"{win.title} [{win.hwnd}]",
37
+ "hwnd": win.hwnd,
38
+ "title": win.title
39
+ })
40
+ return {"status": "success", "data": output}
41
+ except Exception as e:
42
+ return {"status": "error", "message": str(e)}
43
+
44
+ @eel.expose
45
+ def get_ui_tree_data(hwnd, max_depth=0):
46
+ """获取指定窗口的完整 UI 元素树结构。
47
+
48
+ 根据窗口句柄构建 Selector,递归探测窗口中的所有 UI 元素,
49
+ 返回树状结构的字典数据。会自动激活目标窗口。
50
+
51
+ Args:
52
+ hwnd: 窗口句柄,会自动转换为整数类型。
53
+ max_depth: 探测深度限制。0 表示不限深度,大于 0 则限制最大递归层数。
54
+
55
+ Returns:
56
+ dict: 包含以下字段的字典:
57
+ - status (str): "success"、"empty" 或 "error"
58
+ - data (dict): UI 元素树的字典表示(仅成功时包含)
59
+ - message (str): 结果描述或错误信息
60
+ """
61
+ try:
62
+ # 1. 确保 hwnd 是整数类型
63
+ hwnd = int(hwnd)
64
+ max_depth = int(max_depth)
65
+
66
+ if max_depth==0:
67
+ max_depth = 0xFFFFFFFF
68
+
69
+ # 2. 构造 Window 对象
70
+ target_win = Window(hwnd)
71
+ if not target_win.title:
72
+ return {"status": "error", "message": "无效的窗口句柄或窗口已关闭"}
73
+
74
+ target_win.activate()
75
+
76
+ # 3. 初始化 Selector (假设你的 Selector 接收 window 和 max_depth)
77
+ # 这里演示你的 get_uielement_tree 逻辑
78
+ # 注意:如果你的 Selector 类还没写好,这里直接调用你提供的函数逻辑
79
+ selector = Selector(target_win, depth=max_depth)
80
+
81
+ print(f"开始探测窗口 [{target_win.title}],深度: {max_depth}...")
82
+
83
+ # 4. 获取树状结构(调用你写的那个逻辑)
84
+ root_element = selector.get_uielement_tree()
85
+
86
+ if not root_element:
87
+ return {"status": "empty", "message": "未能获取到任何 UI 元素"}
88
+
89
+ # 5. 转换为字典(递归调用 UIElement.to_dict)
90
+ tree_dict = root_element.to_dict()
91
+
92
+ return {
93
+ "status": "success",
94
+ "data": tree_dict
95
+ }
96
+
97
+ except Exception as e:
98
+ import traceback
99
+ print(f"探测失败: {e}")
100
+ traceback.print_exc()
101
+ return {"status": "error", "message": str(e)}
102
+
103
+
104
+ @eel.expose
105
+ def test_selector(selector_str: str):
106
+ """执行 Selector 表达式并返回匹配的 UI 元素结果。
107
+
108
+ 接收前端传入的 Selector 表达式字符串,使用 eval 在安全环境中执行,
109
+ 自动处理未调用 .find() 的情况,并将结果序列化为字典列表返回。
110
+
111
+ Args:
112
+ selector_str: Selector 表达式字符串,如 "Selector(...).find()"。
113
+
114
+ Returns:
115
+ dict: 包含以下字段的字典:
116
+ - status (str): "success" 或 "error"
117
+ - data (list[dict]): 匹配到的 UI 元素字典列表
118
+ - message (str): 错误信息(仅失败时包含)
119
+ """
120
+ print(f"\n[Python Receive] 收到指令: {selector_str}")
121
+ try:
122
+ # 1. 准备环境
123
+ safe_vars = {'Selector': Selector, 'auto': auto}
124
+
125
+ # 2. 执行 eval
126
+ print("[Python Debug] 开始执行 eval...")
127
+ results = eval(selector_str, {"__builtins__": None}, safe_vars)
128
+
129
+ # 3. 核心修复逻辑:自动补全与类型统一
130
+ # 情况 A: 如果返回的是 Selector 实例(用户忘了写 .find())
131
+ if hasattr(results, 'find'):
132
+ print("[Python Debug] 检测到未执行 find,自动调用 .find()")
133
+ results = results.find()
134
+
135
+ # 情况 B: 统一转换为列表 (处理 find_first 返回单个对象的情况)
136
+ if results is None:
137
+ final_list = []
138
+ elif isinstance(results, list):
139
+ final_list = results
140
+ else:
141
+ # 说明是单个 UIElement 对象
142
+ final_list = [results]
143
+
144
+ print(f"[Python Debug] 最终待处理结果数量: {len(final_list)}")
145
+
146
+ # 4. 序列化
147
+ tree_dict = [el.to_dict() for el in final_list]
148
+
149
+ return {
150
+ "status": "success",
151
+ "data": tree_dict
152
+ }
153
+
154
+ except Exception as e:
155
+ import traceback
156
+ error_stack = traceback.format_exc()
157
+ print(f"[Python Error] 详细报错如下:\n{error_stack}")
158
+ return {
159
+ "status": "error",
160
+ "message": str(e),
161
+ "data": []
162
+ }
163
+
164
+
165
+
166
+
167
+ @eel.expose
168
+ def get_screenshot(hwnd, save_path=None, include_data=True):
169
+ """截取指定窗口的截图,返回 base64 编码的预览图。
170
+
171
+ 分阶段处理:参数校验、窗口捕获、磁盘保存(可选)、base64 编码。
172
+ 每个阶段独立捕获异常,确保不会因局部错误导致整体崩溃。
173
+
174
+ Args:
175
+ hwnd: 目标窗口句柄,会自动转换为整数。
176
+ save_path: 图片保存路径(可选)。可以是文件路径或目录路径。
177
+ 若为目录则自动生成文件名。若为 None 则不保存到磁盘。
178
+ include_data: 是否生成并返回 base64 预览。图色工具已经通过保存路径
179
+ 加载图片,传 False 可避免在 Eel WebSocket 中重复传输整张截图。
180
+
181
+ Returns:
182
+ dict: 包含以下字段的字典:
183
+ - status (str): "success" 或 "error"
184
+ - data (str): base64 编码的 JPEG 预览图(data URI 格式)
185
+ - save_info (dict): 磁盘保存结果,含 status 和 path
186
+ - message (str): 错误信息(如有)
187
+ """
188
+ # 初始化返回结构
189
+ result = {
190
+ "status": "error",
191
+ "data": None,
192
+ "save_info": {"status": "none", "path": None},
193
+ "message": ""
194
+ }
195
+
196
+ try:
197
+ # --- 阶段 1: 参数校验 ---
198
+ try:
199
+ hwnd_int = int(hwnd)
200
+ except (ValueError, TypeError):
201
+ result["message"] = f"无效的句柄: {hwnd}"
202
+ return result
203
+
204
+ # --- 阶段 2: 捕获窗口 (最易崩溃点) ---
205
+ try:
206
+ # 假设你的 Window 类在捕获失败时会抛错
207
+ img = Window(hwnd_int).capture()
208
+ if img is None:
209
+ raise Exception("捕获返回对象为空")
210
+ except Exception as capture_err:
211
+ result["message"] = f"窗口捕获失败: {str(capture_err)}"
212
+ return result
213
+
214
+ # --- 阶段 3: 保存逻辑 (独立异常处理) ---
215
+ if save_path:
216
+ try:
217
+ # 规范化路径
218
+ clean_save_path = os.path.abspath(save_path)
219
+
220
+ # 如果是目录,则生成文件名
221
+ if os.path.isdir(clean_save_path):
222
+ filename = f"{int(time.time() * 1000)}.jpg"
223
+ clean_save_path = os.path.join(clean_save_path, filename)
224
+
225
+ # 确保后缀正确
226
+ if not clean_save_path.lower().endswith(('.jpg', '.jpeg')):
227
+ clean_save_path += ".jpg"
228
+
229
+ # 确保父目录存在
230
+ os.makedirs(os.path.dirname(clean_save_path), exist_ok=True)
231
+
232
+ # 保存图片 (转换模式防止 PNG 包含透明通道导致转 JPG 失败)
233
+ if img.mode in ("RGBA", "P"):
234
+ img = img.convert("RGB")
235
+
236
+ img.save(clean_save_path, format="JPEG", quality=100)
237
+ result["save_info"] = {"status": "success", "path": clean_save_path}
238
+ except Exception as save_err:
239
+ result["save_info"] = {"status": "failed", "message": str(save_err)}
240
+ print(f"磁盘保存跳过 (非致命错误): {save_err}")
241
+
242
+ if not include_data:
243
+ result["status"] = "success"
244
+ result["data"] = ""
245
+ return result
246
+
247
+ # --- 阶段 4: 转换 Base64 返回前端 ---
248
+ try:
249
+ buffer = io.BytesIO()
250
+ # 同样转换模式确保转码成功
251
+ preview_img = img if img.mode == "RGB" else img.convert("RGB")
252
+ preview_img.save(buffer, format="JPEG", quality=80) # 预览质量可以略低,提高传输速度
253
+ img_str = base64.b64encode(buffer.getvalue()).decode()
254
+
255
+ result["status"] = "success"
256
+ result["data"] = f"data:image/jpeg;base64,{img_str}"
257
+ except Exception as encode_err:
258
+ result["message"] = f"预览图生成失败: {str(encode_err)}"
259
+
260
+ except Exception as global_err:
261
+ # 最后的兜底,确保 Python 不会闪退
262
+ result["status"] = "error"
263
+ result["message"] = f"系统级异常: {str(global_err)}"
264
+
265
+ return result
266
+
267
+
268
+
269
+
270
+
271
+ @eel.expose
272
+ def open_folder(path):
273
+ """使用系统默认文件浏览器打开指定路径。
274
+
275
+ 自动识别当前操作系统(Windows/macOS/Linux),调用对应的文件管理器打开目录。
276
+
277
+ Args:
278
+ path: 要打开的目录或文件路径,支持相对路径(会自动转为绝对路径)。
279
+
280
+ Returns:
281
+ bool: 打开成功返回 True,路径不存在或打开失败返回 False。
282
+ """
283
+ # 转换路径为当前系统的标准路径(处理斜杠方向)
284
+ path = os.path.abspath(path)
285
+
286
+ if not os.path.exists(path):
287
+ print(f"路径不存在: {path}")
288
+ return False
289
+
290
+ current_os = platform.system()
291
+
292
+ try:
293
+ if current_os == "Windows":
294
+ # Windows: 使用 explorer
295
+ # os.startfile(path) 是另一种简便方法,但 subprocess 更可控
296
+ subprocess.run(['explorer', path])
297
+
298
+ elif current_os == "Darwin":
299
+ # macOS: 使用 open
300
+ subprocess.run(['open', path])
301
+
302
+ else:
303
+ # Linux: 通常使用 xdg-open
304
+ subprocess.run(['xdg-open', path])
305
+
306
+ return True
307
+ except Exception as e:
308
+ print(f"打开文件夹失败: {e}")
309
+ return False
310
+
311
+
312
+ @eel.expose
313
+ def file_delete(path):
314
+ """删除指定路径的文件或清空指定目录的内容。
315
+
316
+ 如果路径是文件则直接删除;如果是目录则仅清空其中内容而不删除目录本身,
317
+ 以防止程序因目录被删除而崩溃。自动处理只读文件权限问题。
318
+
319
+ Args:
320
+ path: 要删除的文件或目录路径,支持相对路径。
321
+
322
+ Returns:
323
+ bool: 操作成功返回 True,路径不存在或操作失败返回 False。
324
+ """
325
+ try:
326
+ abs_path = os.path.abspath(path)
327
+ if not os.path.exists(abs_path):
328
+ return False
329
+
330
+ def on_rm_error(func, path, exc_info):
331
+ """处理删除只读文件时的权限错误回调。
332
+
333
+ Args:
334
+ func: 引发异常的函数(如 os.remove)。
335
+ path: 引发异常的文件路径。
336
+ exc_info: 异常信息元组。
337
+ """
338
+ # 修改权限后重试
339
+ os.chmod(path, stat.S_IWRITE)
340
+ func(path)
341
+
342
+ if os.path.isdir(abs_path):
343
+ # 方案:不删除文件夹,只删除内容,防止程序崩溃
344
+ for item in os.listdir(abs_path):
345
+ item_path = os.path.join(abs_path, item)
346
+ try:
347
+ if os.path.isfile(item_path) or os.path.islink(item_path):
348
+ os.unlink(item_path) # 删除文件或链接
349
+ elif os.path.isdir(item_path):
350
+ shutil.rmtree(item_path, onerror=on_rm_error)
351
+ except Exception as e:
352
+ print(f"无法删除项 {item_path}: {e}")
353
+ print(f"文件夹内容已清空: {abs_path}")
354
+ else:
355
+ os.remove(abs_path)
356
+
357
+ return True
358
+ except Exception as e:
359
+ print(f"删除操作引发崩溃风险,已拦截: {e}")
360
+ return False
361
+
362
+
363
+ import sys
364
+ import os
365
+ import traceback
366
+ import json
367
+ import types
368
+ import numpy as np
369
+
370
+ # 自动将当前脚本目录加入 sys.path,确保 JS 里 `import 你的私有库` 能成功
371
+ CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
372
+ if CURRENT_DIR not in sys.path:
373
+ sys.path.append(CURRENT_DIR)
374
+
375
+
376
+ @eel.expose
377
+ def python_executor(python_code):
378
+ """通用 Python 代码执行器,供前端 JS 调用执行 Python 代码。
379
+
380
+ 在隔离的命名空间中编译并执行传入的 Python 代码字符串,
381
+ 支持 import 语句和复杂逻辑。通过 _result 变量传递执行结果。
382
+
383
+ Args:
384
+ python_code: 要执行的 Python 代码字符串。可在代码中设置 _result 变量作为返回值。
385
+
386
+ Returns:
387
+ dict: 包含以下字段的字典:
388
+ - success (bool): 执行是否成功
389
+ - data: 执行结果(代码中 _result 变量的值),经过 JSON 安全序列化
390
+ - error (dict|None): 错误信息,含 type、message、traceback 字段
391
+ """
392
+ # 结果容器
393
+ response = {
394
+ "success": False,
395
+ "data": None,
396
+ "error": {
397
+ "type": None,
398
+ "message": None,
399
+ "traceback": None
400
+ }
401
+ }
402
+
403
+
404
+ try:
405
+ # 1. 编译代码 (这一步能捕获 SyntaxError)
406
+ byte_code = compile(python_code, '<js_executed_code>', 'exec')
407
+
408
+ # 2. 执行代码
409
+ # 不预设 globals,让 JS 自己 import。但传入常见的内置模块作为兜底
410
+ exec_env = {"__builtins__": __builtins__}
411
+ exec(byte_code, exec_env, exec_env)
412
+
413
+ # 3. 提取结果
414
+ if "_result" in exec_env:
415
+ response["data"] = make_json_serializable(exec_env["_result"])
416
+
417
+ response["success"] = True
418
+ response["error"] = None # 显式清空错误信息
419
+
420
+ except Exception as e:
421
+ # 4. 全量异常捕捉
422
+ response["success"] = False
423
+ response["error"] = {
424
+ "type": e.__class__.__name__,
425
+ "message": str(e),
426
+ "traceback": traceback.format_exc() # 返回完整的错误堆栈,方便 JS 端调试
427
+ }
428
+ except SyntaxError as e:
429
+ # 特殊处理语法错误
430
+ response["success"] = False
431
+ response["error"] = {
432
+ "type": "SyntaxError",
433
+ "message": f"行 {e.lineno}: {e.msg}",
434
+ "traceback": traceback.format_exc()
435
+ }
436
+
437
+ return response
438
+
439
+
440
+ def make_json_serializable(obj):
441
+ """将任意 Python 对象转换为 JSON 可序列化的格式。
442
+
443
+ 递归处理各种数据类型,包括基本类型、容器类型、NumPy 数组、
444
+ 自定义类实例等,确保结果可以安全地通过 JSON 传输到前端。
445
+
446
+ Args:
447
+ obj: 任意 Python 对象。
448
+
449
+ Returns:
450
+ JSON 可序列化的对象(dict、list、str、int、float、bool 或 None)。
451
+ """
452
+ if obj is None: return None
453
+ if isinstance(obj, (int, float, str, bool)): return obj
454
+
455
+ # 优先使用对象自带的转换方法
456
+ if hasattr(obj, 'to_dict') and callable(obj.to_dict):
457
+ return make_json_serializable(obj.to_dict())
458
+
459
+ # 处理容器
460
+ if isinstance(obj, (list, tuple, set)):
461
+ return [make_json_serializable(i) for i in obj]
462
+ if isinstance(obj, dict):
463
+ return {str(k): make_json_serializable(v) for k, v in obj.items()}
464
+
465
+ # 处理 Numpy/OpenCV 数据
466
+ if hasattr(obj, 'tolist'):
467
+ return obj.tolist()
468
+
469
+ # 处理类实例
470
+ if hasattr(obj, '__dict__'):
471
+ return {k: make_json_serializable(v) for k, v in obj.__dict__.items() if not k.startswith('_')}
472
+
473
+ # 最终兜底:转为字符串,防止 JSON 失败导致崩溃
474
+ return str(obj)
475
+
476
+ import eel
477
+ import tkinter as tk
478
+ from tkinter import filedialog
479
+
480
+ # 初始化一个隐藏的 tkinter 窗口,用于弹出对话框
481
+ root = tk.Tk()
482
+ root.withdraw()
483
+ root.wm_attributes('-topmost', 1) # 确保对话框在最前面
484
+ @eel.expose
485
+ def select_folder():
486
+ """弹出系统文件夹选择对话框并返回用户选择的路径。
487
+
488
+ 使用 tkinter 的 filedialog 组件弹出文件夹选择对话框,
489
+ 对话框始终显示在最前面。
490
+
491
+ Returns:
492
+ str: 用户选择的文件夹路径,取消选择则返回空字符串。
493
+ """
494
+ folder_path = filedialog.askdirectory()
495
+ return folder_path if folder_path else ""
File without changes
@@ -0,0 +1,70 @@
1
+ from abc import ABC, abstractmethod
2
+ class GpPlug(ABC):
3
+ """图形处理插件基类(抽象类)。
4
+
5
+ 定义了所有图形处理插件必须实现的标准接口,包括名称、描述、
6
+ 入口页面和图标。子类必须实现所有抽象属性。
7
+ """
8
+
9
+ @property
10
+ @abstractmethod
11
+ def name(self):
12
+ """插件显示名称。
13
+
14
+ Returns:
15
+ str: 插件的中文显示名称。
16
+ """
17
+ pass
18
+
19
+ @property
20
+ @abstractmethod
21
+ def description(self):
22
+ """插件功能描述。
23
+
24
+ Returns:
25
+ str: 插件功能的简要说明文本。
26
+ """
27
+ pass
28
+
29
+ @property
30
+ @abstractmethod
31
+ def entry_html(self):
32
+ """插件入口 HTML 文件的相对路径。
33
+
34
+ Returns:
35
+ str: 相对于 web 目录的 HTML 文件路径。
36
+ """
37
+ pass
38
+
39
+ @property
40
+ @abstractmethod
41
+ def icon(self):
42
+ """插件图标的相对路径。
43
+
44
+ Returns:
45
+ str: 相对于 web 目录的图标文件路径。
46
+ """
47
+ pass
48
+
49
+ @property
50
+ def class_name(self):
51
+ """获取当前插件类的类名。
52
+
53
+ Returns:
54
+ str: 类名字符串,如 'FindImagesPlug'。
55
+ """
56
+ return self.__class__.__name__
57
+
58
+ def to_dict(self):
59
+ """将插件的所有属性转换为字典,方便序列化为 JSON。
60
+
61
+ Returns:
62
+ dict: 包含 name、class_name、description、entry_html、icon 字段的字典。
63
+ """
64
+ return {
65
+ "name": self.name,
66
+ "class_name": self.class_name,
67
+ "description": self.description,
68
+ "entry_html": self.entry_html,
69
+ "icon": self.icon
70
+ }