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/__init__.py +2 -1
- tksheet/column_headers.py +2 -0
- tksheet/constants.py +195 -0
- tksheet/find_window.py +324 -30
- tksheet/functions.py +257 -305
- tksheet/main_table.py +481 -295
- tksheet/row_index.py +9 -9
- tksheet/sheet.py +153 -496
- tksheet/sheet_options.py +7 -1
- tksheet/tksheet_types.py +3 -0
- {tksheet-7.4.6.dist-info → tksheet-7.4.8.dist-info}/METADATA +1 -1
- tksheet-7.4.8.dist-info/RECORD +22 -0
- {tksheet-7.4.6.dist-info → tksheet-7.4.8.dist-info}/WHEEL +1 -1
- tksheet-7.4.6.dist-info/RECORD +0 -22
- {tksheet-7.4.6.dist-info → tksheet-7.4.8.dist-info}/LICENSE.txt +0 -0
- {tksheet-7.4.6.dist-info → tksheet-7.4.8.dist-info}/top_level.txt +0 -0
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=
|
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) -
|
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
|
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
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
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
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
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
|
-
#
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
for
|
942
|
-
for
|
943
|
-
yield (
|
944
|
-
|
945
|
-
|
946
|
-
for
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
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
|
-
|
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
|
-
|
1131
|
-
|
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
|
-
|
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=
|
1290
|
+
from_r=0,
|
1253
1291
|
from_c=None,
|
1254
|
-
upto_r=
|
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
|
-
|
1260
|
-
|
1261
|
-
|
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=
|
1314
|
+
from_c=span_a2i(m[1]),
|
1266
1315
|
upto_r=None,
|
1267
|
-
upto_c=
|
1316
|
+
upto_c=span_a2n(m[1]),
|
1268
1317
|
widget=widget,
|
1269
1318
|
)
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
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=
|
1301
|
-
from_c=
|
1302
|
-
upto_r=
|
1303
|
-
upto_c=
|
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(
|
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=
|
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=
|
1347
|
+
from_r=int(m[1]) - 1,
|
1325
1348
|
from_c=None,
|
1326
|
-
upto_r=
|
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=
|
1355
|
+
from_r=0,
|
1337
1356
|
from_c=None,
|
1338
|
-
upto_r=int(
|
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=
|
1364
|
+
from_c=span_a2i(m[1]),
|
1350
1365
|
upto_r=None,
|
1351
|
-
upto_c=
|
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=
|
1372
|
+
from_c=span_a2i(m[1]),
|
1362
1373
|
upto_r=None,
|
1363
|
-
upto_c=
|
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=
|
1380
|
+
from_c=0,
|
1374
1381
|
upto_r=None,
|
1375
|
-
upto_c=
|
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(
|
1393
|
-
from_c=
|
1394
|
-
upto_r=
|
1395
|
-
upto_c=
|
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(
|
1409
|
-
upto_c=
|
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(
|
1421
|
-
from_c=
|
1403
|
+
from_r=int(m[2]) - 1,
|
1404
|
+
from_c=span_a2i(m[1]),
|
1422
1405
|
upto_r=None,
|
1423
|
-
upto_c=
|
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(
|
1444
|
-
from_c=
|
1445
|
-
upto_r=
|
1446
|
-
upto_c=
|
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=
|
1468
|
-
from_c=
|
1469
|
-
upto_r=
|
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:
|