pyxllib 0.3.197__py3-none-any.whl → 0.3.200__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 (126) hide show
  1. pyxllib/__init__.py +21 -21
  2. pyxllib/algo/__init__.py +8 -8
  3. pyxllib/algo/disjoint.py +54 -54
  4. pyxllib/algo/geo.py +541 -541
  5. pyxllib/algo/intervals.py +964 -964
  6. pyxllib/algo/matcher.py +389 -389
  7. pyxllib/algo/newbie.py +166 -166
  8. pyxllib/algo/pupil.py +629 -629
  9. pyxllib/algo/shapelylib.py +67 -67
  10. pyxllib/algo/specialist.py +241 -241
  11. pyxllib/algo/stat.py +494 -494
  12. pyxllib/algo/treelib.py +149 -149
  13. pyxllib/algo/unitlib.py +66 -66
  14. pyxllib/autogui/__init__.py +5 -5
  15. pyxllib/autogui/activewin.py +246 -246
  16. pyxllib/autogui/all.py +9 -9
  17. pyxllib/autogui/autogui.py +852 -852
  18. pyxllib/autogui/uiautolib.py +362 -362
  19. pyxllib/autogui/virtualkey.py +102 -102
  20. pyxllib/autogui/wechat.py +827 -827
  21. pyxllib/autogui/wechat_msg.py +421 -421
  22. pyxllib/autogui/wxautolib.py +84 -84
  23. pyxllib/cv/__init__.py +5 -5
  24. pyxllib/cv/expert.py +267 -267
  25. pyxllib/cv/imfile.py +159 -159
  26. pyxllib/cv/imhash.py +39 -39
  27. pyxllib/cv/pupil.py +9 -9
  28. pyxllib/cv/rgbfmt.py +1525 -1525
  29. pyxllib/cv/slidercaptcha.py +137 -137
  30. pyxllib/cv/trackbartools.py +251 -251
  31. pyxllib/cv/xlcvlib.py +1040 -1040
  32. pyxllib/cv/xlpillib.py +423 -423
  33. pyxllib/data/echarts.py +240 -240
  34. pyxllib/data/jsonlib.py +89 -89
  35. pyxllib/data/oss.py +72 -72
  36. pyxllib/data/pglib.py +1127 -1127
  37. pyxllib/data/sqlite.py +568 -568
  38. pyxllib/data/sqllib.py +297 -297
  39. pyxllib/ext/JLineViewer.py +505 -505
  40. pyxllib/ext/__init__.py +6 -6
  41. pyxllib/ext/demolib.py +246 -246
  42. pyxllib/ext/drissionlib.py +277 -277
  43. pyxllib/ext/kq5034lib.py +12 -12
  44. pyxllib/ext/old.py +663 -663
  45. pyxllib/ext/qt.py +449 -449
  46. pyxllib/ext/robustprocfile.py +497 -497
  47. pyxllib/ext/seleniumlib.py +76 -76
  48. pyxllib/ext/tk.py +173 -173
  49. pyxllib/ext/unixlib.py +827 -827
  50. pyxllib/ext/utools.py +351 -351
  51. pyxllib/ext/webhook.py +124 -119
  52. pyxllib/ext/win32lib.py +40 -40
  53. pyxllib/ext/wjxlib.py +88 -88
  54. pyxllib/ext/wpsapi.py +124 -124
  55. pyxllib/ext/xlwork.py +9 -9
  56. pyxllib/ext/yuquelib.py +1105 -1105
  57. pyxllib/file/__init__.py +17 -17
  58. pyxllib/file/docxlib.py +761 -761
  59. pyxllib/file/gitlib.py +309 -309
  60. pyxllib/file/libreoffice.py +165 -165
  61. pyxllib/file/movielib.py +148 -148
  62. pyxllib/file/newbie.py +10 -10
  63. pyxllib/file/onenotelib.py +1469 -1469
  64. pyxllib/file/packlib/__init__.py +330 -330
  65. pyxllib/file/packlib/zipfile.py +2441 -2441
  66. pyxllib/file/pdflib.py +426 -426
  67. pyxllib/file/pupil.py +185 -185
  68. pyxllib/file/specialist/__init__.py +685 -685
  69. pyxllib/file/specialist/dirlib.py +799 -799
  70. pyxllib/file/specialist/download.py +193 -193
  71. pyxllib/file/specialist/filelib.py +2829 -2829
  72. pyxllib/file/xlsxlib.py +3131 -3131
  73. pyxllib/file/xlsyncfile.py +341 -341
  74. pyxllib/prog/__init__.py +5 -5
  75. pyxllib/prog/cachetools.py +64 -64
  76. pyxllib/prog/deprecatedlib.py +233 -233
  77. pyxllib/prog/filelock.py +42 -42
  78. pyxllib/prog/ipyexec.py +253 -253
  79. pyxllib/prog/multiprogs.py +940 -940
  80. pyxllib/prog/newbie.py +451 -451
  81. pyxllib/prog/pupil.py +1197 -1197
  82. pyxllib/prog/sitepackages.py +33 -33
  83. pyxllib/prog/specialist/__init__.py +391 -391
  84. pyxllib/prog/specialist/bc.py +203 -203
  85. pyxllib/prog/specialist/browser.py +497 -497
  86. pyxllib/prog/specialist/common.py +347 -347
  87. pyxllib/prog/specialist/datetime.py +198 -198
  88. pyxllib/prog/specialist/tictoc.py +240 -240
  89. pyxllib/prog/specialist/xllog.py +180 -180
  90. pyxllib/prog/xlosenv.py +108 -108
  91. pyxllib/stdlib/__init__.py +17 -17
  92. pyxllib/stdlib/tablepyxl/__init__.py +10 -10
  93. pyxllib/stdlib/tablepyxl/style.py +303 -303
  94. pyxllib/stdlib/tablepyxl/tablepyxl.py +130 -130
  95. pyxllib/text/__init__.py +8 -8
  96. pyxllib/text/ahocorasick.py +39 -39
  97. pyxllib/text/airscript.js +744 -744
  98. pyxllib/text/charclasslib.py +121 -121
  99. pyxllib/text/jiebalib.py +267 -267
  100. pyxllib/text/jinjalib.py +32 -32
  101. pyxllib/text/jsa_ai_prompt.md +271 -271
  102. pyxllib/text/jscode.py +922 -922
  103. pyxllib/text/latex/__init__.py +158 -158
  104. pyxllib/text/levenshtein.py +303 -303
  105. pyxllib/text/nestenv.py +1215 -1215
  106. pyxllib/text/newbie.py +300 -300
  107. pyxllib/text/pupil/__init__.py +8 -8
  108. pyxllib/text/pupil/common.py +1121 -1121
  109. pyxllib/text/pupil/xlalign.py +326 -326
  110. pyxllib/text/pycode.py +47 -47
  111. pyxllib/text/specialist/__init__.py +8 -8
  112. pyxllib/text/specialist/common.py +112 -112
  113. pyxllib/text/specialist/ptag.py +186 -186
  114. pyxllib/text/spellchecker.py +172 -172
  115. pyxllib/text/templates/echart_base.html +10 -10
  116. pyxllib/text/templates/highlight_code.html +16 -16
  117. pyxllib/text/templates/latex_editor.html +102 -102
  118. pyxllib/text/vbacode.py +17 -17
  119. pyxllib/text/xmllib.py +747 -747
  120. pyxllib/xl.py +42 -39
  121. pyxllib/xlcv.py +17 -17
  122. {pyxllib-0.3.197.dist-info → pyxllib-0.3.200.dist-info}/METADATA +1 -1
  123. pyxllib-0.3.200.dist-info/RECORD +126 -0
  124. {pyxllib-0.3.197.dist-info → pyxllib-0.3.200.dist-info}/licenses/LICENSE +190 -190
  125. pyxllib-0.3.197.dist-info/RECORD +0 -126
  126. {pyxllib-0.3.197.dist-info → pyxllib-0.3.200.dist-info}/WHEEL +0 -0
pyxllib/ext/utools.py CHANGED
@@ -1,351 +1,351 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # @Author : 陈坤泽
4
- # @Email : 877362867@qq.com
5
- # @Date : 2021/04/04 17:03
6
-
7
- """ 专门给utools的快捷命令扩展的一系列python工具库
8
- """
9
- from pyxllib.prog.pupil import check_install_package
10
-
11
- check_install_package('fire')
12
- check_install_package('humanfriendly')
13
- check_install_package('pandas')
14
- check_install_package('pyautogui', 'PyAutoGui') # 其实pip install不区分大小写,不过官方这里安装是驼峰名
15
-
16
- import datetime
17
- import json
18
- import os
19
- import pathlib
20
- import pyperclip
21
- import re
22
-
23
- import fire
24
- from humanfriendly import format_timespan
25
- import pandas as pd
26
- import pyautogui
27
-
28
- from pyxllib.prog.specialist import browser, TicToc, parse_datetime
29
- from pyxllib.file.specialist import XlPath
30
- from pyxllib.autogui.autogui import type_text, clipboard_decorator
31
-
32
-
33
- def _print_df_result(df, outfmt='text'):
34
- # subinput可以强制重置输出类型
35
- # TODO argparser可以对str类型进行参数解析?
36
- # if 'browser' in kwargs['subinput']:
37
- # outfmt = 'browser'
38
- # elif 'html' in kwargs['subinput']:
39
- # outfmt = 'html'
40
- # elif 'text' in kwargs['subinput']:
41
- # outfmt = 'text'
42
-
43
- if outfmt == 'html':
44
- # content = df.to_html().replace('–', '-') # 很多app会出现一个utf8特殊的-,转gbk编码会出错+
45
- try: # utools里按gbk会有很多编码问题,如果不能直接显示,就打开浏览器看
46
- print(df.to_html())
47
- except UnicodeEncodeError:
48
- browser(df)
49
- elif outfmt == 'browser':
50
- browser(df)
51
- else:
52
- with pd.option_context('display.max_colwidth', -1, 'display.max_columns', 20,
53
- 'display.width', 200): # 上下文控制格式
54
- print(df)
55
-
56
-
57
- class UtoolsBase:
58
- def __init__(self, cmds, *, outfmt='text'):
59
- """
60
- :param cmds: "快捷命令"响应的命令集,即调用出可传入的输入文件、所在窗口、选中文件等信息
61
- :param outfmt: 输出格式
62
- text,纯文本
63
- html,html格式
64
- browser,用浏览器打开
65
- """
66
- # 1 删除没有值的键
67
- self.cmds = dict()
68
- for k, v in cmds.items():
69
- if cmds[k] not in ('{{' + k + '}}', ''):
70
- self.cmds[k] = v
71
-
72
- # 2 「快捷命令」的ClipText宏展开,如果存在连续3个单引号,会跟py字符串定义冲突,产生bug
73
- # 所以这段不建议用宏展开,这里该类会自动调用pyperclip获取
74
- # 这个可以作为可选功能关闭,但实验证明这个对性能其实没有太大影响
75
- if 'ClipText' not in self.cmds:
76
- self.cmds['ClipText'] = pyperclip.paste().replace('\r\n', '\n')
77
-
78
- # 3 解析json数据
79
- # 解析window模式的WindowInfo
80
- if 'WindowInfo' in self.cmds:
81
- self.cmds['WindowInfo'] = json.loads(self.cmds['WindowInfo'].encode('utf8'))
82
-
83
- # 解析files模式的MatchedFiles
84
- if 'MatchedFiles' in self.cmds:
85
- # 右边引用没写错。因为MatchedFiles本身获取内容不稳定,可能会有bug,取更稳定的payload来初始化MatchedFiles
86
- self.cmds['MatchedFiles'] = json.loads(self.cmds['payload'].encode('utf8'))
87
- # 在有些软件中,比如everything,是可以选中多个不同目录的文件的。。。所以严格来说不能直接按第一个文件算pwd
88
- # 但这个讯息是可以写一个工具分析的
89
- # self.cmds['pwd'] = os.path.dirname(self.cmds['MatchedFiles'][0]['path'])
90
-
91
- # TODO 如果选中的文件全部是同一个目录下的,要存储pwd
92
- dirs = {os.path.dirname(f['path']) for f in self.cmds['MatchedFiles']}
93
- if len(dirs) == 1:
94
- self.cmds['pwd'] = list(dirs)[0]
95
-
96
- # 4 当前工作目录
97
- # 要根据场景切换pwd
98
- # files默认工作目录是app: C:\Users\chen\AppData\Local\Programs\utools
99
- # window默认工作目录是exe
100
- # window模式存储的pwd跟各种软件有关
101
- # explorer就是当前窗口,没问题
102
- # onenote存的是桌面
103
- if 'pwd' in self.cmds:
104
- os.chdir(self.cmds['pwd'])
105
-
106
- # 5 subinput可以用字典结构扩展参数
107
- if 'subinput' in self.cmds:
108
- if re.match(r'\{.+\}$', self.cmds['subinput']):
109
- ext_kwargs = eval(self.cmds['subinput'])
110
- self.cmds.update(ext_kwargs)
111
- else:
112
- self.cmds['subinput'] = ''
113
-
114
- self.outfmt = self.cmds['outfmt'] if 'outfmt' in self.cmds else outfmt
115
-
116
-
117
- class UtoolsName(UtoolsBase):
118
-
119
- def check_cmds(self):
120
- """ 显示所有参数值 """
121
- df = pd.DataFrame.from_records([(k, v) for k, v in self.cmds.items()],
122
- columns=['key', 'content'])
123
- _print_df_result(df, self.outfmt)
124
-
125
- def bcompare(self):
126
- """ 可以通过subinput设置文件后缀类型 """
127
- from pyxllib.prog.specialist import bcompare
128
-
129
- suffix = self.cmds.get('subinput', None)
130
- try:
131
- f1 = XlPath.init('left', XlPath.tempdir(), suffix=suffix)
132
- f2 = XlPath.init('right', XlPath.tempdir(), suffix=suffix)
133
- except OSError: # 忽略错误的扩展名
134
- f1 = XlPath.init('left', XlPath.tempdir())
135
- f2 = XlPath.init('right', XlPath.tempdir())
136
- f1.write_text(self.cmds['ClipText'])
137
- f2.write_text(self.cmds['ClipText'] + '\n')
138
- bcompare(f1, f2, wait=False)
139
-
140
- @clipboard_decorator(copy=False, typing=True)
141
- def common_dir(self):
142
- # 目录路径生成工具
143
- from pyxlpr.data.datasets import common_path
144
-
145
- def func(name, unix_path=False):
146
- p = getattr(common_path, name)
147
-
148
- if unix_path:
149
- # 因为用了符号链接,实际位置会变回D:/slns,这里需要反向替换下
150
- p = str(p).replace('D:/slns', 'D:/home/chenkunze/slns')
151
- p = str(p)[2:]
152
- else:
153
- # slns比较特别,本地要重定向到D:/slns
154
- p = str(p).replace('D:/home/chenkunze/slns', 'D:/slns')
155
- p = p.replace('/', '\\')
156
-
157
- return p
158
-
159
- return fire.Fire(func, self.cmds['subinput'], 'CommonDir')
160
-
161
- def wdate(self):
162
- """ week dates 输入一周的简略日期值 """
163
-
164
- def func(start):
165
- weektag = '一二三四五六日'
166
- for i in range(7):
167
- dt = parse_datetime(start) + datetime.timedelta(i)
168
- type_text(dt.strftime('%y%m%d') + f'周{weektag[dt.weekday()]}')
169
- pyautogui.press('down')
170
-
171
- fire.Fire(func, self.cmds['subinput'], 'wdate')
172
-
173
- def input_digits(self):
174
- """ 输入数字 """
175
-
176
- def func(start, stop, step=1):
177
- for i in range(start, stop, step):
178
- pyautogui.write(str(i))
179
- pyautogui.press('down')
180
-
181
- fire.Fire(func, self.cmds['subinput'], 'input_digits')
182
-
183
- def __win32(self):
184
- """ win32相关自动化
185
-
186
- 目前主要是word自动化相关的功能,这里有很多demo可以学习怎么用win32做word的自动化
187
- """
188
- pass
189
-
190
- def browser(self):
191
- """ 将内容复制到word,另存为html文件后,用浏览器打开查看 """
192
- from pyxllib.file.docxlib import rebuild_document_by_word
193
-
194
- file = fire.Fire(rebuild_document_by_word, self.cmds['subinput'], 'browser')
195
- browser(file)
196
-
197
- def create_text_image(self):
198
- from pyxllib.xl import XlPath
199
- from pyxllib.xlcv import xlpil, create_text_image
200
-
201
- @clipboard_decorator('html')
202
- def func(text, font_size=20, *, size=None, xy=None, bg_color=None, text_color=None):
203
- im = create_text_image(text, size, xy=xy, font_size=font_size, bg_color=bg_color, text_color=text_color)
204
- # 这种只能语雀里用
205
- data = xlpil.to_buffer(im, b64encode=True).decode()
206
- res = '<img src="data:image/jpg;base64,' + data + '"/>'
207
- # 这种只能onenote、qq、微信里用
208
- p = XlPath.tempdir() / 'create_text_image.jpg'
209
- im.save(p)
210
- res += f'<img src="{p}"/>'
211
- # 那么我把两种都复制,就能兼容多种场景了~~
212
- return res
213
-
214
- fire.Fire(func, self.cmds['subinput'], 'create_text_image')
215
-
216
-
217
- class UtoolsText(UtoolsBase):
218
- def __init__(self, cmds, *, outfmt='text'):
219
- super().__init__(cmds, outfmt=outfmt)
220
-
221
- @clipboard_decorator(paste=True)
222
- def coderegex(self):
223
- # tt = TicToc()
224
- s = self.cmds['ClipText']
225
- return eval(self.cmds['subinput'])
226
- # print(f'finished in {format_timespan(tt.tocvalue())}.')
227
-
228
- def refine_text(self, func, rtype='text', *, copy=True, paste=True, typing=False):
229
- from functools import partial
230
-
231
- @clipboard_decorator(rtype=rtype, copy=copy, paste=paste, typing=typing)
232
- def _refine():
233
- return fire.Fire(partial(func, self.cmds['ClipText']),
234
- self.cmds['subinput'], func.__name__)
235
-
236
- return _refine()
237
-
238
- def bc_text(self, func):
239
- """ 用bc软件打开,对比前后文本变化,有用户自己决定如何处理保留 """
240
- from functools import partial
241
- from pyxllib.prog.specialist import bcompare
242
-
243
- text1 = self.cmds['ClipText']
244
- text2 = fire.Fire(partial(func, text1), self.cmds['subinput'], func.__name__)
245
- bcompare(text1, text2)
246
-
247
-
248
- class UtoolsFile(UtoolsBase):
249
- """ 文件相关操作工具 """
250
-
251
- def __init__(self, cmds, *, outfmt='text'):
252
- super().__init__(cmds, outfmt=outfmt)
253
-
254
- # 如果是window等模式,补充 MatchedFiles
255
- if 'MatchedFiles' in self.cmds:
256
- self.paths = [pathlib.Path(f['path']) for f in self.cmds['MatchedFiles']]
257
- else:
258
- self.paths = [pathlib.Path(os.path.abspath(f)).resolve() for f in os.listdir('../robot')]
259
-
260
- @classmethod
261
- def is_image(cls, f):
262
- return bool(re.match('jpe?g|png|gif|bmp', f.suffix.lower()[1:]))
263
-
264
- def codefile(self):
265
- """ 多用途文件处理器
266
-
267
- 核心理念是不设定具体功能,而是让用户在 subinput 自己写需要执行的py代码功能
268
- 而在subinput,可以用一些特殊的标识符来传参
269
-
270
- 以file为例,有4类参数:
271
- file,处理当前目录 文件
272
- rfile,递归处理所有目录下 文件
273
- files,当前目录 所有文件
274
- rfiles,递归获取所有目录下 所有文件
275
-
276
- 类型上,file可以改为
277
- dir,只分析目录
278
- path,所有文件和目录
279
-
280
- 扩展了一些特殊的类型:
281
- imfile,图片文件
282
- """
283
- from functools import reduce
284
- try:
285
- from xlproject.xyz.kzconfig import KzDataSync
286
- from pyxlpr.data.labelme import reduce_labelme_jsonfile
287
- except ModuleNotFoundError:
288
- pass
289
-
290
- tt = TicToc()
291
-
292
- # 1 获得所有标识符
293
- # 每个单词前后都有空格,方便定界
294
- keywords = filter(lambda x: re.match(r'[a-zA-Z_]+$', x),
295
- set(re.findall(r'\.?[a-zA-Z_]+\(?', self.cmds['subinput'])))
296
- keywords = ' ' + ' '.join(keywords) + ' '
297
- # print('keywords:', keywords)
298
-
299
- # 2 生成一些备用的智能参数
300
- # 一级目录下选中的文件
301
- paths = self.paths
302
- # 用正则判断一些智能参数是否要计算,注意不能只判断files,如果出现file,也是要引用files的
303
- files = [XlPath(p) for p in paths if p.is_file()] if re.search(r'files? ', keywords) else []
304
- imfiles = [f for f in files if self.is_image(f)] if re.search(r' imfiles? ', keywords) else []
305
- # 出现r系列的递归操作,都是要计算出dirs的
306
- dirs = [XlPath(p) for p in paths if p.is_dir()] if re.search(r' (dirs?|r[a-zA-Z_]+) ', keywords) else []
307
-
308
- # 递归所有的文件
309
- rpaths = reduce(lambda x, y: x + y.select('**/*').subpaths(), [paths] + dirs) \
310
- if re.search(r' (r[a-zA-Z_]+) ', keywords) else []
311
- rfiles = [XlPath(p) for p in rpaths if p.is_file()] if re.search(r' (r(im)?files?) ', keywords) else []
312
- rimfiles = [f for f in rfiles if self.is_image(f)] if re.search(r' rimfiles? ', keywords) else []
313
- rdirs = [XlPath(p) for p in rpaths if p.is_dir()] if re.search(r' (rdirs?) ', keywords) else []
314
-
315
- # 3 判断是否要智能开循环处理
316
- m = re.search(r' (r?(path|(im)?file|dir)) ', keywords)
317
- if m:
318
- name = m.group(1)
319
- objs = eval(name + 's')
320
- print('len(' + name + 's)=', len(objs))
321
- for x in objs:
322
- locals()[name] = x
323
- eval(self.cmds['subinput'])
324
- else: # 没有的话就直接处理所有文件
325
- eval(self.cmds['subinput'])
326
-
327
- # 4 运行结束标志
328
- print(f'finished in {format_timespan(tt.tocvalue())}.')
329
-
330
- def open_jsonl(self):
331
- """ 打开jsonl文件 """
332
- from pyxllib.ext.JLineViewer import start_jlineviewer
333
- start_jlineviewer(self.paths[0].as_posix())
334
-
335
- def open_jsonl_with_excel(self):
336
- """ 用excel打开jsonl文件 """
337
- from pyxllib.algo.stat import write_dataframes_to_excel
338
-
339
- file = self.paths[0].as_posix()
340
- data = XlPath(file).read_jsonl()
341
-
342
- file = XlPath.create_tempfile_path('.xlsx')
343
- df = pd.DataFrame.from_dict(data)
344
- write_dataframes_to_excel(file, {'Sheet1': df})
345
-
346
- os.startfile(file)
347
-
348
-
349
- if __name__ == '__main__':
350
- with TicToc('utools'):
351
- pass
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # @Author : 陈坤泽
4
+ # @Email : 877362867@qq.com
5
+ # @Date : 2021/04/04 17:03
6
+
7
+ """ 专门给utools的快捷命令扩展的一系列python工具库
8
+ """
9
+ from pyxllib.prog.pupil import check_install_package
10
+
11
+ check_install_package('fire')
12
+ check_install_package('humanfriendly')
13
+ check_install_package('pandas')
14
+ check_install_package('pyautogui', 'PyAutoGui') # 其实pip install不区分大小写,不过官方这里安装是驼峰名
15
+
16
+ import datetime
17
+ import json
18
+ import os
19
+ import pathlib
20
+ import pyperclip
21
+ import re
22
+
23
+ import fire
24
+ from humanfriendly import format_timespan
25
+ import pandas as pd
26
+ import pyautogui
27
+
28
+ from pyxllib.prog.specialist import browser, TicToc, parse_datetime
29
+ from pyxllib.file.specialist import XlPath
30
+ from pyxllib.autogui.autogui import type_text, clipboard_decorator
31
+
32
+
33
+ def _print_df_result(df, outfmt='text'):
34
+ # subinput可以强制重置输出类型
35
+ # TODO argparser可以对str类型进行参数解析?
36
+ # if 'browser' in kwargs['subinput']:
37
+ # outfmt = 'browser'
38
+ # elif 'html' in kwargs['subinput']:
39
+ # outfmt = 'html'
40
+ # elif 'text' in kwargs['subinput']:
41
+ # outfmt = 'text'
42
+
43
+ if outfmt == 'html':
44
+ # content = df.to_html().replace('–', '-') # 很多app会出现一个utf8特殊的-,转gbk编码会出错+
45
+ try: # utools里按gbk会有很多编码问题,如果不能直接显示,就打开浏览器看
46
+ print(df.to_html())
47
+ except UnicodeEncodeError:
48
+ browser(df)
49
+ elif outfmt == 'browser':
50
+ browser(df)
51
+ else:
52
+ with pd.option_context('display.max_colwidth', -1, 'display.max_columns', 20,
53
+ 'display.width', 200): # 上下文控制格式
54
+ print(df)
55
+
56
+
57
+ class UtoolsBase:
58
+ def __init__(self, cmds, *, outfmt='text'):
59
+ """
60
+ :param cmds: "快捷命令"响应的命令集,即调用出可传入的输入文件、所在窗口、选中文件等信息
61
+ :param outfmt: 输出格式
62
+ text,纯文本
63
+ html,html格式
64
+ browser,用浏览器打开
65
+ """
66
+ # 1 删除没有值的键
67
+ self.cmds = dict()
68
+ for k, v in cmds.items():
69
+ if cmds[k] not in ('{{' + k + '}}', ''):
70
+ self.cmds[k] = v
71
+
72
+ # 2 「快捷命令」的ClipText宏展开,如果存在连续3个单引号,会跟py字符串定义冲突,产生bug
73
+ # 所以这段不建议用宏展开,这里该类会自动调用pyperclip获取
74
+ # 这个可以作为可选功能关闭,但实验证明这个对性能其实没有太大影响
75
+ if 'ClipText' not in self.cmds:
76
+ self.cmds['ClipText'] = pyperclip.paste().replace('\r\n', '\n')
77
+
78
+ # 3 解析json数据
79
+ # 解析window模式的WindowInfo
80
+ if 'WindowInfo' in self.cmds:
81
+ self.cmds['WindowInfo'] = json.loads(self.cmds['WindowInfo'].encode('utf8'))
82
+
83
+ # 解析files模式的MatchedFiles
84
+ if 'MatchedFiles' in self.cmds:
85
+ # 右边引用没写错。因为MatchedFiles本身获取内容不稳定,可能会有bug,取更稳定的payload来初始化MatchedFiles
86
+ self.cmds['MatchedFiles'] = json.loads(self.cmds['payload'].encode('utf8'))
87
+ # 在有些软件中,比如everything,是可以选中多个不同目录的文件的。。。所以严格来说不能直接按第一个文件算pwd
88
+ # 但这个讯息是可以写一个工具分析的
89
+ # self.cmds['pwd'] = os.path.dirname(self.cmds['MatchedFiles'][0]['path'])
90
+
91
+ # TODO 如果选中的文件全部是同一个目录下的,要存储pwd
92
+ dirs = {os.path.dirname(f['path']) for f in self.cmds['MatchedFiles']}
93
+ if len(dirs) == 1:
94
+ self.cmds['pwd'] = list(dirs)[0]
95
+
96
+ # 4 当前工作目录
97
+ # 要根据场景切换pwd
98
+ # files默认工作目录是app: C:\Users\chen\AppData\Local\Programs\utools
99
+ # window默认工作目录是exe
100
+ # window模式存储的pwd跟各种软件有关
101
+ # explorer就是当前窗口,没问题
102
+ # onenote存的是桌面
103
+ if 'pwd' in self.cmds:
104
+ os.chdir(self.cmds['pwd'])
105
+
106
+ # 5 subinput可以用字典结构扩展参数
107
+ if 'subinput' in self.cmds:
108
+ if re.match(r'\{.+\}$', self.cmds['subinput']):
109
+ ext_kwargs = eval(self.cmds['subinput'])
110
+ self.cmds.update(ext_kwargs)
111
+ else:
112
+ self.cmds['subinput'] = ''
113
+
114
+ self.outfmt = self.cmds['outfmt'] if 'outfmt' in self.cmds else outfmt
115
+
116
+
117
+ class UtoolsName(UtoolsBase):
118
+
119
+ def check_cmds(self):
120
+ """ 显示所有参数值 """
121
+ df = pd.DataFrame.from_records([(k, v) for k, v in self.cmds.items()],
122
+ columns=['key', 'content'])
123
+ _print_df_result(df, self.outfmt)
124
+
125
+ def bcompare(self):
126
+ """ 可以通过subinput设置文件后缀类型 """
127
+ from pyxllib.prog.specialist import bcompare
128
+
129
+ suffix = self.cmds.get('subinput', None)
130
+ try:
131
+ f1 = XlPath.init('left', XlPath.tempdir(), suffix=suffix)
132
+ f2 = XlPath.init('right', XlPath.tempdir(), suffix=suffix)
133
+ except OSError: # 忽略错误的扩展名
134
+ f1 = XlPath.init('left', XlPath.tempdir())
135
+ f2 = XlPath.init('right', XlPath.tempdir())
136
+ f1.write_text(self.cmds['ClipText'])
137
+ f2.write_text(self.cmds['ClipText'] + '\n')
138
+ bcompare(f1, f2, wait=False)
139
+
140
+ @clipboard_decorator(copy=False, typing=True)
141
+ def common_dir(self):
142
+ # 目录路径生成工具
143
+ from pyxlpr.data.datasets import common_path
144
+
145
+ def func(name, unix_path=False):
146
+ p = getattr(common_path, name)
147
+
148
+ if unix_path:
149
+ # 因为用了符号链接,实际位置会变回D:/slns,这里需要反向替换下
150
+ p = str(p).replace('D:/slns', 'D:/home/chenkunze/slns')
151
+ p = str(p)[2:]
152
+ else:
153
+ # slns比较特别,本地要重定向到D:/slns
154
+ p = str(p).replace('D:/home/chenkunze/slns', 'D:/slns')
155
+ p = p.replace('/', '\\')
156
+
157
+ return p
158
+
159
+ return fire.Fire(func, self.cmds['subinput'], 'CommonDir')
160
+
161
+ def wdate(self):
162
+ """ week dates 输入一周的简略日期值 """
163
+
164
+ def func(start):
165
+ weektag = '一二三四五六日'
166
+ for i in range(7):
167
+ dt = parse_datetime(start) + datetime.timedelta(i)
168
+ type_text(dt.strftime('%y%m%d') + f'周{weektag[dt.weekday()]}')
169
+ pyautogui.press('down')
170
+
171
+ fire.Fire(func, self.cmds['subinput'], 'wdate')
172
+
173
+ def input_digits(self):
174
+ """ 输入数字 """
175
+
176
+ def func(start, stop, step=1):
177
+ for i in range(start, stop, step):
178
+ pyautogui.write(str(i))
179
+ pyautogui.press('down')
180
+
181
+ fire.Fire(func, self.cmds['subinput'], 'input_digits')
182
+
183
+ def __win32(self):
184
+ """ win32相关自动化
185
+
186
+ 目前主要是word自动化相关的功能,这里有很多demo可以学习怎么用win32做word的自动化
187
+ """
188
+ pass
189
+
190
+ def browser(self):
191
+ """ 将内容复制到word,另存为html文件后,用浏览器打开查看 """
192
+ from pyxllib.file.docxlib import rebuild_document_by_word
193
+
194
+ file = fire.Fire(rebuild_document_by_word, self.cmds['subinput'], 'browser')
195
+ browser(file)
196
+
197
+ def create_text_image(self):
198
+ from pyxllib.xl import XlPath
199
+ from pyxllib.xlcv import xlpil, create_text_image
200
+
201
+ @clipboard_decorator('html')
202
+ def func(text, font_size=20, *, size=None, xy=None, bg_color=None, text_color=None):
203
+ im = create_text_image(text, size, xy=xy, font_size=font_size, bg_color=bg_color, text_color=text_color)
204
+ # 这种只能语雀里用
205
+ data = xlpil.to_buffer(im, b64encode=True).decode()
206
+ res = '<img src="data:image/jpg;base64,' + data + '"/>'
207
+ # 这种只能onenote、qq、微信里用
208
+ p = XlPath.tempdir() / 'create_text_image.jpg'
209
+ im.save(p)
210
+ res += f'<img src="{p}"/>'
211
+ # 那么我把两种都复制,就能兼容多种场景了~~
212
+ return res
213
+
214
+ fire.Fire(func, self.cmds['subinput'], 'create_text_image')
215
+
216
+
217
+ class UtoolsText(UtoolsBase):
218
+ def __init__(self, cmds, *, outfmt='text'):
219
+ super().__init__(cmds, outfmt=outfmt)
220
+
221
+ @clipboard_decorator(paste=True)
222
+ def coderegex(self):
223
+ # tt = TicToc()
224
+ s = self.cmds['ClipText']
225
+ return eval(self.cmds['subinput'])
226
+ # print(f'finished in {format_timespan(tt.tocvalue())}.')
227
+
228
+ def refine_text(self, func, rtype='text', *, copy=True, paste=True, typing=False):
229
+ from functools import partial
230
+
231
+ @clipboard_decorator(rtype=rtype, copy=copy, paste=paste, typing=typing)
232
+ def _refine():
233
+ return fire.Fire(partial(func, self.cmds['ClipText']),
234
+ self.cmds['subinput'], func.__name__)
235
+
236
+ return _refine()
237
+
238
+ def bc_text(self, func):
239
+ """ 用bc软件打开,对比前后文本变化,有用户自己决定如何处理保留 """
240
+ from functools import partial
241
+ from pyxllib.prog.specialist import bcompare
242
+
243
+ text1 = self.cmds['ClipText']
244
+ text2 = fire.Fire(partial(func, text1), self.cmds['subinput'], func.__name__)
245
+ bcompare(text1, text2)
246
+
247
+
248
+ class UtoolsFile(UtoolsBase):
249
+ """ 文件相关操作工具 """
250
+
251
+ def __init__(self, cmds, *, outfmt='text'):
252
+ super().__init__(cmds, outfmt=outfmt)
253
+
254
+ # 如果是window等模式,补充 MatchedFiles
255
+ if 'MatchedFiles' in self.cmds:
256
+ self.paths = [pathlib.Path(f['path']) for f in self.cmds['MatchedFiles']]
257
+ else:
258
+ self.paths = [pathlib.Path(os.path.abspath(f)).resolve() for f in os.listdir('../robot')]
259
+
260
+ @classmethod
261
+ def is_image(cls, f):
262
+ return bool(re.match('jpe?g|png|gif|bmp', f.suffix.lower()[1:]))
263
+
264
+ def codefile(self):
265
+ """ 多用途文件处理器
266
+
267
+ 核心理念是不设定具体功能,而是让用户在 subinput 自己写需要执行的py代码功能
268
+ 而在subinput,可以用一些特殊的标识符来传参
269
+
270
+ 以file为例,有4类参数:
271
+ file,处理当前目录 文件
272
+ rfile,递归处理所有目录下 文件
273
+ files,当前目录 所有文件
274
+ rfiles,递归获取所有目录下 所有文件
275
+
276
+ 类型上,file可以改为
277
+ dir,只分析目录
278
+ path,所有文件和目录
279
+
280
+ 扩展了一些特殊的类型:
281
+ imfile,图片文件
282
+ """
283
+ from functools import reduce
284
+ try:
285
+ from xlproject.xyz.kzconfig import KzDataSync
286
+ from pyxlpr.data.labelme import reduce_labelme_jsonfile
287
+ except ModuleNotFoundError:
288
+ pass
289
+
290
+ tt = TicToc()
291
+
292
+ # 1 获得所有标识符
293
+ # 每个单词前后都有空格,方便定界
294
+ keywords = filter(lambda x: re.match(r'[a-zA-Z_]+$', x),
295
+ set(re.findall(r'\.?[a-zA-Z_]+\(?', self.cmds['subinput'])))
296
+ keywords = ' ' + ' '.join(keywords) + ' '
297
+ # print('keywords:', keywords)
298
+
299
+ # 2 生成一些备用的智能参数
300
+ # 一级目录下选中的文件
301
+ paths = self.paths
302
+ # 用正则判断一些智能参数是否要计算,注意不能只判断files,如果出现file,也是要引用files的
303
+ files = [XlPath(p) for p in paths if p.is_file()] if re.search(r'files? ', keywords) else []
304
+ imfiles = [f for f in files if self.is_image(f)] if re.search(r' imfiles? ', keywords) else []
305
+ # 出现r系列的递归操作,都是要计算出dirs的
306
+ dirs = [XlPath(p) for p in paths if p.is_dir()] if re.search(r' (dirs?|r[a-zA-Z_]+) ', keywords) else []
307
+
308
+ # 递归所有的文件
309
+ rpaths = reduce(lambda x, y: x + y.select('**/*').subpaths(), [paths] + dirs) \
310
+ if re.search(r' (r[a-zA-Z_]+) ', keywords) else []
311
+ rfiles = [XlPath(p) for p in rpaths if p.is_file()] if re.search(r' (r(im)?files?) ', keywords) else []
312
+ rimfiles = [f for f in rfiles if self.is_image(f)] if re.search(r' rimfiles? ', keywords) else []
313
+ rdirs = [XlPath(p) for p in rpaths if p.is_dir()] if re.search(r' (rdirs?) ', keywords) else []
314
+
315
+ # 3 判断是否要智能开循环处理
316
+ m = re.search(r' (r?(path|(im)?file|dir)) ', keywords)
317
+ if m:
318
+ name = m.group(1)
319
+ objs = eval(name + 's')
320
+ print('len(' + name + 's)=', len(objs))
321
+ for x in objs:
322
+ locals()[name] = x
323
+ eval(self.cmds['subinput'])
324
+ else: # 没有的话就直接处理所有文件
325
+ eval(self.cmds['subinput'])
326
+
327
+ # 4 运行结束标志
328
+ print(f'finished in {format_timespan(tt.tocvalue())}.')
329
+
330
+ def open_jsonl(self):
331
+ """ 打开jsonl文件 """
332
+ from pyxllib.ext.JLineViewer import start_jlineviewer
333
+ start_jlineviewer(self.paths[0].as_posix())
334
+
335
+ def open_jsonl_with_excel(self):
336
+ """ 用excel打开jsonl文件 """
337
+ from pyxllib.algo.stat import write_dataframes_to_excel
338
+
339
+ file = self.paths[0].as_posix()
340
+ data = XlPath(file).read_jsonl()
341
+
342
+ file = XlPath.create_tempfile_path('.xlsx')
343
+ df = pd.DataFrame.from_dict(data)
344
+ write_dataframes_to_excel(file, {'Sheet1': df})
345
+
346
+ os.startfile(file)
347
+
348
+
349
+ if __name__ == '__main__':
350
+ with TicToc('utools'):
351
+ pass