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 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
- df = pd.DataFrame(data)
1332
- df.to_excel(fpath, **kwargs)
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}")