tksheet 7.5.5__py3-none-any.whl → 7.5.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 +1 -1
- tksheet/column_headers.py +230 -87
- tksheet/constants.py +1 -0
- tksheet/find_window.py +4 -4
- tksheet/formatters.py +4 -2
- tksheet/functions.py +91 -35
- tksheet/main_table.py +374 -643
- tksheet/menus.py +494 -0
- tksheet/other_classes.py +3 -0
- tksheet/row_index.py +235 -97
- tksheet/sheet.py +99 -39
- tksheet/sheet_options.py +5 -1
- tksheet/tksheet_types.py +1 -0
- tksheet/tooltip.py +335 -0
- {tksheet-7.5.5.dist-info → tksheet-7.5.8.dist-info}/METADATA +1 -1
- tksheet-7.5.8.dist-info/RECORD +24 -0
- {tksheet-7.5.5.dist-info → tksheet-7.5.8.dist-info}/WHEEL +1 -1
- tksheet-7.5.5.dist-info/RECORD +0 -22
- {tksheet-7.5.5.dist-info → tksheet-7.5.8.dist-info}/licenses/LICENSE.txt +0 -0
- {tksheet-7.5.5.dist-info → tksheet-7.5.8.dist-info}/top_level.txt +0 -0
tksheet/functions.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import copy
|
3
4
|
import csv
|
4
5
|
import io
|
5
6
|
import re
|
@@ -9,6 +10,7 @@ from collections import deque
|
|
9
10
|
from collections.abc import Callable, Generator, Hashable, Iterable, Iterator, Sequence
|
10
11
|
from difflib import SequenceMatcher
|
11
12
|
from itertools import chain, islice, repeat
|
13
|
+
from types import ModuleType
|
12
14
|
from typing import Any, Literal
|
13
15
|
|
14
16
|
from .colors import color_map
|
@@ -16,7 +18,6 @@ from .constants import align_value_error, symbols_set
|
|
16
18
|
from .formatters import to_bool
|
17
19
|
from .other_classes import DotDict, EventDataDict, Highlight, Loc, Span
|
18
20
|
|
19
|
-
lines_re = re.compile(r"[^\n]+")
|
20
21
|
ORD_A = ord("A")
|
21
22
|
|
22
23
|
|
@@ -33,8 +34,8 @@ def wrap_text(
|
|
33
34
|
line_width = 0
|
34
35
|
if wrap == "c":
|
35
36
|
current_line = []
|
36
|
-
for
|
37
|
-
for char in
|
37
|
+
for line in text.split("\n"):
|
38
|
+
for char in line:
|
38
39
|
try:
|
39
40
|
char_width = widths[char]
|
40
41
|
except KeyError:
|
@@ -75,8 +76,8 @@ def wrap_text(
|
|
75
76
|
space_width = char_width_fn(" ")
|
76
77
|
current_line = []
|
77
78
|
|
78
|
-
for
|
79
|
-
for i, word in enumerate(
|
79
|
+
for line in text.split("\n"):
|
80
|
+
for i, word in enumerate(line.split()):
|
80
81
|
# if we're going to next word and
|
81
82
|
# if a space fits on the end of the current line we add one
|
82
83
|
if i and line_width + space_width < max_width:
|
@@ -155,10 +156,10 @@ def wrap_text(
|
|
155
156
|
line_width = 0
|
156
157
|
|
157
158
|
else:
|
158
|
-
for
|
159
|
+
for line in text.split("\n"):
|
159
160
|
line_width = 0
|
160
161
|
current_line = []
|
161
|
-
for char in
|
162
|
+
for char in line:
|
162
163
|
try:
|
163
164
|
char_width = widths[char]
|
164
165
|
except KeyError:
|
@@ -205,6 +206,16 @@ def get_data_from_clipboard(
|
|
205
206
|
return [[data]]
|
206
207
|
|
207
208
|
|
209
|
+
def widget_descendants(widget: tk.Misc) -> list[tk.Misc]:
|
210
|
+
result = []
|
211
|
+
queue = deque([widget])
|
212
|
+
while queue:
|
213
|
+
current_widget = queue.popleft()
|
214
|
+
result.append(current_widget)
|
215
|
+
queue.extend(current_widget.winfo_children())
|
216
|
+
return result
|
217
|
+
|
218
|
+
|
208
219
|
def recursive_bind(widget: tk.Misc, event: str, callback: Callable) -> None:
|
209
220
|
widget.bind(event, callback)
|
210
221
|
for child in widget.winfo_children():
|
@@ -461,6 +472,8 @@ def get_dropdown_kwargs(
|
|
461
472
|
search_function: Callable = dropdown_search_function,
|
462
473
|
validate_input: bool = True,
|
463
474
|
text: None | str = None,
|
475
|
+
edit_data: bool = True,
|
476
|
+
default_value: Any = None,
|
464
477
|
) -> dict:
|
465
478
|
return {
|
466
479
|
"values": [] if values is None else values,
|
@@ -472,6 +485,8 @@ def get_dropdown_kwargs(
|
|
472
485
|
"search_function": search_function,
|
473
486
|
"validate_input": validate_input,
|
474
487
|
"text": text,
|
488
|
+
"edit_data": edit_data,
|
489
|
+
"default_value": default_value,
|
475
490
|
}
|
476
491
|
|
477
492
|
|
@@ -484,6 +499,7 @@ def get_dropdown_dict(**kwargs) -> dict:
|
|
484
499
|
"validate_input": kwargs["validate_input"],
|
485
500
|
"text": kwargs["text"],
|
486
501
|
"state": kwargs["state"],
|
502
|
+
"default_value": kwargs["default_value"],
|
487
503
|
}
|
488
504
|
|
489
505
|
|
@@ -881,50 +897,38 @@ def rounded_box_coords(
|
|
881
897
|
x2: float,
|
882
898
|
y2: float,
|
883
899
|
radius: int = 5,
|
884
|
-
) -> tuple[float]:
|
900
|
+
) -> tuple[float, ...]:
|
901
|
+
# Handle case where rectangle is too small for rounding
|
885
902
|
if y2 - y1 < 2 or x2 - x1 < 2:
|
886
|
-
return x1, y1, x2, y1, x2, y2, x1, y2
|
903
|
+
return (x1, y1, x2, y1, x2, y2, x1, y2, x1, y1)
|
904
|
+
# Coordinates for a closed rectangle with rounded corners
|
887
905
|
return (
|
888
906
|
x1 + radius,
|
889
|
-
y1,
|
890
|
-
x1 + radius,
|
891
|
-
y1,
|
907
|
+
y1, # Top side start
|
892
908
|
x2 - radius,
|
893
|
-
y1,
|
894
|
-
x2 - radius,
|
895
|
-
y1,
|
909
|
+
y1, # Top side end
|
896
910
|
x2,
|
897
|
-
y1,
|
911
|
+
y1, # Top-right corner
|
898
912
|
x2,
|
899
913
|
y1 + radius,
|
900
914
|
x2,
|
901
|
-
|
915
|
+
y2 - radius, # Right side
|
902
916
|
x2,
|
903
|
-
y2 -
|
904
|
-
x2,
|
905
|
-
y2 - radius,
|
906
|
-
x2,
|
907
|
-
y2,
|
917
|
+
y2, # Bottom-right corner
|
908
918
|
x2 - radius,
|
909
919
|
y2,
|
910
|
-
x2 - radius,
|
911
|
-
y2,
|
912
|
-
x1 + radius,
|
913
|
-
y2,
|
914
920
|
x1 + radius,
|
915
|
-
y2,
|
916
|
-
x1,
|
917
|
-
y2,
|
921
|
+
y2, # Bottom side
|
918
922
|
x1,
|
919
|
-
y2 -
|
923
|
+
y2, # Bottom-left corner
|
920
924
|
x1,
|
921
925
|
y2 - radius,
|
922
926
|
x1,
|
923
|
-
y1 + radius,
|
924
|
-
x1,
|
925
|
-
y1 + radius,
|
927
|
+
y1 + radius, # Left side
|
926
928
|
x1,
|
927
|
-
y1,
|
929
|
+
y1, # Top-left corner
|
930
|
+
x1 + radius,
|
931
|
+
y1, # Close the shape
|
928
932
|
)
|
929
933
|
|
930
934
|
|
@@ -1520,6 +1524,17 @@ def del_named_span_options_nested(
|
|
1520
1524
|
del options[k][type_]
|
1521
1525
|
|
1522
1526
|
|
1527
|
+
def mod_note(options: dict, key: int | tuple[int, int], note: str | None, readonly: bool = True) -> dict:
|
1528
|
+
if note is not None:
|
1529
|
+
if key not in options:
|
1530
|
+
options[key] = {}
|
1531
|
+
options[key]["note"] = {"note": note, "readonly": readonly}
|
1532
|
+
else:
|
1533
|
+
if key in options and "note" in options[key]:
|
1534
|
+
del options[key]["note"]
|
1535
|
+
return options
|
1536
|
+
|
1537
|
+
|
1523
1538
|
def add_highlight(
|
1524
1539
|
options: dict,
|
1525
1540
|
key: int | tuple[int, int],
|
@@ -1754,7 +1769,7 @@ def get_vertical_gridline_points(
|
|
1754
1769
|
positions: list[float],
|
1755
1770
|
start: int,
|
1756
1771
|
end: int,
|
1757
|
-
) -> list[float]:
|
1772
|
+
) -> list[int | float]:
|
1758
1773
|
return list(
|
1759
1774
|
chain.from_iterable(
|
1760
1775
|
(
|
@@ -1770,3 +1785,44 @@ def get_vertical_gridline_points(
|
|
1770
1785
|
for c in range(start, end)
|
1771
1786
|
)
|
1772
1787
|
)
|
1788
|
+
|
1789
|
+
|
1790
|
+
def safe_copy(value: Any) -> Any:
|
1791
|
+
"""
|
1792
|
+
Attempts to create a deep copy of the input value. If copying fails,
|
1793
|
+
returns the original value.
|
1794
|
+
|
1795
|
+
Args:
|
1796
|
+
value: Any Python object to be copied
|
1797
|
+
|
1798
|
+
Returns:
|
1799
|
+
A deep copy of the value if possible, otherwise the original value
|
1800
|
+
"""
|
1801
|
+
try:
|
1802
|
+
# Try deep copy first for most objects
|
1803
|
+
return copy.deepcopy(value)
|
1804
|
+
except Exception:
|
1805
|
+
try:
|
1806
|
+
# For types that deepcopy might fail on, try shallow copy
|
1807
|
+
return copy.copy(value)
|
1808
|
+
except Exception:
|
1809
|
+
try:
|
1810
|
+
# For built-in immutable types, return as-is
|
1811
|
+
if isinstance(value, (int, float, str, bool, bytes, tuple, frozenset)):
|
1812
|
+
return value
|
1813
|
+
# For None
|
1814
|
+
if value is None:
|
1815
|
+
return None
|
1816
|
+
# For functions, return same function
|
1817
|
+
if isinstance(value, Callable):
|
1818
|
+
return value
|
1819
|
+
# For modules
|
1820
|
+
if isinstance(value, ModuleType):
|
1821
|
+
return value
|
1822
|
+
# For classes
|
1823
|
+
if isinstance(value, type):
|
1824
|
+
return value
|
1825
|
+
except Exception:
|
1826
|
+
pass
|
1827
|
+
# If all copy attempts fail, return original value
|
1828
|
+
return value
|