qrpa 1.0.16__py3-none-any.whl → 1.0.18__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.
Potentially problematic release.
This version of qrpa might be problematic. Click here for more details.
- qrpa/__init__.py +2 -0
- qrpa/fun_excel.py +93 -188
- qrpa/shein_excel.py +117 -0
- qrpa/shein_lib.py +305 -24
- qrpa/shein_sqlite.py +154 -0
- {qrpa-1.0.16.dist-info → qrpa-1.0.18.dist-info}/METADATA +1 -1
- {qrpa-1.0.16.dist-info → qrpa-1.0.18.dist-info}/RECORD +9 -8
- {qrpa-1.0.16.dist-info → qrpa-1.0.18.dist-info}/WHEEL +0 -0
- {qrpa-1.0.16.dist-info → qrpa-1.0.18.dist-info}/top_level.txt +0 -0
qrpa/fun_excel.py
CHANGED
|
@@ -16,63 +16,62 @@ from playwright.sync_api import sync_playwright
|
|
|
16
16
|
from .fun_base import log, sanitize_filename, create_file_path, copy_file, add_https, send_exception
|
|
17
17
|
|
|
18
18
|
excel_color_index = {
|
|
19
|
-
"无色(自动)": 0, # 透明/默认
|
|
20
|
-
"黑色": 1, # #000000
|
|
21
|
-
"白色": 2, # #FFFFFF
|
|
22
|
-
"红色": 3, # #FF0000
|
|
23
|
-
"绿色": 4, # #00FF00
|
|
24
|
-
"蓝色": 5, # #0000FF
|
|
25
|
-
"黄色": 6, # #FFFF00
|
|
26
|
-
"粉红色": 7, # #FF00FF
|
|
27
|
-
"青绿色": 8, # #00FFFF
|
|
28
|
-
"深红色": 9, # #800000
|
|
29
|
-
"深绿色": 10, # #008000
|
|
30
|
-
"深蓝色": 11, # #000080
|
|
31
|
-
"橄榄色(深黄)": 12, # #808000
|
|
32
|
-
"紫色": 13, # #800080
|
|
33
|
-
"蓝绿色(水色)": 14, # #008080
|
|
34
|
-
"灰色(25%)": 15, # #808080
|
|
19
|
+
"无色(自动)" : 0, # 透明/默认
|
|
20
|
+
"黑色" : 1, # #000000
|
|
21
|
+
"白色" : 2, # #FFFFFF
|
|
22
|
+
"红色" : 3, # #FF0000
|
|
23
|
+
"绿色" : 4, # #00FF00
|
|
24
|
+
"蓝色" : 5, # #0000FF
|
|
25
|
+
"黄色" : 6, # #FFFF00
|
|
26
|
+
"粉红色" : 7, # #FF00FF
|
|
27
|
+
"青绿色" : 8, # #00FFFF
|
|
28
|
+
"深红色" : 9, # #800000
|
|
29
|
+
"深绿色" : 10, # #008000
|
|
30
|
+
"深蓝色" : 11, # #000080
|
|
31
|
+
"橄榄色(深黄)" : 12, # #808000
|
|
32
|
+
"紫色" : 13, # #800080
|
|
33
|
+
"蓝绿色(水色)" : 14, # #008080
|
|
34
|
+
"灰色(25%)" : 15, # #808080
|
|
35
35
|
"浅灰色(12.5%)": 16, # #C0C0C0
|
|
36
36
|
# 17-19:系统保留(通常不可用)
|
|
37
|
-
"深玫瑰红": 20, # #FF99CC
|
|
38
|
-
"深金色": 21, # #FFCC99
|
|
39
|
-
"深橙红色": 22, # #FF6600
|
|
40
|
-
"深灰色(50%)": 23, # #666666
|
|
41
|
-
"深紫色": 24, # #660066
|
|
42
|
-
"蓝灰色": 25, # #3366FF
|
|
43
|
-
"浅蓝色": 26, # #99CCFF
|
|
44
|
-
"浅紫色": 27, # #CC99FF
|
|
45
|
-
"浅青绿色": 28, # #99FFFF
|
|
46
|
-
"浅绿色": 29, # #CCFFCC
|
|
47
|
-
"浅黄色": 30, # #FFFFCC
|
|
48
|
-
"浅橙红色": 31, # #FFCC99
|
|
49
|
-
"玫瑰红": 32, # #FF9999
|
|
50
|
-
"浅天蓝色": 33, # #99CCFF
|
|
51
|
-
"浅海绿色": 34, # #99FFCC
|
|
52
|
-
"浅草绿色": 35, # #CCFF99
|
|
53
|
-
"浅柠檬黄": 36, # #FFFF99
|
|
54
|
-
"浅珊瑚色": 37, # #FFCC99
|
|
55
|
-
"浅玫瑰红": 38, # #FF9999
|
|
56
|
-
"棕褐色": 39, # #CC9966
|
|
57
|
-
"浅棕褐色": 40, # #FFCC99
|
|
58
|
-
"浅橄榄色": 41, # #CCCC99
|
|
59
|
-
"浅蓝灰色": 42, # #9999FF
|
|
60
|
-
"浅灰绿色": 43, # #99CC99
|
|
61
|
-
"金色": 44, # #FFCC00
|
|
62
|
-
"浅橙黄色": 45, # #FFCC66
|
|
63
|
-
"橙红色": 46, # #FF6600
|
|
64
|
-
"深天蓝色": 47, # #0066CC
|
|
65
|
-
"深海绿色": 48, # #009966
|
|
66
|
-
"深草绿色": 49, # #669900
|
|
67
|
-
"深柠檬黄": 50, # #CCCC00
|
|
68
|
-
"深珊瑚色": 51, # #FF9933
|
|
69
|
-
"深玫瑰红(暗)": 52, # #CC6699
|
|
70
|
-
"深棕褐色": 53, # #996633
|
|
71
|
-
"深橄榄色": 54, # #666600
|
|
72
|
-
"深蓝灰色": 55, # #333399
|
|
37
|
+
"深玫瑰红" : 20, # #FF99CC
|
|
38
|
+
"深金色" : 21, # #FFCC99
|
|
39
|
+
"深橙红色" : 22, # #FF6600
|
|
40
|
+
"深灰色(50%)" : 23, # #666666
|
|
41
|
+
"深紫色" : 24, # #660066
|
|
42
|
+
"蓝灰色" : 25, # #3366FF
|
|
43
|
+
"浅蓝色" : 26, # #99CCFF
|
|
44
|
+
"浅紫色" : 27, # #CC99FF
|
|
45
|
+
"浅青绿色" : 28, # #99FFFF
|
|
46
|
+
"浅绿色" : 29, # #CCFFCC
|
|
47
|
+
"浅黄色" : 30, # #FFFFCC
|
|
48
|
+
"浅橙红色" : 31, # #FFCC99
|
|
49
|
+
"玫瑰红" : 32, # #FF9999
|
|
50
|
+
"浅天蓝色" : 33, # #99CCFF
|
|
51
|
+
"浅海绿色" : 34, # #99FFCC
|
|
52
|
+
"浅草绿色" : 35, # #CCFF99
|
|
53
|
+
"浅柠檬黄" : 36, # #FFFF99
|
|
54
|
+
"浅珊瑚色" : 37, # #FFCC99
|
|
55
|
+
"浅玫瑰红" : 38, # #FF9999
|
|
56
|
+
"棕褐色" : 39, # #CC9966
|
|
57
|
+
"浅棕褐色" : 40, # #FFCC99
|
|
58
|
+
"浅橄榄色" : 41, # #CCCC99
|
|
59
|
+
"浅蓝灰色" : 42, # #9999FF
|
|
60
|
+
"浅灰绿色" : 43, # #99CC99
|
|
61
|
+
"金色" : 44, # #FFCC00
|
|
62
|
+
"浅橙黄色" : 45, # #FFCC66
|
|
63
|
+
"橙红色" : 46, # #FF6600
|
|
64
|
+
"深天蓝色" : 47, # #0066CC
|
|
65
|
+
"深海绿色" : 48, # #009966
|
|
66
|
+
"深草绿色" : 49, # #669900
|
|
67
|
+
"深柠檬黄" : 50, # #CCCC00
|
|
68
|
+
"深珊瑚色" : 51, # #FF9933
|
|
69
|
+
"深玫瑰红(暗)" : 52, # #CC6699
|
|
70
|
+
"深棕褐色" : 53, # #996633
|
|
71
|
+
"深橄榄色" : 54, # #666600
|
|
72
|
+
"深蓝灰色" : 55, # #333399
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
|
|
76
75
|
def set_cell_prefix_red(cell, n, color_name):
|
|
77
76
|
"""
|
|
78
77
|
将指定 Excel 单元格内容的前 n 个字符设置为红色。
|
|
@@ -90,7 +89,6 @@ def set_cell_prefix_red(cell, n, color_name):
|
|
|
90
89
|
except Exception as e:
|
|
91
90
|
print(f"设置字体颜色失败: {e}")
|
|
92
91
|
|
|
93
|
-
|
|
94
92
|
def sort_by_column(data, col_index, start_row=2, reverse=True):
|
|
95
93
|
if not data or start_row >= len(data):
|
|
96
94
|
return data
|
|
@@ -114,7 +112,6 @@ def sort_by_column(data, col_index, start_row=2, reverse=True):
|
|
|
114
112
|
print(f"Error: Column index {col_index} out of range")
|
|
115
113
|
return data
|
|
116
114
|
|
|
117
|
-
|
|
118
115
|
def column_exists(sheet, column_name, header_row=1):
|
|
119
116
|
"""
|
|
120
117
|
检查工作表中是否存在指定列名
|
|
@@ -128,7 +125,6 @@ def column_exists(sheet, column_name, header_row=1):
|
|
|
128
125
|
|
|
129
126
|
return column_name in header_values
|
|
130
127
|
|
|
131
|
-
|
|
132
128
|
def merge_by_column_v2(sheet, column_name, other_columns):
|
|
133
129
|
log('正在处理合并单元格')
|
|
134
130
|
# 最好放到 open_excel 后面,不然容易出错
|
|
@@ -169,7 +165,6 @@ def merge_by_column_v2(sheet, column_name, other_columns):
|
|
|
169
165
|
log(f'处理 {col_name}{start}:{col_name}{end} merge')
|
|
170
166
|
sheet.range(f'{col_name}{start}:{col_name}{end}').merge()
|
|
171
167
|
|
|
172
|
-
|
|
173
168
|
def merge_by_column(sheet, column_name, other_columns):
|
|
174
169
|
log('正在处理合并单元格')
|
|
175
170
|
# 最好放到 open_excel 后面,不然容易出错
|
|
@@ -198,7 +193,6 @@ def merge_by_column(sheet, column_name, other_columns):
|
|
|
198
193
|
if col_name is not None:
|
|
199
194
|
sheet.range(f'{col_name}{start_row}:{col_name}{len(data)}').merge()
|
|
200
195
|
|
|
201
|
-
|
|
202
196
|
def merge_column_v2(sheet, columns):
|
|
203
197
|
if columns is None:
|
|
204
198
|
return
|
|
@@ -229,7 +223,6 @@ def merge_column_v2(sheet, columns):
|
|
|
229
223
|
log(f'处理 {col_letter}{start}:{col_letter}{end} merge')
|
|
230
224
|
sheet.range(f'{col_letter}{start}:{col_letter}{end}').merge()
|
|
231
225
|
|
|
232
|
-
|
|
233
226
|
# 按列相同值合并
|
|
234
227
|
def merge_column(sheet, columns):
|
|
235
228
|
# 最后放到 open_excel 后面,不然容易出错
|
|
@@ -252,7 +245,6 @@ def merge_column(sheet, columns):
|
|
|
252
245
|
if len(data) - start_row > 1:
|
|
253
246
|
sheet.range(f'{col_letter}{start_row}:{col_letter}{len(data)}').merge()
|
|
254
247
|
|
|
255
|
-
|
|
256
248
|
def remove_excel_columns(sheet, columns):
|
|
257
249
|
# 获取第一行(标题行)的所有值
|
|
258
250
|
header_row = sheet.range('1:1').value
|
|
@@ -276,7 +268,6 @@ def remove_excel_columns(sheet, columns):
|
|
|
276
268
|
print(f"成功移除列: {columns_to_remove}")
|
|
277
269
|
return True
|
|
278
270
|
|
|
279
|
-
|
|
280
271
|
def delete_sheet_if_exists(wb, sheet_name):
|
|
281
272
|
"""
|
|
282
273
|
如果工作簿中存在指定名称的工作表,则将其删除。
|
|
@@ -294,7 +285,6 @@ def delete_sheet_if_exists(wb, sheet_name):
|
|
|
294
285
|
else:
|
|
295
286
|
print(f"Sheet 不存在: {sheet_name}")
|
|
296
287
|
|
|
297
|
-
|
|
298
288
|
# 水平对齐:
|
|
299
289
|
# -4108:居中
|
|
300
290
|
# -4131:左对齐
|
|
@@ -316,7 +306,6 @@ def index_to_column_name(index):
|
|
|
316
306
|
index = index // 26
|
|
317
307
|
return column_name
|
|
318
308
|
|
|
319
|
-
|
|
320
309
|
# # 示例:将列索引转换为列名
|
|
321
310
|
# log(index_to_column_name(1)) # 输出: 'A'
|
|
322
311
|
# log(index_to_column_name(26)) # 输出: 'Z'
|
|
@@ -334,7 +323,6 @@ def column_name_to_index(column_name):
|
|
|
334
323
|
index = index * 26 + (ord(char.upper()) - 64)
|
|
335
324
|
return index - 1
|
|
336
325
|
|
|
337
|
-
|
|
338
326
|
# # 示例:将列名转换为列索引
|
|
339
327
|
# log(column_name_to_index('A')) # 输出: 1
|
|
340
328
|
# log(column_name_to_index('Z')) # 输出: 26
|
|
@@ -361,7 +349,6 @@ def find_row_by_data(sheet, column, target_value):
|
|
|
361
349
|
# 如果未找到,返回 None
|
|
362
350
|
return None
|
|
363
351
|
|
|
364
|
-
|
|
365
352
|
def find_column_by_data(sheet, row, target_value):
|
|
366
353
|
"""
|
|
367
354
|
查找指定数据在某一行中第一次出现的列名,包括隐藏的列。
|
|
@@ -382,7 +369,6 @@ def find_column_by_data(sheet, row, target_value):
|
|
|
382
369
|
|
|
383
370
|
return None # 未找到返回 None
|
|
384
371
|
|
|
385
|
-
|
|
386
372
|
def find_column_by_data_old(sheet, row, target_value):
|
|
387
373
|
"""
|
|
388
374
|
查找指定数据在某一行中第一次出现的列名。
|
|
@@ -404,7 +390,6 @@ def find_column_by_data_old(sheet, row, target_value):
|
|
|
404
390
|
# 如果未找到,返回 None
|
|
405
391
|
return None
|
|
406
392
|
|
|
407
|
-
|
|
408
393
|
def set_print_area(sheet, print_range, pdf_path=None, fit_to_width=True, landscape=False):
|
|
409
394
|
"""
|
|
410
395
|
设置指定sheet的打印区域和打印布局为适合A4宽度打印。
|
|
@@ -453,12 +438,10 @@ def set_print_area(sheet, print_range, pdf_path=None, fit_to_width=True, landsca
|
|
|
453
438
|
sheet.to_pdf(path=pdf_path)
|
|
454
439
|
log(f"PDF已成功生成:{pdf_path}")
|
|
455
440
|
|
|
456
|
-
|
|
457
441
|
def minimize(app):
|
|
458
442
|
# 让 Excel 窗口最小化
|
|
459
443
|
app.api.WindowState = -4140 # -4140 对应 Excel 中的 xlMinimized 常量
|
|
460
444
|
|
|
461
|
-
|
|
462
445
|
def insert_fixed_scale_image_v2(sheet, cell, image_path):
|
|
463
446
|
"""
|
|
464
447
|
将图片插入到指定单元格中,自动缩放以适应单元格尺寸,但保持宽高比例不变。
|
|
@@ -521,7 +504,6 @@ def insert_fixed_scale_image_v2(sheet, cell, image_path):
|
|
|
521
504
|
|
|
522
505
|
return None
|
|
523
506
|
|
|
524
|
-
|
|
525
507
|
def insert_fixed_scale_image(sheet, cell, image_path, scale=1.0):
|
|
526
508
|
"""
|
|
527
509
|
按固定比例放大图片并插入到单元格
|
|
@@ -559,8 +541,7 @@ def insert_fixed_scale_image(sheet, cell, image_path, scale=1.0):
|
|
|
559
541
|
|
|
560
542
|
return None
|
|
561
543
|
|
|
562
|
-
|
|
563
|
-
def InsertImageV2(app, wb, sheet, columns=None, platform='shein', img_width=150, img_save_key=None, dir_name=None):
|
|
544
|
+
def InsertImageV2(app, wb, sheet, columns=None, platform='shein', img_width=150, img_save_key=None, dir_name=None, cell_height_with_img=False):
|
|
564
545
|
if not columns:
|
|
565
546
|
return
|
|
566
547
|
|
|
@@ -619,14 +600,17 @@ def InsertImageV2(app, wb, sheet, columns=None, platform='shein', img_width=150,
|
|
|
619
600
|
if cell_height < img_width:
|
|
620
601
|
cell_range.row_height = max(150 / 8, img_width / cell_range.rows.count)
|
|
621
602
|
|
|
603
|
+
if cell_height_with_img:
|
|
604
|
+
cell_range.row_height = img_width
|
|
605
|
+
|
|
622
606
|
# 计算居中位置
|
|
623
607
|
top = cell_range.top + (cell_range.height - img_width) / 2
|
|
624
608
|
left = cell_range.left + (cell_range.width - img_width) / 2
|
|
625
609
|
|
|
626
610
|
area_map[cell_address] = {
|
|
627
|
-
'top': top,
|
|
628
|
-
'left': left,
|
|
629
|
-
'width': img_width,
|
|
611
|
+
'top' : top,
|
|
612
|
+
'left' : left,
|
|
613
|
+
'width' : img_width,
|
|
630
614
|
'cell_list': [c.address for c in cell_range]
|
|
631
615
|
}
|
|
632
616
|
|
|
@@ -684,7 +668,6 @@ def InsertImageV2(app, wb, sheet, columns=None, platform='shein', img_width=150,
|
|
|
684
668
|
else:
|
|
685
669
|
log(f'图片地址不存在 [{img_col_name}] : 第{row}行')
|
|
686
670
|
|
|
687
|
-
|
|
688
671
|
def download_images_concurrently(image_urls, platform='shein', img_save_dir=None):
|
|
689
672
|
# 使用线程池执行并发下载
|
|
690
673
|
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
|
|
@@ -692,7 +675,6 @@ def download_images_concurrently(image_urls, platform='shein', img_save_dir=None
|
|
|
692
675
|
results = list(executor.map(lambda url: download_img_v2(url, platform, img_save_path=img_save_dir), image_urls))
|
|
693
676
|
return results
|
|
694
677
|
|
|
695
|
-
|
|
696
678
|
def download_img_by_chrome(image_url, save_name):
|
|
697
679
|
with sync_playwright() as p:
|
|
698
680
|
browser = p.chromium.launch(headless=True) # 运行时可以看到浏览器
|
|
@@ -707,7 +689,6 @@ def download_img_by_chrome(image_url, save_name):
|
|
|
707
689
|
browser.close()
|
|
708
690
|
return save_name
|
|
709
691
|
|
|
710
|
-
|
|
711
692
|
def download_img_v2(image_url, platform='shein', img_save_path=None):
|
|
712
693
|
image_url = add_https(image_url)
|
|
713
694
|
if image_url is None or 'http' not in image_url:
|
|
@@ -745,8 +726,8 @@ def download_img_v2(image_url, platform='shein', img_save_path=None):
|
|
|
745
726
|
# return False
|
|
746
727
|
|
|
747
728
|
headers = {
|
|
748
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
|
749
|
-
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
729
|
+
"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
|
730
|
+
"Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
750
731
|
"Accept-Encoding": "gzip, deflate",
|
|
751
732
|
"Accept-Language": "zh-CN,zh;q=0.9"
|
|
752
733
|
}
|
|
@@ -779,7 +760,6 @@ def download_img_v2(image_url, platform='shein', img_save_path=None):
|
|
|
779
760
|
|
|
780
761
|
return file_path
|
|
781
762
|
|
|
782
|
-
|
|
783
763
|
# 插入图片函数 注意windows中这个路径反斜杠要是这样的才能插入成功
|
|
784
764
|
# C:\Users\Administrator/Desktop/auto/sku_img\K-CPYZB005-1_1734316546.png
|
|
785
765
|
def insert_cell_image(sheet, cell, file_path, img_width=120):
|
|
@@ -825,7 +805,6 @@ def insert_cell_image(sheet, cell, file_path, img_width=120):
|
|
|
825
805
|
log(f'插入图片失败: {e}, {file_path}')
|
|
826
806
|
send_exception()
|
|
827
807
|
|
|
828
|
-
|
|
829
808
|
# 插入图片函数 注意windows中这个路径反斜杠要是这样的才能插入成功
|
|
830
809
|
# C:\Users\Administrator/Desktop/auto/sku_img\K-CPYZB005-1_1734316546.png
|
|
831
810
|
def insert_image_from_local(sheet, cell, file_path, cell_width=90, cell_height=90):
|
|
@@ -864,7 +843,6 @@ def insert_image_from_local(sheet, cell, file_path, cell_width=90, cell_height=9
|
|
|
864
843
|
except Exception as e:
|
|
865
844
|
log(f'插入图片失败: {e}, {file_path}')
|
|
866
845
|
|
|
867
|
-
|
|
868
846
|
# 插入图片函数 注意windows中这个路径反斜杠要是这样的才能插入成功
|
|
869
847
|
# C:\Users\Administrator/Desktop/auto/sku_img\K-CPYZB005-1_1734316546.png
|
|
870
848
|
def insert_skc_image_from_local(sheet, cell, file_path):
|
|
@@ -897,7 +875,6 @@ def insert_skc_image_from_local(sheet, cell, file_path):
|
|
|
897
875
|
except Exception as e:
|
|
898
876
|
log(f'插入图片失败: {e}')
|
|
899
877
|
|
|
900
|
-
|
|
901
878
|
# # 设置 A 列和第 1 行为接近 100x100 的正方形
|
|
902
879
|
# set_square_cells(sheet, 'A', 1, 100)
|
|
903
880
|
|
|
@@ -916,35 +893,34 @@ def clear_all_pictures(sheet):
|
|
|
916
893
|
send_exception()
|
|
917
894
|
log(f"清空图片失败: {e}")
|
|
918
895
|
|
|
919
|
-
|
|
920
896
|
def get_excel_format(sheet, cell_range):
|
|
921
897
|
rng = sheet.range(cell_range)
|
|
922
898
|
|
|
923
899
|
format_settings = {
|
|
924
900
|
"numberFormat": rng.number_format,
|
|
925
|
-
"font": {
|
|
926
|
-
"name": rng.api.Font.Name,
|
|
927
|
-
"size": rng.api.Font.Size,
|
|
928
|
-
"bold": rng.api.Font.Bold,
|
|
901
|
+
"font" : {
|
|
902
|
+
"name" : rng.api.Font.Name,
|
|
903
|
+
"size" : rng.api.Font.Size,
|
|
904
|
+
"bold" : rng.api.Font.Bold,
|
|
929
905
|
"italic": rng.api.Font.Italic,
|
|
930
|
-
"color": rng.api.Font.Color
|
|
906
|
+
"color" : rng.api.Font.Color
|
|
931
907
|
},
|
|
932
|
-
"alignment": {
|
|
908
|
+
"alignment" : {
|
|
933
909
|
"horizontalAlignment": rng.api.HorizontalAlignment,
|
|
934
|
-
"verticalAlignment": rng.api.VerticalAlignment,
|
|
935
|
-
"wrapText": rng.api.WrapText
|
|
910
|
+
"verticalAlignment" : rng.api.VerticalAlignment,
|
|
911
|
+
"wrapText" : rng.api.WrapText
|
|
936
912
|
},
|
|
937
|
-
"borders": []
|
|
913
|
+
"borders" : []
|
|
938
914
|
}
|
|
939
915
|
|
|
940
916
|
# 获取所有边框设置(Excel 有 8 种边框)
|
|
941
917
|
for index in range(5, 13):
|
|
942
918
|
border = rng.api.Borders(index)
|
|
943
919
|
format_settings["borders"].append({
|
|
944
|
-
"index": index,
|
|
920
|
+
"index" : index,
|
|
945
921
|
"lineStyle": border.LineStyle,
|
|
946
|
-
"color": border.Color,
|
|
947
|
-
"weight": border.Weight
|
|
922
|
+
"color" : border.Color,
|
|
923
|
+
"weight" : border.Weight
|
|
948
924
|
})
|
|
949
925
|
|
|
950
926
|
# 获取背景色
|
|
@@ -958,7 +934,6 @@ def get_excel_format(sheet, cell_range):
|
|
|
958
934
|
|
|
959
935
|
return json.dumps(format_settings, indent=2)
|
|
960
936
|
|
|
961
|
-
|
|
962
937
|
def set_excel_format(sheet, cell_range, json_setting):
|
|
963
938
|
settings = json.loads(json_setting)
|
|
964
939
|
|
|
@@ -1017,7 +992,6 @@ def set_excel_format(sheet, cell_range, json_setting):
|
|
|
1017
992
|
if "formulaHidden" in settings:
|
|
1018
993
|
rng.api.FormulaHidden = settings["formulaHidden"]
|
|
1019
994
|
|
|
1020
|
-
|
|
1021
995
|
# # 获取 A1 单元格格式
|
|
1022
996
|
# json_format = get_excel_format(sheet, "A1")
|
|
1023
997
|
# log("Original Format:", json_format)
|
|
@@ -1056,7 +1030,6 @@ def get_unique_values(sheet, column, start_row, end_row=None):
|
|
|
1056
1030
|
# unique_values = get_unique_values(sheet, 'A', 2)
|
|
1057
1031
|
# log(unique_values)
|
|
1058
1032
|
|
|
1059
|
-
|
|
1060
1033
|
def get_unique_values_by_row(sheet, row, start_col, end_col=None):
|
|
1061
1034
|
"""
|
|
1062
1035
|
获取指定行从指定列开始的不重复值列表,确保读取的值与 Excel 中显示的内容完全一致。
|
|
@@ -1088,7 +1061,6 @@ def get_unique_values_by_row(sheet, row, start_col, end_col=None):
|
|
|
1088
1061
|
# 获取第 2 行从 A 列开始的不重复值
|
|
1089
1062
|
# unique_values = get_unique_values_by_row(sheet, 2, 'A')
|
|
1090
1063
|
|
|
1091
|
-
|
|
1092
1064
|
def find_rows_by_criteria(sheet, col, search_text, match_type='equals'):
|
|
1093
1065
|
"""
|
|
1094
1066
|
在指定列中查找符合条件的数据所在行。
|
|
@@ -1140,7 +1112,6 @@ def find_rows_by_criteria(sheet, col, search_text, match_type='equals'):
|
|
|
1140
1112
|
# result_negative_col = find_rows_by_criteria(sheet, -1, 'xyz', match_type='equals')
|
|
1141
1113
|
# log("倒数第一列匹配结果:", result_negative_col)
|
|
1142
1114
|
|
|
1143
|
-
|
|
1144
1115
|
def find_columns_by_criteria(sheet, row, search_text, match_type='equals'):
|
|
1145
1116
|
"""
|
|
1146
1117
|
在指定行中查找符合条件的数据所在列。
|
|
@@ -1186,12 +1157,10 @@ def find_columns_by_criteria(sheet, row, search_text, match_type='equals'):
|
|
|
1186
1157
|
# result_negative_row = find_columns_by_criteria(sheet, -1, 'xyz', match_type='equals')
|
|
1187
1158
|
# log("倒数第一行匹配结果:", result_negative_row)
|
|
1188
1159
|
|
|
1189
|
-
|
|
1190
1160
|
def check_data(data):
|
|
1191
1161
|
for row in data:
|
|
1192
1162
|
log(len(row), row)
|
|
1193
1163
|
|
|
1194
|
-
|
|
1195
1164
|
def write_data(excel_path, sheet_name, data, format_to_text_colunm=None):
|
|
1196
1165
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1197
1166
|
# 清空工作表中的所有数据
|
|
@@ -1205,7 +1174,6 @@ def write_data(excel_path, sheet_name, data, format_to_text_colunm=None):
|
|
|
1205
1174
|
wb.save()
|
|
1206
1175
|
close_excel(app, wb)
|
|
1207
1176
|
|
|
1208
|
-
|
|
1209
1177
|
def colorize_by_field(app, wb, sheet, field):
|
|
1210
1178
|
minimize(app)
|
|
1211
1179
|
# 读取数据
|
|
@@ -1228,7 +1196,6 @@ def colorize_by_field(app, wb, sheet, field):
|
|
|
1228
1196
|
row_range.color = bg_color # 应用背景色
|
|
1229
1197
|
sheet.range(f"A{row}").api.Font.Bold = True # 让店铺名称加粗
|
|
1230
1198
|
|
|
1231
|
-
|
|
1232
1199
|
def add_borders(sheet, lineStyle=1):
|
|
1233
1200
|
log('添加边框')
|
|
1234
1201
|
# 获取工作表的整个范围(假设表格的数据是从A1开始)
|
|
@@ -1248,7 +1215,6 @@ def add_borders(sheet, lineStyle=1):
|
|
|
1248
1215
|
range_to_border.api.Borders(3).LineStyle = lineStyle # 内部左边框
|
|
1249
1216
|
range_to_border.api.Borders(4).LineStyle = lineStyle # 内部右边框
|
|
1250
1217
|
|
|
1251
|
-
|
|
1252
1218
|
def add_range_border(sheet, coor_A=(1, 1), coor_B=(1, 1), lineStyle=1):
|
|
1253
1219
|
range_to_border = sheet.range(coor_A, coor_B) # 定义范围
|
|
1254
1220
|
|
|
@@ -1264,7 +1230,6 @@ def add_range_border(sheet, coor_A=(1, 1), coor_B=(1, 1), lineStyle=1):
|
|
|
1264
1230
|
range_to_border.api.Borders(3).LineStyle = lineStyle # 内部左边框
|
|
1265
1231
|
range_to_border.api.Borders(4).LineStyle = lineStyle # 内部右边框
|
|
1266
1232
|
|
|
1267
|
-
|
|
1268
1233
|
def open_excel(excel_path, sheet_name='Sheet1'):
|
|
1269
1234
|
try:
|
|
1270
1235
|
# 创建新实例
|
|
@@ -1321,7 +1286,6 @@ def open_excel(excel_path, sheet_name='Sheet1'):
|
|
|
1321
1286
|
# wxwork.notify_error_msg(f'打开 Excel 失败: {traceback.format_exc()}')
|
|
1322
1287
|
return None, None, None
|
|
1323
1288
|
|
|
1324
|
-
|
|
1325
1289
|
def close_excel(app, wb):
|
|
1326
1290
|
if wb is not None:
|
|
1327
1291
|
wb.save()
|
|
@@ -1329,7 +1293,6 @@ def close_excel(app, wb):
|
|
|
1329
1293
|
if app is not None:
|
|
1330
1294
|
app.quit()
|
|
1331
1295
|
|
|
1332
|
-
|
|
1333
1296
|
# 获取某列最后非空行
|
|
1334
1297
|
def get_last_row(sheet, column):
|
|
1335
1298
|
last_row = sheet.range(column + str(sheet.cells.last_cell.row)).end('up').row
|
|
@@ -1340,38 +1303,32 @@ def get_last_row(sheet, column):
|
|
|
1340
1303
|
last_row = cell.merge_area.last_cell.row
|
|
1341
1304
|
return last_row
|
|
1342
1305
|
|
|
1343
|
-
|
|
1344
1306
|
# 获取最后一列字母
|
|
1345
1307
|
def get_last_col(sheet):
|
|
1346
1308
|
# # 获取最后一行的索引
|
|
1347
1309
|
last_col = index_to_column_name(sheet.range('A1').end('right').column) # 里面是索引 返回最后一列 如 C
|
|
1348
1310
|
return last_col
|
|
1349
1311
|
|
|
1350
|
-
|
|
1351
1312
|
# 获取最大列名字母
|
|
1352
1313
|
def get_max_column_letter(sheet):
|
|
1353
1314
|
"""获取当前 sheet 中最大有数据的列的列名(如 'A', 'B', ..., 'Z', 'AA', 'AB')"""
|
|
1354
1315
|
last_col = sheet.used_range.last_cell.column # 获取最大列索引
|
|
1355
1316
|
return xw.utils.col_name(last_col) # 将索引转换为列名
|
|
1356
1317
|
|
|
1357
|
-
|
|
1358
1318
|
# 随机生成颜色
|
|
1359
1319
|
def random_color():
|
|
1360
1320
|
return (random.randint(180, 255), random.randint(180, 255), random.randint(180, 255)) # 亮色背景
|
|
1361
1321
|
|
|
1362
|
-
|
|
1363
1322
|
def get_contrast_text_color(rgb):
|
|
1364
1323
|
"""根据背景色亮度返回适合的字体颜色(黑色或白色)"""
|
|
1365
1324
|
r, g, b = rgb
|
|
1366
1325
|
brightness = r * 0.299 + g * 0.587 + b * 0.114 # 亮度计算公式
|
|
1367
1326
|
return (0, 0, 0) if brightness > 186 else (255, 255, 255) # 186 是经验值
|
|
1368
1327
|
|
|
1369
|
-
|
|
1370
1328
|
def rgb_to_long(r, g, b):
|
|
1371
1329
|
"""将 RGB 颜色转换为 Excel Long 类型"""
|
|
1372
1330
|
return r + (g * 256) + (b * 256 * 256)
|
|
1373
1331
|
|
|
1374
|
-
|
|
1375
1332
|
def read_excel_to_json(file_path, sheet_name="Sheet1"):
|
|
1376
1333
|
app, wb, sheet = open_excel(file_path, sheet_name)
|
|
1377
1334
|
|
|
@@ -1409,20 +1366,20 @@ def read_excel_to_json(file_path, sheet_name="Sheet1"):
|
|
|
1409
1366
|
diagonal_down_info = {"style": diagonal_down.LineStyle, "color": diagonal_down.Color}
|
|
1410
1367
|
|
|
1411
1368
|
cell_info = {
|
|
1412
|
-
"value": cell.value,
|
|
1413
|
-
"color": cell.color,
|
|
1414
|
-
"font_name": cell.api.Font.Name,
|
|
1415
|
-
"font_size": cell.api.Font.Size,
|
|
1416
|
-
"bold": cell.api.Font.Bold,
|
|
1417
|
-
"italic": cell.api.Font.Italic,
|
|
1418
|
-
"font_color": cell.api.Font.Color,
|
|
1369
|
+
"value" : cell.value,
|
|
1370
|
+
"color" : cell.color,
|
|
1371
|
+
"font_name" : cell.api.Font.Name,
|
|
1372
|
+
"font_size" : cell.api.Font.Size,
|
|
1373
|
+
"bold" : cell.api.Font.Bold,
|
|
1374
|
+
"italic" : cell.api.Font.Italic,
|
|
1375
|
+
"font_color" : cell.api.Font.Color,
|
|
1419
1376
|
"horizontal_align": cell.api.HorizontalAlignment,
|
|
1420
|
-
"vertical_align": cell.api.VerticalAlignment,
|
|
1421
|
-
"number_format": cell.api.NumberFormat,
|
|
1422
|
-
"border": {
|
|
1423
|
-
"left": {"style": cell.api.Borders(1).LineStyle, "color": cell.api.Borders(1).Color},
|
|
1424
|
-
"right": {"style": cell.api.Borders(2).LineStyle, "color": cell.api.Borders(2).Color},
|
|
1425
|
-
"top": {"style": cell.api.Borders(3).LineStyle, "color": cell.api.Borders(3).Color},
|
|
1377
|
+
"vertical_align" : cell.api.VerticalAlignment,
|
|
1378
|
+
"number_format" : cell.api.NumberFormat,
|
|
1379
|
+
"border" : {
|
|
1380
|
+
"left" : {"style": cell.api.Borders(1).LineStyle, "color": cell.api.Borders(1).Color},
|
|
1381
|
+
"right" : {"style": cell.api.Borders(2).LineStyle, "color": cell.api.Borders(2).Color},
|
|
1382
|
+
"top" : {"style": cell.api.Borders(3).LineStyle, "color": cell.api.Borders(3).Color},
|
|
1426
1383
|
"bottom": {"style": cell.api.Borders(4).LineStyle, "color": cell.api.Borders(4).Color},
|
|
1427
1384
|
}
|
|
1428
1385
|
}
|
|
@@ -1445,10 +1402,10 @@ def read_excel_to_json(file_path, sheet_name="Sheet1"):
|
|
|
1445
1402
|
app.quit()
|
|
1446
1403
|
|
|
1447
1404
|
final_data = {
|
|
1448
|
-
"cells": data,
|
|
1449
|
-
"merged_cells": merged_cells,
|
|
1405
|
+
"cells" : data,
|
|
1406
|
+
"merged_cells" : merged_cells,
|
|
1450
1407
|
"column_widths": column_widths,
|
|
1451
|
-
"row_heights": row_heights
|
|
1408
|
+
"row_heights" : row_heights
|
|
1452
1409
|
}
|
|
1453
1410
|
|
|
1454
1411
|
with open("excel_data.json", "w", encoding="utf-8") as f:
|
|
@@ -1456,7 +1413,6 @@ def read_excel_to_json(file_path, sheet_name="Sheet1"):
|
|
|
1456
1413
|
|
|
1457
1414
|
print("✅ Excel 数据已存储为 JSON")
|
|
1458
1415
|
|
|
1459
|
-
|
|
1460
1416
|
def write_json_to_excel(json_file, new_excel="new_test.xlsx", sheet_name="Sheet1"):
|
|
1461
1417
|
with open(json_file, "r", encoding="utf-8") as f:
|
|
1462
1418
|
final_data = json.load(f)
|
|
@@ -1519,7 +1475,6 @@ def write_json_to_excel(json_file, new_excel="new_test.xlsx", sheet_name="Sheet1
|
|
|
1519
1475
|
print(f"✅ 数据已成功写入 {new_excel}")
|
|
1520
1476
|
time.sleep(2) # 这里需要一个延时
|
|
1521
1477
|
|
|
1522
|
-
|
|
1523
1478
|
def safe_expand_down(sheet, start_cell='A2'):
|
|
1524
1479
|
rng = sheet.range(start_cell)
|
|
1525
1480
|
if not rng.value:
|
|
@@ -1530,7 +1485,6 @@ def safe_expand_down(sheet, start_cell='A2'):
|
|
|
1530
1485
|
log(f'safe_expand_down failed: {e}')
|
|
1531
1486
|
return [rng] # 返回单元格本身
|
|
1532
1487
|
|
|
1533
|
-
|
|
1534
1488
|
# 初始化一个表格
|
|
1535
1489
|
# data 需要是一个二维列表
|
|
1536
1490
|
def init_progress_ex(key_id, excel_path, sheet_name='Sheet1'):
|
|
@@ -1565,7 +1519,6 @@ def init_progress_ex(key_id, excel_path, sheet_name='Sheet1'):
|
|
|
1565
1519
|
|
|
1566
1520
|
wb.save()
|
|
1567
1521
|
|
|
1568
|
-
|
|
1569
1522
|
def init_data_ex(key_id, excel_path, header, sheet_name='Sheet1'):
|
|
1570
1523
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1571
1524
|
|
|
@@ -1594,7 +1547,6 @@ def init_data_ex(key_id, excel_path, header, sheet_name='Sheet1'):
|
|
|
1594
1547
|
|
|
1595
1548
|
wb.save()
|
|
1596
1549
|
|
|
1597
|
-
|
|
1598
1550
|
def format_header_row(sheet, column_count):
|
|
1599
1551
|
"""
|
|
1600
1552
|
设置标题行样式和列对齐
|
|
@@ -1616,7 +1568,6 @@ def format_header_row(sheet, column_count):
|
|
|
1616
1568
|
# 自动调整列宽
|
|
1617
1569
|
sheet.range(f'{col_letter}:{col_letter}').autofit()
|
|
1618
1570
|
|
|
1619
|
-
|
|
1620
1571
|
# 初始化一个表格
|
|
1621
1572
|
# data 需要是一个二维列表
|
|
1622
1573
|
def init_progress(excel_path, keyID, sheet_name='Sheet1'):
|
|
@@ -1668,7 +1619,6 @@ def init_progress(excel_path, keyID, sheet_name='Sheet1'):
|
|
|
1668
1619
|
|
|
1669
1620
|
wb.save()
|
|
1670
1621
|
|
|
1671
|
-
|
|
1672
1622
|
def get_progress(excel_path, keyID, sheet_name="Sheet1"):
|
|
1673
1623
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1674
1624
|
# 遍历可用行
|
|
@@ -1685,7 +1635,6 @@ def get_progress(excel_path, keyID, sheet_name="Sheet1"):
|
|
|
1685
1635
|
else:
|
|
1686
1636
|
return False
|
|
1687
1637
|
|
|
1688
|
-
|
|
1689
1638
|
def get_progress_ex(keyID, excel_path, sheet_name="Sheet1"):
|
|
1690
1639
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1691
1640
|
# 遍历可用行
|
|
@@ -1703,7 +1652,6 @@ def get_progress_ex(keyID, excel_path, sheet_name="Sheet1"):
|
|
|
1703
1652
|
return False
|
|
1704
1653
|
close_excel(app, wb)
|
|
1705
1654
|
|
|
1706
|
-
|
|
1707
1655
|
def get_progress_data(excel_path, keyID, sheet_name="Sheet1"):
|
|
1708
1656
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1709
1657
|
# 遍历可用行
|
|
@@ -1718,7 +1666,6 @@ def get_progress_data(excel_path, keyID, sheet_name="Sheet1"):
|
|
|
1718
1666
|
return result
|
|
1719
1667
|
return None
|
|
1720
1668
|
|
|
1721
|
-
|
|
1722
1669
|
def get_progress_data_ex(keyID, excel_path, sheet_name="Sheet1"):
|
|
1723
1670
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1724
1671
|
# 遍历可用行
|
|
@@ -1733,7 +1680,6 @@ def get_progress_data_ex(keyID, excel_path, sheet_name="Sheet1"):
|
|
|
1733
1680
|
return result
|
|
1734
1681
|
return None
|
|
1735
1682
|
|
|
1736
|
-
|
|
1737
1683
|
def set_progress(excel_path, keyID, status='已完成', sheet_name="Sheet1"):
|
|
1738
1684
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1739
1685
|
# 遍历可用行
|
|
@@ -1748,7 +1694,6 @@ def set_progress(excel_path, keyID, status='已完成', sheet_name="Sheet1"):
|
|
|
1748
1694
|
wb.save()
|
|
1749
1695
|
return
|
|
1750
1696
|
|
|
1751
|
-
|
|
1752
1697
|
def set_progress_ex(keyID, excel_path, status='已完成', sheet_name="Sheet1"):
|
|
1753
1698
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1754
1699
|
# 遍历可用行
|
|
@@ -1765,7 +1710,6 @@ def set_progress_ex(keyID, excel_path, status='已完成', sheet_name="Sheet1"):
|
|
|
1765
1710
|
return
|
|
1766
1711
|
close_excel(app, wb)
|
|
1767
1712
|
|
|
1768
|
-
|
|
1769
1713
|
def set_data_ex(keyID, data, excel_path, sheet_name="Sheet1"):
|
|
1770
1714
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1771
1715
|
# 遍历可用行
|
|
@@ -1780,7 +1724,6 @@ def set_data_ex(keyID, data, excel_path, sheet_name="Sheet1"):
|
|
|
1780
1724
|
wb.save()
|
|
1781
1725
|
return
|
|
1782
1726
|
|
|
1783
|
-
|
|
1784
1727
|
def set_progress_data(excel_path, keyID, data, sheet_name="Sheet1"):
|
|
1785
1728
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1786
1729
|
# 遍历可用行
|
|
@@ -1796,7 +1739,6 @@ def set_progress_data(excel_path, keyID, data, sheet_name="Sheet1"):
|
|
|
1796
1739
|
wb.save()
|
|
1797
1740
|
return
|
|
1798
1741
|
|
|
1799
|
-
|
|
1800
1742
|
def set_progress_data_ex(keyID, data, excel_path, sheet_name="Sheet1"):
|
|
1801
1743
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1802
1744
|
# 遍历可用行
|
|
@@ -1812,7 +1754,6 @@ def set_progress_data_ex(keyID, data, excel_path, sheet_name="Sheet1"):
|
|
|
1812
1754
|
wb.save()
|
|
1813
1755
|
return
|
|
1814
1756
|
|
|
1815
|
-
|
|
1816
1757
|
def check_progress(excel_path, listKeyID, sheet_name="Sheet1"):
|
|
1817
1758
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1818
1759
|
# 读取整个任务表数据
|
|
@@ -1827,7 +1768,6 @@ def check_progress(excel_path, listKeyID, sheet_name="Sheet1"):
|
|
|
1827
1768
|
incomplete_tasks = [task_id for task_id in listKeyID if task_status_dict.get(task_id) != "已完成"]
|
|
1828
1769
|
return len(incomplete_tasks) == 0, incomplete_tasks
|
|
1829
1770
|
|
|
1830
|
-
|
|
1831
1771
|
def check_progress_ex(listKeyID, excel_path, sheet_name="Sheet1"):
|
|
1832
1772
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
1833
1773
|
# 读取整个任务表数据
|
|
@@ -1842,7 +1782,6 @@ def check_progress_ex(listKeyID, excel_path, sheet_name="Sheet1"):
|
|
|
1842
1782
|
incomplete_tasks = [task_id for task_id in listKeyID if task_status_dict.get(task_id) != "已完成"]
|
|
1843
1783
|
return len(incomplete_tasks) == 0, incomplete_tasks
|
|
1844
1784
|
|
|
1845
|
-
|
|
1846
1785
|
def read_excel_sheet_to_list(file_path, sheet_name=None):
|
|
1847
1786
|
"""
|
|
1848
1787
|
使用 xlwings 读取 Excel 文件中指定工作表的数据,并返回为二维列表。
|
|
@@ -1864,7 +1803,6 @@ def read_excel_sheet_to_list(file_path, sheet_name=None):
|
|
|
1864
1803
|
else:
|
|
1865
1804
|
return [data]
|
|
1866
1805
|
|
|
1867
|
-
|
|
1868
1806
|
def excel_to_dict(excel_path, column_key, column_value, sheet_name=None):
|
|
1869
1807
|
"""
|
|
1870
1808
|
从 Excel 文件中读取指定两列,生成字典返回(不受中间空行影响)
|
|
@@ -1912,7 +1850,6 @@ def excel_to_dict(excel_path, column_key, column_value, sheet_name=None):
|
|
|
1912
1850
|
wb.close()
|
|
1913
1851
|
app.quit()
|
|
1914
1852
|
|
|
1915
|
-
|
|
1916
1853
|
def format_to_text_v2(sheet, columns=None):
|
|
1917
1854
|
if columns is None or len(columns) == 0:
|
|
1918
1855
|
return
|
|
@@ -1922,7 +1859,6 @@ def format_to_text_v2(sheet, columns=None):
|
|
|
1922
1859
|
log(f'设置[{col_name}] 文本格式')
|
|
1923
1860
|
sheet.range(f'{col_name}:{col_name}').number_format = '@'
|
|
1924
1861
|
|
|
1925
|
-
|
|
1926
1862
|
def format_to_text(sheet, columns=None):
|
|
1927
1863
|
if columns is None:
|
|
1928
1864
|
return
|
|
@@ -1936,7 +1872,6 @@ def format_to_text(sheet, columns=None):
|
|
|
1936
1872
|
log(f'设置[{c}] 文本格式')
|
|
1937
1873
|
sheet.range(f'{col_name}:{col_name}').number_format = '@'
|
|
1938
1874
|
|
|
1939
|
-
|
|
1940
1875
|
def format_to_date(sheet, columns=None):
|
|
1941
1876
|
if columns is None:
|
|
1942
1877
|
return
|
|
@@ -1952,7 +1887,6 @@ def format_to_date(sheet, columns=None):
|
|
|
1952
1887
|
log(f'设置[{c}] 时间格式')
|
|
1953
1888
|
sheet.range(f'{col_name}:{col_name}').number_format = 'yyyy-mm-dd'
|
|
1954
1889
|
|
|
1955
|
-
|
|
1956
1890
|
def format_to_datetime(sheet, columns=None):
|
|
1957
1891
|
if columns is None:
|
|
1958
1892
|
return
|
|
@@ -1968,7 +1902,6 @@ def format_to_datetime(sheet, columns=None):
|
|
|
1968
1902
|
log(f'设置[{c}] 时间格式')
|
|
1969
1903
|
sheet.range(f'{col_name}:{col_name}').number_format = 'yyyy-mm-dd hh:mm:ss'
|
|
1970
1904
|
|
|
1971
|
-
|
|
1972
1905
|
def format_to_month(sheet, columns=None):
|
|
1973
1906
|
if columns is None:
|
|
1974
1907
|
return
|
|
@@ -1982,7 +1915,6 @@ def format_to_month(sheet, columns=None):
|
|
|
1982
1915
|
log(f'设置[{c}] 年月格式')
|
|
1983
1916
|
sheet.range(f'{col_name}:{col_name}').number_format = 'yyyy-mm'
|
|
1984
1917
|
|
|
1985
|
-
|
|
1986
1918
|
def add_sum_for_cell(sheet, col_list, row=2):
|
|
1987
1919
|
last_row = sheet.range('A' + str(sheet.cells.last_cell.row)).end('up').row
|
|
1988
1920
|
for col_name in col_list:
|
|
@@ -1991,14 +1923,12 @@ def add_sum_for_cell(sheet, col_list, row=2):
|
|
|
1991
1923
|
sheet.range(f'{col_letter}{row}').api.Font.Color = 255
|
|
1992
1924
|
sheet.range(f'{col_letter}:{col_letter}').autofit()
|
|
1993
1925
|
|
|
1994
|
-
|
|
1995
1926
|
def clear_for_cell(sheet, col_list, row=2):
|
|
1996
1927
|
last_row = sheet.range('A' + str(sheet.cells.last_cell.row)).end('up').row
|
|
1997
1928
|
for col_name in col_list:
|
|
1998
1929
|
col_letter = find_column_by_data(sheet, 1, col_name)
|
|
1999
1930
|
sheet.range(f'{col_letter}{row}').value = ''
|
|
2000
1931
|
|
|
2001
|
-
|
|
2002
1932
|
def color_for_column(sheet, col_list, color_name, start_row=2):
|
|
2003
1933
|
last_row = sheet.range('A' + str(sheet.cells.last_cell.row)).end('up').row
|
|
2004
1934
|
for col_name in col_list:
|
|
@@ -2007,7 +1937,6 @@ def color_for_column(sheet, col_list, color_name, start_row=2):
|
|
|
2007
1937
|
sheet.range(f'{col_letter}{start_row}:{col_letter}{last_row}').api.Font.ColorIndex = excel_color_index[
|
|
2008
1938
|
color_name]
|
|
2009
1939
|
|
|
2010
|
-
|
|
2011
1940
|
def add_formula_for_column(sheet, col_name, formula, start_row=2):
|
|
2012
1941
|
last_row = sheet.range('A' + str(sheet.cells.last_cell.row)).end('up').row
|
|
2013
1942
|
col_letter = find_column_by_data(sheet, 1, col_name)
|
|
@@ -2021,7 +1950,6 @@ def add_formula_for_column(sheet, col_name, formula, start_row=2):
|
|
|
2021
1950
|
sheet.range(f'{col_letter}{start_row}').api.AutoFill(
|
|
2022
1951
|
sheet.range(f'{col_letter}{start_row}:{col_letter}{last_row}').api)
|
|
2023
1952
|
|
|
2024
|
-
|
|
2025
1953
|
def autofit_column(sheet, columns=None):
|
|
2026
1954
|
if columns is None:
|
|
2027
1955
|
return
|
|
@@ -2040,7 +1968,6 @@ def autofit_column(sheet, columns=None):
|
|
|
2040
1968
|
sheet.range(f'{col_name}:{col_name}').api.WrapText = True
|
|
2041
1969
|
sheet.range(f'{col_name}:{col_name}').autofit()
|
|
2042
1970
|
|
|
2043
|
-
|
|
2044
1971
|
def specify_column_width(sheet, columns=None, width=150):
|
|
2045
1972
|
if columns is None:
|
|
2046
1973
|
return
|
|
@@ -2056,7 +1983,6 @@ def specify_column_width(sheet, columns=None, width=150):
|
|
|
2056
1983
|
log(f'设置[{c}]宽度: {width}')
|
|
2057
1984
|
sheet.range(f'{col_name}:{col_name}').column_width = width
|
|
2058
1985
|
|
|
2059
|
-
|
|
2060
1986
|
def format_to_money(sheet, columns=None):
|
|
2061
1987
|
if columns is None:
|
|
2062
1988
|
return
|
|
@@ -2072,7 +1998,6 @@ def format_to_money(sheet, columns=None):
|
|
|
2072
1998
|
log(f'设置[{c}] 金额格式')
|
|
2073
1999
|
sheet.range(f'{col_name}:{col_name}').number_format = '¥#,##0.00'
|
|
2074
2000
|
|
|
2075
|
-
|
|
2076
2001
|
def format_to_percent(sheet, columns=None, decimal_places=2):
|
|
2077
2002
|
if columns is None:
|
|
2078
2003
|
return
|
|
@@ -2092,7 +2017,6 @@ def format_to_percent(sheet, columns=None, decimal_places=2):
|
|
|
2092
2017
|
else:
|
|
2093
2018
|
sheet.range(f'{col_name}:{col_name}').number_format = f'0.{"0" * decimal_places}%'
|
|
2094
2019
|
|
|
2095
|
-
|
|
2096
2020
|
def format_to_number(sheet, columns=None, decimal_places=2):
|
|
2097
2021
|
if not columns or not isinstance(columns, (list, tuple, set)):
|
|
2098
2022
|
log(f'未提供有效列名列表({columns}),跳过格式转换')
|
|
@@ -2117,7 +2041,6 @@ def format_to_number(sheet, columns=None, decimal_places=2):
|
|
|
2117
2041
|
sheet.range(f'{col_name}:{col_name}').number_format = number_format
|
|
2118
2042
|
break # 如果一列只匹配一个关键词可提前退出
|
|
2119
2043
|
|
|
2120
|
-
|
|
2121
2044
|
# def format_to_number(sheet, columns=None, decimal_places=2):
|
|
2122
2045
|
# if columns is None or not isinstance(columns, list):
|
|
2123
2046
|
# log('跳过格式化成数字', columns)
|
|
@@ -2157,7 +2080,6 @@ def column_to_right(sheet, columns=None):
|
|
|
2157
2080
|
sheet.range(f'{col_name}:{col_name}').api.HorizontalAlignment = -4152
|
|
2158
2081
|
sheet.range(f'{col_name}:{col_name}').api.VerticalAlignment = -4108
|
|
2159
2082
|
|
|
2160
|
-
|
|
2161
2083
|
def column_to_left(sheet, columns=None):
|
|
2162
2084
|
if columns is None:
|
|
2163
2085
|
return
|
|
@@ -2177,7 +2099,6 @@ def column_to_left(sheet, columns=None):
|
|
|
2177
2099
|
sheet.range(f'{col_name}:{col_name}').api.HorizontalAlignment = -4131
|
|
2178
2100
|
sheet.range(f'{col_name}:{col_name}').api.VerticalAlignment = -4108
|
|
2179
2101
|
|
|
2180
|
-
|
|
2181
2102
|
def beautify_title(sheet):
|
|
2182
2103
|
log('美化标题')
|
|
2183
2104
|
used_range_col = sheet.range('A1').expand('right')
|
|
@@ -2196,7 +2117,6 @@ def beautify_title(sheet):
|
|
|
2196
2117
|
sheet.range(f'{col_name}:{col_name}').api.VerticalAlignment = -4108
|
|
2197
2118
|
sheet.autofit()
|
|
2198
2119
|
|
|
2199
|
-
|
|
2200
2120
|
def set_title_style(sheet, rows=2):
|
|
2201
2121
|
col = get_max_column_letter(sheet)
|
|
2202
2122
|
range = sheet.range(f'A1:{col}{rows}')
|
|
@@ -2219,7 +2139,6 @@ def set_title_style(sheet, rows=2):
|
|
|
2219
2139
|
|
|
2220
2140
|
sheet.autofit()
|
|
2221
2141
|
|
|
2222
|
-
|
|
2223
2142
|
def move_sheet_to_position(wb, sheet_name, position):
|
|
2224
2143
|
# 获取要移动的工作表
|
|
2225
2144
|
sheet = wb.sheets[sheet_name]
|
|
@@ -2233,13 +2152,11 @@ def move_sheet_to_position(wb, sheet_name, position):
|
|
|
2233
2152
|
# 保存工作簿
|
|
2234
2153
|
wb.save()
|
|
2235
2154
|
|
|
2236
|
-
|
|
2237
2155
|
# Excel 文件锁管理器
|
|
2238
2156
|
import threading
|
|
2239
2157
|
import time
|
|
2240
2158
|
from collections import defaultdict
|
|
2241
2159
|
|
|
2242
|
-
|
|
2243
2160
|
class ExcelFileLockManager:
|
|
2244
2161
|
"""Excel 文件锁管理器,用于管理不同 Excel 文件的并发访问"""
|
|
2245
2162
|
|
|
@@ -2272,7 +2189,7 @@ class ExcelFileLockManager:
|
|
|
2272
2189
|
# 记录等待请求
|
|
2273
2190
|
with self._lock:
|
|
2274
2191
|
self._waiting_queue[excel_path].append({
|
|
2275
|
-
'priority': priority,
|
|
2192
|
+
'priority' : priority,
|
|
2276
2193
|
'timestamp': time.time(),
|
|
2277
2194
|
'thread_id': threading.get_ident()
|
|
2278
2195
|
})
|
|
@@ -2345,11 +2262,9 @@ class ExcelFileLockManager:
|
|
|
2345
2262
|
# 比如检查文件最后访问时间等
|
|
2346
2263
|
pass
|
|
2347
2264
|
|
|
2348
|
-
|
|
2349
2265
|
# 全局 Excel 文件锁管理器实例
|
|
2350
2266
|
excel_lock_manager = ExcelFileLockManager()
|
|
2351
2267
|
|
|
2352
|
-
|
|
2353
2268
|
def open_excel_with_lock(excel_path, sheet_name='Sheet1', timeout=30):
|
|
2354
2269
|
"""
|
|
2355
2270
|
带锁的 Excel 打开函数,支持复用已打开的实例
|
|
@@ -2409,7 +2324,6 @@ def open_excel_with_lock(excel_path, sheet_name='Sheet1', timeout=30):
|
|
|
2409
2324
|
excel_lock_manager.release_excel_lock(excel_path)
|
|
2410
2325
|
return None, None, None
|
|
2411
2326
|
|
|
2412
|
-
|
|
2413
2327
|
def close_excel_with_lock(excel_path, app, wb, force_close=False):
|
|
2414
2328
|
"""
|
|
2415
2329
|
带锁的 Excel 关闭函数
|
|
@@ -2435,7 +2349,6 @@ def close_excel_with_lock(excel_path, app, wb, force_close=False):
|
|
|
2435
2349
|
finally:
|
|
2436
2350
|
excel_lock_manager.release_excel_lock(excel_path)
|
|
2437
2351
|
|
|
2438
|
-
|
|
2439
2352
|
def write_data_with_lock(excel_path, sheet_name, data, format_to_text_colunm=None):
|
|
2440
2353
|
"""
|
|
2441
2354
|
带锁的数据写入函数,复用 Excel 实例
|
|
@@ -2466,7 +2379,6 @@ def write_data_with_lock(excel_path, sheet_name, data, format_to_text_colunm=Non
|
|
|
2466
2379
|
log(f"写入数据失败: {e}")
|
|
2467
2380
|
return False
|
|
2468
2381
|
|
|
2469
|
-
|
|
2470
2382
|
def format_excel_with_lock(excel_path, sheet_name, format_func, *args, **kwargs):
|
|
2471
2383
|
"""
|
|
2472
2384
|
带锁的 Excel 格式化函数
|
|
@@ -2493,7 +2405,6 @@ def format_excel_with_lock(excel_path, sheet_name, format_func, *args, **kwargs)
|
|
|
2493
2405
|
log(f"格式化失败: {e}")
|
|
2494
2406
|
return False
|
|
2495
2407
|
|
|
2496
|
-
|
|
2497
2408
|
def batch_excel_operations(excel_path, operations):
|
|
2498
2409
|
"""
|
|
2499
2410
|
批量 Excel 操作函数,一次性打开 Excel 执行多个操作
|
|
@@ -2555,7 +2466,6 @@ def batch_excel_operations(excel_path, operations):
|
|
|
2555
2466
|
# 释放锁但不关闭 Excel(保持复用)
|
|
2556
2467
|
excel_lock_manager.release_excel_lock(excel_path)
|
|
2557
2468
|
|
|
2558
|
-
|
|
2559
2469
|
def force_close_excel_file(excel_path):
|
|
2560
2470
|
"""
|
|
2561
2471
|
强制关闭指定的 Excel 文件
|
|
@@ -2572,7 +2482,6 @@ def force_close_excel_file(excel_path):
|
|
|
2572
2482
|
except Exception as e:
|
|
2573
2483
|
log(f"强制关闭 Excel 文件失败: {e}")
|
|
2574
2484
|
|
|
2575
|
-
|
|
2576
2485
|
def wait_for_excel_available(excel_path, timeout=60, check_interval=1):
|
|
2577
2486
|
"""
|
|
2578
2487
|
等待 Excel 文件可用
|
|
@@ -2594,7 +2503,6 @@ def wait_for_excel_available(excel_path, timeout=60, check_interval=1):
|
|
|
2594
2503
|
log(f"等待 Excel 文件可用超时: {excel_path}")
|
|
2595
2504
|
return False
|
|
2596
2505
|
|
|
2597
|
-
|
|
2598
2506
|
def smart_excel_operation(excel_path, operation_func, priority=0, timeout=60, max_retries=3):
|
|
2599
2507
|
"""
|
|
2600
2508
|
智能 Excel 操作函数,支持优先级、重试和更好的错误处理
|
|
@@ -2661,7 +2569,6 @@ def smart_excel_operation(excel_path, operation_func, priority=0, timeout=60, ma
|
|
|
2661
2569
|
|
|
2662
2570
|
return False
|
|
2663
2571
|
|
|
2664
|
-
|
|
2665
2572
|
def batch_excel_operations_with_priority(excel_path, operations, priority=0, timeout=60):
|
|
2666
2573
|
"""
|
|
2667
2574
|
带优先级的批量 Excel 操作函数
|
|
@@ -2715,7 +2622,6 @@ def batch_excel_operations_with_priority(excel_path, operations, priority=0, tim
|
|
|
2715
2622
|
|
|
2716
2623
|
return smart_excel_operation(excel_path, batch_operation, priority, timeout)
|
|
2717
2624
|
|
|
2718
|
-
|
|
2719
2625
|
def wait_for_excel_available_with_priority(excel_path, timeout=60, check_interval=1, priority=0):
|
|
2720
2626
|
"""
|
|
2721
2627
|
等待 Excel 文件可用(带优先级)
|
|
@@ -2738,7 +2644,6 @@ def wait_for_excel_available_with_priority(excel_path, timeout=60, check_interva
|
|
|
2738
2644
|
log(f"等待 Excel 文件可用超时: {excel_path}")
|
|
2739
2645
|
return False
|
|
2740
2646
|
|
|
2741
|
-
|
|
2742
2647
|
def get_excel_status(excel_path):
|
|
2743
2648
|
"""
|
|
2744
2649
|
获取 Excel 文件状态信息
|
|
@@ -2750,8 +2655,8 @@ def get_excel_status(excel_path):
|
|
|
2750
2655
|
dict: 状态信息
|
|
2751
2656
|
"""
|
|
2752
2657
|
return {
|
|
2753
|
-
'is_open': excel_lock_manager.is_excel_open(excel_path),
|
|
2754
|
-
'waiting_count': excel_lock_manager.get_waiting_count(excel_path),
|
|
2658
|
+
'is_open' : excel_lock_manager.is_excel_open(excel_path),
|
|
2659
|
+
'waiting_count' : excel_lock_manager.get_waiting_count(excel_path),
|
|
2755
2660
|
'operation_count': excel_lock_manager.get_operation_count(excel_path),
|
|
2756
|
-
'has_lock': excel_lock_manager.get_file_lock(excel_path).locked()
|
|
2661
|
+
'has_lock' : excel_lock_manager.get_file_lock(excel_path).locked()
|
|
2757
2662
|
}
|