qrpa 1.0.17__py3-none-any.whl → 1.0.19__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/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
  }