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,272 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import time
|
|
3
|
+
import os
|
|
4
|
+
from .web_window import WebWindow
|
|
5
|
+
from .. import license
|
|
6
|
+
from ..system import KeyValue, R, EnvManager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AppStoreWindow(WebWindow):
|
|
10
|
+
"""应用商店窗口类。
|
|
11
|
+
|
|
12
|
+
提供应用搜索、激活码验证、应用下载与启动等功能的 Web 窗口界面。
|
|
13
|
+
继承自 WebWindow,通过 HTML 页面与 Python 后端进行交互。
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, default_id: str = None, on_start=None, env_path: str = None): # 增加默认 ID 参数
|
|
17
|
+
"""初始化应用商店窗口。
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
default_id: 默认的应用 ID,用于自动填充搜索框。
|
|
21
|
+
on_start: 应用启动时的回调函数,接收 (app_info, license_data) 两个参数。
|
|
22
|
+
env_path: 虚拟环境的根目录路径,传递给 EnvManager。
|
|
23
|
+
"""
|
|
24
|
+
self.default_id = default_id
|
|
25
|
+
self.on_start = on_start # 存储回调
|
|
26
|
+
self.app_info = None
|
|
27
|
+
self.license_data = None
|
|
28
|
+
self.target_path = None
|
|
29
|
+
self.env_manager = EnvManager(root_path=env_path)
|
|
30
|
+
|
|
31
|
+
_root = R.internal("windows", "tools", "web")
|
|
32
|
+
_filepath = R.internal("windows", "tools", "web", "app_store_window.html")
|
|
33
|
+
|
|
34
|
+
super().__init__(_filepath, project_root=_root)
|
|
35
|
+
|
|
36
|
+
self.expose(self.get_app_info)
|
|
37
|
+
self.expose(self.activate_key)
|
|
38
|
+
self.expose(self.start_app)
|
|
39
|
+
self.expose(self.get_history)
|
|
40
|
+
self.expose(self.get_cached_key)
|
|
41
|
+
# 暴露一个获取初始 ID 的方法
|
|
42
|
+
self.expose(self.get_init_config)
|
|
43
|
+
self.expose(self.check_status)
|
|
44
|
+
self.expose(self.stop_app)
|
|
45
|
+
self.expose(self.clear_env)
|
|
46
|
+
self.expose(self.download_app)
|
|
47
|
+
|
|
48
|
+
def get_init_config(self):
|
|
49
|
+
"""获取初始配置信息。
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
dict: 包含 default_id 的字典,前端用于初始化界面。
|
|
53
|
+
"""
|
|
54
|
+
return {"default_id": self.default_id}
|
|
55
|
+
|
|
56
|
+
def get_app_info(self, app_id):
|
|
57
|
+
"""根据应用 ID 搜索并获取应用信息。
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
app_id: 应用的唯一标识符。
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
dict: 成功时返回 {"code": 1, "data": app_info};
|
|
64
|
+
失败时返回 {"code": 0, "msg": 错误信息}。
|
|
65
|
+
"""
|
|
66
|
+
app_id = str(app_id).strip() # 强制转字符串,解决类型报错
|
|
67
|
+
res = license.achttp.get_apps(app_id)
|
|
68
|
+
if res and len(res) > 0:
|
|
69
|
+
self.app_info = res[0]
|
|
70
|
+
return {"code": 1, "data": self.app_info}
|
|
71
|
+
return {"code": 0, "msg": f"未能找到 ID 为 '{app_id}' 的应用,请检查输入是否正确。"}
|
|
72
|
+
|
|
73
|
+
def get_cached_key(self, app_id):
|
|
74
|
+
"""按应用 ID 获取本地缓存的激活码。
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
app_id: 应用的唯一标识符。
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
str: 缓存的激活码字符串,若无缓存则返回空字符串。
|
|
81
|
+
"""
|
|
82
|
+
return KeyValue.get(f"lic_cache_{str(app_id)}", "")
|
|
83
|
+
|
|
84
|
+
def activate_key(self, app_id, key: str):
|
|
85
|
+
"""使用激活码对指定应用进行授权激活。
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
app_id: 应用的唯一标识符。
|
|
89
|
+
key: 用户输入的激活码。
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
dict: 服务端返回的激活结果,成功时 code 为 1 并包含 data 字段。
|
|
93
|
+
"""
|
|
94
|
+
app_id = str(app_id)
|
|
95
|
+
res = license.achttp.get_card(app_id, key)
|
|
96
|
+
if res and res.get('code') == 1:
|
|
97
|
+
self.license_data = res.get('data')
|
|
98
|
+
# 区分 ID 存储
|
|
99
|
+
KeyValue.save(f"lic_cache_{app_id}", key)
|
|
100
|
+
else:
|
|
101
|
+
self.license_data = None
|
|
102
|
+
return res
|
|
103
|
+
|
|
104
|
+
def get_history(self):
|
|
105
|
+
"""获取应用运行历史记录列表。
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
list: 运行记录列表,每项包含 id 和 name 字段;异常时返回空列表。
|
|
109
|
+
"""
|
|
110
|
+
try:
|
|
111
|
+
history_str = KeyValue.get("app_run_history", "[]")
|
|
112
|
+
return json.loads(history_str)
|
|
113
|
+
except:
|
|
114
|
+
return []
|
|
115
|
+
|
|
116
|
+
# 在 AppStoreWindow 类中新增/修改以下方法
|
|
117
|
+
def download_app(self, app_info):
|
|
118
|
+
"""下载应用文件到本地存储目录。
|
|
119
|
+
|
|
120
|
+
根据应用信息中的文件路径下载 whl 等文件,支持断点检测(已存在则跳过),
|
|
121
|
+
并在下载过程中通过 JS 回调更新进度条。
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
app_info: 应用信息字典,需包含 filePath(下载地址)、fileMd5(文件校验值)、id(应用 ID)。
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
dict: 成功时返回 {"code": 1, "msg": ..., "path": 本地路径};
|
|
128
|
+
失败时返回 {"code": 0, "msg": 错误信息}。
|
|
129
|
+
"""
|
|
130
|
+
url = app_info['filePath']
|
|
131
|
+
file_md5 = app_info['fileMd5']
|
|
132
|
+
user_id = str(app_info["id"])
|
|
133
|
+
|
|
134
|
+
# 从 URL 中提取原始文件名(例如: myapp-1.0.whl)
|
|
135
|
+
# 如果 URL 后面带参数,可以用 url.split('?')[0]
|
|
136
|
+
remote_filename = os.path.basename(url.split('?')[0])
|
|
137
|
+
# if not remote_filename.endswith('.whl'):
|
|
138
|
+
# remote_filename = f"{file_md5}.whl"
|
|
139
|
+
|
|
140
|
+
# 1. 获取目标文件夹路径:module/user_id/file_md5/
|
|
141
|
+
# 这里的 get_module_storage_path 会返回完整的:.../module/1085/md5_string/xxx.whl
|
|
142
|
+
self.target_path = os.path.join(self.env_manager.get_model_dir(user_id), file_md5, remote_filename)
|
|
143
|
+
|
|
144
|
+
# 确保文件夹存在(即 module/user_id/file_md5/ 这一层)
|
|
145
|
+
storage_dir = os.path.dirname(self.target_path)
|
|
146
|
+
if not os.path.exists(storage_dir):
|
|
147
|
+
os.makedirs(storage_dir, exist_ok=True)
|
|
148
|
+
|
|
149
|
+
# 2. 如果文件已存在,直接返回
|
|
150
|
+
if os.path.exists(self.target_path):
|
|
151
|
+
return {"code": 1, "msg": "已存在", "path": self.target_path}
|
|
152
|
+
|
|
153
|
+
# 3. 开始执行下载
|
|
154
|
+
try:
|
|
155
|
+
import requests
|
|
156
|
+
headers = {'User-Agent': 'Mozilla/5.0'}
|
|
157
|
+
response = requests.get(url, stream=True, timeout=30, headers=headers)
|
|
158
|
+
response.raise_for_status()
|
|
159
|
+
|
|
160
|
+
total_size = int(response.headers.get('content-length', 0))
|
|
161
|
+
downloaded = 0
|
|
162
|
+
|
|
163
|
+
with open(self.target_path, "wb") as f:
|
|
164
|
+
for chunk in response.iter_content(chunk_size=1024 * 64):
|
|
165
|
+
if chunk:
|
|
166
|
+
f.write(chunk)
|
|
167
|
+
downloaded += len(chunk)
|
|
168
|
+
if total_size > 0:
|
|
169
|
+
percent = int(downloaded * 100 / total_size)
|
|
170
|
+
# 确保 webview_instance 存在
|
|
171
|
+
if hasattr(self, 'webview_instance'):
|
|
172
|
+
self.webview_instance.evaluate_js(f"updateDownloadProgress({percent})")
|
|
173
|
+
|
|
174
|
+
return {"code": 1, "msg": "下载完成", "path": self.target_path}
|
|
175
|
+
except Exception as e:
|
|
176
|
+
if os.path.exists(self.target_path):
|
|
177
|
+
try:
|
|
178
|
+
os.remove(self.target_path)
|
|
179
|
+
except:
|
|
180
|
+
pass
|
|
181
|
+
return {"code": 0, "msg": f"下载失败: {str(e)}"}
|
|
182
|
+
|
|
183
|
+
def start_app(self, app_info: dict):
|
|
184
|
+
"""启动应用程序。
|
|
185
|
+
|
|
186
|
+
保存运行记录并调用外部回调函数启动应用引擎。
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
app_info: 应用信息字典,需包含 id 和 name 字段。
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
dict: 返回 {"code": 1, "msg": "启动成功"}。
|
|
193
|
+
"""
|
|
194
|
+
# 1. 保存/更新运行记录
|
|
195
|
+
history = self.get_history()
|
|
196
|
+
new_item = {"id": str(app_info['id']), "name": app_info['name']}
|
|
197
|
+
|
|
198
|
+
# 去重:如果已存在则先删除旧记录
|
|
199
|
+
history = [i for i in history if str(i.get('id')) != new_item['id']]
|
|
200
|
+
# 插入到开头
|
|
201
|
+
history.insert(0, new_item)
|
|
202
|
+
# 限制数量为 10 条
|
|
203
|
+
KeyValue.save("app_run_history", json.dumps(history[:10]))
|
|
204
|
+
|
|
205
|
+
# 2. 执行回调逻辑
|
|
206
|
+
if self.on_start:
|
|
207
|
+
# 将 app_info 和当前激活的 license_data 一并传出
|
|
208
|
+
# 这样外部调用者就能拿到所有必要信息去启动引擎
|
|
209
|
+
started = self.on_start(app_info, self.license_data)
|
|
210
|
+
if started is False:
|
|
211
|
+
self._return_value = False
|
|
212
|
+
return {
|
|
213
|
+
"code": 0,
|
|
214
|
+
"msg": "环境部署或程序启动失败,请查看错误弹窗及部署日志。"
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# 2. 这里的逻辑可以根据您的业务决定是否关闭窗口
|
|
218
|
+
self._return_value = True
|
|
219
|
+
# self.close() # 如果需要启动后关闭商城窗口,请取消注释
|
|
220
|
+
return {"code": 1, "msg": "启动成功"}
|
|
221
|
+
|
|
222
|
+
def check_status(self, app_id):
|
|
223
|
+
"""检查指定应用是否正在运行。
|
|
224
|
+
|
|
225
|
+
供前端轮询调用,判断应用进程的运行状态。
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
app_id: 应用的唯一标识符。
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
dict: 返回 {"running": bool},表示应用是否在运行中。
|
|
232
|
+
"""
|
|
233
|
+
# 调用之前我们完善的 EnvManager.is_running
|
|
234
|
+
# 假设实例名为 self.env_manager
|
|
235
|
+
is_run = self.env_manager.is_running(str(app_id))
|
|
236
|
+
return {"running": is_run}
|
|
237
|
+
|
|
238
|
+
def stop_app(self, app_id):
|
|
239
|
+
"""强行停止指定应用的运行。
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
app_id: 应用的唯一标识符。
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
dict: 返回 {"code": 1, "msg": "已发送停止指令"}。
|
|
246
|
+
"""
|
|
247
|
+
self.env_manager.stop_app(str(app_id))
|
|
248
|
+
return {"code": 1, "msg": "已发送停止指令"}
|
|
249
|
+
|
|
250
|
+
def clear_env(self, app_id):
|
|
251
|
+
"""清理指定应用的虚拟环境目录。
|
|
252
|
+
|
|
253
|
+
仅在应用未运行时允许清理,运行中会返回错误提示。
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
app_id: 应用的唯一标识符。
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
dict: 成功时返回 {"code": 1, "msg": "环境已重置"};
|
|
260
|
+
失败时返回 {"code": 0, "msg": 错误信息}。
|
|
261
|
+
"""
|
|
262
|
+
# 只有在未运行的情况下才允许清理
|
|
263
|
+
if self.env_manager.is_running(str(app_id)):
|
|
264
|
+
return {"code": 0, "msg": "程序运行中,无法清理环境"}
|
|
265
|
+
|
|
266
|
+
env_dir = self.env_manager.get_env_dir(str(app_id))
|
|
267
|
+
import shutil
|
|
268
|
+
try:
|
|
269
|
+
shutil.rmtree(env_dir, ignore_errors=True)
|
|
270
|
+
return {"code": 1, "msg": "环境已重置"}
|
|
271
|
+
except Exception as e:
|
|
272
|
+
return {"code": 0, "msg": str(e)}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
from ctypes import wintypes
|
|
3
|
+
import threading
|
|
4
|
+
import time
|
|
5
|
+
import struct
|
|
6
|
+
|
|
7
|
+
# --- 系统环境检查 ---
|
|
8
|
+
IS_64BIT = struct.calcsize("P") == 8
|
|
9
|
+
PTR_TYPE = ctypes.c_int64 if IS_64BIT else ctypes.c_int32
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DrawWindow:
|
|
13
|
+
"""屏幕绘制覆盖层窗口。
|
|
14
|
+
|
|
15
|
+
使用 Win32 API 创建一个全屏透明置顶窗口,支持在屏幕上绘制矩形、线条和文本。
|
|
16
|
+
绘制内容不会被截图捕获(使用 WDA_EXCLUDEFROMCAPTURE)。
|
|
17
|
+
所有方法均为静态方法,全局共享一个绘制窗口实例。
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
_hwnd = None
|
|
21
|
+
_thread = None
|
|
22
|
+
_shapes = [] # 存储任务: {'type': 'rect', 'data': (x, y, w, h, color, thickness)}
|
|
23
|
+
_lock = threading.Lock()
|
|
24
|
+
_wnd_proc_ptr = None
|
|
25
|
+
|
|
26
|
+
# 窗口过程回调定义
|
|
27
|
+
WNDPROC = ctypes.WINFUNCTYPE(PTR_TYPE, wintypes.HWND, ctypes.c_uint, wintypes.WPARAM, wintypes.LPARAM)
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def _create_window():
|
|
31
|
+
"""创建全屏透明置顶的绘制覆盖层窗口。
|
|
32
|
+
|
|
33
|
+
注册自定义窗口类,创建带有 WS_EX_LAYERED、WS_EX_TRANSPARENT、WS_EX_TOPMOST
|
|
34
|
+
扩展样式的窗口,并进入消息循环。该方法在后台线程中运行。
|
|
35
|
+
"""
|
|
36
|
+
user32 = ctypes.windll.user32
|
|
37
|
+
gdi32 = ctypes.windll.gdi32
|
|
38
|
+
kernel32 = ctypes.windll.kernel32
|
|
39
|
+
|
|
40
|
+
# 1. 开启 DPI 感知 (防止 Win10/Win11 缩放导致坐标偏移)
|
|
41
|
+
try:
|
|
42
|
+
ctypes.windll.shcore.SetProcessDpiAwareness(1)
|
|
43
|
+
except Exception:
|
|
44
|
+
user32.SetProcessDPIAware()
|
|
45
|
+
|
|
46
|
+
clsname = f"DrawWindow_{int(time.time())}"
|
|
47
|
+
hinst = kernel32.GetModuleHandleW(None)
|
|
48
|
+
|
|
49
|
+
def wnd_proc(hwnd, msg, wparam, lparam):
|
|
50
|
+
if msg == 0x000F: # WM_PAINT
|
|
51
|
+
ps = ctypes.create_string_buffer(64)
|
|
52
|
+
hdc = user32.BeginPaint(hwnd, ps)
|
|
53
|
+
|
|
54
|
+
with DrawWindow._lock:
|
|
55
|
+
for item in DrawWindow._shapes:
|
|
56
|
+
t, d = item['type'], item['data']
|
|
57
|
+
|
|
58
|
+
if t in ('rect', 'line'):
|
|
59
|
+
color, thick = d[4], d[5]
|
|
60
|
+
pen = gdi32.CreatePen(0, thick, color)
|
|
61
|
+
old_pen = gdi32.SelectObject(hdc, pen)
|
|
62
|
+
if t == 'rect':
|
|
63
|
+
brush = gdi32.GetStockObject(5) # NULL_BRUSH
|
|
64
|
+
old_brush = gdi32.SelectObject(hdc, brush)
|
|
65
|
+
gdi32.Rectangle(hdc, d[0], d[1], d[0] + d[2], d[1] + d[3])
|
|
66
|
+
gdi32.SelectObject(hdc, old_brush)
|
|
67
|
+
else: # line
|
|
68
|
+
gdi32.MoveToEx(hdc, d[0], d[1], None)
|
|
69
|
+
gdi32.LineTo(hdc, d[2], d[3])
|
|
70
|
+
gdi32.SelectObject(hdc, old_pen)
|
|
71
|
+
gdi32.DeleteObject(pen)
|
|
72
|
+
|
|
73
|
+
elif t == 'text':
|
|
74
|
+
x, y, text, color, size = d
|
|
75
|
+
font = gdi32.CreateFontW(size, 0, 0, 0, 400, 0, 0, 0, 1, 0, 0, 0, 0, "Microsoft YaHei")
|
|
76
|
+
old_font = gdi32.SelectObject(hdc, font)
|
|
77
|
+
gdi32.SetTextColor(hdc, color)
|
|
78
|
+
gdi32.SetBkMode(hdc, 1) # TRANSPARENT
|
|
79
|
+
gdi32.TextOutW(hdc, x, y, text, len(text))
|
|
80
|
+
gdi32.SelectObject(hdc, old_font)
|
|
81
|
+
gdi32.DeleteObject(font)
|
|
82
|
+
|
|
83
|
+
user32.EndPaint(hwnd, ps)
|
|
84
|
+
return 0
|
|
85
|
+
|
|
86
|
+
if msg == 0x0002: # WM_DESTROY
|
|
87
|
+
user32.PostQuitMessage(0)
|
|
88
|
+
return 0
|
|
89
|
+
|
|
90
|
+
# 安全调用默认窗口过程
|
|
91
|
+
user32.DefWindowProcW.argtypes = [wintypes.HWND, ctypes.c_uint, wintypes.WPARAM, wintypes.LPARAM]
|
|
92
|
+
user32.DefWindowProcW.restype = PTR_TYPE
|
|
93
|
+
return user32.DefWindowProcW(hwnd, msg, wparam, lparam)
|
|
94
|
+
|
|
95
|
+
DrawWindow._wnd_proc_ptr = DrawWindow.WNDPROC(wnd_proc)
|
|
96
|
+
|
|
97
|
+
from ctypes import Structure, c_uint, c_int
|
|
98
|
+
class WNDCLASS(Structure):
|
|
99
|
+
_fields_ = [("style", c_uint), ("lpfnWndProc", DrawWindow.WNDPROC), ("cbClsExtra", c_int),
|
|
100
|
+
("cbWndExtra", c_int), ("hInstance", wintypes.HINSTANCE), ("hIcon", wintypes.HICON),
|
|
101
|
+
("hCursor", wintypes.HANDLE), ("hbrBackground", wintypes.HANDLE),
|
|
102
|
+
("lpszMenuName", wintypes.LPCWSTR), ("lpszClassName", wintypes.LPCWSTR)]
|
|
103
|
+
|
|
104
|
+
wc = WNDCLASS()
|
|
105
|
+
wc.lpfnWndProc = DrawWindow._wnd_proc_ptr
|
|
106
|
+
wc.hInstance = hinst
|
|
107
|
+
wc.lpszClassName = clsname
|
|
108
|
+
wc.hbrBackground = gdi32.GetStockObject(4) # BLACK_BRUSH
|
|
109
|
+
wc.hCursor = user32.LoadCursorW(0, 32512) # IDC_ARROW
|
|
110
|
+
user32.RegisterClassW(ctypes.byref(wc))
|
|
111
|
+
|
|
112
|
+
# 获取当前屏幕物理分辨率
|
|
113
|
+
sw = user32.GetSystemMetrics(0)
|
|
114
|
+
sh = user32.GetSystemMetrics(1)
|
|
115
|
+
|
|
116
|
+
# 创建全屏透明置顶窗口
|
|
117
|
+
# WS_EX_LAYERED(0x80000) | WS_EX_TRANSPARENT(0x20) | WS_EX_TOPMOST(0x08)
|
|
118
|
+
DrawWindow._hwnd = user32.CreateWindowExW(
|
|
119
|
+
0x00080000 | 0x00000020 | 0x00000008,
|
|
120
|
+
clsname, "DrawOverlay", 0x80000000,
|
|
121
|
+
0, 0, sw, sh, 0, 0, hinst, 0
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# 防截图核心 API
|
|
125
|
+
user32.SetWindowDisplayAffinity(DrawWindow._hwnd, 0x11) # WDA_EXCLUDEFROMCAPTURE
|
|
126
|
+
# 黑色透明
|
|
127
|
+
user32.SetLayeredWindowAttributes(DrawWindow._hwnd, 0, 255, 0x1)
|
|
128
|
+
user32.ShowWindow(DrawWindow._hwnd, 5)
|
|
129
|
+
|
|
130
|
+
msg = wintypes.MSG()
|
|
131
|
+
while user32.GetMessageW(ctypes.byref(msg), 0, 0, 0) != 0:
|
|
132
|
+
user32.TranslateMessage(ctypes.byref(msg))
|
|
133
|
+
user32.DispatchMessageW(ctypes.byref(msg))
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def _init():
|
|
137
|
+
"""初始化绘制窗口。
|
|
138
|
+
|
|
139
|
+
如果窗口线程尚未启动,则创建后台线程并等待窗口句柄就绪。
|
|
140
|
+
"""
|
|
141
|
+
if DrawWindow._thread is None:
|
|
142
|
+
DrawWindow._thread = threading.Thread(target=DrawWindow._create_window, daemon=True)
|
|
143
|
+
DrawWindow._thread.start()
|
|
144
|
+
while DrawWindow._hwnd is None: time.sleep(0.05)
|
|
145
|
+
|
|
146
|
+
@staticmethod
|
|
147
|
+
def draw_rect(x, y, w, h, color=0x0000FF, thickness=2):
|
|
148
|
+
"""在屏幕上绘制一个空心矩形。
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
x: 矩形左上角的 X 坐标(像素)。
|
|
152
|
+
y: 矩形左上角的 Y 坐标(像素)。
|
|
153
|
+
w: 矩形的宽度(像素)。
|
|
154
|
+
h: 矩形的高度(像素)。
|
|
155
|
+
color: 颜色值,BGR 格式,默认为红色 (0x0000FF)。
|
|
156
|
+
thickness: 边框线条粗细(像素),默认为 2。
|
|
157
|
+
"""
|
|
158
|
+
DrawWindow._init()
|
|
159
|
+
with DrawWindow._lock:
|
|
160
|
+
DrawWindow._shapes.append({'type': 'rect', 'data': (x, y, w, h, color, thickness)})
|
|
161
|
+
ctypes.windll.user32.InvalidateRect(DrawWindow._hwnd, None, True)
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def draw_line(x1, y1, x2, y2, color=0x0000FF, thickness=2):
|
|
165
|
+
"""在屏幕上绘制一条直线。
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
x1: 起点 X 坐标(像素)。
|
|
169
|
+
y1: 起点 Y 坐标(像素)。
|
|
170
|
+
x2: 终点 X 坐标(像素)。
|
|
171
|
+
y2: 终点 Y 坐标(像素)。
|
|
172
|
+
color: 颜色值,BGR 格式,默认为红色 (0x0000FF)。
|
|
173
|
+
thickness: 线条粗细(像素),默认为 2。
|
|
174
|
+
"""
|
|
175
|
+
DrawWindow._init()
|
|
176
|
+
with DrawWindow._lock:
|
|
177
|
+
DrawWindow._shapes.append({'type': 'line', 'data': (x1, y1, x2, y2, color, thickness)})
|
|
178
|
+
ctypes.windll.user32.InvalidateRect(DrawWindow._hwnd, None, True)
|
|
179
|
+
|
|
180
|
+
@staticmethod
|
|
181
|
+
def draw_text(x, y, text, color=0x0000FF, font_size=20):
|
|
182
|
+
"""在屏幕上绘制文本。
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
x: 文本起始位置的 X 坐标(像素)。
|
|
186
|
+
y: 文本起始位置的 Y 坐标(像素)。
|
|
187
|
+
text: 要绘制的文本内容。
|
|
188
|
+
color: 颜色值,BGR 格式,默认为红色 (0x0000FF)。
|
|
189
|
+
font_size: 字体大小(像素),默认为 20。
|
|
190
|
+
"""
|
|
191
|
+
DrawWindow._init()
|
|
192
|
+
with DrawWindow._lock:
|
|
193
|
+
DrawWindow._shapes.append({'type': 'text', 'data': (x, y, text, color, font_size)})
|
|
194
|
+
ctypes.windll.user32.InvalidateRect(DrawWindow._hwnd, None, True)
|
|
195
|
+
|
|
196
|
+
@staticmethod
|
|
197
|
+
def clear():
|
|
198
|
+
"""清除屏幕上所有已绘制的图形。"""
|
|
199
|
+
if DrawWindow._hwnd:
|
|
200
|
+
with DrawWindow._lock: DrawWindow._shapes.clear()
|
|
201
|
+
ctypes.windll.user32.InvalidateRect(DrawWindow._hwnd, None, True)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# --- 辅助工具:RGB 转 BGR ---
|
|
205
|
+
def RGB(r, g, b):
|
|
206
|
+
"""将 RGB 颜色值转换为 BGR 格式。
|
|
207
|
+
|
|
208
|
+
DrawWindow 使用 BGR 颜色格式,此函数将常见的 RGB 值转换为对应的 BGR 值。
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
r: 红色通道值 (0-255)。
|
|
212
|
+
g: 绿色通道值 (0-255)。
|
|
213
|
+
b: 蓝色通道值 (0-255)。
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
int: BGR 格式的颜色整数值。
|
|
217
|
+
"""
|
|
218
|
+
return (b << 16) | (g << 8) | r
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
# --- 示例使用 ---
|
|
222
|
+
if __name__ == "__main__":
|
|
223
|
+
# 画一个三角形(三条线)
|
|
224
|
+
DrawWindow.draw_line(500, 200, 300, 500, thickness=3) # 蓝色
|
|
225
|
+
DrawWindow.draw_line(300, 500, 700, 500, color=0xFF0000, thickness=3)
|
|
226
|
+
DrawWindow.draw_line(700, 500, 500, 200, color=0xFF0000, thickness=3)
|
|
227
|
+
|
|
228
|
+
DrawWindow.draw_text(450, 520, "Triangle Base", color=0xFFFFFF, font_size=25)
|
|
229
|
+
|
|
230
|
+
time.sleep(10)
|
|
231
|
+
DrawWindow.clear()
|