pyxllib 0.3.96__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 (358) 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 -529
  5. pyxllib/algo/intervals.py +964 -964
  6. pyxllib/algo/matcher.py +389 -311
  7. pyxllib/algo/newbie.py +166 -166
  8. pyxllib/algo/pupil.py +629 -461
  9. pyxllib/algo/shapelylib.py +67 -67
  10. pyxllib/algo/specialist.py +241 -240
  11. pyxllib/algo/stat.py +494 -458
  12. pyxllib/algo/treelib.py +149 -149
  13. pyxllib/algo/unitlib.py +66 -66
  14. {pyxlpr → pyxllib/autogui}/__init__.py +5 -5
  15. pyxllib/autogui/activewin.py +246 -0
  16. pyxllib/autogui/all.py +9 -0
  17. pyxllib/{ext/autogui → autogui}/autogui.py +852 -823
  18. pyxllib/autogui/uiautolib.py +362 -0
  19. pyxllib/{ext/autogui → autogui}/virtualkey.py +102 -102
  20. pyxllib/autogui/wechat.py +827 -0
  21. pyxllib/autogui/wechat_msg.py +421 -0
  22. pyxllib/autogui/wxautolib.py +84 -0
  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 -0
  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 -129
  34. pyxllib/data/jsonlib.py +89 -0
  35. pyxllib/data/oss.py +72 -72
  36. pyxllib/data/pglib.py +1127 -643
  37. pyxllib/data/sqlite.py +568 -341
  38. pyxllib/data/sqllib.py +297 -297
  39. pyxllib/ext/JLineViewer.py +505 -492
  40. pyxllib/ext/__init__.py +6 -6
  41. pyxllib/ext/demolib.py +246 -246
  42. pyxllib/ext/drissionlib.py +277 -0
  43. pyxllib/ext/kq5034lib.py +12 -1606
  44. pyxllib/ext/old.py +663 -663
  45. pyxllib/ext/qt.py +449 -449
  46. pyxllib/ext/robustprocfile.py +497 -0
  47. pyxllib/ext/seleniumlib.py +76 -76
  48. pyxllib/ext/tk.py +173 -173
  49. pyxllib/ext/unixlib.py +827 -826
  50. pyxllib/ext/utools.py +351 -338
  51. pyxllib/ext/webhook.py +124 -101
  52. pyxllib/ext/win32lib.py +40 -40
  53. pyxllib/ext/wjxlib.py +88 -0
  54. pyxllib/ext/wpsapi.py +124 -0
  55. pyxllib/ext/xlwork.py +9 -0
  56. pyxllib/ext/yuquelib.py +1105 -173
  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 -0
  61. pyxllib/file/movielib.py +148 -139
  62. pyxllib/file/newbie.py +10 -10
  63. pyxllib/file/onenotelib.py +1469 -1469
  64. pyxllib/file/packlib/__init__.py +330 -293
  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 -186
  71. pyxllib/file/specialist/filelib.py +2829 -2618
  72. pyxllib/file/xlsxlib.py +3131 -2976
  73. pyxllib/file/xlsyncfile.py +341 -0
  74. pyxllib/prog/__init__.py +5 -5
  75. pyxllib/prog/cachetools.py +64 -0
  76. pyxllib/prog/deprecatedlib.py +233 -233
  77. pyxllib/prog/filelock.py +42 -0
  78. pyxllib/prog/ipyexec.py +253 -253
  79. pyxllib/prog/multiprogs.py +940 -0
  80. pyxllib/prog/newbie.py +451 -444
  81. pyxllib/prog/pupil.py +1197 -1128
  82. pyxllib/prog/sitepackages.py +33 -33
  83. pyxllib/prog/specialist/__init__.py +391 -217
  84. pyxllib/prog/specialist/bc.py +203 -200
  85. pyxllib/prog/specialist/browser.py +497 -488
  86. pyxllib/prog/specialist/common.py +347 -347
  87. pyxllib/prog/specialist/datetime.py +199 -131
  88. pyxllib/prog/specialist/tictoc.py +240 -241
  89. pyxllib/prog/specialist/xllog.py +180 -180
  90. pyxllib/prog/xlosenv.py +108 -101
  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 -0
  98. pyxllib/text/charclasslib.py +121 -109
  99. pyxllib/text/jiebalib.py +267 -264
  100. pyxllib/text/jinjalib.py +32 -0
  101. pyxllib/text/jsa_ai_prompt.md +271 -0
  102. pyxllib/text/jscode.py +922 -767
  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 -288
  107. pyxllib/text/pupil/__init__.py +8 -8
  108. pyxllib/text/pupil/common.py +1121 -1095
  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 +11 -0
  116. pyxllib/text/templates/highlight_code.html +17 -0
  117. pyxllib/text/templates/latex_editor.html +103 -0
  118. pyxllib/text/vbacode.py +17 -17
  119. pyxllib/text/xmllib.py +747 -685
  120. pyxllib/xl.py +42 -38
  121. pyxllib/xlcv.py +17 -17
  122. pyxllib-0.3.200.dist-info/METADATA +48 -0
  123. pyxllib-0.3.200.dist-info/RECORD +126 -0
  124. {pyxllib-0.3.96.dist-info → pyxllib-0.3.200.dist-info}/WHEEL +1 -2
  125. {pyxllib-0.3.96.dist-info → pyxllib-0.3.200.dist-info/licenses}/LICENSE +190 -190
  126. pyxllib/ext/autogui/__init__.py +0 -8
  127. pyxllib-0.3.96.dist-info/METADATA +0 -51
  128. pyxllib-0.3.96.dist-info/RECORD +0 -333
  129. pyxllib-0.3.96.dist-info/top_level.txt +0 -2
  130. pyxlpr/ai/__init__.py +0 -5
  131. pyxlpr/ai/clientlib.py +0 -1281
  132. pyxlpr/ai/specialist.py +0 -286
  133. pyxlpr/ai/torch_app.py +0 -172
  134. pyxlpr/ai/xlpaddle.py +0 -655
  135. pyxlpr/ai/xltorch.py +0 -705
  136. pyxlpr/data/__init__.py +0 -11
  137. pyxlpr/data/coco.py +0 -1325
  138. pyxlpr/data/datacls.py +0 -365
  139. pyxlpr/data/datasets.py +0 -200
  140. pyxlpr/data/gptlib.py +0 -1291
  141. pyxlpr/data/icdar/__init__.py +0 -96
  142. pyxlpr/data/icdar/deteval.py +0 -377
  143. pyxlpr/data/icdar/icdar2013.py +0 -341
  144. pyxlpr/data/icdar/iou.py +0 -340
  145. pyxlpr/data/icdar/rrc_evaluation_funcs_1_1.py +0 -463
  146. pyxlpr/data/imtextline.py +0 -473
  147. pyxlpr/data/labelme.py +0 -866
  148. pyxlpr/data/removeline.py +0 -179
  149. pyxlpr/data/specialist.py +0 -57
  150. pyxlpr/eval/__init__.py +0 -85
  151. pyxlpr/paddleocr.py +0 -776
  152. pyxlpr/ppocr/__init__.py +0 -15
  153. pyxlpr/ppocr/configs/rec/multi_language/generate_multi_language_configs.py +0 -226
  154. pyxlpr/ppocr/data/__init__.py +0 -135
  155. pyxlpr/ppocr/data/imaug/ColorJitter.py +0 -26
  156. pyxlpr/ppocr/data/imaug/__init__.py +0 -67
  157. pyxlpr/ppocr/data/imaug/copy_paste.py +0 -170
  158. pyxlpr/ppocr/data/imaug/east_process.py +0 -437
  159. pyxlpr/ppocr/data/imaug/gen_table_mask.py +0 -244
  160. pyxlpr/ppocr/data/imaug/iaa_augment.py +0 -114
  161. pyxlpr/ppocr/data/imaug/label_ops.py +0 -789
  162. pyxlpr/ppocr/data/imaug/make_border_map.py +0 -184
  163. pyxlpr/ppocr/data/imaug/make_pse_gt.py +0 -106
  164. pyxlpr/ppocr/data/imaug/make_shrink_map.py +0 -126
  165. pyxlpr/ppocr/data/imaug/operators.py +0 -433
  166. pyxlpr/ppocr/data/imaug/pg_process.py +0 -906
  167. pyxlpr/ppocr/data/imaug/randaugment.py +0 -143
  168. pyxlpr/ppocr/data/imaug/random_crop_data.py +0 -239
  169. pyxlpr/ppocr/data/imaug/rec_img_aug.py +0 -533
  170. pyxlpr/ppocr/data/imaug/sast_process.py +0 -777
  171. pyxlpr/ppocr/data/imaug/text_image_aug/__init__.py +0 -17
  172. pyxlpr/ppocr/data/imaug/text_image_aug/augment.py +0 -120
  173. pyxlpr/ppocr/data/imaug/text_image_aug/warp_mls.py +0 -168
  174. pyxlpr/ppocr/data/lmdb_dataset.py +0 -115
  175. pyxlpr/ppocr/data/pgnet_dataset.py +0 -104
  176. pyxlpr/ppocr/data/pubtab_dataset.py +0 -107
  177. pyxlpr/ppocr/data/simple_dataset.py +0 -372
  178. pyxlpr/ppocr/losses/__init__.py +0 -61
  179. pyxlpr/ppocr/losses/ace_loss.py +0 -52
  180. pyxlpr/ppocr/losses/basic_loss.py +0 -135
  181. pyxlpr/ppocr/losses/center_loss.py +0 -88
  182. pyxlpr/ppocr/losses/cls_loss.py +0 -30
  183. pyxlpr/ppocr/losses/combined_loss.py +0 -67
  184. pyxlpr/ppocr/losses/det_basic_loss.py +0 -208
  185. pyxlpr/ppocr/losses/det_db_loss.py +0 -80
  186. pyxlpr/ppocr/losses/det_east_loss.py +0 -63
  187. pyxlpr/ppocr/losses/det_pse_loss.py +0 -149
  188. pyxlpr/ppocr/losses/det_sast_loss.py +0 -121
  189. pyxlpr/ppocr/losses/distillation_loss.py +0 -272
  190. pyxlpr/ppocr/losses/e2e_pg_loss.py +0 -140
  191. pyxlpr/ppocr/losses/kie_sdmgr_loss.py +0 -113
  192. pyxlpr/ppocr/losses/rec_aster_loss.py +0 -99
  193. pyxlpr/ppocr/losses/rec_att_loss.py +0 -39
  194. pyxlpr/ppocr/losses/rec_ctc_loss.py +0 -44
  195. pyxlpr/ppocr/losses/rec_enhanced_ctc_loss.py +0 -70
  196. pyxlpr/ppocr/losses/rec_nrtr_loss.py +0 -30
  197. pyxlpr/ppocr/losses/rec_sar_loss.py +0 -28
  198. pyxlpr/ppocr/losses/rec_srn_loss.py +0 -47
  199. pyxlpr/ppocr/losses/table_att_loss.py +0 -109
  200. pyxlpr/ppocr/metrics/__init__.py +0 -44
  201. pyxlpr/ppocr/metrics/cls_metric.py +0 -45
  202. pyxlpr/ppocr/metrics/det_metric.py +0 -82
  203. pyxlpr/ppocr/metrics/distillation_metric.py +0 -73
  204. pyxlpr/ppocr/metrics/e2e_metric.py +0 -86
  205. pyxlpr/ppocr/metrics/eval_det_iou.py +0 -274
  206. pyxlpr/ppocr/metrics/kie_metric.py +0 -70
  207. pyxlpr/ppocr/metrics/rec_metric.py +0 -75
  208. pyxlpr/ppocr/metrics/table_metric.py +0 -50
  209. pyxlpr/ppocr/modeling/architectures/__init__.py +0 -32
  210. pyxlpr/ppocr/modeling/architectures/base_model.py +0 -88
  211. pyxlpr/ppocr/modeling/architectures/distillation_model.py +0 -60
  212. pyxlpr/ppocr/modeling/backbones/__init__.py +0 -54
  213. pyxlpr/ppocr/modeling/backbones/det_mobilenet_v3.py +0 -268
  214. pyxlpr/ppocr/modeling/backbones/det_resnet_vd.py +0 -246
  215. pyxlpr/ppocr/modeling/backbones/det_resnet_vd_sast.py +0 -285
  216. pyxlpr/ppocr/modeling/backbones/e2e_resnet_vd_pg.py +0 -265
  217. pyxlpr/ppocr/modeling/backbones/kie_unet_sdmgr.py +0 -186
  218. pyxlpr/ppocr/modeling/backbones/rec_mobilenet_v3.py +0 -138
  219. pyxlpr/ppocr/modeling/backbones/rec_mv1_enhance.py +0 -258
  220. pyxlpr/ppocr/modeling/backbones/rec_nrtr_mtb.py +0 -48
  221. pyxlpr/ppocr/modeling/backbones/rec_resnet_31.py +0 -210
  222. pyxlpr/ppocr/modeling/backbones/rec_resnet_aster.py +0 -143
  223. pyxlpr/ppocr/modeling/backbones/rec_resnet_fpn.py +0 -307
  224. pyxlpr/ppocr/modeling/backbones/rec_resnet_vd.py +0 -286
  225. pyxlpr/ppocr/modeling/heads/__init__.py +0 -54
  226. pyxlpr/ppocr/modeling/heads/cls_head.py +0 -52
  227. pyxlpr/ppocr/modeling/heads/det_db_head.py +0 -118
  228. pyxlpr/ppocr/modeling/heads/det_east_head.py +0 -121
  229. pyxlpr/ppocr/modeling/heads/det_pse_head.py +0 -37
  230. pyxlpr/ppocr/modeling/heads/det_sast_head.py +0 -128
  231. pyxlpr/ppocr/modeling/heads/e2e_pg_head.py +0 -253
  232. pyxlpr/ppocr/modeling/heads/kie_sdmgr_head.py +0 -206
  233. pyxlpr/ppocr/modeling/heads/multiheadAttention.py +0 -163
  234. pyxlpr/ppocr/modeling/heads/rec_aster_head.py +0 -393
  235. pyxlpr/ppocr/modeling/heads/rec_att_head.py +0 -202
  236. pyxlpr/ppocr/modeling/heads/rec_ctc_head.py +0 -88
  237. pyxlpr/ppocr/modeling/heads/rec_nrtr_head.py +0 -826
  238. pyxlpr/ppocr/modeling/heads/rec_sar_head.py +0 -402
  239. pyxlpr/ppocr/modeling/heads/rec_srn_head.py +0 -280
  240. pyxlpr/ppocr/modeling/heads/self_attention.py +0 -406
  241. pyxlpr/ppocr/modeling/heads/table_att_head.py +0 -246
  242. pyxlpr/ppocr/modeling/necks/__init__.py +0 -32
  243. pyxlpr/ppocr/modeling/necks/db_fpn.py +0 -111
  244. pyxlpr/ppocr/modeling/necks/east_fpn.py +0 -188
  245. pyxlpr/ppocr/modeling/necks/fpn.py +0 -138
  246. pyxlpr/ppocr/modeling/necks/pg_fpn.py +0 -314
  247. pyxlpr/ppocr/modeling/necks/rnn.py +0 -92
  248. pyxlpr/ppocr/modeling/necks/sast_fpn.py +0 -284
  249. pyxlpr/ppocr/modeling/necks/table_fpn.py +0 -110
  250. pyxlpr/ppocr/modeling/transforms/__init__.py +0 -28
  251. pyxlpr/ppocr/modeling/transforms/stn.py +0 -135
  252. pyxlpr/ppocr/modeling/transforms/tps.py +0 -308
  253. pyxlpr/ppocr/modeling/transforms/tps_spatial_transformer.py +0 -156
  254. pyxlpr/ppocr/optimizer/__init__.py +0 -61
  255. pyxlpr/ppocr/optimizer/learning_rate.py +0 -228
  256. pyxlpr/ppocr/optimizer/lr_scheduler.py +0 -49
  257. pyxlpr/ppocr/optimizer/optimizer.py +0 -160
  258. pyxlpr/ppocr/optimizer/regularizer.py +0 -52
  259. pyxlpr/ppocr/postprocess/__init__.py +0 -55
  260. pyxlpr/ppocr/postprocess/cls_postprocess.py +0 -33
  261. pyxlpr/ppocr/postprocess/db_postprocess.py +0 -234
  262. pyxlpr/ppocr/postprocess/east_postprocess.py +0 -143
  263. pyxlpr/ppocr/postprocess/locality_aware_nms.py +0 -200
  264. pyxlpr/ppocr/postprocess/pg_postprocess.py +0 -52
  265. pyxlpr/ppocr/postprocess/pse_postprocess/__init__.py +0 -15
  266. pyxlpr/ppocr/postprocess/pse_postprocess/pse/__init__.py +0 -29
  267. pyxlpr/ppocr/postprocess/pse_postprocess/pse/setup.py +0 -14
  268. pyxlpr/ppocr/postprocess/pse_postprocess/pse_postprocess.py +0 -118
  269. pyxlpr/ppocr/postprocess/rec_postprocess.py +0 -654
  270. pyxlpr/ppocr/postprocess/sast_postprocess.py +0 -355
  271. pyxlpr/ppocr/tools/__init__.py +0 -14
  272. pyxlpr/ppocr/tools/eval.py +0 -83
  273. pyxlpr/ppocr/tools/export_center.py +0 -77
  274. pyxlpr/ppocr/tools/export_model.py +0 -129
  275. pyxlpr/ppocr/tools/infer/predict_cls.py +0 -151
  276. pyxlpr/ppocr/tools/infer/predict_det.py +0 -300
  277. pyxlpr/ppocr/tools/infer/predict_e2e.py +0 -169
  278. pyxlpr/ppocr/tools/infer/predict_rec.py +0 -414
  279. pyxlpr/ppocr/tools/infer/predict_system.py +0 -204
  280. pyxlpr/ppocr/tools/infer/utility.py +0 -629
  281. pyxlpr/ppocr/tools/infer_cls.py +0 -83
  282. pyxlpr/ppocr/tools/infer_det.py +0 -134
  283. pyxlpr/ppocr/tools/infer_e2e.py +0 -122
  284. pyxlpr/ppocr/tools/infer_kie.py +0 -153
  285. pyxlpr/ppocr/tools/infer_rec.py +0 -146
  286. pyxlpr/ppocr/tools/infer_table.py +0 -107
  287. pyxlpr/ppocr/tools/program.py +0 -596
  288. pyxlpr/ppocr/tools/test_hubserving.py +0 -117
  289. pyxlpr/ppocr/tools/train.py +0 -163
  290. pyxlpr/ppocr/tools/xlprog.py +0 -748
  291. pyxlpr/ppocr/utils/EN_symbol_dict.txt +0 -94
  292. pyxlpr/ppocr/utils/__init__.py +0 -24
  293. pyxlpr/ppocr/utils/dict/ar_dict.txt +0 -117
  294. pyxlpr/ppocr/utils/dict/arabic_dict.txt +0 -162
  295. pyxlpr/ppocr/utils/dict/be_dict.txt +0 -145
  296. pyxlpr/ppocr/utils/dict/bg_dict.txt +0 -140
  297. pyxlpr/ppocr/utils/dict/chinese_cht_dict.txt +0 -8421
  298. pyxlpr/ppocr/utils/dict/cyrillic_dict.txt +0 -163
  299. pyxlpr/ppocr/utils/dict/devanagari_dict.txt +0 -167
  300. pyxlpr/ppocr/utils/dict/en_dict.txt +0 -63
  301. pyxlpr/ppocr/utils/dict/fa_dict.txt +0 -136
  302. pyxlpr/ppocr/utils/dict/french_dict.txt +0 -136
  303. pyxlpr/ppocr/utils/dict/german_dict.txt +0 -143
  304. pyxlpr/ppocr/utils/dict/hi_dict.txt +0 -162
  305. pyxlpr/ppocr/utils/dict/it_dict.txt +0 -118
  306. pyxlpr/ppocr/utils/dict/japan_dict.txt +0 -4399
  307. pyxlpr/ppocr/utils/dict/ka_dict.txt +0 -153
  308. pyxlpr/ppocr/utils/dict/korean_dict.txt +0 -3688
  309. pyxlpr/ppocr/utils/dict/latin_dict.txt +0 -185
  310. pyxlpr/ppocr/utils/dict/mr_dict.txt +0 -153
  311. pyxlpr/ppocr/utils/dict/ne_dict.txt +0 -153
  312. pyxlpr/ppocr/utils/dict/oc_dict.txt +0 -96
  313. pyxlpr/ppocr/utils/dict/pu_dict.txt +0 -130
  314. pyxlpr/ppocr/utils/dict/rs_dict.txt +0 -91
  315. pyxlpr/ppocr/utils/dict/rsc_dict.txt +0 -134
  316. pyxlpr/ppocr/utils/dict/ru_dict.txt +0 -125
  317. pyxlpr/ppocr/utils/dict/ta_dict.txt +0 -128
  318. pyxlpr/ppocr/utils/dict/table_dict.txt +0 -277
  319. pyxlpr/ppocr/utils/dict/table_structure_dict.txt +0 -2759
  320. pyxlpr/ppocr/utils/dict/te_dict.txt +0 -151
  321. pyxlpr/ppocr/utils/dict/ug_dict.txt +0 -114
  322. pyxlpr/ppocr/utils/dict/uk_dict.txt +0 -142
  323. pyxlpr/ppocr/utils/dict/ur_dict.txt +0 -137
  324. pyxlpr/ppocr/utils/dict/xi_dict.txt +0 -110
  325. pyxlpr/ppocr/utils/dict90.txt +0 -90
  326. pyxlpr/ppocr/utils/e2e_metric/Deteval.py +0 -574
  327. pyxlpr/ppocr/utils/e2e_metric/polygon_fast.py +0 -83
  328. pyxlpr/ppocr/utils/e2e_utils/extract_batchsize.py +0 -87
  329. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_fast.py +0 -457
  330. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_slow.py +0 -592
  331. pyxlpr/ppocr/utils/e2e_utils/pgnet_pp_utils.py +0 -162
  332. pyxlpr/ppocr/utils/e2e_utils/visual.py +0 -162
  333. pyxlpr/ppocr/utils/en_dict.txt +0 -95
  334. pyxlpr/ppocr/utils/gen_label.py +0 -81
  335. pyxlpr/ppocr/utils/ic15_dict.txt +0 -36
  336. pyxlpr/ppocr/utils/iou.py +0 -54
  337. pyxlpr/ppocr/utils/logging.py +0 -69
  338. pyxlpr/ppocr/utils/network.py +0 -84
  339. pyxlpr/ppocr/utils/ppocr_keys_v1.txt +0 -6623
  340. pyxlpr/ppocr/utils/profiler.py +0 -110
  341. pyxlpr/ppocr/utils/save_load.py +0 -150
  342. pyxlpr/ppocr/utils/stats.py +0 -72
  343. pyxlpr/ppocr/utils/utility.py +0 -80
  344. pyxlpr/ppstructure/__init__.py +0 -13
  345. pyxlpr/ppstructure/predict_system.py +0 -187
  346. pyxlpr/ppstructure/table/__init__.py +0 -13
  347. pyxlpr/ppstructure/table/eval_table.py +0 -72
  348. pyxlpr/ppstructure/table/matcher.py +0 -192
  349. pyxlpr/ppstructure/table/predict_structure.py +0 -136
  350. pyxlpr/ppstructure/table/predict_table.py +0 -221
  351. pyxlpr/ppstructure/table/table_metric/__init__.py +0 -16
  352. pyxlpr/ppstructure/table/table_metric/parallel.py +0 -51
  353. pyxlpr/ppstructure/table/table_metric/table_metric.py +0 -247
  354. pyxlpr/ppstructure/table/tablepyxl/__init__.py +0 -13
  355. pyxlpr/ppstructure/table/tablepyxl/style.py +0 -283
  356. pyxlpr/ppstructure/table/tablepyxl/tablepyxl.py +0 -118
  357. pyxlpr/ppstructure/utility.py +0 -71
  358. pyxlpr/xlai.py +0 -10
pyxllib/ext/qt.py CHANGED
@@ -1,449 +1,449 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # @Author : 陈坤泽
4
- # @Email : 877362867@qq.com
5
- # @Date : 2021/05/26 17:24
6
-
7
- import os.path as osp
8
- import sys
9
- import time
10
- from datetime import datetime, timedelta
11
-
12
- from PyQt5 import QtWidgets
13
- from PyQt5.QtCore import QTimer, Qt, QThread, pyqtSignal, QEventLoop
14
- from PyQt5.QtWidgets import QApplication, QMainWindow, QFrame, QInputDialog, QMessageBox, QVBoxLayout, \
15
- QTextEdit, QSizePolicy, QLabel, QProgressBar, QDialog
16
- from PyQt5.QtGui import QTextOption
17
-
18
- from pyxllib.prog.newbie import CvtType
19
-
20
- here = osp.dirname(osp.abspath(__file__))
21
-
22
-
23
- class QHLine(QFrame):
24
- """ https://stackoverflow.com/questions/5671354/how-to-programmatically-make-a-horizontal-line-in-qt """
25
-
26
- def __init__(self):
27
- super(QHLine, self).__init__()
28
- self.setFrameShape(QFrame.HLine)
29
- self.setFrameShadow(QFrame.Sunken)
30
-
31
-
32
- class QVLine(QFrame):
33
- def __init__(self):
34
- super(QVLine, self).__init__()
35
- self.setFrameShape(QFrame.VLine)
36
- self.setFrameShadow(QFrame.Sunken)
37
-
38
-
39
- class XlLineEdit(QtWidgets.QLineEdit):
40
- correctChanged = pyqtSignal(object) # 符合数值类型的修改
41
- wrongChanged = pyqtSignal(str) # 不符合数值类型的修改
42
-
43
- def __init__(self, text=None, parent=None, *, valcvt=None):
44
- """
45
- :param valcvt: 数值类型转换器
46
- """
47
- super().__init__(str(text), parent)
48
-
49
- def check():
50
- # TODO 目前是强制重置样式,可以考虑怎么保留原样式基础上修改属性值
51
- s = self.text()
52
- try:
53
- if valcvt:
54
- s = valcvt(s)
55
- self.setStyleSheet('')
56
- self.setToolTip('')
57
- self.correctChanged.emit(s)
58
- except ValueError:
59
- self.setStyleSheet('background-color: lightpink;')
60
- self.setToolTip(f'输入数据不是{valcvt}类型')
61
- self.wrongChanged.emit(s)
62
-
63
- self.textChanged.connect(check)
64
- if text:
65
- check()
66
-
67
- # self.setStyleSheet(self.styleSheet() + 'qproperty-cursorPosition: 0;')
68
- self.setStyleSheet(self.styleSheet())
69
-
70
-
71
- class XlComboBox(QtWidgets.QComboBox):
72
- # 这个控件一般没有类型检查,但在支持填入自定义值时,是存在类型错误问题的
73
- # 但在工程上还是依然写了 correctChanged,方便下游任务统一接口
74
- correctChanged = pyqtSignal(object) # 符合数值类型的修改
75
- wrongChanged = pyqtSignal(str) # 不符合数值类型的修改
76
-
77
- def __init__(self, parent=None, *, text=None, items=None, valcvt=None, editable=False):
78
- """
79
- """
80
- # 1 基础设置
81
- super().__init__(parent)
82
- self.reset_items(items)
83
- self.editable = editable
84
- if self.editable:
85
- self.setEditable(True)
86
-
87
- # 2 检查功能
88
- def check(s):
89
- try:
90
- if valcvt:
91
- s = valcvt(s)
92
-
93
- if self.editable: # 支持自定义值
94
- self.setStyleSheet('')
95
- self.setToolTip('')
96
- self.correctChanged.emit(s)
97
- elif s not in self.items_set: # 不支持自定义值,但出现自定义值
98
- self.setStyleSheet('background-color: yellow;')
99
- self.setToolTip(f'不在清单里的非法值')
100
- self.wrongChanged.emit(s)
101
- else: # 不支持自定义值,且目前值在清单中
102
- self.setEditable(False)
103
- self.setStyleSheet('')
104
- self.setToolTip('')
105
- self.correctChanged.emit(s)
106
- except ValueError:
107
- self.setStyleSheet('background-color: lightpink;')
108
- self.setToolTip(f'输入数据不是{valcvt}类型')
109
- self.wrongChanged.emit(s)
110
-
111
- self.currentTextChanged.connect(check)
112
- # self.wrongChanged.connect(lambda s: print('非法值:', s)) # 可以监控非法值
113
-
114
- # 3 是否有预设值
115
- if text:
116
- self.setText(text)
117
-
118
- # 4 补充格式
119
- # self.setStyleSheet(self.styleSheet() + 'qproperty-cursorPosition: 0;')
120
- self.setStyleSheet(self.styleSheet())
121
-
122
- def setText(self, text):
123
- text = str(text)
124
- if text not in self.items and not self.editable:
125
- # 虽然不支持editable,但是出现了意外值,需要强制升级为可编辑
126
- self.setEditable(True)
127
- self.setCurrentText(text)
128
-
129
- def reset_items(self, items):
130
- # 1 存储配置清单
131
- self.clear()
132
- self.raw_items = items # noqa
133
- self.items = [str(x) for x in items if x is not None] # noqa
134
- self.items_set = set(self.items) # noqa 便于判断是否存在的集合类型
135
- self.addItems(self.items)
136
-
137
- # 2 画出 元素值、分隔符
138
- cnt = 0
139
- for i in range(len(items)):
140
- if items[i] is None:
141
- self.insertSeparator(i - cnt) # 不过这个分割符没那么显眼
142
- cnt += 1
143
-
144
-
145
- def get_input_widget(items=None, cur_value=None, *, parent=None, valcvt=None,
146
- n_widget=1, enabled=True,
147
- correct_changed=None):
148
- """ 根据items参数情况,智能判断生成对应的widget
149
-
150
- :param items:
151
- None, 普通的文本编辑框
152
- 普通数组,下拉框 (可以用None元素表示分隔符)
153
- list,除了列表中枚举值,也支持自定义输入其他值
154
- tuple,只能用列表中的枚举值
155
- 多级嵌套数组,多级下拉框 (未实装) (一般都是不可改的,并且同类型的数据)
156
- [('福建', [('龙岩', ['连城', '长汀', ...], ...)]), ('北京', ...)]
157
- 这种情况会返回多个widget
158
- :param cur_value: 当前显示的文本值
159
- :param valcvt: 数值类型转换函数,非法时抛出ValueError
160
- 很多输入框是传入文本,有时需要转为int、float、list等类型
161
- 支持输入常见类型转换的字符串名称,比如int、float
162
- :param correct_changed: 文本改变时的回调函数,一般用于数值有效性检查
163
- :param n_widget: 配合items为嵌套数组使用,需要指定嵌套层数
164
- 此时cur_value、cvt、enabled、text_changed等系列值可以传入n_widget长度的list
165
- :param enabled: 是否可编辑
166
- """
167
- if n_widget > 1:
168
- raise NotImplementedError
169
-
170
- # 1 封装类型检查功能
171
- if isinstance(valcvt, str):
172
- cvtfunc = CvtType.factory(valcvt)
173
- else:
174
- cvtfunc = valcvt
175
-
176
- # 2 正式生成控件
177
- if isinstance(items, (list, tuple)):
178
- # 带有 items 的字段支持候选下拉菜单
179
- w = XlComboBox(parent, text=cur_value, items=items, valcvt=cvtfunc, editable=isinstance(items, list))
180
- elif items is None:
181
- # 普通填充框
182
- w = XlLineEdit(cur_value, parent=parent, valcvt=cvtfunc)
183
- else:
184
- raise ValueError(f'{type(items)}')
185
-
186
- # 3 通用配置
187
- if callable(correct_changed):
188
- w.correctChanged.connect(correct_changed)
189
- if not enabled:
190
- w.setEnabled(enabled)
191
-
192
- return w
193
-
194
-
195
- def __other():
196
- pass
197
-
198
-
199
- def main_qapp(window):
200
- """ 执行Qt应用 """
201
- app = QApplication(sys.argv)
202
- window.show() # 展示窗口
203
- sys.exit(app.exec_())
204
-
205
-
206
- def qt_clipboard_monitor(func=None, verbose=1, *, cooldown=0.5):
207
- """ qt实现的剪切板监控器
208
-
209
- :param cooldown: cd,冷切时间,防止短时间内因为重复操作响应剪切板,重复执行功能
210
-
211
- 感觉这个组件还有很多可以扩展的,比如设置可以退出的快捷键
212
- """
213
- import pyperclip
214
-
215
- last_response = time.time()
216
-
217
- if func is None:
218
- func = lambda s: s
219
-
220
- def on_clipboard_change():
221
- # 1 数据内容一样则跳过不处理,表示很可能是该函数调用pyperclip.copy(s)产生的重复响应
222
- nonlocal last_response
223
- s0 = pyperclip.paste()
224
- s0 = s0.replace('\r\n', '\n')
225
-
226
- cur_time = time.time()
227
-
228
- if cur_time - last_response < cooldown:
229
- return
230
- last_response = cur_time
231
-
232
- # 2 处理函数
233
- s1 = func(s0)
234
- if s1 != s0:
235
- if verbose:
236
- print('【处理前】', time.strftime('%H:%M:%S'))
237
- print(s0)
238
- print('【处理后】')
239
- print(s1)
240
- print()
241
- pyperclip.copy(s1)
242
-
243
- app = QApplication([])
244
- clipboard = app.clipboard()
245
- clipboard.dataChanged.connect(on_clipboard_change)
246
- app.exec_()
247
-
248
-
249
- class XlThreadWorker(QThread):
250
- result = pyqtSignal(object) # 运行结果信号
251
- error = pyqtSignal(Exception) # 错误信号
252
- progress = pyqtSignal(int) # 进度信号
253
-
254
- def __init__(self, func, *args, use_progress=False, **kwargs):
255
- super().__init__()
256
- self.func = func
257
- self.args = args
258
- self.kwargs = kwargs
259
- if use_progress:
260
- self.kwargs['progress_callback'] = lambda v: self.progress.emit(v)
261
-
262
- def run(self):
263
- try:
264
- result = self.func(*self.args, **self.kwargs)
265
- self.result.emit(result) # Emit the result when done
266
- except Exception as e:
267
- self.error.emit(e)
268
-
269
-
270
- class WaitDialog(QDialog):
271
-
272
- def __init__(self, parent=None, text='', title='正在执行任务...', delay_seconds=5):
273
- super().__init__(parent)
274
- self.base_text = text
275
- self.setWindowTitle(title)
276
-
277
- self.timer = QTimer()
278
- self.timer.timeout.connect(self.update_text)
279
- self.start_time = None
280
- self.result = None
281
- self.worker = None
282
- self.error = None
283
-
284
- self.delay_milliseconds = delay_seconds * 1000 # 延迟弹窗
285
- self.is_running = False
286
-
287
- self.layout = QVBoxLayout() # 布局
288
- self.label = QLabel(self.base_text) # 标签
289
- self.layout.addWidget(self.label)
290
- self.pbar = QProgressBar() # 进度条
291
- self.layout.addWidget(self.pbar)
292
- self.setLayout(self.layout)
293
-
294
- def handle_result(self, result):
295
- self.result = result
296
- self.is_running = False
297
-
298
- def handle_error(self, error):
299
- self.error = error
300
- self.label.setText(f"{self.base_text}\n运行出现错误: {error}")
301
- self.is_running = False
302
-
303
- def handle_progress(self, progress):
304
- self.pbar.setValue(progress)
305
-
306
- def update_text(self):
307
- elapsed_time = int((datetime.now() - self.start_time).total_seconds()) + self.delay_milliseconds // 1000
308
- self.label.setText(f"{self.base_text}\n已运行 {elapsed_time} 秒")
309
-
310
- def run(self, func, *args, **kwargs):
311
- """
312
- def func():
313
- ... # 程序功能
314
-
315
- msg = WaitDialog().run(func) # 开一个等待窗口等程序运行
316
- """
317
- self.worker = XlThreadWorker(func, *args, **kwargs)
318
- self.worker.result.connect(self.handle_result)
319
- self.worker.error.connect(self.handle_error)
320
- self.is_running = True
321
- self.worker.start()
322
-
323
- QTimer.singleShot(self.delay_milliseconds, self.check_and_show)
324
-
325
- # 阻塞主线程,直到子线程完成
326
- while self.is_running:
327
- QApplication.processEvents() # 刷新UI,保持其响应性
328
- time.sleep(0.1) # 等待一段时间,以减少CPU使用率
329
-
330
- self.timer.stop()
331
- self.accept()
332
-
333
- return self.result
334
-
335
- def run_with_progress(self, func, *args, **kwargs):
336
- """
337
- def func(progress_callback):
338
- progress_callback(50) # 可以在运行中设置进度,进度值为0~100
339
- ... # 其他功能
340
-
341
- msg = WaitDialog().run_with_progress(func) # 运行完获得返回值
342
- """
343
- self.worker = XlThreadWorker(func, *args, use_progress=True, **kwargs)
344
- self.worker.result.connect(self.handle_result)
345
- self.worker.error.connect(self.handle_error)
346
- self.worker.progress.connect(self.handle_progress)
347
- self.is_running = True
348
- self.worker.start()
349
-
350
- QTimer.singleShot(self.delay_milliseconds, self.check_and_show)
351
-
352
- # 阻塞主线程,直到子线程完成
353
- while self.is_running:
354
- QApplication.processEvents() # 刷新UI,保持其响应性
355
- time.sleep(0.1) # 等待一段时间,以减少CPU使用率
356
-
357
- self.timer.stop()
358
- self.accept()
359
-
360
- return self.result
361
-
362
- def start_timer(self):
363
- self.start_time = datetime.now()
364
- self.timer.start(1000)
365
-
366
- def check_and_show(self):
367
- if self.is_running:
368
- self.show()
369
- self.start_timer()
370
-
371
- def __enter__(self):
372
- """ with写法比较简洁,但不太推荐这种使用方法,这样并不工程化
373
- 这样会把要运行的功能变成主线程,这个提示窗口会被挂起
374
-
375
- 这里功能设计上也比较简单些,不考虑写的很完善强大了。
376
- """
377
- self.show()
378
- QApplication.processEvents()
379
- return self
380
-
381
- def __exit__(self, exc_type, exc_val, exc_tb):
382
- self.accept()
383
-
384
-
385
- class CustomMessageBox(QMessageBox):
386
- def __init__(self, icon, title, text, copyable):
387
- super().__init__(icon, title, "")
388
- self.init_ui(title, text, copyable)
389
-
390
- def init_ui(self, title, text, copyable=False):
391
- layout = QVBoxLayout()
392
-
393
- if copyable:
394
- widget = QTextEdit()
395
- widget.setText(text)
396
- widget.setReadOnly(True)
397
- widget.setWordWrapMode(QTextOption.WrapAnywhere)
398
- widget.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
399
- widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
400
- widget.document().documentLayout().documentSizeChanged.connect(
401
- lambda: widget.setMinimumHeight(min(widget.document().size().height(), 700))
402
- )
403
- widget.setMinimumHeight(100)
404
- widget.setMaximumHeight(700)
405
- else:
406
- widget = QLabel()
407
- widget.setText(text)
408
- widget.setWordWrap(True)
409
-
410
- min_width = max(len(title) * 15, 600)
411
- widget.setMinimumWidth(min_width)
412
-
413
- layout.addWidget(widget)
414
- self.layout().addLayout(layout, 1, 1)
415
-
416
-
417
- def show_message_box(text, title=None, icon=None, detail=None,
418
- buttons=QMessageBox.Ok | QMessageBox.Cancel,
419
- default_button=QMessageBox.Ok, copyable=False):
420
- """ 显示一个提示框
421
-
422
- :param text: 提示框的文本内容
423
- :param title: 提示框的标题,默认值为 "提示"
424
- :param icon: 提示框的图标,默认值为 QMessageBox.NoIcon
425
- 注意Information、Warning、Critical等都会附带一个提示音
426
- 而Question是不带提示音的,默认的NoIcon也是不带提示音的
427
- :param detail: 提示框的详细信息,默认值为 None
428
- :param buttons: 提示框的按钮,默认值为 QMessageBox.Ok
429
- :param copyable: 消息窗中的文本是否可复制
430
-
431
- :return: 选择的按钮
432
-
433
- 实现上,本来应该依据setMinimumWidth可以搞定的事,但不知道为什么就是会有bug问题,总之最后问gpt靠实现一个类来解决了
434
-
435
- """
436
- if title is None:
437
- title = "提示"
438
-
439
- if icon is None:
440
- icon = QMessageBox.NoIcon
441
-
442
- msg_box = CustomMessageBox(icon, title, text, copyable)
443
-
444
- if detail is not None:
445
- msg_box.setDetailedText(detail)
446
- msg_box.setStandardButtons(buttons)
447
- msg_box.setDefaultButton(default_button)
448
-
449
- return msg_box.exec_()
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # @Author : 陈坤泽
4
+ # @Email : 877362867@qq.com
5
+ # @Date : 2021/05/26 17:24
6
+
7
+ import os.path as osp
8
+ import sys
9
+ import time
10
+ from datetime import datetime, timedelta
11
+
12
+ from PyQt5 import QtWidgets
13
+ from PyQt5.QtCore import QTimer, Qt, QThread, pyqtSignal, QEventLoop
14
+ from PyQt5.QtWidgets import QApplication, QMainWindow, QFrame, QInputDialog, QMessageBox, QVBoxLayout, \
15
+ QTextEdit, QSizePolicy, QLabel, QProgressBar, QDialog
16
+ from PyQt5.QtGui import QTextOption
17
+
18
+ from pyxllib.prog.newbie import CvtType
19
+
20
+ here = osp.dirname(osp.abspath(__file__))
21
+
22
+
23
+ class QHLine(QFrame):
24
+ """ https://stackoverflow.com/questions/5671354/how-to-programmatically-make-a-horizontal-line-in-qt """
25
+
26
+ def __init__(self):
27
+ super(QHLine, self).__init__()
28
+ self.setFrameShape(QFrame.HLine)
29
+ self.setFrameShadow(QFrame.Sunken)
30
+
31
+
32
+ class QVLine(QFrame):
33
+ def __init__(self):
34
+ super(QVLine, self).__init__()
35
+ self.setFrameShape(QFrame.VLine)
36
+ self.setFrameShadow(QFrame.Sunken)
37
+
38
+
39
+ class XlLineEdit(QtWidgets.QLineEdit):
40
+ correctChanged = pyqtSignal(object) # 符合数值类型的修改
41
+ wrongChanged = pyqtSignal(str) # 不符合数值类型的修改
42
+
43
+ def __init__(self, text=None, parent=None, *, valcvt=None):
44
+ """
45
+ :param valcvt: 数值类型转换器
46
+ """
47
+ super().__init__(str(text), parent)
48
+
49
+ def check():
50
+ # TODO 目前是强制重置样式,可以考虑怎么保留原样式基础上修改属性值
51
+ s = self.text()
52
+ try:
53
+ if valcvt:
54
+ s = valcvt(s)
55
+ self.setStyleSheet('')
56
+ self.setToolTip('')
57
+ self.correctChanged.emit(s)
58
+ except ValueError:
59
+ self.setStyleSheet('background-color: lightpink;')
60
+ self.setToolTip(f'输入数据不是{valcvt}类型')
61
+ self.wrongChanged.emit(s)
62
+
63
+ self.textChanged.connect(check)
64
+ if text:
65
+ check()
66
+
67
+ # self.setStyleSheet(self.styleSheet() + 'qproperty-cursorPosition: 0;')
68
+ self.setStyleSheet(self.styleSheet())
69
+
70
+
71
+ class XlComboBox(QtWidgets.QComboBox):
72
+ # 这个控件一般没有类型检查,但在支持填入自定义值时,是存在类型错误问题的
73
+ # 但在工程上还是依然写了 correctChanged,方便下游任务统一接口
74
+ correctChanged = pyqtSignal(object) # 符合数值类型的修改
75
+ wrongChanged = pyqtSignal(str) # 不符合数值类型的修改
76
+
77
+ def __init__(self, parent=None, *, text=None, items=None, valcvt=None, editable=False):
78
+ """
79
+ """
80
+ # 1 基础设置
81
+ super().__init__(parent)
82
+ self.reset_items(items)
83
+ self.editable = editable
84
+ if self.editable:
85
+ self.setEditable(True)
86
+
87
+ # 2 检查功能
88
+ def check(s):
89
+ try:
90
+ if valcvt:
91
+ s = valcvt(s)
92
+
93
+ if self.editable: # 支持自定义值
94
+ self.setStyleSheet('')
95
+ self.setToolTip('')
96
+ self.correctChanged.emit(s)
97
+ elif s not in self.items_set: # 不支持自定义值,但出现自定义值
98
+ self.setStyleSheet('background-color: yellow;')
99
+ self.setToolTip(f'不在清单里的非法值')
100
+ self.wrongChanged.emit(s)
101
+ else: # 不支持自定义值,且目前值在清单中
102
+ self.setEditable(False)
103
+ self.setStyleSheet('')
104
+ self.setToolTip('')
105
+ self.correctChanged.emit(s)
106
+ except ValueError:
107
+ self.setStyleSheet('background-color: lightpink;')
108
+ self.setToolTip(f'输入数据不是{valcvt}类型')
109
+ self.wrongChanged.emit(s)
110
+
111
+ self.currentTextChanged.connect(check)
112
+ # self.wrongChanged.connect(lambda s: print('非法值:', s)) # 可以监控非法值
113
+
114
+ # 3 是否有预设值
115
+ if text:
116
+ self.setText(text)
117
+
118
+ # 4 补充格式
119
+ # self.setStyleSheet(self.styleSheet() + 'qproperty-cursorPosition: 0;')
120
+ self.setStyleSheet(self.styleSheet())
121
+
122
+ def setText(self, text):
123
+ text = str(text)
124
+ if text not in self.items and not self.editable:
125
+ # 虽然不支持editable,但是出现了意外值,需要强制升级为可编辑
126
+ self.setEditable(True)
127
+ self.setCurrentText(text)
128
+
129
+ def reset_items(self, items):
130
+ # 1 存储配置清单
131
+ self.clear()
132
+ self.raw_items = items # noqa
133
+ self.items = [str(x) for x in items if x is not None] # noqa
134
+ self.items_set = set(self.items) # noqa 便于判断是否存在的集合类型
135
+ self.addItems(self.items)
136
+
137
+ # 2 画出 元素值、分隔符
138
+ cnt = 0
139
+ for i in range(len(items)):
140
+ if items[i] is None:
141
+ self.insertSeparator(i - cnt) # 不过这个分割符没那么显眼
142
+ cnt += 1
143
+
144
+
145
+ def get_input_widget(items=None, cur_value=None, *, parent=None, valcvt=None,
146
+ n_widget=1, enabled=True,
147
+ correct_changed=None):
148
+ """ 根据items参数情况,智能判断生成对应的widget
149
+
150
+ :param items:
151
+ None, 普通的文本编辑框
152
+ 普通数组,下拉框 (可以用None元素表示分隔符)
153
+ list,除了列表中枚举值,也支持自定义输入其他值
154
+ tuple,只能用列表中的枚举值
155
+ 多级嵌套数组,多级下拉框 (未实装) (一般都是不可改的,并且同类型的数据)
156
+ [('福建', [('龙岩', ['连城', '长汀', ...], ...)]), ('北京', ...)]
157
+ 这种情况会返回多个widget
158
+ :param cur_value: 当前显示的文本值
159
+ :param valcvt: 数值类型转换函数,非法时抛出ValueError
160
+ 很多输入框是传入文本,有时需要转为int、float、list等类型
161
+ 支持输入常见类型转换的字符串名称,比如int、float
162
+ :param correct_changed: 文本改变时的回调函数,一般用于数值有效性检查
163
+ :param n_widget: 配合items为嵌套数组使用,需要指定嵌套层数
164
+ 此时cur_value、cvt、enabled、text_changed等系列值可以传入n_widget长度的list
165
+ :param enabled: 是否可编辑
166
+ """
167
+ if n_widget > 1:
168
+ raise NotImplementedError
169
+
170
+ # 1 封装类型检查功能
171
+ if isinstance(valcvt, str):
172
+ cvtfunc = CvtType.factory(valcvt)
173
+ else:
174
+ cvtfunc = valcvt
175
+
176
+ # 2 正式生成控件
177
+ if isinstance(items, (list, tuple)):
178
+ # 带有 items 的字段支持候选下拉菜单
179
+ w = XlComboBox(parent, text=cur_value, items=items, valcvt=cvtfunc, editable=isinstance(items, list))
180
+ elif items is None:
181
+ # 普通填充框
182
+ w = XlLineEdit(cur_value, parent=parent, valcvt=cvtfunc)
183
+ else:
184
+ raise ValueError(f'{type(items)}')
185
+
186
+ # 3 通用配置
187
+ if callable(correct_changed):
188
+ w.correctChanged.connect(correct_changed)
189
+ if not enabled:
190
+ w.setEnabled(enabled)
191
+
192
+ return w
193
+
194
+
195
+ def __other():
196
+ pass
197
+
198
+
199
+ def main_qapp(window):
200
+ """ 执行Qt应用 """
201
+ app = QApplication(sys.argv)
202
+ window.show() # 展示窗口
203
+ sys.exit(app.exec_())
204
+
205
+
206
+ def qt_clipboard_monitor(func=None, verbose=1, *, cooldown=0.5):
207
+ """ qt实现的剪切板监控器
208
+
209
+ :param cooldown: cd,冷切时间,防止短时间内因为重复操作响应剪切板,重复执行功能
210
+
211
+ 感觉这个组件还有很多可以扩展的,比如设置可以退出的快捷键
212
+ """
213
+ import pyperclip
214
+
215
+ last_response = time.time()
216
+
217
+ if func is None:
218
+ func = lambda s: s
219
+
220
+ def on_clipboard_change():
221
+ # 1 数据内容一样则跳过不处理,表示很可能是该函数调用pyperclip.copy(s)产生的重复响应
222
+ nonlocal last_response
223
+ s0 = pyperclip.paste()
224
+ s0 = s0.replace('\r\n', '\n')
225
+
226
+ cur_time = time.time()
227
+
228
+ if cur_time - last_response < cooldown:
229
+ return
230
+ last_response = cur_time
231
+
232
+ # 2 处理函数
233
+ s1 = func(s0)
234
+ if s1 != s0:
235
+ if verbose:
236
+ print('【处理前】', time.strftime('%H:%M:%S'))
237
+ print(s0)
238
+ print('【处理后】')
239
+ print(s1)
240
+ print()
241
+ pyperclip.copy(s1)
242
+
243
+ app = QApplication([])
244
+ clipboard = app.clipboard()
245
+ clipboard.dataChanged.connect(on_clipboard_change)
246
+ app.exec_()
247
+
248
+
249
+ class XlThreadWorker(QThread):
250
+ result = pyqtSignal(object) # 运行结果信号
251
+ error = pyqtSignal(Exception) # 错误信号
252
+ progress = pyqtSignal(int) # 进度信号
253
+
254
+ def __init__(self, func, *args, use_progress=False, **kwargs):
255
+ super().__init__()
256
+ self.func = func
257
+ self.args = args
258
+ self.kwargs = kwargs
259
+ if use_progress:
260
+ self.kwargs['progress_callback'] = lambda v: self.progress.emit(v)
261
+
262
+ def run(self):
263
+ try:
264
+ result = self.func(*self.args, **self.kwargs)
265
+ self.result.emit(result) # Emit the result when done
266
+ except Exception as e:
267
+ self.error.emit(e)
268
+
269
+
270
+ class WaitDialog(QDialog):
271
+
272
+ def __init__(self, parent=None, text='', title='正在执行任务...', delay_seconds=5):
273
+ super().__init__(parent)
274
+ self.base_text = text
275
+ self.setWindowTitle(title)
276
+
277
+ self.timer = QTimer()
278
+ self.timer.timeout.connect(self.update_text)
279
+ self.start_time = None
280
+ self.result = None
281
+ self.worker = None
282
+ self.error = None
283
+
284
+ self.delay_milliseconds = delay_seconds * 1000 # 延迟弹窗
285
+ self.is_running = False
286
+
287
+ self.layout = QVBoxLayout() # 布局
288
+ self.label = QLabel(self.base_text) # 标签
289
+ self.layout.addWidget(self.label)
290
+ self.pbar = QProgressBar() # 进度条
291
+ self.layout.addWidget(self.pbar)
292
+ self.setLayout(self.layout)
293
+
294
+ def handle_result(self, result):
295
+ self.result = result
296
+ self.is_running = False
297
+
298
+ def handle_error(self, error):
299
+ self.error = error
300
+ self.label.setText(f"{self.base_text}\n运行出现错误: {error}")
301
+ self.is_running = False
302
+
303
+ def handle_progress(self, progress):
304
+ self.pbar.setValue(progress)
305
+
306
+ def update_text(self):
307
+ elapsed_time = int((datetime.now() - self.start_time).total_seconds()) + self.delay_milliseconds // 1000
308
+ self.label.setText(f"{self.base_text}\n已运行 {elapsed_time} 秒")
309
+
310
+ def run(self, func, *args, **kwargs):
311
+ """
312
+ def func():
313
+ ... # 程序功能
314
+
315
+ msg = WaitDialog().run(func) # 开一个等待窗口等程序运行
316
+ """
317
+ self.worker = XlThreadWorker(func, *args, **kwargs)
318
+ self.worker.result.connect(self.handle_result)
319
+ self.worker.error.connect(self.handle_error)
320
+ self.is_running = True
321
+ self.worker.start()
322
+
323
+ QTimer.singleShot(self.delay_milliseconds, self.check_and_show)
324
+
325
+ # 阻塞主线程,直到子线程完成
326
+ while self.is_running:
327
+ QApplication.processEvents() # 刷新UI,保持其响应性
328
+ time.sleep(0.1) # 等待一段时间,以减少CPU使用率
329
+
330
+ self.timer.stop()
331
+ self.accept()
332
+
333
+ return self.result
334
+
335
+ def run_with_progress(self, func, *args, **kwargs):
336
+ """
337
+ def func(progress_callback):
338
+ progress_callback(50) # 可以在运行中设置进度,进度值为0~100
339
+ ... # 其他功能
340
+
341
+ msg = WaitDialog().run_with_progress(func) # 运行完获得返回值
342
+ """
343
+ self.worker = XlThreadWorker(func, *args, use_progress=True, **kwargs)
344
+ self.worker.result.connect(self.handle_result)
345
+ self.worker.error.connect(self.handle_error)
346
+ self.worker.progress.connect(self.handle_progress)
347
+ self.is_running = True
348
+ self.worker.start()
349
+
350
+ QTimer.singleShot(self.delay_milliseconds, self.check_and_show)
351
+
352
+ # 阻塞主线程,直到子线程完成
353
+ while self.is_running:
354
+ QApplication.processEvents() # 刷新UI,保持其响应性
355
+ time.sleep(0.1) # 等待一段时间,以减少CPU使用率
356
+
357
+ self.timer.stop()
358
+ self.accept()
359
+
360
+ return self.result
361
+
362
+ def start_timer(self):
363
+ self.start_time = datetime.now()
364
+ self.timer.start(1000)
365
+
366
+ def check_and_show(self):
367
+ if self.is_running:
368
+ self.show()
369
+ self.start_timer()
370
+
371
+ def __enter__(self):
372
+ """ with写法比较简洁,但不太推荐这种使用方法,这样并不工程化
373
+ 这样会把要运行的功能变成主线程,这个提示窗口会被挂起
374
+
375
+ 这里功能设计上也比较简单些,不考虑写的很完善强大了。
376
+ """
377
+ self.show()
378
+ QApplication.processEvents()
379
+ return self
380
+
381
+ def __exit__(self, exc_type, exc_val, exc_tb):
382
+ self.accept()
383
+
384
+
385
+ class CustomMessageBox(QMessageBox):
386
+ def __init__(self, icon, title, text, copyable):
387
+ super().__init__(icon, title, "")
388
+ self.init_ui(title, text, copyable)
389
+
390
+ def init_ui(self, title, text, copyable=False):
391
+ layout = QVBoxLayout()
392
+
393
+ if copyable:
394
+ widget = QTextEdit()
395
+ widget.setText(text)
396
+ widget.setReadOnly(True)
397
+ widget.setWordWrapMode(QTextOption.WrapAnywhere)
398
+ widget.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
399
+ widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
400
+ widget.document().documentLayout().documentSizeChanged.connect(
401
+ lambda: widget.setMinimumHeight(min(widget.document().size().height(), 700))
402
+ )
403
+ widget.setMinimumHeight(100)
404
+ widget.setMaximumHeight(700)
405
+ else:
406
+ widget = QLabel()
407
+ widget.setText(text)
408
+ widget.setWordWrap(True)
409
+
410
+ min_width = max(len(title) * 15, 600)
411
+ widget.setMinimumWidth(min_width)
412
+
413
+ layout.addWidget(widget)
414
+ self.layout().addLayout(layout, 1, 1)
415
+
416
+
417
+ def show_message_box(text, title=None, icon=None, detail=None,
418
+ buttons=QMessageBox.Ok | QMessageBox.Cancel,
419
+ default_button=QMessageBox.Ok, copyable=False):
420
+ """ 显示一个提示框
421
+
422
+ :param text: 提示框的文本内容
423
+ :param title: 提示框的标题,默认值为 "提示"
424
+ :param icon: 提示框的图标,默认值为 QMessageBox.NoIcon
425
+ 注意Information、Warning、Critical等都会附带一个提示音
426
+ 而Question是不带提示音的,默认的NoIcon也是不带提示音的
427
+ :param detail: 提示框的详细信息,默认值为 None
428
+ :param buttons: 提示框的按钮,默认值为 QMessageBox.Ok
429
+ :param copyable: 消息窗中的文本是否可复制
430
+
431
+ :return: 选择的按钮
432
+
433
+ 实现上,本来应该依据setMinimumWidth可以搞定的事,但不知道为什么就是会有bug问题,总之最后问gpt靠实现一个类来解决了
434
+
435
+ """
436
+ if title is None:
437
+ title = "提示"
438
+
439
+ if icon is None:
440
+ icon = QMessageBox.NoIcon
441
+
442
+ msg_box = CustomMessageBox(icon, title, text, copyable)
443
+
444
+ if detail is not None:
445
+ msg_box.setDetailedText(detail)
446
+ msg_box.setStandardButtons(buttons)
447
+ msg_box.setDefaultButton(default_button)
448
+
449
+ return msg_box.exec_()