gomyck-tools 1.3.1__py3-none-any.whl → 1.3.2__py3-none-any.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 (64) hide show
  1. ctools/__init__.py +0 -0
  2. ctools/aes_tools.py +35 -0
  3. ctools/api_result.py +55 -0
  4. ctools/application.py +386 -0
  5. ctools/b64.py +7 -0
  6. ctools/bashPath.py +13 -0
  7. ctools/bottle_web_base.py +169 -0
  8. ctools/bottle_webserver.py +143 -0
  9. ctools/bottle_websocket.py +75 -0
  10. ctools/browser_element_tools.py +314 -0
  11. ctools/call.py +71 -0
  12. ctools/cftp.py +74 -0
  13. ctools/cjson.py +54 -0
  14. ctools/ckafka.py +159 -0
  15. ctools/compile_tools.py +18 -0
  16. ctools/console.py +55 -0
  17. ctools/coord_trans.py +127 -0
  18. ctools/credis.py +111 -0
  19. ctools/cron_lite.py +252 -0
  20. ctools/ctoken.py +34 -0
  21. ctools/cword.py +30 -0
  22. ctools/czip.py +130 -0
  23. ctools/database.py +185 -0
  24. ctools/date_utils.py +43 -0
  25. ctools/dict_wrapper.py +20 -0
  26. ctools/douglas_rarefy.py +136 -0
  27. ctools/download_tools.py +57 -0
  28. ctools/enums.py +4 -0
  29. ctools/ex.py +31 -0
  30. ctools/excelOpt.py +36 -0
  31. ctools/html_soup.py +35 -0
  32. ctools/http_utils.py +24 -0
  33. ctools/images_tools.py +27 -0
  34. ctools/imgDialog.py +44 -0
  35. ctools/metrics.py +131 -0
  36. ctools/mqtt_utils.py +289 -0
  37. ctools/obj.py +20 -0
  38. ctools/pacth.py +74 -0
  39. ctools/plan_area_tools.py +97 -0
  40. ctools/process_pool.py +36 -0
  41. ctools/pty_tools.py +72 -0
  42. ctools/resource_bundle_tools.py +121 -0
  43. ctools/rsa.py +70 -0
  44. ctools/screenshot_tools.py +127 -0
  45. ctools/sign.py +20 -0
  46. ctools/sm_tools.py +49 -0
  47. ctools/snow_id.py +76 -0
  48. ctools/str_diff.py +20 -0
  49. ctools/string_tools.py +85 -0
  50. ctools/sys_info.py +157 -0
  51. ctools/sys_log.py +89 -0
  52. ctools/thread_pool.py +35 -0
  53. ctools/upload_tools.py +40 -0
  54. ctools/win_canvas.py +83 -0
  55. ctools/win_control.py +106 -0
  56. ctools/word_fill.py +562 -0
  57. ctools/word_fill_entity.py +46 -0
  58. ctools/work_path.py +69 -0
  59. {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/METADATA +1 -1
  60. gomyck_tools-1.3.2.dist-info/RECORD +62 -0
  61. gomyck_tools-1.3.2.dist-info/top_level.txt +1 -0
  62. gomyck_tools-1.3.1.dist-info/RECORD +0 -4
  63. gomyck_tools-1.3.1.dist-info/top_level.txt +0 -1
  64. {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/WHEEL +0 -0
ctools/mqtt_utils.py ADDED
@@ -0,0 +1,289 @@
1
+ import time
2
+ from enum import Enum
3
+ from typing import Dict
4
+
5
+ from ctools.dict_wrapper import DictWrapper as DictToObj
6
+ from paho.mqtt import client as mqtt
7
+ from paho.mqtt.enums import CallbackAPIVersion
8
+
9
+ from ctools import sys_log, cjson, string_tools, sys_info, date_utils, sm_tools, thread_pool
10
+
11
+ class MQTTEvent(Enum):
12
+
13
+ pass
14
+
15
+ '''
16
+ MQTT服务使用示例:
17
+
18
+ mqtt_utils = MQTTUtils("192.168.3.100")
19
+ mqtt_utils.connect(username="rpa", password="123")
20
+
21
+ # 订阅
22
+ mqtt_utils.subscribe("test/topic1", "test/topic2")
23
+
24
+ # 异步发送
25
+ msg = {"name": "测试消息"}
26
+ mqtt_utils.publish_async(msg, event=MQTTEvent.END_TASK)
27
+
28
+ # 同步发送
29
+ mqtt_resp = mqtt_utils.publish_sync(msg, event=MQTTEvent.UPLOAD_PROCESS, timeout=60)
30
+ status = mqtt_resp.status
31
+ body = mqtt_resp.body
32
+ print("%s, %s" % (status, body))
33
+ '''
34
+
35
+ log = sys_log.flog
36
+
37
+ NO_LOG_EVENT = [MQTTEvent.HEARTBEAT]
38
+
39
+
40
+ class MQTTRequest:
41
+
42
+ def __init__(self, **kwargs):
43
+ self.event: str = kwargs.get('event')
44
+ self.trace_id: str = kwargs.get('trace_id') if kwargs.get('trace_id') else string_tools.get_uuid()
45
+ self.client_id: str = kwargs.get('client_id')
46
+ self.time: str = kwargs.get('time') if kwargs.get('time') else date_utils.get_date_time()
47
+ self.body = kwargs.get('body')
48
+
49
+ def __str__(self):
50
+ res_str = ""
51
+ for k, v in self.__dict__.items():
52
+ res_str += "%s=%s," % (k, v)
53
+ return res_str[:-1]
54
+
55
+
56
+ class MQTTResponse:
57
+
58
+ def __init__(self, **kwargs):
59
+ self.event: str = kwargs.get('event')
60
+ self.trace_id: str = kwargs.get('trace_id')
61
+ self.status: int = kwargs.get('status')
62
+ self.time: str = kwargs.get('time') if kwargs.get('time') else date_utils.get_date_time()
63
+ self.msg: str = kwargs.get('msg')
64
+ self.body = kwargs.get('body')
65
+
66
+ def __str__(self):
67
+ res_str = ""
68
+ for k, v in self.__dict__.items():
69
+ res_str += "%s=%s," % (k, v)
70
+ return res_str[:-1]
71
+
72
+
73
+ class MQTTUtils:
74
+ def __init__(self, broker_address, port=1883, publish_topic=None, client_id=None, keepalive=60, clean_session=False,
75
+ userdata=None, protocol=mqtt.MQTTv311, message_handle=None, broker_encrypt_key=None):
76
+ self.publish_topic = publish_topic
77
+ self.broker_address = broker_address
78
+ self.port = port
79
+ self.client_id = client_id if client_id else sys_info.get_machine_code()
80
+ self.keepalive = keepalive
81
+ self.clean_session = clean_session
82
+ self.userdata = userdata
83
+ self.protocol = protocol
84
+ self.message_handle = message_handle
85
+ self.broker_encrypt_key = broker_encrypt_key
86
+
87
+ self.client = mqtt.Client(CallbackAPIVersion.VERSION2, client_id=self.client_id, clean_session=self.clean_session,
88
+ userdata=self.userdata, protocol=self.protocol)
89
+ self.client.on_connect = self._on_connect
90
+ self.client.on_message = self._on_message_thread
91
+ self.client.on_disconnect = self._on_disconnect
92
+ if self.publish_topic:
93
+ will_msg = {
94
+ "event": "offline",
95
+ "client_id": self.client_id,
96
+ "trace_id": string_tools.get_uuid(),
97
+ "time": date_utils.get_date_time(),
98
+ "body": {"type": "will_msg"}
99
+ }
100
+ self.client.will_set(self.publish_topic, cjson.dumps(will_msg), 2, False)
101
+ self.connected = False
102
+ self._publish_trace_id = []
103
+ self._received_message: Dict[str: MQTTResponse] = {}
104
+
105
+ def _on_connect(self, client, userdata, flags, rc, properties):
106
+ if rc == 0:
107
+ log.info("mqtt服务连接成功")
108
+ self.connected = True
109
+ else:
110
+ log.info("mqtt服务连接失败, broker地址: %s:%s, rc: %s" % (self.broker_address, self.port, rc))
111
+
112
+ def _on_message_thread(self, client, userdata, message):
113
+ thread_pool.submit(self._on_message, client=client, userdata=userdata, message=message)
114
+
115
+ def _on_message(self, client, userdata, message):
116
+ # try:
117
+ if message.topic != self.publish_topic and message.payload:
118
+ mqtt_msg = cjson.loads(message.payload.decode())
119
+ trace_id = mqtt_msg.get('trace_id')
120
+ if trace_id in self._publish_trace_id:
121
+ mqtt_resp = MQTTResponse(**mqtt_msg)
122
+ self._received_message[trace_id] = mqtt_resp
123
+ self._publish_trace_id.remove(trace_id)
124
+ else:
125
+ mqtt_req = MQTTRequest(**mqtt_msg)
126
+
127
+ try:
128
+ if mqtt_req.event not in NO_LOG_EVENT:
129
+ log.debug("异步接收mqtt消息: %s" % str(mqtt_req))
130
+ if mqtt_req.body:
131
+ try:
132
+ mqtt_req.body = self.decrypt_body(mqtt_req.body)
133
+ except Exception:
134
+ if mqtt_req.event != MQTTEvent.HAND_SHARK:
135
+ log.error('解密消息失败: {}'.format(mqtt_req))
136
+ return
137
+ if isinstance(mqtt_req.body, dict):
138
+ mqtt_req.body = DictToObj(mqtt_req.body)
139
+ except Exception as e:
140
+ log.info("异步接收mqtt消息异常: %s" % e)
141
+ self.message_handle(mqtt_req, message.topic)
142
+ # except Exception as e:
143
+ # log.error("接收mqtt消息异常: %s" % e)
144
+
145
+ def _on_disconnect(self, client, userdata, flags, rc, properties):
146
+ self.connected = False
147
+ log.info("mqtt服务已断开连接, rc: %s" % rc)
148
+
149
+ def encrypt_body(self, body):
150
+ if self.broker_encrypt_key:
151
+ try:
152
+ return sm_tools.encrypt_with_sm4(self.broker_encrypt_key, cjson.dumps(body))
153
+ except Exception:
154
+ return sm_tools.encrypt_with_sm4(self.broker_encrypt_key, body)
155
+
156
+ def decrypt_body(self, body):
157
+ if self.broker_encrypt_key:
158
+ try:
159
+ return cjson.loads(sm_tools.decrypt_with_sm4(self.broker_encrypt_key, body))
160
+ except Exception:
161
+ sm_tools.decrypt_with_sm4(self.broker_encrypt_key, body)
162
+
163
+ def connect(self, username=None, password=None, timeout=10):
164
+ if username and password:
165
+ self.client.username_pw_set(username, password)
166
+ self.client.connect_async(host=self.broker_address, port=self.port, keepalive=self.keepalive)
167
+ self.client.loop_start()
168
+
169
+ start_time = time.time()
170
+ while not self.connected and time.time() - start_time < timeout:
171
+ time.sleep(0.1)
172
+
173
+ if not self.connected:
174
+ log.info("mqtt服务连接超时, broker地址: %s:%s" % (self.broker_address, self.port))
175
+
176
+ def publish_resp(self, mqtt_resp: MQTTResponse, topic=None, encrypt=True, qos=2, retain=False):
177
+ """
178
+ 发送mqtt响应信息
179
+ :param mqtt_resp: 响应信息, 使用 mqtt_service.init_resp('ok', req)
180
+ :param topic: 主题, 默认用订阅的主题
181
+ :param encrypt: 是否加密
182
+ :param qos: 消息质量
183
+ :param retain: 是否保留
184
+ """
185
+ topic = topic if topic else self.publish_topic
186
+ if self.connected:
187
+ if mqtt_resp:
188
+ try:
189
+ if encrypt and mqtt_resp.body:
190
+ mqtt_resp.body = self.encrypt_body(mqtt_resp.body)
191
+ mqtt_resp.client_id = self.client_id
192
+ mqtt_data = cjson.dumps(mqtt_resp)
193
+ self.client.publish(topic, mqtt_data, qos=qos, retain=retain)
194
+ log.debug("异步发送mqtt响应消息: topic: %s message: %s" % (topic, mqtt_data))
195
+ except Exception as e:
196
+ log.error('异步发送mqtt响应消息异常: %s' % e)
197
+ else:
198
+ log.info("异步发送mqtt响应-mqtt服务未连接, 无法发送消息")
199
+
200
+ def publish_async(self, message, event: MQTTEvent = None, encrypt=True, topic=None, qos=2, retain=False):
201
+ topic = topic if topic else self.publish_topic
202
+ if self.connected:
203
+ try:
204
+ if encrypt and message:
205
+ message = self.encrypt_body(message)
206
+
207
+ mqtt_req = MQTTRequest()
208
+ mqtt_req.client_id = self.client_id
209
+ mqtt_req.body = message
210
+ if event:
211
+ mqtt_req.event = event.value
212
+ mqtt_data = cjson.dumps(mqtt_req)
213
+ self.client.publish(topic, mqtt_data, qos=qos, retain=retain)
214
+ if event not in NO_LOG_EVENT:
215
+ log.debug("异步发送mqtt消息: topic: %s message: %s" % (topic, mqtt_data))
216
+ except Exception as e:
217
+ log.error('异步发送mqtt消息异常: %s' % e)
218
+ else:
219
+ log.info("异步发送mqtt消息-mqtt服务未连接, 无法发送消息")
220
+
221
+ def publish_sync(self, message, event: MQTTEvent, topic=None, encrypt=True,
222
+ qos=2, retain=False, timeout: int = 15, send_time: str = None) -> MQTTResponse:
223
+ if not event: raise Exception("事件不能为空")
224
+ mqtt_resp = MQTTResponse()
225
+ mqtt_resp.status = 500
226
+ mqtt_resp.msg = "服务端未响应, 请联系管理员!"
227
+ topic = topic if topic else self.publish_topic
228
+ if self.connected:
229
+ try:
230
+ if encrypt and message:
231
+ message = self.encrypt_body(message)
232
+
233
+ mqtt_req = MQTTRequest()
234
+ mqtt_req.source = "client"
235
+ mqtt_req.client_id = self.client_id
236
+ mqtt_req.body = message
237
+ mqtt_req.time = send_time if send_time else mqtt_req.time
238
+ mqtt_req.event = event.value
239
+ mqtt_data = cjson.dumps(mqtt_req)
240
+ self._publish_trace_id.append(mqtt_req.trace_id)
241
+ self.client.publish(topic, mqtt_data, qos=qos, retain=retain)
242
+ if event not in NO_LOG_EVENT:
243
+ log.debug("同步发送mqtt消息: topic: %s message: %s" % (topic, mqtt_data))
244
+ is_timeout = False
245
+ start_time = time.time()
246
+ while True:
247
+ if mqtt_req.trace_id in self._received_message:
248
+ mqtt_resp = self._received_message.pop(mqtt_req.trace_id)
249
+ if mqtt_resp.body and encrypt:
250
+ mqtt_resp.body = self.decrypt_body(mqtt_resp.body)
251
+ break
252
+ if (time.time() - start_time) >= timeout:
253
+ is_timeout = True
254
+ break
255
+ time.sleep(0.5)
256
+ if not is_timeout:
257
+ if event not in NO_LOG_EVENT:
258
+ log.debug("同步接收mqtt消息: topic: %s message: %s" % (topic, mqtt_resp))
259
+ else:
260
+ log.info("同步发送mqtt消息, 等待返回信息超时: %s" % mqtt_req)
261
+ except Exception as e:
262
+ log.error('同步发送mqtt消息异常: %s-%s' % (event.value, e))
263
+ else:
264
+ log.info("同步接收mqtt消息-mqtt服务未连接, 无法发送消息")
265
+ return mqtt_resp
266
+
267
+ def subscribe(self, *topic, qos=0):
268
+ if self.connected:
269
+ if topic and len(topic) > 0:
270
+ for t in topic:
271
+ self.client.subscribe(t, qos=qos)
272
+ log.info("已订阅mqtt主题[%s]" % t)
273
+ else:
274
+ log.info("mqtt服务未连接, 无法订阅主题")
275
+
276
+ def unsubscribe(self, *topic):
277
+ if self.connected:
278
+ if topic and len(topic) > 0:
279
+ for t in topic:
280
+ self.client.unsubscribe(t)
281
+ log.info("已解除订阅mqtt主题[%s]" % t)
282
+ else:
283
+ log.info("mqtt服务未连接, 无法解除订阅主题")
284
+
285
+ def disconnect(self):
286
+ if self.connected:
287
+ self.client.disconnect()
288
+ self.client.loop_stop()
289
+ log.info("已关闭mqtt服务连接")
ctools/obj.py ADDED
@@ -0,0 +1,20 @@
1
+
2
+ def isNull(param) -> bool:
3
+ if type(param) == str:
4
+ return param == ''
5
+ elif type(param) == list:
6
+ return len(param) == 0
7
+ elif type(param) == dict:
8
+ return len(param) == 0
9
+ elif type(param) == int:
10
+ return param == 0
11
+ elif type(param) == float:
12
+ return param == 0.0
13
+ elif type(param) == bool:
14
+ return param == False
15
+ else:
16
+ return param is None
17
+
18
+ def isNotNull(param) -> bool:
19
+ return not isNull(param)
20
+
ctools/pacth.py ADDED
@@ -0,0 +1,74 @@
1
+ import os
2
+ import shutil
3
+
4
+ from sqlalchemy.sql import text
5
+
6
+ from ctools import database
7
+
8
+
9
+ class Patch:
10
+
11
+ def __init__(self, oldVersion, newVersion, pythonPath, playwrightPath, driverPath) -> None:
12
+ super().__init__()
13
+ self.oldV = oldVersion[len('V'):].replace('.', '').replace('-snapshot', '')
14
+ self.currentV = newVersion[len('V'):].replace('.', '').replace('-snapshot', '')
15
+ self.snapshot = '-snapshot' in newVersion or '-snapshot' in oldVersion
16
+ self.pythonPath = pythonPath
17
+ self.playwrightPath = playwrightPath
18
+ self.driverPath = driverPath
19
+ self.deleteSDK, self.deleteHPL, self.deleteDriver = False, False, False
20
+
21
+ def apply_patch(self):
22
+ patch_methods = [method for method in dir(self) if callable(getattr(self, method)) and method.startswith('patch_V')]
23
+ patch_methods.sort(key=lambda x: int(x[len('patch_V'):]))
24
+ max_method_name = patch_methods[-1]
25
+ exec_max_method = False
26
+ for method_name in patch_methods:
27
+ slVersion = method_name[len('patch_V'):]
28
+ if int(self.currentV) > int(slVersion) >= int(self.oldV):
29
+ if max_method_name == method_name: exec_max_method = True
30
+ method = getattr(self, method_name)
31
+ print('start exec patch {}'.format(method_name))
32
+ method()
33
+ print('patch {} update success'.format(method_name))
34
+ if self.snapshot and not exec_max_method:
35
+ print('start exec snapshot patch {}'.format(max_method_name))
36
+ method = getattr(self, max_method_name)
37
+ method()
38
+ print('snapshot patch {} update success'.format(max_method_name))
39
+
40
+ def patch_V220(self):
41
+ pass
42
+
43
+ def run_sqls(self, sqls):
44
+ with database.get_session() as s:
45
+ for sql in sqls.split(";"):
46
+ try:
47
+ s.execute(text(sql.strip()))
48
+ s.commit()
49
+ except Exception as e:
50
+ print('结构升级错误, 请检查!!! {}'.format(e.__cause__))
51
+
52
+ def remove_sdk(self):
53
+ if self.deleteSDK: return
54
+ try:
55
+ self.deleteSDK = True
56
+ if os.path.exists(self.pythonPath): shutil.rmtree(self.pythonPath)
57
+ except Exception as e:
58
+ print('删除SDK错误: {}'.format(e))
59
+
60
+ def remove_hpl(self):
61
+ if self.deleteHPL: return
62
+ try:
63
+ self.deleteHPL = True
64
+ if os.path.exists(self.playwrightPath): shutil.rmtree(self.playwrightPath)
65
+ except Exception as e:
66
+ print('删除HPL错误: {}'.format(e))
67
+
68
+ def remove_driver(self):
69
+ if self.deleteDriver: return
70
+ try:
71
+ self.deleteDriver = True
72
+ if os.path.exists(self.driverPath): shutil.rmtree(self.driverPath)
73
+ except Exception as e:
74
+ print('删除Driver错误: {}'.format(e))
@@ -0,0 +1,97 @@
1
+ import tkinter as tk
2
+
3
+ """
4
+ 规划区域工具类, 按回车获取区域坐标
5
+ image_path=screenshot_tools.screenshot()
6
+ """
7
+
8
+
9
+ class PlanAreaTools:
10
+ def __init__(self):
11
+ self.root = tk.Tk()
12
+ self.root.overrideredirect(True)
13
+ self.root.attributes("-alpha", 0.1)
14
+ self.root.attributes('-topmost', 'true')
15
+ self.root.geometry("{0}x{1}+0+0".format(self.root.winfo_screenwidth(), self.root.winfo_screenheight()))
16
+ self.root.configure(bg="black")
17
+
18
+ self.canvas = tk.Canvas(self.root)
19
+
20
+ self.x, self.y = 0, 0
21
+ self.xstart, self.ystart = 0, 0
22
+ self.xend, self.yend = 0, 0
23
+ self.xcenter, self.ycenter = 0, 0
24
+
25
+ self.select_area = self.canvas.create_rectangle(0, 0, 0, 0, outline='red', width=0, dash=(4, 4))
26
+ self.center_offset = (0, 0)
27
+
28
+ self.pos_div = tk.Toplevel(self.root)
29
+ self.pos_div.overrideredirect(True)
30
+ self.pos_div.attributes('-topmost', 'true')
31
+ self.pos_div.configure(bg="grey")
32
+ self.pos_div.geometry("270x25")
33
+
34
+ self.pos_div_text = "POS: (%s, %s), Enter键-获取区域, Esc键-退出"
35
+ self.pos_label = tk.Label(self.pos_div, text=self.pos_div_text % (0, 0), bg="black", fg="white")
36
+ self.pos_label.pack(fill="both", expand=True)
37
+
38
+ self.root.bind('<Escape>', self.sys_out) # 键盘Esc键->退出
39
+ self.root.bind('<Motion>', self.print_pos)
40
+ self.root.bind("<Button-1>", self.mouse_left_down) # 鼠标左键点击->显示子窗口
41
+ self.root.bind("<B1-Motion>", self.mouse_left_move) # 鼠标左键移动->改变子窗口大小
42
+ self.root.bind("<ButtonRelease-1>", self.mouse_left_up) # 鼠标左键释放->记录最后光标的位置
43
+ self.root.bind("<Return>", self.save_area) # 回车键->获取区域信息
44
+ self.root.focus()
45
+
46
+ self.area = [0, 0, 0, 0]
47
+
48
+ def start(self):
49
+ self.root.mainloop()
50
+
51
+ def print_pos(self, event):
52
+ self.x, self.y = event.widget.winfo_pointerxy()
53
+ self.pos_label.config(text=self.pos_div_text % (self.x, self.y))
54
+ self.pos_div.geometry(f"+{self.x + 10}+{self.y + 10}")
55
+
56
+ def mouse_left_down(self, event):
57
+ self.x, self.y = event.x, event.y
58
+ self.xstart, self.ystart = event.x, event.y
59
+ self.canvas.configure(height=1)
60
+ self.canvas.configure(width=1)
61
+ self.canvas.config(highlightthickness=0)
62
+ self.canvas.place(x=event.x, y=event.y)
63
+
64
+ def mouse_left_move(self, event):
65
+ self.x, self.y = event.x, event.y
66
+ self.xcenter = self.xstart + int((self.x-self.xstart)/2)-2
67
+ self.ycenter = self.ystart + int((self.y-self.ystart)/2)-2
68
+
69
+ self.pos_label.config(text=self.pos_div_text % (self.x, self.y))
70
+ self.pos_div.geometry(f"+{self.x + 10}+{self.y + 10}")
71
+
72
+ self.canvas.configure(height=event.y - self.ystart)
73
+ self.canvas.configure(width=event.x - self.xstart)
74
+ self.canvas.coords(self.select_area, 0, 0, event.x - self.xstart, event.y - self.ystart)
75
+
76
+ def mouse_left_up(self, event):
77
+ self.xend, self.yend = event.x, event.y
78
+
79
+ def save_area(self, event):
80
+ try:
81
+ self.area = [self.xstart, self.ystart, self.xend, self.yend]
82
+ self.canvas.delete(self.select_area)
83
+ self.canvas.place_forget()
84
+ self.sys_out()
85
+
86
+ except Exception:
87
+ pass
88
+
89
+ def sys_out(self, event=None):
90
+ self.pos_div.destroy()
91
+ self.root.destroy()
92
+
93
+
94
+ def get_area():
95
+ pat = PlanAreaTools()
96
+ pat.start()
97
+ return tuple(pat.area)
ctools/process_pool.py ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2024/9/20 12:00'
5
+ import os
6
+ import time
7
+ from concurrent.futures import ProcessPoolExecutor
8
+
9
+ from ctools import call
10
+
11
+ _process_pool: ProcessPoolExecutor = None
12
+
13
+ @call.init
14
+ def init():
15
+ global _process_pool
16
+ max_workers = min(32, os.cpu_count()) # 最多 32 个
17
+ _process_pool = ProcessPoolExecutor(max_workers=max_workers)
18
+
19
+ def cb(f, callback):
20
+ exc = f.exception()
21
+ if exc:
22
+ if callback: callback(exc)
23
+ raise exc
24
+ else:
25
+ if callback: callback(f.result())
26
+
27
+ def submit(func, *args, callback=None, **kwargs):
28
+ if _process_pool is None: raise Exception('process pool is not init')
29
+ future = _process_pool.submit(func, *args, **kwargs)
30
+ future.add_done_callback(lambda f: cb(f, callback))
31
+ time.sleep(0.01)
32
+ return future
33
+
34
+ def shutdown(wait=True):
35
+ if _process_pool is None: raise Exception('process pool is not init')
36
+ _process_pool.shutdown(wait=wait)
ctools/pty_tools.py ADDED
@@ -0,0 +1,72 @@
1
+ import _thread
2
+ import queue
3
+ import time
4
+
5
+ # 伪终端交互
6
+
7
+ from winpty import PtyProcess
8
+
9
+
10
+ class Code:
11
+ SUCCESS = 200
12
+ FAIL = 201
13
+ PART = 206
14
+
15
+
16
+ class PtyTools:
17
+ def __init__(self, cmd, prefix: str = ""):
18
+ self._process = PtyProcess.spawn(cmd)
19
+ self._read_queue = queue.Queue()
20
+ self._prefix = prefix
21
+ self._is_enable = False
22
+
23
+ def _read(self):
24
+ try:
25
+ while True:
26
+ msg = self._process.readline()
27
+ self._is_enable = True
28
+ if msg and self._prefix not in msg[:len(self._prefix)]:
29
+ self._read_queue.put(msg)
30
+ elif self._is_exit():
31
+ break
32
+ except Exception:
33
+ pass
34
+
35
+ def _is_exit(self):
36
+ return self._process.exitstatus is not None
37
+
38
+ def run(self):
39
+ _thread.start_new_thread(self._read, ())
40
+ while not self._is_enable:
41
+ time.sleep(1)
42
+
43
+ def read(self, end_str: str = None, is_async=False, timeout: int = 1):
44
+ code = Code.SUCCESS
45
+ content = ""
46
+ start_time = time.time()
47
+ while True:
48
+ if not self._read_queue.empty():
49
+ line = self._read_queue.get()
50
+ content += line
51
+ start_time = time.time()
52
+ if end_str is not None and end_str in line:
53
+ break
54
+ if is_async:
55
+ code = Code.PART
56
+ break
57
+ else:
58
+ if time.time() - start_time >= timeout:
59
+ break
60
+ if self._is_exit():
61
+ code = Code.FAIL
62
+ break
63
+ time.sleep(0.1)
64
+ return code, content
65
+
66
+ def send(self, msg, timeout: int = 0.5):
67
+ self._process.write('%s\r\n' % msg)
68
+ return self.read(timeout=timeout)
69
+
70
+ def close(self):
71
+ self._process.close(force=True)
72
+