gomyck-tools 1.3.1__py3-none-any.whl → 1.3.3__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.
- ctools/__init__.py +0 -0
- ctools/aes_tools.py +35 -0
- ctools/api_result.py +55 -0
- ctools/application.py +386 -0
- ctools/b64.py +7 -0
- ctools/bashPath.py +13 -0
- ctools/bottle_web_base.py +169 -0
- ctools/bottle_webserver.py +143 -0
- ctools/bottle_websocket.py +75 -0
- ctools/browser_element_tools.py +314 -0
- ctools/call.py +71 -0
- ctools/cdebug.py +143 -0
- ctools/cftp.py +74 -0
- ctools/cjson.py +54 -0
- ctools/ckafka.py +159 -0
- ctools/compile_tools.py +18 -0
- ctools/console.py +55 -0
- ctools/coord_trans.py +127 -0
- ctools/credis.py +111 -0
- ctools/cron_lite.py +245 -0
- ctools/ctoken.py +34 -0
- ctools/cword.py +30 -0
- ctools/czip.py +130 -0
- ctools/database.py +185 -0
- ctools/date_utils.py +43 -0
- ctools/dict_wrapper.py +20 -0
- ctools/douglas_rarefy.py +136 -0
- ctools/download_tools.py +57 -0
- ctools/enums.py +4 -0
- ctools/ex.py +31 -0
- ctools/excelOpt.py +36 -0
- ctools/html_soup.py +35 -0
- ctools/http_utils.py +24 -0
- ctools/images_tools.py +27 -0
- ctools/imgDialog.py +44 -0
- ctools/metrics.py +131 -0
- ctools/mqtt_utils.py +289 -0
- ctools/obj.py +20 -0
- ctools/pacth.py +74 -0
- ctools/plan_area_tools.py +97 -0
- ctools/process_pool.py +36 -0
- ctools/pty_tools.py +72 -0
- ctools/resource_bundle_tools.py +121 -0
- ctools/rsa.py +70 -0
- ctools/screenshot_tools.py +127 -0
- ctools/sign.py +20 -0
- ctools/sm_tools.py +49 -0
- ctools/snow_id.py +76 -0
- ctools/str_diff.py +20 -0
- ctools/string_tools.py +85 -0
- ctools/sys_info.py +157 -0
- ctools/sys_log.py +89 -0
- ctools/thread_pool.py +35 -0
- ctools/upload_tools.py +40 -0
- ctools/win_canvas.py +83 -0
- ctools/win_control.py +106 -0
- ctools/word_fill.py +562 -0
- ctools/word_fill_entity.py +46 -0
- ctools/work_path.py +69 -0
- {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.3.dist-info}/METADATA +4 -2
- gomyck_tools-1.3.3.dist-info/RECORD +64 -0
- {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.3.dist-info}/WHEEL +1 -1
- gomyck_tools-1.3.3.dist-info/licenses/LICENSE +13 -0
- gomyck_tools-1.3.3.dist-info/top_level.txt +1 -0
- gomyck_tools-1.3.1.dist-info/RECORD +0 -4
- gomyck_tools-1.3.1.dist-info/top_level.txt +0 -1
ctools/cdebug.py
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
import subprocess
|
2
|
+
import sys
|
3
|
+
import threading
|
4
|
+
from datetime import datetime
|
5
|
+
from queue import Queue, Empty
|
6
|
+
|
7
|
+
|
8
|
+
class ProgramInterceptor:
|
9
|
+
def __init__(self, command):
|
10
|
+
self.command = command
|
11
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
12
|
+
self.log_filename = f"program_io_log_{timestamp}.txt"
|
13
|
+
self.process = None
|
14
|
+
self.log_queue = Queue()
|
15
|
+
|
16
|
+
def start(self):
|
17
|
+
# 启动子进程
|
18
|
+
self.process = subprocess.Popen(
|
19
|
+
self.command,
|
20
|
+
stdin=subprocess.PIPE,
|
21
|
+
stdout=subprocess.PIPE,
|
22
|
+
stderr=subprocess.PIPE,
|
23
|
+
bufsize=0,
|
24
|
+
universal_newlines=True
|
25
|
+
)
|
26
|
+
|
27
|
+
# 启动日志写入线程
|
28
|
+
log_thread = threading.Thread(target=self._write_log_thread, daemon=True)
|
29
|
+
log_thread.start()
|
30
|
+
|
31
|
+
# 记录初始信息
|
32
|
+
self._enqueue_log("header", f"Command: {' '.join(self.command)}")
|
33
|
+
self._enqueue_log("header", f"Start time: {datetime.now()}")
|
34
|
+
self._enqueue_log("header", "-" * 50)
|
35
|
+
|
36
|
+
# 启动输出转发线程
|
37
|
+
stdout_thread = threading.Thread(
|
38
|
+
target=self._forward_stream,
|
39
|
+
args=(self.process.stdout, sys.stdout, "stdout"),
|
40
|
+
daemon=True
|
41
|
+
)
|
42
|
+
stderr_thread = threading.Thread(
|
43
|
+
target=self._forward_stream,
|
44
|
+
args=(self.process.stderr, sys.stderr, "stderr"),
|
45
|
+
daemon=True
|
46
|
+
)
|
47
|
+
|
48
|
+
stdout_thread.start()
|
49
|
+
stderr_thread.start()
|
50
|
+
|
51
|
+
# 主线程处理输入转发
|
52
|
+
try:
|
53
|
+
while True:
|
54
|
+
if self.process.poll() is not None:
|
55
|
+
break
|
56
|
+
|
57
|
+
# 读取用户输入
|
58
|
+
try:
|
59
|
+
user_input = sys.stdin.readline()
|
60
|
+
if not user_input: # EOF
|
61
|
+
break
|
62
|
+
|
63
|
+
# 记录输入
|
64
|
+
self._enqueue_log("stdin", user_input)
|
65
|
+
|
66
|
+
# 转发到子进程
|
67
|
+
try:
|
68
|
+
self.process.stdin.write(user_input)
|
69
|
+
self.process.stdin.flush()
|
70
|
+
except (BrokenPipeError, OSError):
|
71
|
+
break
|
72
|
+
|
73
|
+
except KeyboardInterrupt:
|
74
|
+
break
|
75
|
+
|
76
|
+
finally:
|
77
|
+
# 清理工作
|
78
|
+
self.process.terminate()
|
79
|
+
|
80
|
+
# 等待所有输出处理完成
|
81
|
+
stdout_thread.join(timeout=1)
|
82
|
+
stderr_thread.join(timeout=1)
|
83
|
+
|
84
|
+
# 记录结束信息
|
85
|
+
self._enqueue_log("header", "-" * 50)
|
86
|
+
self._enqueue_log("header", f"End time: {datetime.now()}")
|
87
|
+
self._enqueue_log("header", f"Exit code: {self.process.returncode}")
|
88
|
+
|
89
|
+
# 等待日志写入完成
|
90
|
+
self.log_queue.put(None) # 结束信号
|
91
|
+
log_thread.join(timeout=2)
|
92
|
+
|
93
|
+
def _forward_stream(self, source, target, stream_name):
|
94
|
+
"""转发数据流并记录"""
|
95
|
+
for line in iter(source.readline, ''):
|
96
|
+
# 转发到目标
|
97
|
+
target.write(line)
|
98
|
+
target.flush()
|
99
|
+
|
100
|
+
# 记录输出
|
101
|
+
self._enqueue_log(stream_name, line)
|
102
|
+
|
103
|
+
def _enqueue_log(self, stream_type, content):
|
104
|
+
"""将日志内容放入队列"""
|
105
|
+
self.log_queue.put((stream_type, content))
|
106
|
+
|
107
|
+
def _write_log_thread(self):
|
108
|
+
"""日志写入线程"""
|
109
|
+
with open(self.log_filename, 'w', encoding='utf-8') as log_file:
|
110
|
+
while True:
|
111
|
+
try:
|
112
|
+
item = self.log_queue.get(timeout=0.5)
|
113
|
+
if item is None: # 结束信号
|
114
|
+
break
|
115
|
+
|
116
|
+
stream_type, content = item
|
117
|
+
|
118
|
+
if stream_type == 'stderr': continue
|
119
|
+
|
120
|
+
if stream_type == "header":
|
121
|
+
log_file.write(content + "\n")
|
122
|
+
else:
|
123
|
+
direction = ">>>" if stream_type == "stdin" else "<<<"
|
124
|
+
log_file.write(f"{direction} {stream_type}: {content}")
|
125
|
+
|
126
|
+
log_file.flush()
|
127
|
+
|
128
|
+
except Empty:
|
129
|
+
if self.process.poll() is not None:
|
130
|
+
continue
|
131
|
+
|
132
|
+
|
133
|
+
def main():
|
134
|
+
if len(sys.argv) < 2:
|
135
|
+
print("Usage: cdebug.py <program> [args...]")
|
136
|
+
print("Example: cdebug.py python script.py arg1 arg2")
|
137
|
+
sys.exit(1)
|
138
|
+
interceptor = ProgramInterceptor(sys.argv[1:])
|
139
|
+
interceptor.start()
|
140
|
+
|
141
|
+
|
142
|
+
if __name__ == "__main__":
|
143
|
+
main()
|
ctools/cftp.py
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
__author__ = 'haoyang'
|
4
|
+
__date__ = '2025/1/24 08:53'
|
5
|
+
|
6
|
+
import io
|
7
|
+
import time
|
8
|
+
from ftplib import FTP, error_perm, error_temp
|
9
|
+
|
10
|
+
class CFTP:
|
11
|
+
"""
|
12
|
+
with open('xx/xx.md', 'rb') as file:
|
13
|
+
ftp_host = 'x.x.x.x'
|
14
|
+
ftp_username = 'x'
|
15
|
+
ftp_password = 'x'
|
16
|
+
CFTP(ftp_host, ftp_username, ftp_password).upload_file_to_ftp('xx.md', file)
|
17
|
+
"""
|
18
|
+
def __init__(self, host, username, password, timeout=30, max_retries=3, retry_delay=5):
|
19
|
+
self.host = host
|
20
|
+
self.username = username
|
21
|
+
self.password = password
|
22
|
+
self.ftp:FTP = None
|
23
|
+
self.timeout = timeout
|
24
|
+
self.max_retries = max_retries
|
25
|
+
self.retry_delay = retry_delay
|
26
|
+
|
27
|
+
def upload_file_to_ftp(self, file_name, file:io.BytesIO, ftp_directory='/'):
|
28
|
+
if not file_name: raise Exception('文件名不能为空')
|
29
|
+
if not file: raise Exception('文件不能为空')
|
30
|
+
for attempt in range(self.max_retries):
|
31
|
+
try:
|
32
|
+
if not self.ftp:
|
33
|
+
ftp = FTP(self.host, timeout=self.timeout)
|
34
|
+
ftp.login(self.username, self.password)
|
35
|
+
ftp.set_pasv(True)
|
36
|
+
self.ftp = ftp
|
37
|
+
try:
|
38
|
+
self.ftp.cwd(ftp_directory)
|
39
|
+
except error_perm:
|
40
|
+
print(f"FTP 目录不存在:{ftp_directory}")
|
41
|
+
self.ftp.mkd(ftp_directory)
|
42
|
+
print(f"FTP 目录创建成功:{ftp_directory}, 正在切换到目录:{ftp_directory}")
|
43
|
+
self.ftp.cwd(ftp_directory)
|
44
|
+
print(f"正在上传文件:{file_name}")
|
45
|
+
self.ftp.storbinary(f"STOR {file_name}", file)
|
46
|
+
self.ftp.quit()
|
47
|
+
print(f"文件成功上传到 FTP: {file_name}")
|
48
|
+
return
|
49
|
+
except (error_perm, error_temp) as e:
|
50
|
+
try:
|
51
|
+
self.ftp.quit()
|
52
|
+
except Exception:
|
53
|
+
pass
|
54
|
+
self.ftp = None
|
55
|
+
print(f"FTP 错误:{e}")
|
56
|
+
if attempt < self.max_retries - 1:
|
57
|
+
print(f"正在尝试重连... 第 {attempt + 1} 次重试...")
|
58
|
+
time.sleep(self.retry_delay)
|
59
|
+
else:
|
60
|
+
print("重试次数已用尽,上传失败。")
|
61
|
+
raise
|
62
|
+
except Exception as e:
|
63
|
+
try:
|
64
|
+
self.ftp.quit()
|
65
|
+
except Exception:
|
66
|
+
pass
|
67
|
+
self.ftp = None
|
68
|
+
print(f"连接或上传出现异常:{e}")
|
69
|
+
if attempt < self.max_retries - 1:
|
70
|
+
print(f"正在尝试重连... 第 {attempt + 1} 次重试...")
|
71
|
+
time.sleep(self.retry_delay)
|
72
|
+
else:
|
73
|
+
print("重试次数已用尽,上传失败。")
|
74
|
+
raise
|
ctools/cjson.py
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
import jsonpickle
|
2
|
+
|
3
|
+
# 需要转换成str的属性
|
4
|
+
str_value_keys = []
|
5
|
+
jsonpickle.set_preferred_backend('json')
|
6
|
+
jsonpickle.set_encoder_options('json', ensure_ascii=False)
|
7
|
+
jsonpickle.set_decoder_options('json')
|
8
|
+
|
9
|
+
def dumps(obj, **kwargs) -> str:
|
10
|
+
"""
|
11
|
+
将对象转换为json字符串
|
12
|
+
:param obj: 对象
|
13
|
+
:return: json 字符串
|
14
|
+
"""
|
15
|
+
# indent = 2 可以美化输出
|
16
|
+
if obj is None: return None
|
17
|
+
if type(obj) == str: return obj
|
18
|
+
return f'{jsonpickle.encode(obj, unpicklable=False, make_refs=False, **kwargs)}'
|
19
|
+
|
20
|
+
def loads(json_str: str, **kwargs) -> dict:
|
21
|
+
"""
|
22
|
+
将json字符串转换为对象
|
23
|
+
:param json_str: json 字符串
|
24
|
+
:return: 对象
|
25
|
+
"""
|
26
|
+
return jsonpickle.decode(json_str, **kwargs)
|
27
|
+
|
28
|
+
def unify_to_str(json_str: str) -> str:
|
29
|
+
if not str_value_keys and len(str_value_keys) == 0: return json_str
|
30
|
+
obj = loads(json_str)
|
31
|
+
if isinstance(obj, list):
|
32
|
+
_handle_list(obj)
|
33
|
+
elif isinstance(obj, dict):
|
34
|
+
_handle_dict(obj)
|
35
|
+
return dumps(obj)
|
36
|
+
|
37
|
+
def _handle_list(data):
|
38
|
+
for o in data:
|
39
|
+
if isinstance(o, list):
|
40
|
+
_handle_list(o)
|
41
|
+
elif isinstance(o, dict):
|
42
|
+
_handle_dict(o)
|
43
|
+
|
44
|
+
def _handle_dict(data):
|
45
|
+
for k, v in data.items():
|
46
|
+
if isinstance(v, list):
|
47
|
+
_handle_list(v)
|
48
|
+
elif isinstance(v, dict):
|
49
|
+
_handle_dict(v)
|
50
|
+
elif k in str_value_keys:
|
51
|
+
try:
|
52
|
+
data[k] = str(v)
|
53
|
+
except Exception:
|
54
|
+
pass
|
ctools/ckafka.py
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
__author__ = 'haoyang'
|
4
|
+
__date__ = '2024/9/5 10:39'
|
5
|
+
|
6
|
+
import time
|
7
|
+
from threading import Thread, Lock
|
8
|
+
|
9
|
+
from kafka import KafkaProducer, errors, KafkaConsumer
|
10
|
+
from kafka.producer.future import FutureRecordMetadata
|
11
|
+
|
12
|
+
from ctools import thread_pool
|
13
|
+
from ctools.cjson import dumps
|
14
|
+
|
15
|
+
"""
|
16
|
+
import time
|
17
|
+
from datetime import datetime
|
18
|
+
|
19
|
+
from ctools import thread_pool, string_tools
|
20
|
+
from ctools.ckafka import CKafka
|
21
|
+
|
22
|
+
c = CKafka(kafka_url='192.168.3.160:9094', secure=True)
|
23
|
+
|
24
|
+
producer = c.init_producer()
|
25
|
+
consumer = c.init_consumer(enable_auto_commit=False)
|
26
|
+
|
27
|
+
def send_msg():
|
28
|
+
while True:
|
29
|
+
command = input('发送消息: Y/n \n')
|
30
|
+
if command.strip() not in ['N', 'n']:
|
31
|
+
producer.send_msg('jqxx', '{{"jqid": "{}", "xxxx": "{}"}}'.format(string_tools.get_snowflake_id(), datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')))
|
32
|
+
else:
|
33
|
+
break
|
34
|
+
|
35
|
+
thread_pool.submit(send_msg)
|
36
|
+
|
37
|
+
def consumer_callback(msg):
|
38
|
+
print(msg)
|
39
|
+
return True
|
40
|
+
|
41
|
+
consumer.receive_msg('jqxx', callBack=consumer_callback)
|
42
|
+
|
43
|
+
while True: time.sleep(1)
|
44
|
+
"""
|
45
|
+
class KafkaInstance:
|
46
|
+
def __init__(self, producer: KafkaProducer, consumer: KafkaConsumer):
|
47
|
+
self.start_consumer = False
|
48
|
+
self.quited = False
|
49
|
+
self.producer = producer
|
50
|
+
self.consumer = consumer
|
51
|
+
self.consumer_callback = {"topic_key": []}
|
52
|
+
|
53
|
+
# FutureRecordMetadata 可以添加回调, 来监听是否发送成功
|
54
|
+
# r.add_callback(lambda x: print(x))
|
55
|
+
# r.get() 可以同步获取结果
|
56
|
+
def send_msg(self, topic, msg, key: str=None, partition:int=None) -> FutureRecordMetadata:
|
57
|
+
if self.producer is None: raise RuntimeError("Producer is not initialized")
|
58
|
+
if self.quited: return
|
59
|
+
return self.producer.send(topic=topic, value=msg, key=None if key is None else key.encode('utf-8'), partition=partition)
|
60
|
+
|
61
|
+
def receive_msg(self, topics: str, callBack=print):
|
62
|
+
if self.consumer is None: raise RuntimeError("Consumer is not initialized")
|
63
|
+
for topic in topics.split(','):
|
64
|
+
if topic not in self.consumer_callback.keys():
|
65
|
+
self.consumer_callback[topic] = []
|
66
|
+
self.consumer.subscribe(self.consumer_callback.keys())
|
67
|
+
self.consumer_callback[topic].append(callBack)
|
68
|
+
if not self.start_consumer:
|
69
|
+
t = Thread(target=self._start_consumer_poll, daemon=True)
|
70
|
+
t.start()
|
71
|
+
|
72
|
+
def _start_consumer_poll(self):
|
73
|
+
self.start_consumer = True
|
74
|
+
for msg in self.consumer:
|
75
|
+
if self.quited: break
|
76
|
+
funcList = []
|
77
|
+
begin_time = time.time()
|
78
|
+
for func in self.consumer_callback[msg.topic]:
|
79
|
+
if self.quited: break
|
80
|
+
res = func(msg)
|
81
|
+
if not self.consumer.config['enable_auto_commit'] and res: self.consumer.commit()
|
82
|
+
funcList.append(func.__name__)
|
83
|
+
end_time = time.time()
|
84
|
+
if end_time - begin_time > 1: print(f"kafka consume too slow!!! {funcList} time cost: ", f'{round(end_time - begin_time, 2)}s')
|
85
|
+
funcList.clear()
|
86
|
+
|
87
|
+
def shutdown(self):
|
88
|
+
self.quited = True
|
89
|
+
try: self.consumer.close()
|
90
|
+
except Exception: pass
|
91
|
+
try: self.producer.close()
|
92
|
+
except Exception: pass
|
93
|
+
|
94
|
+
|
95
|
+
class CKafka:
|
96
|
+
|
97
|
+
def __init__(self, kafka_url: str = '127.0.0.1:9092', secure: bool = False, username: str = 'client', password: str = 'hylink_user_password'):
|
98
|
+
self.kafka_url = kafka_url
|
99
|
+
self.secure = secure
|
100
|
+
self.username = username
|
101
|
+
self.password = password
|
102
|
+
|
103
|
+
def init_producer(self, acks=1) -> KafkaInstance:
|
104
|
+
print("[ Producer ] Connecting to Kafka [{}]".format(self.kafka_url))
|
105
|
+
for i in range(0, 6):
|
106
|
+
try:
|
107
|
+
if self.secure:
|
108
|
+
producer = KafkaProducer(
|
109
|
+
acks=acks,
|
110
|
+
bootstrap_servers=self.kafka_url,
|
111
|
+
value_serializer=lambda x: dumps(x).encode('utf-8'),
|
112
|
+
sasl_plain_username=self.username,
|
113
|
+
sasl_plain_password=self.password,
|
114
|
+
security_protocol='SASL_PLAINTEXT',
|
115
|
+
sasl_mechanism='PLAIN'
|
116
|
+
)
|
117
|
+
else:
|
118
|
+
producer = KafkaProducer(
|
119
|
+
acks=acks,
|
120
|
+
bootstrap_servers=self.kafka_url,
|
121
|
+
value_serializer=lambda x: dumps(x).encode('utf-8')
|
122
|
+
)
|
123
|
+
print("[ Producer ] Success Connected to Kafka [{}]".format(self.kafka_url))
|
124
|
+
return KafkaInstance(producer=producer, consumer=None)
|
125
|
+
except errors.NoBrokersAvailable:
|
126
|
+
print("[ Producer ] Waiting for Kafka [{}] to become available...".format(self.kafka_url))
|
127
|
+
time.sleep(3)
|
128
|
+
raise RuntimeError("[ Producer ] Failed to connect to Kafka [{}] within 60 seconds".format(self.kafka_url))
|
129
|
+
|
130
|
+
def init_consumer(self, client_id: str = 'ck-py-kafka-consumer', consumer_group: str = 'ck-py-kafka-consumer', enable_auto_commit: bool = True) -> KafkaInstance:
|
131
|
+
print("[ Consumer ] Connecting to Kafka [{}]".format(self.kafka_url))
|
132
|
+
for i in range(0, 6):
|
133
|
+
try:
|
134
|
+
if self.secure:
|
135
|
+
consumer = KafkaConsumer(
|
136
|
+
client_id=client_id,
|
137
|
+
group_id=consumer_group,
|
138
|
+
enable_auto_commit=enable_auto_commit,
|
139
|
+
bootstrap_servers=self.kafka_url,
|
140
|
+
value_deserializer=lambda x: x.decode('utf-8'),
|
141
|
+
sasl_plain_username=self.username,
|
142
|
+
sasl_plain_password=self.password,
|
143
|
+
security_protocol='SASL_PLAINTEXT',
|
144
|
+
sasl_mechanism='PLAIN'
|
145
|
+
)
|
146
|
+
else:
|
147
|
+
consumer = KafkaProducer(
|
148
|
+
client_id=client_id,
|
149
|
+
group_id=consumer_group,
|
150
|
+
enable_auto_commit=enable_auto_commit,
|
151
|
+
bootstrap_servers=self.kafka_url,
|
152
|
+
value_deserializer=lambda x: x.decode('utf-8')
|
153
|
+
)
|
154
|
+
print("[ Consumer ] Success Connected to Kafka [{}]".format(self.kafka_url))
|
155
|
+
return KafkaInstance(producer=None, consumer=consumer)
|
156
|
+
except errors.NoBrokersAvailable:
|
157
|
+
print("[ Consumer ] Waiting for Kafka [{}] to become available...".format(self.kafka_url))
|
158
|
+
time.sleep(3)
|
159
|
+
raise RuntimeError("[ Consumer ] Failed to connect to Kafka [{}] within 60 seconds".format(self.kafka_url))
|
ctools/compile_tools.py
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
import importlib
|
2
|
+
import os
|
3
|
+
import time
|
4
|
+
|
5
|
+
|
6
|
+
def code_to_pyc(code: str, out_file_path: str):
|
7
|
+
file_name = os.path.split(out_file_path)[-1]
|
8
|
+
compiled_code = compile(code, file_name, 'exec')
|
9
|
+
bytecode = importlib._bootstrap_external._code_to_timestamp_pyc(compiled_code, time.time(), len(code))
|
10
|
+
with open(out_file_path, 'wb') as f:
|
11
|
+
f.write(bytecode)
|
12
|
+
|
13
|
+
|
14
|
+
def file_to_pyc(file_path: str, out_file_path: str):
|
15
|
+
with open(file_path, 'r') as f:
|
16
|
+
code = f.read()
|
17
|
+
if code:
|
18
|
+
code_to_pyc(code, out_file_path)
|
ctools/console.py
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
import logging
|
2
|
+
import sys
|
3
|
+
import tkinter as tk
|
4
|
+
|
5
|
+
|
6
|
+
class Console:
|
7
|
+
|
8
|
+
def __init__(self, master):
|
9
|
+
self.master = master
|
10
|
+
|
11
|
+
# 创建文本框和滚动条
|
12
|
+
self.textbox = tk.Text(self.master, wrap=tk.NONE)
|
13
|
+
|
14
|
+
self.vertical_scrollbar = tk.Scrollbar(self.textbox, command=self.textbox.yview)
|
15
|
+
self.horizontal_scrollbar = tk.Scrollbar(self.textbox, command=self.textbox.xview, orient=tk.HORIZONTAL)
|
16
|
+
|
17
|
+
self.textbox.configure(yscrollcommand=self.vertical_scrollbar.set, xscrollcommand=self.horizontal_scrollbar.set)
|
18
|
+
self.textbox.pack(side=tk.LEFT, pady=10, padx=10, ipadx=10, ipady=10, fill=tk.BOTH, expand=True)
|
19
|
+
|
20
|
+
self.vertical_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
21
|
+
self.horizontal_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
|
22
|
+
|
23
|
+
# 将标准输出和标准错误输出重定向到文本框中
|
24
|
+
sys.stdout = self
|
25
|
+
sys.stderr = self
|
26
|
+
|
27
|
+
# # 创建输入框和按钮
|
28
|
+
# self.entry = tk.Entry(self.master)
|
29
|
+
# self.entry.pack(side=tk.BOTTOM, fill=tk.X, expand=True)
|
30
|
+
# self.button = tk.Button(self.master, text="Send", command=self.send)
|
31
|
+
# self.button.pack(side=tk.BOTTOM)
|
32
|
+
|
33
|
+
# 将日志输出到文本框中
|
34
|
+
self.log_handler = logging.StreamHandler(self)
|
35
|
+
self.log_handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
|
36
|
+
logging.getLogger().addHandler(self.log_handler)
|
37
|
+
# logging.getLogger().setLevel(logging.INFO)
|
38
|
+
|
39
|
+
def write(self, message):
|
40
|
+
# 在文本框中输出消息
|
41
|
+
self.textbox.insert(tk.END, message + '\n')
|
42
|
+
self.textbox.see(tk.END)
|
43
|
+
|
44
|
+
def flush(self):
|
45
|
+
pass
|
46
|
+
|
47
|
+
def send(self):
|
48
|
+
# 获取输入框中的文本并打印到控制台
|
49
|
+
text = self.entry.get()
|
50
|
+
print(text)
|
51
|
+
self.entry.delete(0, tk.END)
|
52
|
+
|
53
|
+
def __del__(self):
|
54
|
+
# 关闭日志处理器
|
55
|
+
logging.getLogger().removeHandler(self.log_handler)
|
ctools/coord_trans.py
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
import math
|
3
|
+
|
4
|
+
x_pi = 3.14159265358979324 * 3000.0 / 180.0
|
5
|
+
pi = 3.1415926535897932384626 # π
|
6
|
+
a = 6378245.0 # 长半轴
|
7
|
+
ee = 0.00669342162296594323 # 偏心率平方
|
8
|
+
|
9
|
+
def gcj02_to_bd09(lng, lat):
|
10
|
+
"""
|
11
|
+
火星坐标系(GCJ-02)转百度坐标系(BD-09)
|
12
|
+
谷歌、高德——>百度
|
13
|
+
:param lng:火星坐标经度
|
14
|
+
:param lat:火星坐标纬度
|
15
|
+
:return:
|
16
|
+
"""
|
17
|
+
z = math.sqrt(lng * lng + lat * lat) + 0.00002 * math.sin(lat * x_pi)
|
18
|
+
theta = math.atan2(lat, lng) + 0.000003 * math.cos(lng * x_pi)
|
19
|
+
bd_lng = z * math.cos(theta) + 0.0065
|
20
|
+
bd_lat = z * math.sin(theta) + 0.006
|
21
|
+
return [bd_lng, bd_lat]
|
22
|
+
|
23
|
+
|
24
|
+
def bd09_to_gcj02(bd_lon, bd_lat):
|
25
|
+
"""
|
26
|
+
百度坐标系(BD-09)转火星坐标系(GCJ-02)
|
27
|
+
百度——>谷歌、高德
|
28
|
+
:param bd_lat:百度坐标纬度
|
29
|
+
:param bd_lon:百度坐标经度
|
30
|
+
:return:转换后的坐标列表形式
|
31
|
+
"""
|
32
|
+
x = bd_lon - 0.0065
|
33
|
+
y = bd_lat - 0.006
|
34
|
+
z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * x_pi)
|
35
|
+
theta = math.atan2(y, x) - 0.000003 * math.cos(x * x_pi)
|
36
|
+
gg_lng = z * math.cos(theta)
|
37
|
+
gg_lat = z * math.sin(theta)
|
38
|
+
return [gg_lng, gg_lat]
|
39
|
+
|
40
|
+
|
41
|
+
def wgs84_to_gcj02(lng, lat):
|
42
|
+
"""
|
43
|
+
WGS84转GCJ02(火星坐标系)
|
44
|
+
:param lng:WGS84坐标系的经度
|
45
|
+
:param lat:WGS84坐标系的纬度
|
46
|
+
:return:
|
47
|
+
"""
|
48
|
+
if out_of_china(lng, lat): # 判断是否在国内
|
49
|
+
return [lng, lat]
|
50
|
+
dlat = _transformlat(lng - 105.0, lat - 35.0)
|
51
|
+
dlng = _transformlng(lng - 105.0, lat - 35.0)
|
52
|
+
radlat = lat / 180.0 * pi
|
53
|
+
magic = math.sin(radlat)
|
54
|
+
magic = 1 - ee * magic * magic
|
55
|
+
sqrtmagic = math.sqrt(magic)
|
56
|
+
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi)
|
57
|
+
dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * pi)
|
58
|
+
mglat = lat + dlat
|
59
|
+
mglng = lng + dlng
|
60
|
+
return [mglng, mglat]
|
61
|
+
|
62
|
+
|
63
|
+
def gcj02_to_wgs84(lng, lat):
|
64
|
+
"""
|
65
|
+
GCJ02(火星坐标系)转GPS84
|
66
|
+
:param lng:火星坐标系的经度
|
67
|
+
:param lat:火星坐标系纬度
|
68
|
+
:return:
|
69
|
+
"""
|
70
|
+
if out_of_china(lng, lat):
|
71
|
+
return [lng, lat]
|
72
|
+
dlat = _transformlat(lng - 105.0, lat - 35.0)
|
73
|
+
dlng = _transformlng(lng - 105.0, lat - 35.0)
|
74
|
+
radlat = lat / 180.0 * pi
|
75
|
+
magic = math.sin(radlat)
|
76
|
+
magic = 1 - ee * magic * magic
|
77
|
+
sqrtmagic = math.sqrt(magic)
|
78
|
+
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi)
|
79
|
+
dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * pi)
|
80
|
+
mglat = lat + dlat
|
81
|
+
mglng = lng + dlng
|
82
|
+
return [lng * 2 - mglng, lat * 2 - mglat]
|
83
|
+
|
84
|
+
|
85
|
+
def bd09_to_wgs84(bd_lon, bd_lat):
|
86
|
+
lon, lat = bd09_to_gcj02(bd_lon, bd_lat)
|
87
|
+
return gcj02_to_wgs84(lon, lat)
|
88
|
+
|
89
|
+
|
90
|
+
def wgs84_to_bd09(lon, lat):
|
91
|
+
lon, lat = wgs84_to_gcj02(lon, lat)
|
92
|
+
return gcj02_to_bd09(lon, lat)
|
93
|
+
|
94
|
+
|
95
|
+
def _transformlat(lng, lat):
|
96
|
+
ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + \
|
97
|
+
0.1 * lng * lat + 0.2 * math.sqrt(math.fabs(lng))
|
98
|
+
ret += (20.0 * math.sin(6.0 * lng * pi) + 20.0 *
|
99
|
+
math.sin(2.0 * lng * pi)) * 2.0 / 3.0
|
100
|
+
ret += (20.0 * math.sin(lat * pi) + 40.0 *
|
101
|
+
math.sin(lat / 3.0 * pi)) * 2.0 / 3.0
|
102
|
+
ret += (160.0 * math.sin(lat / 12.0 * pi) + 320 *
|
103
|
+
math.sin(lat * pi / 30.0)) * 2.0 / 3.0
|
104
|
+
return ret
|
105
|
+
|
106
|
+
|
107
|
+
def _transformlng(lng, lat):
|
108
|
+
ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + \
|
109
|
+
0.1 * lng * lat + 0.1 * math.sqrt(math.fabs(lng))
|
110
|
+
ret += (20.0 * math.sin(6.0 * lng * pi) + 20.0 *
|
111
|
+
math.sin(2.0 * lng * pi)) * 2.0 / 3.0
|
112
|
+
ret += (20.0 * math.sin(lng * pi) + 40.0 *
|
113
|
+
math.sin(lng / 3.0 * pi)) * 2.0 / 3.0
|
114
|
+
ret += (150.0 * math.sin(lng / 12.0 * pi) + 300.0 *
|
115
|
+
math.sin(lng / 30.0 * pi)) * 2.0 / 3.0
|
116
|
+
return ret
|
117
|
+
|
118
|
+
|
119
|
+
def out_of_china(lng, lat):
|
120
|
+
"""
|
121
|
+
判断是否在国内,不在国内不做偏移
|
122
|
+
:param lng:
|
123
|
+
:param lat:
|
124
|
+
:return:
|
125
|
+
"""
|
126
|
+
return not (lng > 73.66 and lng < 135.05 and lat > 3.86 and lat < 53.55)
|
127
|
+
|