py2ls 0.1.10.1__py3-none-any.whl → 0.1.10.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py2ls/ips.py +777 -2
- py2ls/netfinder.py +33 -8
- py2ls/ocr.py +258 -94
- py2ls/translator.py +470 -119
- {py2ls-0.1.10.1.dist-info → py2ls-0.1.10.3.dist-info}/METADATA +1 -1
- {py2ls-0.1.10.1.dist-info → py2ls-0.1.10.3.dist-info}/RECORD +7 -7
- {py2ls-0.1.10.1.dist-info → py2ls-0.1.10.3.dist-info}/WHEEL +1 -1
py2ls/ips.py
CHANGED
@@ -9,6 +9,8 @@ from cycler import cycler
|
|
9
9
|
from mpl_toolkits.mplot3d import Axes3D
|
10
10
|
import seaborn as sns
|
11
11
|
|
12
|
+
from sklearn.kernel_approximation import KERNEL_PARAMS
|
13
|
+
from sympy import is_increasing
|
12
14
|
import sys, os, shutil, re, yaml, json, subprocess
|
13
15
|
import importlib.util
|
14
16
|
import time
|
@@ -519,6 +521,23 @@ def str2num(s, *args, **kwargs):
|
|
519
521
|
# print(str2num("12345.6789", " ", 2)) # Output: 12 345.68
|
520
522
|
# print(str2num('111113.34555',3,',')) # Output: 111,113.346
|
521
523
|
# print(str2num("123.55555 sec miniuets",3)) # Output: 1.3
|
524
|
+
|
525
|
+
|
526
|
+
def text2num(text):
|
527
|
+
# extract the digital nums from a text
|
528
|
+
num = []
|
529
|
+
for s in ssplit(text, by="digital"):
|
530
|
+
try:
|
531
|
+
if float(s):
|
532
|
+
num.append(float(s))
|
533
|
+
except:
|
534
|
+
pass
|
535
|
+
if len(num) == 1:
|
536
|
+
return num[0]
|
537
|
+
else:
|
538
|
+
return num
|
539
|
+
|
540
|
+
|
522
541
|
def num2str(num, *args, **kwargs):
|
523
542
|
delimiter = kwargs.get("sep", None)
|
524
543
|
round_digits = kwargs.get("round", None)
|
@@ -1328,8 +1347,28 @@ def fsave(
|
|
1328
1347
|
df.to_csv(fpath, **kwargs)
|
1329
1348
|
|
1330
1349
|
def save_xlsx(fpath, data, **kwargs):
|
1331
|
-
|
1332
|
-
|
1350
|
+
format = kwargs.get("format", None)
|
1351
|
+
if format:
|
1352
|
+
kwargs.pop("format", None)
|
1353
|
+
format_excel(df=data, filename=fpath, **kwargs)
|
1354
|
+
else:
|
1355
|
+
kwargs.pop("format", None)
|
1356
|
+
kwargs.pop("usage", None)
|
1357
|
+
kwargs.pop("cell", None)
|
1358
|
+
kwargs.pop("width", None)
|
1359
|
+
kwargs.pop("height", None)
|
1360
|
+
kwargs.pop("width", None)
|
1361
|
+
kwargs.pop("height_max", None)
|
1362
|
+
kwargs.pop("merge", None)
|
1363
|
+
kwargs.pop("shade", None)
|
1364
|
+
kwargs.pop("comment", None)
|
1365
|
+
kwargs.pop("link", None)
|
1366
|
+
kwargs.pop("protect", None)
|
1367
|
+
kwargs.pop("number_format", None)
|
1368
|
+
kwargs.pop("conditional_format", None)
|
1369
|
+
kwargs.pop("index_default", None)
|
1370
|
+
df = pd.DataFrame(data)
|
1371
|
+
df.to_excel(fpath, **kwargs)
|
1333
1372
|
|
1334
1373
|
def save_ipynb(fpath, data, **kwargs):
|
1335
1374
|
# Split the content by code fences to distinguish between code and markdown
|
@@ -1574,6 +1613,22 @@ def listdir(
|
|
1574
1613
|
orient="list",
|
1575
1614
|
output="df", # 'list','dict','records','index','series'
|
1576
1615
|
):
|
1616
|
+
if isinstance(kind, list):
|
1617
|
+
f_ = []
|
1618
|
+
for kind_ in kind:
|
1619
|
+
f_tmp = listdir(
|
1620
|
+
rootdir=rootdir,
|
1621
|
+
kind=kind_,
|
1622
|
+
sort_by=sort_by,
|
1623
|
+
ascending=ascending,
|
1624
|
+
contains=contains,
|
1625
|
+
orient=orient,
|
1626
|
+
output=output,
|
1627
|
+
)
|
1628
|
+
f_.append(f_tmp)
|
1629
|
+
if f_:
|
1630
|
+
return pd.concat(f_, ignore_index=True)
|
1631
|
+
|
1577
1632
|
if not kind.startswith("."):
|
1578
1633
|
kind = "." + kind
|
1579
1634
|
|
@@ -2923,3 +2978,723 @@ def finfo(fpath):
|
|
2923
2978
|
kind=data["kind"],
|
2924
2979
|
extra_info=extra_info,
|
2925
2980
|
)
|
2981
|
+
|
2982
|
+
|
2983
|
+
# ! format excel file
|
2984
|
+
import pandas as pd
|
2985
|
+
from datetime import datetime
|
2986
|
+
from openpyxl import load_workbook
|
2987
|
+
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
2988
|
+
from openpyxl.utils import get_column_letter
|
2989
|
+
from openpyxl.worksheet.datavalidation import DataValidation
|
2990
|
+
from openpyxl.comments import Comment
|
2991
|
+
from openpyxl.formatting.rule import ColorScaleRule
|
2992
|
+
|
2993
|
+
|
2994
|
+
def hex2argb(hex_color):
|
2995
|
+
"""
|
2996
|
+
Convert a hex color code to aARGB format required by openpyxl.
|
2997
|
+
|
2998
|
+
:param hex_color: A hex color code in the format #RRGGBB, RRGGBB, or aARRGGBB.
|
2999
|
+
:return: A hex color code in the format aARRGGBB.
|
3000
|
+
|
3001
|
+
# Example usage
|
3002
|
+
print(hex_to_argb("FFFF00")) # Outputs: FFFFFF00
|
3003
|
+
print(hex_to_argb("#DF4245")) # Outputs: FFFFFF00
|
3004
|
+
print(hex_to_argb("FF00FF00")) # Outputs: FF00FF00 (already in aARGB format)
|
3005
|
+
"""
|
3006
|
+
# Remove the hash if present
|
3007
|
+
if hex_color.startswith("#"):
|
3008
|
+
hex_color = hex_color[1:]
|
3009
|
+
|
3010
|
+
# Check if it's already in aARGB format (8 characters)
|
3011
|
+
if len(hex_color) == 8:
|
3012
|
+
return hex_color
|
3013
|
+
|
3014
|
+
# Otherwise, assume it's in RRGGBB format and prepend FF for opaque
|
3015
|
+
if len(hex_color) == 6:
|
3016
|
+
return f"FF{hex_color}"
|
3017
|
+
|
3018
|
+
raise ValueError(
|
3019
|
+
"Invalid hex color format. Use RRGGBB, #RRGGBB, or aARRGGBB format."
|
3020
|
+
)
|
3021
|
+
|
3022
|
+
|
3023
|
+
def convert_indices_to_range(row_slice, col_slice):
|
3024
|
+
"""Convert numerical row and column slices to Excel-style range strings."""
|
3025
|
+
start_row = row_slice.start + 1
|
3026
|
+
end_row = row_slice.stop if row_slice.stop is not None else None
|
3027
|
+
start_col = col_slice.start + 1
|
3028
|
+
end_col = col_slice.stop if col_slice.stop is not None else None
|
3029
|
+
|
3030
|
+
start_col_letter = get_column_letter(start_col)
|
3031
|
+
end_col_letter = get_column_letter(end_col) if end_col else None
|
3032
|
+
return (
|
3033
|
+
f"{start_col_letter}{start_row}:{end_col_letter}{end_row}"
|
3034
|
+
if end_col_letter
|
3035
|
+
else f"{start_col_letter}{start_row}"
|
3036
|
+
)
|
3037
|
+
|
3038
|
+
|
3039
|
+
def apply_format(ws, cell, cell_range):
|
3040
|
+
"""Apply cell formatting to a specified range."""
|
3041
|
+
cell_font, cell_fill, cell_alignment, border = None, None, None, None
|
3042
|
+
kws_cell = ["font", "fill", "alignment", "border"]
|
3043
|
+
for K, _ in cell.items():
|
3044
|
+
if strcmp(K, kws_cell)[0] == "font":
|
3045
|
+
#! font
|
3046
|
+
font_color = "000000"
|
3047
|
+
font_name = "Arial"
|
3048
|
+
font_underline = "none"
|
3049
|
+
font_size = 14
|
3050
|
+
font_bold = False
|
3051
|
+
font_strike = False
|
3052
|
+
font_italic = False
|
3053
|
+
kws_font = [
|
3054
|
+
"name",
|
3055
|
+
"size",
|
3056
|
+
"bold",
|
3057
|
+
"underline",
|
3058
|
+
"color",
|
3059
|
+
"strike",
|
3060
|
+
"italic",
|
3061
|
+
]
|
3062
|
+
for k_, v_ in cell.get(K, {}).items():
|
3063
|
+
if strcmp(k_, kws_font)[0] == "name":
|
3064
|
+
font_name = v_
|
3065
|
+
elif strcmp(k_, kws_font)[0] == "size":
|
3066
|
+
font_size = v_
|
3067
|
+
elif strcmp(k_, kws_font)[0] == "bold":
|
3068
|
+
font_bold = v_
|
3069
|
+
elif strcmp(k_, kws_font)[0] == "underline":
|
3070
|
+
font_underline = strcmp(v_, ["none", "single", "double"])[0]
|
3071
|
+
elif strcmp(k_, kws_font)[0] == "color":
|
3072
|
+
font_color = hex2argb(v_)
|
3073
|
+
elif strcmp(k_, kws_font)[0] == "strike":
|
3074
|
+
font_strike = v_
|
3075
|
+
elif strcmp(k_, kws_font)[0] == "italic":
|
3076
|
+
font_italic = v_
|
3077
|
+
|
3078
|
+
cell_font = Font(
|
3079
|
+
name=font_name,
|
3080
|
+
size=font_size,
|
3081
|
+
bold=font_bold,
|
3082
|
+
italic=font_italic,
|
3083
|
+
underline=font_underline,
|
3084
|
+
strike=font_strike,
|
3085
|
+
color=font_color,
|
3086
|
+
)
|
3087
|
+
|
3088
|
+
if strcmp(K, kws_cell)[0] == "fill":
|
3089
|
+
#! fill
|
3090
|
+
kws_fill = ["start_color", "end_color", "fill_type", "color"]
|
3091
|
+
kws_fill_type = [
|
3092
|
+
"darkVertical",
|
3093
|
+
"lightDown",
|
3094
|
+
"lightGrid",
|
3095
|
+
"solid",
|
3096
|
+
"darkDown",
|
3097
|
+
"lightGray",
|
3098
|
+
"lightUp",
|
3099
|
+
"gray0625",
|
3100
|
+
"lightVertical",
|
3101
|
+
"lightHorizontal",
|
3102
|
+
"darkHorizontal",
|
3103
|
+
"gray125",
|
3104
|
+
"darkUp",
|
3105
|
+
"mediumGray",
|
3106
|
+
"darkTrellis",
|
3107
|
+
"darkGray",
|
3108
|
+
"lightTrellis",
|
3109
|
+
"darkGrid",
|
3110
|
+
]
|
3111
|
+
start_color, end_color, fill_type = "FFFFFF", "FFFFFF", "solid" # default
|
3112
|
+
for k, v in cell.get(K, {}).items():
|
3113
|
+
if strcmp(k, kws_fill)[0] == "color":
|
3114
|
+
start_color, end_color = hex2argb(v), hex2argb(v)
|
3115
|
+
break
|
3116
|
+
for k, v in cell.get(K, {}).items():
|
3117
|
+
if strcmp(k, kws_fill)[0] == "start_color":
|
3118
|
+
start_color = hex2argb(v)
|
3119
|
+
elif strcmp(k, kws_fill)[0] == "end_color":
|
3120
|
+
end_color = hex2argb(v)
|
3121
|
+
elif strcmp(k, kws_fill)[0] == "fill_type":
|
3122
|
+
fill_type = strcmp(v, kws_fill_type)[0]
|
3123
|
+
cell_fill = PatternFill(
|
3124
|
+
start_color=start_color,
|
3125
|
+
end_color=end_color,
|
3126
|
+
fill_type=fill_type,
|
3127
|
+
)
|
3128
|
+
|
3129
|
+
if strcmp(K, kws_cell)[0] == "alignment":
|
3130
|
+
#! alignment
|
3131
|
+
# default
|
3132
|
+
align_horizontal = "general"
|
3133
|
+
align_vertical = "center"
|
3134
|
+
align_rot = 0
|
3135
|
+
align_wrap = False
|
3136
|
+
align_shrink = False
|
3137
|
+
align_indent = 0
|
3138
|
+
kws_align = [
|
3139
|
+
"horizontal",
|
3140
|
+
"ha",
|
3141
|
+
"vertical",
|
3142
|
+
"va",
|
3143
|
+
"text_rotation",
|
3144
|
+
"rotat",
|
3145
|
+
"rot",
|
3146
|
+
"wrap_text",
|
3147
|
+
"wrap",
|
3148
|
+
"shrink_to_fit",
|
3149
|
+
"shrink",
|
3150
|
+
"indent",
|
3151
|
+
]
|
3152
|
+
for k, v in cell.get(K, {}).items():
|
3153
|
+
if strcmp(k, kws_align)[0] in ["horizontal", "ha"]:
|
3154
|
+
align_horizontal = strcmp(
|
3155
|
+
v, ["general", "left", "right", "center"]
|
3156
|
+
)[0]
|
3157
|
+
elif strcmp(k, kws_align)[0] in ["vertical", "va"]:
|
3158
|
+
align_vertical = strcmp(v, ["top", "center", "bottom"])[0]
|
3159
|
+
elif strcmp(k, kws_align)[0] in ["text_rotation", "rotat", "rot"]:
|
3160
|
+
align_rot = v
|
3161
|
+
elif strcmp(k, kws_align)[0] in ["wrap_text", "wrap"]:
|
3162
|
+
align_wrap = v
|
3163
|
+
elif strcmp(k, kws_align)[0] in [
|
3164
|
+
"shrink_to_fit",
|
3165
|
+
"shrink",
|
3166
|
+
"wrap_text",
|
3167
|
+
"wrap",
|
3168
|
+
]:
|
3169
|
+
align_shrink = v
|
3170
|
+
elif strcmp(k, kws_align)[0] in ["indent"]:
|
3171
|
+
align_indent = v
|
3172
|
+
cell_alignment = Alignment(
|
3173
|
+
horizontal=align_horizontal,
|
3174
|
+
vertical=align_vertical,
|
3175
|
+
text_rotation=align_rot,
|
3176
|
+
wrap_text=align_wrap,
|
3177
|
+
shrink_to_fit=align_shrink,
|
3178
|
+
indent=align_indent,
|
3179
|
+
)
|
3180
|
+
|
3181
|
+
if strcmp(K, kws_cell)[0] == "border":
|
3182
|
+
#! border
|
3183
|
+
kws_border = [
|
3184
|
+
"color_left",
|
3185
|
+
"color_l",
|
3186
|
+
"color_right",
|
3187
|
+
"color_r",
|
3188
|
+
"color_top",
|
3189
|
+
"color_t",
|
3190
|
+
"color_bottom",
|
3191
|
+
"color_b",
|
3192
|
+
"color_diagonal",
|
3193
|
+
"color_d",
|
3194
|
+
"color_outline",
|
3195
|
+
"color_o",
|
3196
|
+
"color_vertical",
|
3197
|
+
"color_v",
|
3198
|
+
"color_horizontal",
|
3199
|
+
"color_h",
|
3200
|
+
"color",
|
3201
|
+
"style_left",
|
3202
|
+
"style_l",
|
3203
|
+
"style_right",
|
3204
|
+
"style_r",
|
3205
|
+
"style_top",
|
3206
|
+
"style_t",
|
3207
|
+
"style_bottom",
|
3208
|
+
"style_b",
|
3209
|
+
"style_diagonal",
|
3210
|
+
"style_d",
|
3211
|
+
"style_outline",
|
3212
|
+
"style_o",
|
3213
|
+
"style_vertical",
|
3214
|
+
"style_v",
|
3215
|
+
"style_horizontal",
|
3216
|
+
"style_h",
|
3217
|
+
"style",
|
3218
|
+
]
|
3219
|
+
# * border color
|
3220
|
+
border_color_l, border_color_r, border_color_t, border_color_b = (
|
3221
|
+
"FF000000",
|
3222
|
+
"FF000000",
|
3223
|
+
"FF000000",
|
3224
|
+
"FF000000",
|
3225
|
+
)
|
3226
|
+
border_color_d, border_color_o, border_color_v, border_color_h = (
|
3227
|
+
"FF000000",
|
3228
|
+
"FF000000",
|
3229
|
+
"FF000000",
|
3230
|
+
"FF000000",
|
3231
|
+
)
|
3232
|
+
# get colors config
|
3233
|
+
for k, v in cell.get(K, {}).items():
|
3234
|
+
if strcmp(k, kws_border)[0] in ["color"]:
|
3235
|
+
border_color_all = hex2argb(v)
|
3236
|
+
# 如果设置了color,表示其它的所有的都设置成为一样的
|
3237
|
+
# 然后再才开始自己定义其它的color
|
3238
|
+
border_color_l, border_color_r, border_color_t, border_color_b = (
|
3239
|
+
border_color_all,
|
3240
|
+
border_color_all,
|
3241
|
+
border_color_all,
|
3242
|
+
border_color_all,
|
3243
|
+
)
|
3244
|
+
border_color_d, border_color_o, border_color_v, border_color_h = (
|
3245
|
+
border_color_all,
|
3246
|
+
border_color_all,
|
3247
|
+
border_color_all,
|
3248
|
+
border_color_all,
|
3249
|
+
)
|
3250
|
+
elif strcmp(k, kws_border)[0] in ["color_left", "color_l"]:
|
3251
|
+
border_color_l = hex2argb(v)
|
3252
|
+
elif strcmp(k, kws_border)[0] in ["color_right", "color_r"]:
|
3253
|
+
border_color_r = hex2argb(v)
|
3254
|
+
elif strcmp(k, kws_border)[0] in ["color_top", "color_t"]:
|
3255
|
+
border_color_t = hex2argb(v)
|
3256
|
+
elif strcmp(k, kws_border)[0] in ["color_bottom", "color_b"]:
|
3257
|
+
border_color_b = hex2argb(v)
|
3258
|
+
elif strcmp(k, kws_border)[0] in ["color_diagonal", "color_d"]:
|
3259
|
+
border_color_d = hex2argb(v)
|
3260
|
+
elif strcmp(k, kws_border)[0] in ["color_outline", "color_o"]:
|
3261
|
+
border_color_o = hex2argb(v)
|
3262
|
+
elif strcmp(k, kws_border)[0] in ["color_vertical", "color_v"]:
|
3263
|
+
border_color_v = hex2argb(v)
|
3264
|
+
elif strcmp(k, kws_border)[0] in ["color_horizontal", "color_h"]:
|
3265
|
+
border_color_h = hex2argb(v)
|
3266
|
+
# *border style
|
3267
|
+
border_styles = [
|
3268
|
+
"thin",
|
3269
|
+
"medium",
|
3270
|
+
"thick",
|
3271
|
+
"dotted",
|
3272
|
+
"dashed",
|
3273
|
+
"hair",
|
3274
|
+
"mediumDashed",
|
3275
|
+
"dashDot",
|
3276
|
+
"dashDotDot",
|
3277
|
+
"slantDashDot",
|
3278
|
+
"none",
|
3279
|
+
]
|
3280
|
+
border_style_l, border_style_r, border_style_t, border_style_b = (
|
3281
|
+
None,
|
3282
|
+
None,
|
3283
|
+
None,
|
3284
|
+
None,
|
3285
|
+
)
|
3286
|
+
border_style_d, border_style_o, border_style_v, border_style_h = (
|
3287
|
+
None,
|
3288
|
+
None,
|
3289
|
+
None,
|
3290
|
+
None,
|
3291
|
+
)
|
3292
|
+
# get styles config
|
3293
|
+
for k, v in cell.get(K, {}).items():
|
3294
|
+
# if not "style" in k:
|
3295
|
+
# break
|
3296
|
+
if strcmp(k, kws_border)[0] in ["style"]:
|
3297
|
+
border_style_all = strcmp(v, border_styles)[0]
|
3298
|
+
# 如果设置了style,表示其它的所有的都设置成为一样的
|
3299
|
+
# 然后再才开始自己定义其它的style
|
3300
|
+
border_style_l, border_style_r, border_style_t, border_style_b = (
|
3301
|
+
border_style_all,
|
3302
|
+
border_style_all,
|
3303
|
+
border_style_all,
|
3304
|
+
border_style_all,
|
3305
|
+
)
|
3306
|
+
border_style_d, border_style_o, border_style_v, border_style_h = (
|
3307
|
+
border_style_all,
|
3308
|
+
border_style_all,
|
3309
|
+
border_style_all,
|
3310
|
+
border_style_all,
|
3311
|
+
)
|
3312
|
+
elif strcmp(k, kws_border)[0] in ["style_left", "style_l"]:
|
3313
|
+
border_style_l = strcmp(v, border_styles)[0]
|
3314
|
+
elif strcmp(k, kws_border)[0] in ["style_right", "style_r"]:
|
3315
|
+
border_style_r = strcmp(v, border_styles)[0]
|
3316
|
+
elif strcmp(k, kws_border)[0] in ["style_top", "style_t"]:
|
3317
|
+
border_style_t = strcmp(v, border_styles)[0]
|
3318
|
+
elif strcmp(k, kws_border)[0] in ["style_bottom", "style_b"]:
|
3319
|
+
border_style_b = strcmp(v, border_styles)[0]
|
3320
|
+
elif strcmp(k, kws_border)[0] in ["style_diagonal", "style_d"]:
|
3321
|
+
border_style_d = strcmp(v, border_styles)[0]
|
3322
|
+
elif strcmp(k, kws_border)[0] in ["style_outline", "style_o"]:
|
3323
|
+
border_style_o = strcmp(v, border_styles)[0]
|
3324
|
+
elif strcmp(k, kws_border)[0] in ["style_vertical", "style_v"]:
|
3325
|
+
border_style_v = strcmp(v, border_styles)[0]
|
3326
|
+
elif strcmp(k, kws_border)[0] in ["style_horizontal", "style_h"]:
|
3327
|
+
border_style_h = strcmp(v, border_styles)[0]
|
3328
|
+
# * apply border config
|
3329
|
+
border = Border(
|
3330
|
+
left=Side(border_style=border_style_l, color=border_color_l),
|
3331
|
+
right=Side(border_style=border_style_r, color=border_color_r),
|
3332
|
+
top=Side(border_style=border_style_t, color=border_color_t),
|
3333
|
+
bottom=Side(border_style=border_style_b, color=border_color_b),
|
3334
|
+
diagonal=Side(border_style=border_style_d, color=border_color_d),
|
3335
|
+
diagonal_direction=0,
|
3336
|
+
outline=Side(border_style=border_style_o, color=border_color_o),
|
3337
|
+
vertical=Side(border_style=border_style_v, color=border_color_v),
|
3338
|
+
horizontal=Side(border_style=border_style_h, color=border_color_h),
|
3339
|
+
)
|
3340
|
+
|
3341
|
+
#! final apply configs
|
3342
|
+
for row in ws[cell_range]:
|
3343
|
+
for cell_ in row:
|
3344
|
+
if cell_font:
|
3345
|
+
cell_.font = cell_font
|
3346
|
+
if cell_fill:
|
3347
|
+
cell_.fill = cell_fill
|
3348
|
+
if cell_alignment:
|
3349
|
+
cell_.alignment = cell_alignment
|
3350
|
+
if border:
|
3351
|
+
cell_.border = border
|
3352
|
+
|
3353
|
+
|
3354
|
+
def format_excel(
|
3355
|
+
df=None,
|
3356
|
+
filename=None,
|
3357
|
+
sheet_name=0,
|
3358
|
+
usage=False,
|
3359
|
+
cell=None, # dict: or list for multiple locs setting:
|
3360
|
+
width=None, # dict
|
3361
|
+
height=None, # dict e.g., {2: 50, 3: 25}, keys are columns
|
3362
|
+
height_max=25,
|
3363
|
+
merge=None, # tuple e.g., (slice(0, 1), slice(1, 3)),
|
3364
|
+
shade=None, # dict
|
3365
|
+
comment=None, # dict e.g., {(2, 4): "This is a comment"},
|
3366
|
+
link=None, # dict e.g., {(2, 2): "https://example.com"},
|
3367
|
+
protect=None, # dict
|
3368
|
+
number_format=None, # dict: e.g., {1:"0.00", 2:"#,##0",3:"0%",4:"$#,##0.00"}
|
3369
|
+
data_validation=None, # dict
|
3370
|
+
conditional_format=None, # dict
|
3371
|
+
index_default=False,
|
3372
|
+
**kwargs,
|
3373
|
+
):
|
3374
|
+
if not isinstance(df, pd.DataFrame):
|
3375
|
+
try:
|
3376
|
+
print(f"is loading file {os.path.basename(df)}")
|
3377
|
+
df = fload(df)
|
3378
|
+
except:
|
3379
|
+
print(f"check the fpath:{df}")
|
3380
|
+
if filename is None:
|
3381
|
+
filename = str(datetime.now().strftime("%y%m%d_%H.xlsx"))
|
3382
|
+
# !show usage:
|
3383
|
+
func_xample = """
|
3384
|
+
# Example usage
|
3385
|
+
data = {
|
3386
|
+
"Header 1": [10, 2000000, 30],
|
3387
|
+
"Header 2": [
|
3388
|
+
40000,
|
3389
|
+
'"start_color": "4F81BD", "end_color","start_color": "a1cd1e","start_color": "4F81BD", "end_color","start_color": "a1cd1e"',
|
3390
|
+
60,
|
3391
|
+
],
|
3392
|
+
"Header 3": [70, 80, 90],
|
3393
|
+
}
|
3394
|
+
df = pd.DataFrame(data)
|
3395
|
+
|
3396
|
+
|
3397
|
+
format_excel(
|
3398
|
+
df,
|
3399
|
+
filename="example.xlsx",
|
3400
|
+
sheet_name="Sheet1",
|
3401
|
+
cell=[
|
3402
|
+
{
|
3403
|
+
(slice(0, 1), slice(0, len(df.columns))): {
|
3404
|
+
"font": {
|
3405
|
+
"name": "Calibri", # Font name
|
3406
|
+
"size": 14, # Font size
|
3407
|
+
"bold": True, # Bold text
|
3408
|
+
"italic": False, # Italic text
|
3409
|
+
"underline": "single", # Underline (single, double)
|
3410
|
+
"color": "#FFFFFF", # Font color
|
3411
|
+
},
|
3412
|
+
"fill": {
|
3413
|
+
"start_color": "a1cd1e", # Starting color
|
3414
|
+
"end_color": "4F81BD", # Ending color (useful for gradients)
|
3415
|
+
"fill_type": "solid", # Fill type (solid, gradient, etc.)
|
3416
|
+
},
|
3417
|
+
"alignment": {
|
3418
|
+
"horizontal": "center", # Horizontal alignment (left, center, right)
|
3419
|
+
"vertical": "center", # Vertical alignment (top, center, bottom)
|
3420
|
+
"wrap_text": True, # Wrap text in the cell
|
3421
|
+
"shrink_to_fit": True, # Shrink text to fit within cell
|
3422
|
+
"text_rotation": 0, # Text rotation angle
|
3423
|
+
},
|
3424
|
+
"border": {
|
3425
|
+
"left": "thin",
|
3426
|
+
"right": "thin",
|
3427
|
+
"top": "thin",
|
3428
|
+
"bottom": "thin",
|
3429
|
+
"color": "000000", # Border color
|
3430
|
+
},
|
3431
|
+
}
|
3432
|
+
},
|
3433
|
+
{
|
3434
|
+
(slice(0, 3), slice(1, 3)): {
|
3435
|
+
"font": {
|
3436
|
+
"name": "Arial", # Font name
|
3437
|
+
"size": 12, # Font size
|
3438
|
+
"bold": False, # Bold text
|
3439
|
+
"italic": True, # Italic text
|
3440
|
+
"underline": None, # No underline
|
3441
|
+
"color": "#000000", # Font color
|
3442
|
+
},
|
3443
|
+
"fill": {
|
3444
|
+
"start_color": "#c61313", # Background color
|
3445
|
+
"end_color": "#490606", # End color
|
3446
|
+
"fill_type": "solid", # Fill type
|
3447
|
+
},
|
3448
|
+
"alignment": {
|
3449
|
+
"horizontal": "left", # Horizontal alignment
|
3450
|
+
"vertical": "top", # Vertical alignment
|
3451
|
+
"wrap_text": True, # Enable text wrapping
|
3452
|
+
"shrink_to_fit": False, # Disable shrink to fit
|
3453
|
+
"indent": 1, # Indentation level
|
3454
|
+
"text_rotation": 0, # Text rotation angle
|
3455
|
+
},
|
3456
|
+
"border": {
|
3457
|
+
"left": "thin", # Left border style
|
3458
|
+
"right": "thin", # Right border style
|
3459
|
+
"top": "thin", # Top border style
|
3460
|
+
"bottom": "thin", # Bottom border style
|
3461
|
+
"color": "000000", # Border color
|
3462
|
+
},
|
3463
|
+
}
|
3464
|
+
# * border settings
|
3465
|
+
# "thin": Thin border line
|
3466
|
+
# "medium": Medium border line
|
3467
|
+
# "thick": Thick border line
|
3468
|
+
# "dotted": Dotted border line
|
3469
|
+
# "dashed": Dashed border line
|
3470
|
+
# "hair": Hairline border (very thin)
|
3471
|
+
# "mediumDashed": Medium dashed border line
|
3472
|
+
# "dashDot": Dash-dot border line
|
3473
|
+
# "dashDotDot": Dash-dot-dot border line
|
3474
|
+
# "slantDashDot": Slant dash-dot border line
|
3475
|
+
},
|
3476
|
+
],
|
3477
|
+
width={2: 30}, # when it is None, it will automatic adjust width
|
3478
|
+
height={2: 50, 3: 25}, # keys are columns
|
3479
|
+
merge=(slice(0, 1), slice(1, 3)),
|
3480
|
+
shade={
|
3481
|
+
(slice(1, 4), slice(1, 3)): {
|
3482
|
+
"bg_color": "FFFF00", # Background color
|
3483
|
+
"pattern_type": "solid", # Fill pattern (e.g., solid, darkGrid, lightGrid)
|
3484
|
+
"fg_color": "#0000FF", # Foreground color, used in patterns
|
3485
|
+
"end_color": "0000FF", # End color, useful for gradients
|
3486
|
+
"fill_type": "solid", # Type of fill (solid, gradient, etc.)
|
3487
|
+
}
|
3488
|
+
},
|
3489
|
+
comment={(2, 4): "This is a comment"},
|
3490
|
+
link={(2, 2): "https://example.com"},
|
3491
|
+
protect={
|
3492
|
+
"password": "123", # Password for sheet protection
|
3493
|
+
"sheet": False, # True, # Protect the sheet
|
3494
|
+
"objects": False, # True, # Protect objects
|
3495
|
+
"scenarios": False, # True, # Protect scenarios
|
3496
|
+
"formatCells": False, # Disable formatting cells
|
3497
|
+
"formatColumns": False, # Disable formatting columns
|
3498
|
+
"formatRows": False, # Disable formatting rows
|
3499
|
+
"insertColumns": False, # Disable inserting columns
|
3500
|
+
"insertRows": False, # Disable inserting rows
|
3501
|
+
"deleteColumns": False, # Disable deleting columns
|
3502
|
+
"deleteRows": False, # Disable deleting rows
|
3503
|
+
},
|
3504
|
+
number_format={
|
3505
|
+
1: "0.00", # Two decimal places for column index 1
|
3506
|
+
2: "#,##0", # Thousands separator
|
3507
|
+
3: "0%", # Percentage format
|
3508
|
+
4: "$#,##0.00", # Currency format
|
3509
|
+
},
|
3510
|
+
data_validation={
|
3511
|
+
(slice(1, 2), slice(2, 10)): {
|
3512
|
+
"type": "list",
|
3513
|
+
"formula1": '"Option1,Option2,Option3"', # List of options
|
3514
|
+
"allow_blank": True,
|
3515
|
+
"showDropDown": True,
|
3516
|
+
"showErrorMessage": True,
|
3517
|
+
"errorTitle": "Invalid input",
|
3518
|
+
"error": "Please select a valid option.",
|
3519
|
+
}
|
3520
|
+
},
|
3521
|
+
conditional_format={
|
3522
|
+
(slice(1, 2), slice(2, 10)): [
|
3523
|
+
{
|
3524
|
+
"color_scale": {
|
3525
|
+
"start_type": "min", # Type of start point (min, max, percent)
|
3526
|
+
"start_color": "#FF0000", # Starting color
|
3527
|
+
"end_type": "max", # End type (min, max, percent)
|
3528
|
+
"end_color": "00FF00", # Ending color
|
3529
|
+
"mid_type": "percentile", # Midpoint type (optional)
|
3530
|
+
"mid_value": 50, # Midpoint value (optional)
|
3531
|
+
"mid_color": "FFFF00", # Midpoint color (optional)
|
3532
|
+
}
|
3533
|
+
}
|
3534
|
+
]
|
3535
|
+
},
|
3536
|
+
)
|
3537
|
+
"""
|
3538
|
+
if usage:
|
3539
|
+
print(func_xample)
|
3540
|
+
return None
|
3541
|
+
kwargs.pop("format", None) # 更好地跟fsave结合使用
|
3542
|
+
|
3543
|
+
# Save DataFrame to Excel file
|
3544
|
+
if not index_default:
|
3545
|
+
df.to_excel(filename, index=False, **kwargs)
|
3546
|
+
else:
|
3547
|
+
df.to_excel(filename, **kwargs)
|
3548
|
+
|
3549
|
+
wb = load_workbook(filename)
|
3550
|
+
ws = wb.worksheets[sheet_name]
|
3551
|
+
|
3552
|
+
# !Apply cell formatting
|
3553
|
+
if cell:
|
3554
|
+
if not isinstance(cell, list):
|
3555
|
+
cell = [cell]
|
3556
|
+
for cell_ in cell:
|
3557
|
+
for indices, format_options in cell_.items():
|
3558
|
+
cell_range = convert_indices_to_range(*indices)
|
3559
|
+
apply_format(ws, format_options, cell_range)
|
3560
|
+
|
3561
|
+
# !Apply cell shading
|
3562
|
+
if shade:
|
3563
|
+
for indices, shading in shade.items():
|
3564
|
+
cell_range = convert_indices_to_range(*indices)
|
3565
|
+
fill = PatternFill(
|
3566
|
+
start_color=hex2argb(shading.get("bg_color", "FFFFFF")),
|
3567
|
+
end_color=hex2argb(shading.get("end_color", "FFFFFF")),
|
3568
|
+
fill_type=shading.get("fill_type", "solid"),
|
3569
|
+
patternType=shading.get("pattern_type", "solid"),
|
3570
|
+
fgColor=hex2argb(shading.get("fg_color", "0000FF")),
|
3571
|
+
)
|
3572
|
+
for row in ws[cell_range]:
|
3573
|
+
for cell in row:
|
3574
|
+
cell.fill = fill
|
3575
|
+
|
3576
|
+
# !number formatting
|
3577
|
+
if number_format:
|
3578
|
+
for col_idx, fmt in number_format.items():
|
3579
|
+
col_letter = get_column_letter(col_idx)
|
3580
|
+
for cell in ws[col_letter][1:]: # Skip the header
|
3581
|
+
cell.number_format = fmt
|
3582
|
+
|
3583
|
+
# !widths
|
3584
|
+
if width is None: # automatic adust width
|
3585
|
+
for col in ws.columns:
|
3586
|
+
len_ = [len(str(cell.value)) for cell in col if cell.value]
|
3587
|
+
if len_:
|
3588
|
+
# scaling_factor = 1.2
|
3589
|
+
max_length = max(len_) # * scaling_factor
|
3590
|
+
ws.column_dimensions[get_column_letter(col[0].column)].width = (
|
3591
|
+
max_length
|
3592
|
+
)
|
3593
|
+
else:
|
3594
|
+
for col_idx, width_ in width.items():
|
3595
|
+
col_letter = get_column_letter(col_idx)
|
3596
|
+
ws.column_dimensions[col_letter].width = width_
|
3597
|
+
|
3598
|
+
# !heights
|
3599
|
+
if height is None: # automatic adust height
|
3600
|
+
for row in ws.iter_rows(min_row=1, max_row=ws.max_row):
|
3601
|
+
max_height = height_max
|
3602
|
+
for cell in row:
|
3603
|
+
if cell.value:
|
3604
|
+
lines = str(cell.value).split("\n")
|
3605
|
+
max_line_length = max(len(line) for line in lines)
|
3606
|
+
estimated_height = 15 * len(lines)
|
3607
|
+
if max_line_length > 20:
|
3608
|
+
estimated_height += 5 * (max_line_length // 20)
|
3609
|
+
max_height = max(max_height, estimated_height)
|
3610
|
+
ws.row_dimensions[row[0].row].height = max_height
|
3611
|
+
else:
|
3612
|
+
for row, height_ in height.items():
|
3613
|
+
ws.row_dimensions[row].height = height_
|
3614
|
+
|
3615
|
+
# !Merge cells using slice indices
|
3616
|
+
if merge:
|
3617
|
+
if isinstance(merge, tuple):
|
3618
|
+
merge = [merge]
|
3619
|
+
for indices in merge:
|
3620
|
+
# Ensure indices are slice objects
|
3621
|
+
if len(indices) == 2:
|
3622
|
+
row_slice, col_slice = indices
|
3623
|
+
merge_range = convert_indices_to_range(row_slice, col_slice)
|
3624
|
+
ws.merge_cells(merge_range)
|
3625
|
+
elif len(indices) == 4:
|
3626
|
+
start_row, start_col, end_row, end_col = indices
|
3627
|
+
# Convert column numbers to letters (e.g., 1 -> 'A')
|
3628
|
+
start_cell = f"{get_column_letter(start_col)}{start_row}"
|
3629
|
+
end_cell = f"{get_column_letter(end_col)}{end_row}"
|
3630
|
+
merge_range = f"{start_cell}:{end_cell}"
|
3631
|
+
ws.merge_cells(merge_range)
|
3632
|
+
else:
|
3633
|
+
raise ValueError(
|
3634
|
+
f"两种方式: 1. format: (start_row, start_col, end_row, end_col), 2. format: (slice(0, 3), slice(1, 2))"
|
3635
|
+
)
|
3636
|
+
|
3637
|
+
# !Add comment
|
3638
|
+
if comment:
|
3639
|
+
if not isinstance(comment, list):
|
3640
|
+
comment = [comment]
|
3641
|
+
for comment_ in comment:
|
3642
|
+
for (row, col), comment_str in comment_.items():
|
3643
|
+
ws.cell(row=row, column=col).comment = Comment(comment_str, "Author")
|
3644
|
+
|
3645
|
+
# !Add link
|
3646
|
+
if link:
|
3647
|
+
if not isinstance(link, list):
|
3648
|
+
link = [link]
|
3649
|
+
for link_ in link:
|
3650
|
+
for (row, col), link_str in link_.items():
|
3651
|
+
ws.cell(row=row, column=col).hyperlink = link_str
|
3652
|
+
|
3653
|
+
# !Apply data validation
|
3654
|
+
if data_validation:
|
3655
|
+
for indices, validation in data_validation.items():
|
3656
|
+
cell_range = convert_indices_to_range(*indices)
|
3657
|
+
dv = DataValidation(**validation)
|
3658
|
+
ws.add_data_validation(dv)
|
3659
|
+
dv.add(cell_range)
|
3660
|
+
|
3661
|
+
# !Protect sheet with a password
|
3662
|
+
if protect:
|
3663
|
+
ws.protection.password = protect.get("password", False)
|
3664
|
+
ws.protection.objects = protect.get("objects", True)
|
3665
|
+
ws.protection.sheet = protect.get("sheet", True)
|
3666
|
+
ws.protection.scenarios = protect.get("scenarios", True)
|
3667
|
+
ws.protection.formatCells = protect.get("formatCells", False)
|
3668
|
+
ws.protection.formatColumns = protect.get("formatColumns", False)
|
3669
|
+
ws.protection.formatRows = protect.get("formatRows", False)
|
3670
|
+
ws.protection.insertColumns = protect.get("insertColumns", True)
|
3671
|
+
ws.protection.insertRows = protect.get("insertRows", True)
|
3672
|
+
ws.protection.deleteColumns = protect.get("deleteColumns", True)
|
3673
|
+
ws.protection.deleteRows = protect.get("deleteRows", True)
|
3674
|
+
|
3675
|
+
# !conditional formatting
|
3676
|
+
if conditional_format:
|
3677
|
+
for indices, rules in conditional_format.items():
|
3678
|
+
cell_range = convert_indices_to_range(*indices)
|
3679
|
+
for rule in rules:
|
3680
|
+
if "color_scale" in rule:
|
3681
|
+
color_scale = rule["color_scale"]
|
3682
|
+
start_color = hex2argb(color_scale.get("start_color", "FFFFFF"))
|
3683
|
+
mid_color = hex2argb(color_scale.get("mid_color", "FFFFFF"))
|
3684
|
+
end_color = hex2argb(color_scale.get("end_color", "FFFFFF"))
|
3685
|
+
|
3686
|
+
color_scale_rule = ColorScaleRule(
|
3687
|
+
start_type=color_scale.get("start_type", "min"),
|
3688
|
+
start_value=color_scale.get("start_value"),
|
3689
|
+
start_color=start_color,
|
3690
|
+
mid_type=color_scale.get("mid_type"),
|
3691
|
+
mid_value=color_scale.get("mid_value"),
|
3692
|
+
mid_color=mid_color,
|
3693
|
+
end_type=color_scale.get("end_type", "max"),
|
3694
|
+
end_value=color_scale.get("end_value"),
|
3695
|
+
end_color=end_color,
|
3696
|
+
)
|
3697
|
+
ws.conditional_formatting.add(cell_range, color_scale_rule)
|
3698
|
+
# Save the workbook
|
3699
|
+
wb.save(filename)
|
3700
|
+
print(f"Formatted Excel file saved as:\n{filename}")
|