tksheet 7.4.6__py3-none-any.whl → 7.4.8__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.
tksheet/functions.py CHANGED
@@ -20,6 +20,7 @@ from .tksheet_types import AnyIter
20
20
 
21
21
  unpickle_obj = pickle.loads
22
22
  lines_re = re.compile(r"[^\n]+")
23
+ ORD_A = ord("A")
23
24
 
24
25
 
25
26
  def wrap_text(
@@ -202,6 +203,12 @@ def recursive_bind(widget: tk.Misc, event: str, callback: Callable) -> None:
202
203
  recursive_bind(child, event, callback)
203
204
 
204
205
 
206
+ def recursive_unbind(widget: tk.Misc, event: str) -> None:
207
+ widget.unbind(event)
208
+ for child in widget.winfo_children():
209
+ recursive_unbind(child, event)
210
+
211
+
205
212
  def tksheet_type_error(kwarg: str, valid_types: list[str], not_type: Any) -> str:
206
213
  valid_types = ", ".join(f"{type_}" for type_ in valid_types)
207
214
  return f"Argument '{kwarg}' must be one of the following types: {valid_types}, not {type(not_type)}."
@@ -333,7 +340,7 @@ def event_dict(
333
340
  selection_boxes={} if boxes is None else boxes,
334
341
  selected=() if selected is None else selected,
335
342
  being_selected=() if being_selected is None else being_selected,
336
- data=[] if data is None else data,
343
+ data={} if data is None else data,
337
344
  key="" if key is None else key,
338
345
  value=None if value is None else value,
339
346
  loc=() if loc is None else loc,
@@ -413,6 +420,27 @@ def push_n(num: int, sorted_seq: Sequence[int]) -> int:
413
420
  return num + lo
414
421
 
415
422
 
423
+ def get_menu_kwargs(ops: DotDict[str, Any]) -> DotDict[str, Any]:
424
+ return DotDict(
425
+ {
426
+ "font": ops.table_font,
427
+ "foreground": ops.popup_menu_fg,
428
+ "background": ops.popup_menu_bg,
429
+ "activebackground": ops.popup_menu_highlight_bg,
430
+ "activeforeground": ops.popup_menu_highlight_fg,
431
+ }
432
+ )
433
+
434
+
435
+ def get_bg_fg(ops: DotDict[str, Any]) -> dict[str, str]:
436
+ return {
437
+ "bg": ops.table_editor_bg,
438
+ "fg": ops.table_editor_fg,
439
+ "select_bg": ops.table_editor_select_bg,
440
+ "select_fg": ops.table_editor_select_fg,
441
+ }
442
+
443
+
416
444
  def get_dropdown_kwargs(
417
445
  values: list[Any] | None = None,
418
446
  set_value: Any = None,
@@ -517,9 +545,8 @@ def force_bool(o: Any) -> bool:
517
545
  def alpha2idx(a: str) -> int | None:
518
546
  try:
519
547
  n = 0
520
- orda = ord("A")
521
548
  for c in a.upper():
522
- n = n * 26 + ord(c) - orda + 1
549
+ n = n * 26 + ord(c) - ORD_A + 1
523
550
  return n - 1
524
551
  except Exception:
525
552
  return None
@@ -645,7 +672,7 @@ def color_tup(color: str) -> tuple[int, int, int]:
645
672
  return int(res[1:3], 16), int(res[3:5], 16), int(res[5:], 16)
646
673
 
647
674
 
648
- def down_cell_within_box(
675
+ def cell_down_within_box(
649
676
  r: int,
650
677
  c: int,
651
678
  r1: int,
@@ -909,45 +936,68 @@ def gen_coords(
909
936
 
910
937
 
911
938
  def box_gen_coords(
912
- start_row: int,
913
- start_col: int,
914
- total_cols: int,
915
- total_rows: int,
916
- reverse: bool = False,
939
+ from_r: int,
940
+ from_c: int,
941
+ upto_r: int,
942
+ upto_c: int,
943
+ start_r: int,
944
+ start_c: int,
945
+ reverse: bool,
946
+ all_rows_displayed: bool = True,
947
+ all_cols_displayed: bool = True,
948
+ displayed_cols: list[int] | None = None,
949
+ displayed_rows: list[int] | None = None,
950
+ no_wrap: bool = False,
917
951
  ) -> Generator[tuple[int, int]]:
918
- if reverse:
919
- # yield start cell
920
- yield (start_row, start_col)
921
- # yield any remaining cells in the starting row before the start column
922
- if start_col:
923
- for col in reversed(range(start_col)):
924
- yield (start_row, col)
925
- # yield any cells above start row
926
- for row in reversed(range(start_row)):
927
- for col in reversed(range(total_cols)):
928
- yield (row, col)
929
- # yield cells from bottom of table upward
930
- for row in range(total_rows - 1, start_row, -1):
931
- for col in reversed(range(total_cols)):
932
- yield (row, col)
933
- # yield any remaining cells in start row
934
- for col in range(total_cols - 1, start_col, -1):
935
- yield (start_row, col)
952
+ # Initialize empty lists if None
953
+ if displayed_rows is None:
954
+ displayed_rows = []
955
+ if displayed_cols is None:
956
+ displayed_cols = []
957
+
958
+ # Adjust row indices based on displayed_rows
959
+ if not all_rows_displayed:
960
+ from_r = displayed_rows[from_r]
961
+ upto_r = displayed_rows[upto_r - 1] + 1
962
+ start_r = displayed_rows[start_r]
963
+ # Adjust column indices based on displayed_cols (fixing original bug)
964
+ if not all_cols_displayed:
965
+ from_c = displayed_cols[from_c]
966
+ upto_c = displayed_cols[upto_c - 1] + 1
967
+ start_c = displayed_cols[start_c]
968
+
969
+ if not reverse:
970
+ # Forward direction
971
+ # Part 1: From (start_r, start_c) to the end of the box
972
+ for c in range(start_c, upto_c):
973
+ yield (start_r, c)
974
+ for r in range(start_r + 1, upto_r):
975
+ for c in range(from_c, upto_c):
976
+ yield (r, c)
977
+ if not no_wrap:
978
+ # Part 2: Wrap around from beginning to just before (start_r, start_c)
979
+ for r in range(from_r, start_r):
980
+ for c in range(from_c, upto_c):
981
+ yield (r, c)
982
+ if start_c > from_c: # Only if there are columns before start_c
983
+ for c in range(from_c, start_c):
984
+ yield (start_r, c)
936
985
  else:
937
- # Yield cells from the start position to the end of the current row
938
- for col in range(start_col, total_cols):
939
- yield (start_row, col)
940
- # yield from the next row to the last row
941
- for row in range(start_row + 1, total_rows):
942
- for col in range(total_cols):
943
- yield (row, col)
944
- # yield from the beginning up to the start
945
- for row in range(start_row):
946
- for col in range(total_cols):
947
- yield (row, col)
948
- # yield any remaining cells in the starting row before the start column
949
- for col in range(start_col):
950
- yield (start_row, col)
986
+ # Reverse direction
987
+ # Part 1: From (start_r, start_c) backwards to the start of the box
988
+ for c in range(start_c, from_c - 1, -1):
989
+ yield (start_r, c)
990
+ for r in range(start_r - 1, from_r - 1, -1):
991
+ for c in range(upto_c - 1, from_c - 1, -1):
992
+ yield (r, c)
993
+ if not no_wrap:
994
+ # Part 2: Wrap around from end to just after (start_r, start_c)
995
+ for r in range(upto_r - 1, start_r, -1):
996
+ for c in range(upto_c - 1, from_c - 1, -1):
997
+ yield (r, c)
998
+ if start_c < upto_c - 1: # Only if there are columns after start_c
999
+ for c in range(upto_c - 1, start_c, -1):
1000
+ yield (start_r, c)
951
1001
 
952
1002
 
953
1003
  def next_cell(
@@ -1108,6 +1158,37 @@ def coords_to_span(
1108
1158
  )
1109
1159
 
1110
1160
 
1161
+ PATTERN_ROW = re.compile(r"^(\d+)$") # "1"
1162
+ PATTERN_COL = re.compile(r"^([A-Z]+)$") # "A"
1163
+ PATTERN_CELL = re.compile(r"^([A-Z]+)(\d+)$") # "A1"
1164
+ PATTERN_RANGE = re.compile(r"^([A-Z]+)(\d+):([A-Z]+)(\d+)$") # "A1:B2"
1165
+ PATTERN_ROW_RANGE = re.compile(r"^(\d+):(\d+)$") # "1:2"
1166
+ PATTERN_ROW_START = re.compile(r"^(\d+):$") # "2:"
1167
+ PATTERN_ROW_END = re.compile(r"^:(\d+)$") # ":2"
1168
+ PATTERN_COL_RANGE = re.compile(r"^([A-Z]+):([A-Z]+)$") # "A:B"
1169
+ PATTERN_COL_START = re.compile(r"^([A-Z]+):$") # "A:"
1170
+ PATTERN_COL_END = re.compile(r"^:([A-Z]+)$") # ":B"
1171
+ PATTERN_CELL_START = re.compile(r"^([A-Z]+)(\d+):$") # "A1:"
1172
+ PATTERN_CELL_END = re.compile(r"^:([A-Z]+)(\d+)$") # ":B1"
1173
+ PATTERN_CELL_COL = re.compile(r"^([A-Z]+)(\d+):([A-Z]+)$") # "A1:B"
1174
+ PATTERN_CELL_ROW = re.compile(r"^([A-Z]+)(\d+):(\d+)$") # "A1:2"
1175
+ PATTERN_ALL = re.compile(r"^:$") # ":"
1176
+
1177
+
1178
+ def span_a2i(a: str) -> int | None:
1179
+ n = 0
1180
+ for c in a:
1181
+ n = n * 26 + ord(c) - ORD_A + 1
1182
+ return n - 1
1183
+
1184
+
1185
+ def span_a2n(a: str) -> int | None:
1186
+ n = 0
1187
+ for c in a:
1188
+ n = n * 26 + ord(c) - ORD_A + 1
1189
+ return n
1190
+
1191
+
1111
1192
  def key_to_span(
1112
1193
  key: (
1113
1194
  str
@@ -1120,76 +1201,32 @@ def key_to_span(
1120
1201
  spans: dict[str, Span],
1121
1202
  widget: Any = None,
1122
1203
  ) -> Span:
1204
+ """
1205
+ Convert various input types to a Span object representing a 2D range.
1206
+
1207
+ Args:
1208
+ key: The input to convert (str, int, slice, sequence, or None).
1209
+ spans: A dictionary of named spans (e.g., {"<name>": Span(...)}).
1210
+ widget: Optional widget context for span creation.
1211
+
1212
+ Returns:
1213
+ A Span object or an error message string if the key is invalid.
1214
+ """
1215
+ # Handle Span object directly
1123
1216
  if isinstance(key, Span):
1124
1217
  return key
1218
+
1219
+ # Handle None as full span
1125
1220
  elif key is None:
1126
- key = (None, None, None, None)
1221
+ return coords_to_span(widget=widget, from_r=None, from_c=None, upto_r=None, upto_c=None)
1222
+
1223
+ # Validate input type
1127
1224
  elif not isinstance(key, (str, int, slice, list, tuple)):
1128
- return f"Key type must be either str, int, list, tuple or slice, not '{type(key)}'."
1225
+ return f"Key type must be either str, int, list, tuple or slice, not '{type(key).__name__}'."
1226
+
1129
1227
  try:
1130
- if isinstance(key, (list, tuple)):
1131
- if isinstance(key[0], int) or key[0] is None:
1132
- if len(key) == 2:
1133
- """
1134
- (int | None, int | None) -
1135
- (0, 0) - row 0, column 0 - the first cell
1136
- (0, None) - row 0, all columns
1137
- (None, 0) - column 0, all rows
1138
- """
1139
- return span_dict(
1140
- from_r=key[0] if isinstance(key[0], int) else 0,
1141
- from_c=key[1] if isinstance(key[1], int) else 0,
1142
- upto_r=(key[0] + 1) if isinstance(key[0], int) else None,
1143
- upto_c=(key[1] + 1) if isinstance(key[1], int) else None,
1144
- widget=widget,
1145
- )
1146
-
1147
- elif len(key) == 4:
1148
- """
1149
- (int | None, int | None, int | None, int | None) -
1150
- (from row, from column, up to row, up to column)
1151
- """
1152
- return coords_to_span(
1153
- widget=widget,
1154
- from_r=key[0],
1155
- from_c=key[1],
1156
- upto_r=key[2],
1157
- upto_c=key[3],
1158
- )
1159
- # return span_dict(
1160
- # from_r=key[0] if isinstance(key[0], int) else 0,
1161
- # from_c=key[1] if isinstance(key[1], int) else 0,
1162
- # upto_r=key[2] if isinstance(key[2], int) else None,
1163
- # upto_c=key[3] if isinstance(key[3], int) else None,
1164
- # widget=widget,
1165
- # )
1166
-
1167
- elif isinstance(key[0], (list, tuple)):
1168
- """
1169
- ((int | None, int | None), (int | None, int | None))
1170
-
1171
- First Sequence is start row and column
1172
- Second Sequence is up to but not including row and column
1173
- """
1174
- return coords_to_span(
1175
- widget=widget,
1176
- from_r=key[0][0],
1177
- from_c=key[0][1],
1178
- upto_r=key[1][0],
1179
- upto_c=key[1][1],
1180
- )
1181
- # return span_dict(
1182
- # from_r=key[0][0] if isinstance(key[0][0], int) else 0,
1183
- # from_c=key[0][1] if isinstance(key[0][1], int) else 0,
1184
- # upto_r=key[1][0],
1185
- # upto_c=key[1][1],
1186
- # widget=widget,
1187
- # )
1188
-
1189
- elif isinstance(key, int):
1190
- """
1191
- [int] - Whole row at that index
1192
- """
1228
+ # Integer key: whole row
1229
+ if isinstance(key, int):
1193
1230
  return span_dict(
1194
1231
  from_r=key,
1195
1232
  from_c=None,
@@ -1198,29 +1235,8 @@ def key_to_span(
1198
1235
  widget=widget,
1199
1236
  )
1200
1237
 
1238
+ # Slice key: row range
1201
1239
  elif isinstance(key, slice):
1202
- """
1203
- [slice]
1204
- """
1205
- """
1206
- [:] - All rows
1207
- """
1208
- if key.start is None and key.stop is None:
1209
- """
1210
- [:]
1211
- """
1212
- return span_dict(
1213
- from_r=0,
1214
- from_c=None,
1215
- upto_r=None,
1216
- upto_c=None,
1217
- widget=widget,
1218
- )
1219
- """
1220
- [1:3] - Rows 1, 2
1221
- [:2] - Rows up to but not including 2
1222
- [2:] - Rows starting from and including 2
1223
- """
1224
1240
  start = 0 if key.start is None else key.start
1225
1241
  return span_dict(
1226
1242
  from_r=start,
@@ -1230,251 +1246,187 @@ def key_to_span(
1230
1246
  widget=widget,
1231
1247
  )
1232
1248
 
1249
+ # Sequence key: various span formats
1250
+ elif isinstance(key, (list, tuple)):
1251
+ if (
1252
+ len(key) == 2
1253
+ and (isinstance(key[0], int) or key[0] is None)
1254
+ and (isinstance(key[1], int) or key[1] is None)
1255
+ ):
1256
+ # Single cell or partial span: (row, col)
1257
+ r_int = isinstance(key[0], int)
1258
+ c_int = isinstance(key[1], int)
1259
+ return span_dict(
1260
+ from_r=key[0] if r_int else 0,
1261
+ from_c=key[1] if c_int else 0,
1262
+ upto_r=key[0] + 1 if r_int else None,
1263
+ upto_c=key[1] + 1 if c_int else None,
1264
+ widget=widget,
1265
+ )
1266
+ elif len(key) == 4:
1267
+ # Full span coordinates: (from_r, from_c, upto_r, upto_c)
1268
+ return coords_to_span(
1269
+ widget=widget,
1270
+ from_r=key[0],
1271
+ from_c=key[1],
1272
+ upto_r=key[2],
1273
+ upto_c=key[3],
1274
+ )
1275
+ elif len(key) == 2 and all(isinstance(k, (list, tuple)) for k in key):
1276
+ # Start and end points: ((from_r, from_c), (upto_r, upto_c))
1277
+ return coords_to_span(
1278
+ widget=widget,
1279
+ from_r=key[0][0],
1280
+ from_c=key[0][1],
1281
+ upto_r=key[1][0],
1282
+ upto_c=key[1][1],
1283
+ )
1284
+
1285
+ # String key: parse various span formats
1233
1286
  elif isinstance(key, str):
1234
1287
  if not key:
1235
- key = ":"
1236
-
1237
- if key.startswith("<") and key.endswith(">"):
1238
- if (key := key[1:-1]) in spans:
1239
- """
1240
- ["<name>"] - Surrounded by "<" ">" cells from a named range
1241
- """
1242
- return spans[key]
1243
- return f"'{key}' not in named spans."
1244
-
1245
- key = key.upper()
1246
-
1247
- if key.isdigit():
1248
- """
1249
- ["1"] - Row 0
1250
- """
1288
+ # Empty string treated as full span
1251
1289
  return span_dict(
1252
- from_r=int(key) - 1,
1290
+ from_r=0,
1253
1291
  from_c=None,
1254
- upto_r=int(key),
1292
+ upto_r=None,
1255
1293
  upto_c=None,
1256
1294
  widget=widget,
1257
1295
  )
1296
+ elif key.startswith("<") and key.endswith(">"):
1297
+ name = key[1:-1]
1298
+ return spans.get(name, f"'{name}' not in named spans.")
1299
+
1300
+ key = key.upper() # Case-insensitive parsing
1258
1301
 
1259
- if key.isalpha():
1260
- """
1261
- ["A"] - Column 0
1262
- """
1302
+ # Match string against precompiled patterns
1303
+ if m := PATTERN_ROW.match(key):
1304
+ return span_dict(
1305
+ from_r=int(m[1]) - 1,
1306
+ from_c=None,
1307
+ upto_r=int(m[1]),
1308
+ upto_c=None,
1309
+ widget=widget,
1310
+ )
1311
+ elif m := PATTERN_COL.match(key):
1263
1312
  return span_dict(
1264
1313
  from_r=None,
1265
- from_c=alpha2idx(key),
1314
+ from_c=span_a2i(m[1]),
1266
1315
  upto_r=None,
1267
- upto_c=alpha2idx(key) + 1,
1316
+ upto_c=span_a2n(m[1]),
1268
1317
  widget=widget,
1269
1318
  )
1270
-
1271
- splitk = key.split(":")
1272
- if len(splitk) > 2:
1273
- return f"'{key}' could not be converted to span."
1274
-
1275
- if len(splitk) == 1 and not splitk[0].isdigit() and not splitk[0].isalpha() and not splitk[0][0].isdigit():
1276
- """
1277
- ["A1"] - Cell (0, 0)
1278
- """
1279
- keys_digits = re.search(r"\d", splitk[0])
1280
- if keys_digits:
1281
- digits_start = keys_digits.start()
1282
- if not digits_start:
1283
- return f"'{key}' could not be converted to span."
1284
- if digits_start:
1285
- key_row = splitk[0][digits_start:]
1286
- key_column = splitk[0][:digits_start]
1287
- return span_dict(
1288
- from_r=int(key_row) - 1,
1289
- from_c=alpha2idx(key_column),
1290
- upto_r=int(key_row),
1291
- upto_c=alpha2idx(key_column) + 1,
1292
- widget=widget,
1293
- )
1294
-
1295
- if not splitk[0] and not splitk[1]:
1296
- """
1297
- [":"] - All rows
1298
- """
1319
+ elif m := PATTERN_CELL.match(key):
1320
+ c = span_a2i(m[1])
1321
+ r = int(m[2]) - 1
1299
1322
  return span_dict(
1300
- from_r=0,
1301
- from_c=None,
1302
- upto_r=None,
1303
- upto_c=None,
1323
+ from_r=r,
1324
+ from_c=c,
1325
+ upto_r=r + 1,
1326
+ upto_c=c + 1,
1304
1327
  widget=widget,
1305
1328
  )
1306
-
1307
- if splitk[0].isdigit() and not splitk[1]:
1308
- """
1309
- ["2:"] - Rows starting from and including 1
1310
- """
1329
+ elif m := PATTERN_RANGE.match(key):
1311
1330
  return span_dict(
1312
- from_r=int(splitk[0]) - 1,
1331
+ from_r=int(m[2]) - 1,
1332
+ from_c=span_a2i(m[1]),
1333
+ upto_r=int(m[4]),
1334
+ upto_c=span_a2n(m[3]),
1335
+ widget=widget,
1336
+ )
1337
+ elif m := PATTERN_ROW_RANGE.match(key):
1338
+ return span_dict(
1339
+ from_r=int(m[1]) - 1,
1313
1340
  from_c=None,
1314
- upto_r=None,
1341
+ upto_r=int(m[2]),
1315
1342
  upto_c=None,
1316
1343
  widget=widget,
1317
1344
  )
1318
-
1319
- if splitk[1].isdigit() and not splitk[0]:
1320
- """
1321
- [":2"] - Rows up to and including 1
1322
- """
1345
+ elif m := PATTERN_ROW_START.match(key):
1323
1346
  return span_dict(
1324
- from_r=0,
1347
+ from_r=int(m[1]) - 1,
1325
1348
  from_c=None,
1326
- upto_r=int(splitk[1]),
1349
+ upto_r=None,
1327
1350
  upto_c=None,
1328
1351
  widget=widget,
1329
1352
  )
1330
-
1331
- if splitk[0].isdigit() and splitk[1].isdigit():
1332
- """
1333
- ["1:2"] - Rows 0, 1
1334
- """
1353
+ elif m := PATTERN_ROW_END.match(key):
1335
1354
  return span_dict(
1336
- from_r=int(splitk[0]) - 1,
1355
+ from_r=0,
1337
1356
  from_c=None,
1338
- upto_r=int(splitk[1]),
1357
+ upto_r=int(m[1]),
1339
1358
  upto_c=None,
1340
1359
  widget=widget,
1341
1360
  )
1342
-
1343
- if splitk[0].isalpha() and not splitk[1]:
1344
- """
1345
- ["B:"] - Columns starting from and including 2
1346
- """
1361
+ elif m := PATTERN_COL_RANGE.match(key):
1347
1362
  return span_dict(
1348
1363
  from_r=None,
1349
- from_c=alpha2idx(splitk[0]),
1364
+ from_c=span_a2i(m[1]),
1350
1365
  upto_r=None,
1351
- upto_c=None,
1366
+ upto_c=span_a2n(m[2]),
1352
1367
  widget=widget,
1353
1368
  )
1354
-
1355
- if splitk[1].isalpha() and not splitk[0]:
1356
- """
1357
- [":B"] - Columns up to and including 2
1358
- """
1369
+ elif m := PATTERN_COL_START.match(key):
1359
1370
  return span_dict(
1360
1371
  from_r=None,
1361
- from_c=0,
1372
+ from_c=span_a2i(m[1]),
1362
1373
  upto_r=None,
1363
- upto_c=alpha2idx(splitk[1]) + 1,
1374
+ upto_c=None,
1364
1375
  widget=widget,
1365
1376
  )
1366
-
1367
- if splitk[0].isalpha() and splitk[1].isalpha():
1368
- """
1369
- ["A:B"] - Columns 0, 1
1370
- """
1377
+ elif m := PATTERN_COL_END.match(key):
1371
1378
  return span_dict(
1372
1379
  from_r=None,
1373
- from_c=alpha2idx(splitk[0]),
1380
+ from_c=0,
1374
1381
  upto_r=None,
1375
- upto_c=alpha2idx(splitk[1]) + 1,
1382
+ upto_c=span_a2n(m[1]),
1376
1383
  widget=widget,
1377
1384
  )
1378
-
1379
- m1 = re.search(r"\d", splitk[0])
1380
- m2 = re.search(r"\d", splitk[1])
1381
- m1start = m1.start() if m1 else None
1382
- m2start = m2.start() if m2 else None
1383
- if m1start and m2start:
1384
- """
1385
- ["A1:B1"] - Cells (0, 0), (0, 1)
1386
- """
1387
- c1 = splitk[0][:m1start]
1388
- r1 = splitk[0][m1start:]
1389
- c2 = splitk[1][:m2start]
1390
- r2 = splitk[1][m2start:]
1385
+ elif m := PATTERN_CELL_START.match(key):
1391
1386
  return span_dict(
1392
- from_r=int(r1) - 1,
1393
- from_c=alpha2idx(c1),
1394
- upto_r=int(r2),
1395
- upto_c=alpha2idx(c2) + 1,
1387
+ from_r=int(m[2]) - 1,
1388
+ from_c=span_a2i(m[1]),
1389
+ upto_r=None,
1390
+ upto_c=None,
1396
1391
  widget=widget,
1397
1392
  )
1398
-
1399
- if not splitk[0] and m2start:
1400
- """
1401
- [":B1"] - Cells (0, 0), (0, 1)
1402
- """
1403
- c2 = splitk[1][:m2start]
1404
- r2 = splitk[1][m2start:]
1393
+ elif m := PATTERN_CELL_END.match(key):
1405
1394
  return span_dict(
1406
1395
  from_r=0,
1407
1396
  from_c=0,
1408
- upto_r=int(r2),
1409
- upto_c=alpha2idx(c2) + 1,
1397
+ upto_r=int(m[2]),
1398
+ upto_c=span_a2n(m[1]),
1410
1399
  widget=widget,
1411
1400
  )
1412
-
1413
- if not splitk[1] and m1start:
1414
- """
1415
- ["A1:"] - Cells starting from and including (0, 0)
1416
- """
1417
- c1 = splitk[0][:m1start]
1418
- r1 = splitk[0][m1start:]
1401
+ elif m := PATTERN_CELL_COL.match(key):
1419
1402
  return span_dict(
1420
- from_r=int(r1) - 1,
1421
- from_c=alpha2idx(c1),
1403
+ from_r=int(m[2]) - 1,
1404
+ from_c=span_a2i(m[1]),
1422
1405
  upto_r=None,
1423
- upto_c=None,
1406
+ upto_c=span_a2n(m[3]),
1424
1407
  widget=widget,
1425
1408
  )
1426
-
1427
- if m1start and splitk[1].isalpha():
1428
- """
1429
- ["A1:B"] - All the cells starting from (0, 0)
1430
- expanding out to include column 1
1431
- but not including cells beyond column
1432
- 1 and expanding down to include all rows
1433
- A B C D
1434
- 1 x x
1435
- 2 x x
1436
- 3 x x
1437
- 4 x x
1438
- ...
1439
- """
1440
- c1 = splitk[0][:m1start]
1441
- r1 = splitk[0][m1start:]
1409
+ elif m := PATTERN_CELL_ROW.match(key):
1442
1410
  return span_dict(
1443
- from_r=int(r1) - 1,
1444
- from_c=alpha2idx(c1),
1445
- upto_r=None,
1446
- upto_c=alpha2idx(splitk[1]) + 1,
1411
+ from_r=int(m[2]) - 1,
1412
+ from_c=span_a2i(m[1]),
1413
+ upto_r=int(m[3]),
1414
+ upto_c=None,
1447
1415
  widget=widget,
1448
1416
  )
1449
-
1450
- if m1start and splitk[1].isdigit():
1451
- """
1452
- ["A1:2"] - All the cells starting from (0, 0)
1453
- expanding down to include row 1
1454
- but not including cells beyond row
1455
- 1 and expanding out to include all
1456
- columns
1457
- A B C D
1458
- 1 x x x x
1459
- 2 x x x x
1460
- 3
1461
- 4
1462
- ...
1463
- """
1464
- c1 = splitk[0][:m1start]
1465
- r1 = splitk[0][m1start:]
1417
+ elif PATTERN_ALL.match(key):
1466
1418
  return span_dict(
1467
- from_r=int(r1) - 1,
1468
- from_c=alpha2idx(c1),
1469
- upto_r=int(splitk[1]),
1419
+ from_r=0,
1420
+ from_c=None,
1421
+ upto_r=None,
1470
1422
  upto_c=None,
1471
1423
  widget=widget,
1472
1424
  )
1425
+ else:
1426
+ return f"'{key}' could not be converted to span."
1473
1427
 
1474
1428
  except ValueError as error:
1475
1429
  return f"Error, '{key}' could not be converted to span: {error}"
1476
- else:
1477
- return f"'{key}' could not be converted to span."
1478
1430
 
1479
1431
 
1480
1432
  def span_is_cell(span: Span) -> bool: