tksheet 7.4.3__tar.gz → 7.4.5__tar.gz
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-7.4.3/tksheet.egg-info → tksheet-7.4.5}/PKG-INFO +4 -2
- {tksheet-7.4.3 → tksheet-7.4.5}/pyproject.toml +8 -4
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/__init__.py +2 -2
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/column_headers.py +74 -69
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/constants.py +2 -2
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/formatters.py +20 -36
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/functions.py +37 -47
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/main_table.py +239 -376
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/other_classes.py +10 -16
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/row_index.py +116 -83
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/sheet.py +133 -152
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/sheet_options.py +2 -0
- tksheet-7.4.5/tksheet/sorting.py +531 -0
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/text_editor.py +9 -3
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/top_left_rectangle.py +5 -6
- {tksheet-7.4.3 → tksheet-7.4.5/tksheet.egg-info}/PKG-INFO +4 -2
- tksheet-7.4.3/tksheet/sorting.py +0 -318
- {tksheet-7.4.3 → tksheet-7.4.5}/LICENSE.txt +0 -0
- {tksheet-7.4.3 → tksheet-7.4.5}/README.md +0 -0
- {tksheet-7.4.3 → tksheet-7.4.5}/setup.cfg +0 -0
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/colors.py +0 -0
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/find_window.py +0 -0
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/themes.py +0 -0
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet/tksheet_types.py +0 -0
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet.egg-info/SOURCES.txt +0 -0
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet.egg-info/dependency_links.txt +0 -0
- {tksheet-7.4.3 → tksheet-7.4.5}/tksheet.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: tksheet
|
3
|
-
Version: 7.4.
|
3
|
+
Version: 7.4.5
|
4
4
|
Summary: Tkinter table / sheet and treeview widget
|
5
5
|
Author-email: ragardner <github@ragardner.simplelogin.com>
|
6
6
|
License: Copyright (c) 2019 ragardner and open source contributors
|
@@ -24,7 +24,9 @@ License: Copyright (c) 2019 ragardner and open source contributors
|
|
24
24
|
SOFTWARE.
|
25
25
|
|
26
26
|
Project-URL: Homepage, https://github.com/ragardner/tksheet
|
27
|
-
Project-URL:
|
27
|
+
Project-URL: Documentation, https://github.com/ragardner/tksheet/wiki/Version-7
|
28
|
+
Project-URL: Changelog, https://github.com/ragardner/tksheet/blob/master/docs/CHANGELOG.md
|
29
|
+
Project-URL: Issues, https://github.com/ragardner/tksheet/issues
|
28
30
|
Project-URL: Funding, https://github.com/ragardner
|
29
31
|
Keywords: tkinter,table,widget,tree,treeview,sheet,grid,tk
|
30
32
|
Classifier: Development Status :: 5 - Production/Stable
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
6
6
|
name = "tksheet"
|
7
7
|
description = "Tkinter table / sheet and treeview widget"
|
8
8
|
readme = "README.md"
|
9
|
-
version = "7.4.
|
9
|
+
version = "7.4.5"
|
10
10
|
authors = [{ name = "ragardner", email = "github@ragardner.simplelogin.com" }]
|
11
11
|
requires-python = ">=3.8"
|
12
12
|
license = {file = "LICENSE.txt"}
|
@@ -25,9 +25,13 @@ classifiers = [
|
|
25
25
|
]
|
26
26
|
|
27
27
|
[project.urls]
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
Homepage = "https://github.com/ragardner/tksheet"
|
29
|
+
Documentation = "https://github.com/ragardner/tksheet/wiki/Version-7"
|
30
|
+
Changelog = "https://github.com/ragardner/tksheet/blob/master/docs/CHANGELOG.md"
|
31
|
+
Issues = "https://github.com/ragardner/tksheet/issues"
|
32
|
+
Funding = "https://github.com/ragardner"
|
31
33
|
|
32
34
|
[tool.ruff]
|
33
35
|
line-length = 120
|
36
|
+
extend-select = ["I", "F", "C4", "E", "B", "SIM"]
|
37
|
+
fix = true
|
@@ -4,7 +4,7 @@
|
|
4
4
|
tksheet - A Python tkinter table widget
|
5
5
|
"""
|
6
6
|
|
7
|
-
__version__ = "7.4.
|
7
|
+
__version__ = "7.4.5"
|
8
8
|
|
9
9
|
from .colors import (
|
10
10
|
color_map,
|
@@ -93,7 +93,7 @@ from .other_classes import (
|
|
93
93
|
from .row_index import RowIndex
|
94
94
|
from .sheet import Dropdown, Sheet
|
95
95
|
from .sheet_options import new_sheet_options
|
96
|
-
from .sorting import natural_sort_key
|
96
|
+
from .sorting import fast_sort_key, natural_sort_key, version_sort_key
|
97
97
|
from .text_editor import (
|
98
98
|
TextEditor,
|
99
99
|
TextEditorTkText,
|
@@ -89,7 +89,7 @@ class ColumnHeaders(tk.Canvas):
|
|
89
89
|
self.edit_cell_enabled = False
|
90
90
|
self.dragged_col = None
|
91
91
|
self.visible_col_dividers = {}
|
92
|
-
self.col_height_resize_bbox =
|
92
|
+
self.col_height_resize_bbox = ()
|
93
93
|
self.cell_options = {}
|
94
94
|
self.rsz_w = None
|
95
95
|
self.rsz_h = None
|
@@ -255,28 +255,32 @@ class ColumnHeaders(tk.Canvas):
|
|
255
255
|
def shift_b1_press(self, event: object) -> None:
|
256
256
|
self.mouseclick_outside_editor_or_dropdown_all_canvases(inside=True)
|
257
257
|
c = self.MT.identify_col(x=event.x)
|
258
|
-
if (
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
self.
|
271
|
-
self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
|
272
|
-
sel_event = self.MT.get_select_event(being_drawn_item=self.being_drawn_item)
|
273
|
-
try_binding(self.shift_selection_binding_func, sel_event)
|
274
|
-
self.PAR.emit_event("<<SheetSelect>>", data=sel_event)
|
275
|
-
elif c_selected:
|
276
|
-
self.dragged_col = DraggedRowColumn(
|
277
|
-
dragged=c,
|
278
|
-
to_move=sorted(self.MT.get_selected_cols()),
|
258
|
+
if (
|
259
|
+
(self.drag_and_drop_enabled or self.col_selection_enabled)
|
260
|
+
and self.rsz_h is None
|
261
|
+
and self.rsz_w is None
|
262
|
+
and c < len(self.MT.col_positions) - 1
|
263
|
+
):
|
264
|
+
c_selected = self.MT.col_selected(c)
|
265
|
+
if not c_selected and self.col_selection_enabled:
|
266
|
+
if self.MT.selected and self.MT.selected.type_ == "columns":
|
267
|
+
r_to_sel, c_to_sel = self.MT.selected.row, self.MT.selected.column
|
268
|
+
self.MT.deselect("all", redraw=False)
|
269
|
+
self.being_drawn_item = self.MT.create_selection_box(
|
270
|
+
*self.get_shift_select_box(c, c_to_sel), "columns"
|
279
271
|
)
|
272
|
+
self.MT.set_currently_selected(r_to_sel, c_to_sel, self.being_drawn_item)
|
273
|
+
else:
|
274
|
+
self.being_drawn_item = self.select_col(c, run_binding_func=False)
|
275
|
+
self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
|
276
|
+
sel_event = self.MT.get_select_event(being_drawn_item=self.being_drawn_item)
|
277
|
+
try_binding(self.shift_selection_binding_func, sel_event)
|
278
|
+
self.PAR.emit_event("<<SheetSelect>>", data=sel_event)
|
279
|
+
elif c_selected:
|
280
|
+
self.dragged_col = DraggedRowColumn(
|
281
|
+
dragged=c,
|
282
|
+
to_move=sorted(self.MT.get_selected_cols()),
|
283
|
+
)
|
280
284
|
|
281
285
|
def get_shift_select_box(self, c: int, min_c: int) -> tuple[int, int, int, int, str]:
|
282
286
|
if c >= min_c:
|
@@ -351,12 +355,11 @@ class ColumnHeaders(tk.Canvas):
|
|
351
355
|
self.rsz_h = None
|
352
356
|
except Exception:
|
353
357
|
self.rsz_h = None
|
354
|
-
if not mouse_over_resize:
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
self.MT.current_cursor = "hand2"
|
358
|
+
if not mouse_over_resize and self.MT.col_selected(self.MT.identify_col(event, allow_end=False)):
|
359
|
+
mouse_over_selected = True
|
360
|
+
if self.MT.current_cursor != "hand2":
|
361
|
+
self.config(cursor="hand2")
|
362
|
+
self.MT.current_cursor = "hand2"
|
360
363
|
if not mouse_over_resize and not mouse_over_selected:
|
361
364
|
self.MT.reset_mouse_motion_creations()
|
362
365
|
try_binding(self.extra_motion_func, event)
|
@@ -718,23 +721,19 @@ class ColumnHeaders(tk.Canvas):
|
|
718
721
|
self.MT.set_xviews("moveto", 1)
|
719
722
|
|
720
723
|
def event_over_dropdown(self, c: int, datacn: int, event: object, canvasx: float) -> bool:
|
721
|
-
|
724
|
+
return (
|
722
725
|
event.y < self.MT.header_txt_height + 5
|
723
726
|
and self.get_cell_kwargs(datacn, key="dropdown")
|
724
727
|
and canvasx < self.MT.col_positions[c + 1]
|
725
728
|
and canvasx > self.MT.col_positions[c + 1] - self.MT.header_txt_height - 4
|
726
|
-
)
|
727
|
-
return True
|
728
|
-
return False
|
729
|
+
)
|
729
730
|
|
730
731
|
def event_over_checkbox(self, c: int, datacn: int, event: object, canvasx: float) -> bool:
|
731
|
-
|
732
|
+
return (
|
732
733
|
event.y < self.MT.header_txt_height + 5
|
733
734
|
and self.get_cell_kwargs(datacn, key="checkbox")
|
734
735
|
and canvasx < self.MT.col_positions[c] + self.MT.header_txt_height + 4
|
735
|
-
)
|
736
|
-
return True
|
737
|
-
return False
|
736
|
+
)
|
738
737
|
|
739
738
|
def drag_width_resize(self) -> None:
|
740
739
|
new_col_pos = int(self.coords("rwl")[0])
|
@@ -874,6 +873,8 @@ class ColumnHeaders(tk.Canvas):
|
|
874
873
|
columns = list(range(0, len(self.MT.col_positions) - 1))
|
875
874
|
event_data = self.MT.new_event_dict("edit_table")
|
876
875
|
try_binding(self.MT.extra_begin_sort_cells_func, event_data)
|
876
|
+
if key is None:
|
877
|
+
key = self.PAR.ops.sort_key
|
877
878
|
for c in columns:
|
878
879
|
datacn = self.MT.datacn(c)
|
879
880
|
for r, val in enumerate(
|
@@ -923,6 +924,8 @@ class ColumnHeaders(tk.Canvas):
|
|
923
924
|
return event_data
|
924
925
|
column = self.MT.selected.column
|
925
926
|
if try_binding(self.ch_extra_begin_sort_rows_func, event_data, "begin_move_rows"):
|
927
|
+
if key is None:
|
928
|
+
key = self.PAR.ops.sort_key
|
926
929
|
disp_new_idxs, disp_row_ctr = {}, 0
|
927
930
|
if self.ops.treeview:
|
928
931
|
new_nodes_order, data_new_idxs = sort_tree_view(
|
@@ -1179,10 +1182,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1179
1182
|
# table
|
1180
1183
|
if self.MT.data:
|
1181
1184
|
if self.MT.all_rows_displayed:
|
1182
|
-
if visible_only
|
1183
|
-
iterable = range(*self.MT.visible_text_rows)
|
1184
|
-
else:
|
1185
|
-
iterable = range(0, len(self.MT.data))
|
1185
|
+
iterable = range(*self.MT.visible_text_rows) if visible_only else range(0, len(self.MT.data))
|
1186
1186
|
else:
|
1187
1187
|
if visible_only:
|
1188
1188
|
start_row, end_row = self.MT.visible_text_rows
|
@@ -1199,11 +1199,13 @@ class ColumnHeaders(tk.Canvas):
|
|
1199
1199
|
qconf(qtxtm, text=txt, font=qfont)
|
1200
1200
|
b = qbbox(qtxtm)
|
1201
1201
|
if (
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1202
|
+
(
|
1203
|
+
self.MT.get_cell_kwargs(datarn, datacn, key="dropdown")
|
1204
|
+
or self.MT.get_cell_kwargs(datarn, datacn, key="checkbox")
|
1205
|
+
)
|
1206
|
+
and (tw := b[2] - b[0] + qtxth + 7) > w
|
1207
|
+
or (tw := b[2] - b[0] + 7) > w
|
1208
|
+
):
|
1207
1209
|
w = tw
|
1208
1210
|
if hw > w:
|
1209
1211
|
w = hw
|
@@ -1512,9 +1514,8 @@ class ColumnHeaders(tk.Canvas):
|
|
1512
1514
|
col_pos_exists: bool,
|
1513
1515
|
set_scrollregion: bool,
|
1514
1516
|
) -> bool:
|
1515
|
-
if set_scrollregion:
|
1516
|
-
|
1517
|
-
return False
|
1517
|
+
if set_scrollregion and not self.configure_scrollregion(last_col_line_pos=last_col_line_pos):
|
1518
|
+
return False
|
1518
1519
|
self.hidd_text.update(self.disp_text)
|
1519
1520
|
self.disp_text = {}
|
1520
1521
|
self.hidd_high.update(self.disp_high)
|
@@ -1630,7 +1631,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1630
1631
|
if not text:
|
1631
1632
|
continue
|
1632
1633
|
max_lines = int((self.current_height - top - 2) / txt_h)
|
1633
|
-
for
|
1634
|
+
for wrapped in wrap_text(
|
1634
1635
|
text=text,
|
1635
1636
|
max_width=max_width,
|
1636
1637
|
max_lines=max_lines,
|
@@ -1644,7 +1645,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1644
1645
|
if showing:
|
1645
1646
|
self.itemconfig(
|
1646
1647
|
iid,
|
1647
|
-
text=
|
1648
|
+
text=wrapped,
|
1648
1649
|
fill=fill,
|
1649
1650
|
font=font,
|
1650
1651
|
anchor=align,
|
@@ -1652,7 +1653,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1652
1653
|
else:
|
1653
1654
|
self.itemconfig(
|
1654
1655
|
iid,
|
1655
|
-
text=
|
1656
|
+
text=wrapped,
|
1656
1657
|
fill=fill,
|
1657
1658
|
font=font,
|
1658
1659
|
anchor=align,
|
@@ -1663,7 +1664,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1663
1664
|
iid = self.create_text(
|
1664
1665
|
draw_x,
|
1665
1666
|
draw_y,
|
1666
|
-
text=
|
1667
|
+
text=wrapped,
|
1667
1668
|
fill=fill,
|
1668
1669
|
font=font,
|
1669
1670
|
anchor=align,
|
@@ -1791,7 +1792,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1791
1792
|
self.text_editor.set_text(self.text_editor.get() + "" if not isinstance(text, str) else text)
|
1792
1793
|
return False
|
1793
1794
|
self.hide_text_editor()
|
1794
|
-
if not self.MT.see(
|
1795
|
+
if not self.MT.see(0, c, keep_yscroll=True):
|
1795
1796
|
self.MT.main_table_redraw_grid_and_text(True, True)
|
1796
1797
|
x = self.MT.col_positions[c] + 1
|
1797
1798
|
y = 0
|
@@ -2239,16 +2240,18 @@ class ColumnHeaders(tk.Canvas):
|
|
2239
2240
|
self.MT._headers[datacn] = value
|
2240
2241
|
|
2241
2242
|
def input_valid_for_cell(self, datacn: int, value: object, check_readonly: bool = True) -> bool:
|
2242
|
-
|
2243
|
+
kwargs = self.get_cell_kwargs(datacn, key=None)
|
2244
|
+
if check_readonly and "readonly" in kwargs:
|
2243
2245
|
return False
|
2244
|
-
|
2246
|
+
elif "checkbox" in kwargs:
|
2245
2247
|
return is_bool_like(value)
|
2246
|
-
|
2247
|
-
return
|
2248
|
-
|
2249
|
-
|
2250
|
-
|
2251
|
-
|
2248
|
+
else:
|
2249
|
+
return not (
|
2250
|
+
self.cell_equal_to(datacn, value)
|
2251
|
+
or (kwargs := kwargs.get("dropdown", {}))
|
2252
|
+
and kwargs["validate_input"]
|
2253
|
+
and value not in kwargs["values"]
|
2254
|
+
)
|
2252
2255
|
|
2253
2256
|
def cell_equal_to(self, datacn: int, value: object) -> bool:
|
2254
2257
|
self.fix_header(datacn)
|
@@ -2303,12 +2306,13 @@ class ColumnHeaders(tk.Canvas):
|
|
2303
2306
|
return value
|
2304
2307
|
|
2305
2308
|
def get_value_for_empty_cell(self, datacn: int, c_ops: bool = True) -> object:
|
2306
|
-
|
2309
|
+
kwargs = self.get_cell_kwargs(datacn, key=None, cell=c_ops)
|
2310
|
+
if "checkbox" in kwargs:
|
2307
2311
|
return False
|
2308
|
-
kwargs
|
2309
|
-
if kwargs and kwargs["validate_input"] and kwargs["values"]:
|
2312
|
+
elif (kwargs := kwargs.get("dropdown", {})) and kwargs["validate_input"] and kwargs["values"]:
|
2310
2313
|
return kwargs["values"][0]
|
2311
|
-
|
2314
|
+
else:
|
2315
|
+
return ""
|
2312
2316
|
|
2313
2317
|
def get_empty_header_seq(self, end: int, start: int = 0, c_ops: bool = True) -> list[object]:
|
2314
2318
|
return [self.get_value_for_empty_cell(datacn, c_ops=c_ops) for datacn in range(start, end)]
|
@@ -2376,7 +2380,8 @@ class ColumnHeaders(tk.Canvas):
|
|
2376
2380
|
if redraw:
|
2377
2381
|
self.MT.refresh()
|
2378
2382
|
|
2379
|
-
def get_cell_kwargs(self, datacn: int, key: Hashable = "dropdown", cell: bool = True) -> dict:
|
2380
|
-
if cell and datacn in self.cell_options
|
2381
|
-
return self.cell_options[datacn][key
|
2382
|
-
|
2383
|
+
def get_cell_kwargs(self, datacn: int, key: Hashable | None = "dropdown", cell: bool = True) -> dict:
|
2384
|
+
if cell and datacn in self.cell_options:
|
2385
|
+
return self.cell_options[datacn] if key is None else self.cell_options[datacn].get(key, {})
|
2386
|
+
else:
|
2387
|
+
return {}
|
@@ -7,8 +7,8 @@ ctrl_key: str = "Command" if USER_OS == "darwin" else "Control"
|
|
7
7
|
rc_binding: str = "<2>" if USER_OS == "darwin" else "<3>"
|
8
8
|
symbols_set: set[str] = set("""!#$%&'()*+,-./:;"@[]^_`{|}~>?= \\""")
|
9
9
|
nonelike: set[object] = {None, "none", ""}
|
10
|
-
truthy: set[object] = {True, "true", "t", "yes", "y", "on", "1"
|
11
|
-
falsy: set[object] = {False, "false", "f", "no", "n", "off", "0"
|
10
|
+
truthy: set[object] = {True, "true", "t", "yes", "y", "on", "1"}
|
11
|
+
falsy: set[object] = {False, "false", "f", "no", "n", "off", "0"}
|
12
12
|
_test_str: str = "aiW_-|"
|
13
13
|
|
14
14
|
val_modifying_options: set[str] = {"checkbox", "format", "dropdown"}
|
@@ -1,14 +1,13 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from collections.abc import Callable
|
4
|
+
from contextlib import suppress
|
4
5
|
|
5
6
|
from .constants import falsy, nonelike, truthy
|
6
7
|
|
7
8
|
|
8
9
|
def is_none_like(o: object) -> bool:
|
9
|
-
|
10
|
-
return True
|
11
|
-
return False
|
10
|
+
return (isinstance(o, str) and o.lower().replace(" ", "") in nonelike) or o in nonelike
|
12
11
|
|
13
12
|
|
14
13
|
def to_int(o: object, **kwargs) -> int:
|
@@ -42,18 +41,9 @@ def alt_to_percentage(o: object, **kwargs) -> float:
|
|
42
41
|
def to_bool(val: object, **kwargs) -> bool:
|
43
42
|
if isinstance(val, bool):
|
44
43
|
return val
|
45
|
-
if isinstance(val, str)
|
46
|
-
|
47
|
-
|
48
|
-
v = val
|
49
|
-
if "truthy" in kwargs:
|
50
|
-
_truthy = kwargs["truthy"]
|
51
|
-
else:
|
52
|
-
_truthy = truthy
|
53
|
-
if "falsy" in kwargs:
|
54
|
-
_falsy = kwargs["falsy"]
|
55
|
-
else:
|
56
|
-
_falsy = falsy
|
44
|
+
v = val.lower() if isinstance(val, str) else val
|
45
|
+
_truthy = kwargs.get("truthy", truthy)
|
46
|
+
_falsy = kwargs.get("falsy", falsy)
|
57
47
|
if v in _truthy:
|
58
48
|
return True
|
59
49
|
elif v in _falsy:
|
@@ -191,16 +181,16 @@ def formatter(
|
|
191
181
|
**kwargs,
|
192
182
|
) -> dict:
|
193
183
|
return {
|
194
|
-
**
|
195
|
-
datatypes
|
196
|
-
format_function
|
197
|
-
to_str_function
|
198
|
-
invalid_value
|
199
|
-
nullable
|
200
|
-
pre_format_function
|
201
|
-
post_format_function
|
202
|
-
clipboard_function
|
203
|
-
|
184
|
+
**{
|
185
|
+
"datatypes": datatypes,
|
186
|
+
"format_function": format_function,
|
187
|
+
"to_str_function": to_str_function,
|
188
|
+
"invalid_value": invalid_value,
|
189
|
+
"nullable": nullable,
|
190
|
+
"pre_format_function": pre_format_function,
|
191
|
+
"post_format_function": post_format_function,
|
192
|
+
"clipboard_function": clipboard_function,
|
193
|
+
},
|
204
194
|
**kwargs,
|
205
195
|
}
|
206
196
|
|
@@ -219,10 +209,8 @@ def format_data(
|
|
219
209
|
if nullable and is_none_like(value):
|
220
210
|
value = None
|
221
211
|
else:
|
222
|
-
|
212
|
+
with suppress(Exception):
|
223
213
|
value = format_function(value, **kwargs)
|
224
|
-
except Exception:
|
225
|
-
pass
|
226
214
|
if post_format_function and isinstance(value, datatypes):
|
227
215
|
value = post_format_function(value)
|
228
216
|
return value
|
@@ -243,7 +231,7 @@ def data_to_str(
|
|
243
231
|
return to_str_function(value, **kwargs)
|
244
232
|
|
245
233
|
|
246
|
-
def get_data_with_valid_check(value="", datatypes: tuple[()] | tuple[object] | object =
|
234
|
+
def get_data_with_valid_check(value="", datatypes: tuple[()] | tuple[object] | object = (), invalid_value="NA"):
|
247
235
|
if isinstance(value, datatypes):
|
248
236
|
return value
|
249
237
|
return invalid_value
|
@@ -274,12 +262,10 @@ class Formatter:
|
|
274
262
|
) -> None:
|
275
263
|
if nullable:
|
276
264
|
if isinstance(datatypes, (list, tuple)):
|
277
|
-
datatypes = tuple(
|
265
|
+
datatypes = tuple(set(datatypes) | {type(None)})
|
278
266
|
else:
|
279
267
|
datatypes = (datatypes, type(None))
|
280
|
-
elif isinstance(datatypes, (list, tuple)) and type(None) in datatypes:
|
281
|
-
raise TypeError("Non-nullable cells cannot have NoneType as a datatype.")
|
282
|
-
elif datatypes is type(None):
|
268
|
+
elif isinstance(datatypes, (list, tuple)) and type(None) in datatypes or datatypes is type(None):
|
283
269
|
raise TypeError("Non-nullable cells cannot have NoneType as a datatype.")
|
284
270
|
self.kwargs = kwargs
|
285
271
|
self.valid_datatypes = datatypes
|
@@ -305,9 +291,7 @@ class Formatter:
|
|
305
291
|
def valid(self, value: object = None) -> bool:
|
306
292
|
if value is None:
|
307
293
|
value = self.value
|
308
|
-
|
309
|
-
return True
|
310
|
-
return False
|
294
|
+
return isinstance(value, self.valid_datatypes)
|
311
295
|
|
312
296
|
def format_data(self, value: object) -> object:
|
313
297
|
if self.pre_format_function:
|
@@ -15,7 +15,7 @@ from typing import Literal
|
|
15
15
|
from .colors import color_map
|
16
16
|
from .constants import align_value_error, symbols_set
|
17
17
|
from .formatters import to_bool
|
18
|
-
from .other_classes import
|
18
|
+
from .other_classes import DotDict, EventDataDict, Highlight, Loc, Span
|
19
19
|
from .tksheet_types import AnyIter
|
20
20
|
|
21
21
|
unpickle_obj = pickle.loads
|
@@ -279,10 +279,6 @@ def float_to_int(f: int | float) -> int | float:
|
|
279
279
|
return int(f)
|
280
280
|
|
281
281
|
|
282
|
-
def selection_box_tup_to_dict(box: tuple) -> dict:
|
283
|
-
return {Box_nt(*box[:-1]): box[-1]}
|
284
|
-
|
285
|
-
|
286
282
|
def event_dict(
|
287
283
|
name: str = None,
|
288
284
|
sheet: object = None,
|
@@ -334,15 +330,13 @@ def event_dict(
|
|
334
330
|
),
|
335
331
|
named_spans=DotDict() if named_spans is None else named_spans,
|
336
332
|
options=DotDict(),
|
337
|
-
selection_boxes=
|
338
|
-
|
339
|
-
),
|
340
|
-
selected=tuple() if selected is None else selected,
|
341
|
-
being_selected=tuple() if being_selected is None else being_selected,
|
333
|
+
selection_boxes={} if boxes is None else boxes,
|
334
|
+
selected=() if selected is None else selected,
|
335
|
+
being_selected=() if being_selected is None else being_selected,
|
342
336
|
data=[] if data is None else data,
|
343
337
|
key="" if key is None else key,
|
344
338
|
value=None if value is None else value,
|
345
|
-
loc=
|
339
|
+
loc=() if loc is None else loc,
|
346
340
|
row=row,
|
347
341
|
column=column,
|
348
342
|
resized=DotDict(
|
@@ -424,7 +418,7 @@ def push_n(num: int, sorted_seq: Sequence[int]) -> int:
|
|
424
418
|
|
425
419
|
|
426
420
|
def get_dropdown_kwargs(
|
427
|
-
values: list =
|
421
|
+
values: list[object] | None = None,
|
428
422
|
set_value: object = None,
|
429
423
|
state: str = "normal",
|
430
424
|
redraw: bool = True,
|
@@ -435,7 +429,7 @@ def get_dropdown_kwargs(
|
|
435
429
|
text: None | str = None,
|
436
430
|
) -> dict:
|
437
431
|
return {
|
438
|
-
"values": values,
|
432
|
+
"values": [] if values is None else values,
|
439
433
|
"set_value": set_value,
|
440
434
|
"state": state,
|
441
435
|
"redraw": redraw,
|
@@ -753,6 +747,17 @@ def move_elements_by_mapping(
|
|
753
747
|
return [seq[old_idxs[i]] if i in old_idxs else next(remaining_values) for i in range(len(seq))]
|
754
748
|
|
755
749
|
|
750
|
+
def move_elements_by_mapping_gen(
|
751
|
+
seq: list[object],
|
752
|
+
new_idxs: dict[int, int],
|
753
|
+
old_idxs: dict[int, int] | None = None,
|
754
|
+
) -> Generator[object]:
|
755
|
+
if old_idxs is None:
|
756
|
+
old_idxs = dict(zip(new_idxs.values(), new_idxs))
|
757
|
+
remaining_values = (e for i, e in enumerate(seq) if i not in new_idxs)
|
758
|
+
return (seq[old_idxs[i]] if i in old_idxs else next(remaining_values) for i in range(len(seq)))
|
759
|
+
|
760
|
+
|
756
761
|
def move_elements_to(
|
757
762
|
seq: list[object],
|
758
763
|
move_to: int,
|
@@ -770,15 +775,16 @@ def move_elements_to(
|
|
770
775
|
|
771
776
|
def get_new_indexes(
|
772
777
|
move_to: int,
|
773
|
-
to_move:
|
778
|
+
to_move: Iterable[int],
|
774
779
|
get_inverse: bool = False,
|
775
|
-
) -> tuple[dict]:
|
780
|
+
) -> tuple[dict[int, int]] | dict[int, int]:
|
776
781
|
"""
|
782
|
+
move_to: A positive int, could possibly be the same as an element of to_move
|
783
|
+
to_move: An iterable of ints, could be a dict, could be in any order
|
777
784
|
returns {old idx: new idx, ...}
|
778
785
|
"""
|
779
786
|
offset = sum(1 for i in to_move if i < move_to)
|
780
|
-
new_idxs = range(move_to - offset, move_to - offset + len(to_move))
|
781
|
-
new_idxs = {old: new for old, new in zip(to_move, new_idxs)}
|
787
|
+
new_idxs = dict(zip(to_move, range(move_to - offset, move_to - offset + len(to_move))))
|
782
788
|
if get_inverse:
|
783
789
|
return new_idxs, dict(zip(new_idxs.values(), new_idxs))
|
784
790
|
return new_idxs
|
@@ -789,6 +795,11 @@ def insert_items(
|
|
789
795
|
to_insert: dict[int, object],
|
790
796
|
seq_len_func: Callable | None = None,
|
791
797
|
) -> list[object]:
|
798
|
+
"""
|
799
|
+
seq: list[object]
|
800
|
+
to_insert: keys are ints sorted in reverse, representing list indexes to insert items.
|
801
|
+
Values are any object, e.g. {1: 200, 0: 200}
|
802
|
+
"""
|
792
803
|
if to_insert:
|
793
804
|
if seq_len_func and next(iter(to_insert)) >= len(seq) + len(to_insert):
|
794
805
|
seq_len_func(next(iter(to_insert)) - len(to_insert))
|
@@ -801,7 +812,7 @@ def del_placeholder_dict_key(
|
|
801
812
|
d: dict[Hashable, object],
|
802
813
|
k: Hashable,
|
803
814
|
v: object,
|
804
|
-
p: tuple =
|
815
|
+
p: tuple = (),
|
805
816
|
) -> dict[Hashable, object]:
|
806
817
|
if p in d:
|
807
818
|
del d[p]
|
@@ -876,24 +887,12 @@ def rounded_box_coords(
|
|
876
887
|
)
|
877
888
|
|
878
889
|
|
879
|
-
def diff_list(seq: list[float]) -> list[int]:
|
880
|
-
return [
|
881
|
-
int(b - a)
|
882
|
-
for a, b in zip(
|
883
|
-
seq,
|
884
|
-
islice(seq, 1, None),
|
885
|
-
)
|
886
|
-
]
|
887
|
-
|
888
|
-
|
889
890
|
def diff_gen(seq: list[float]) -> Generator[int]:
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
)
|
896
|
-
)
|
891
|
+
it = iter(seq)
|
892
|
+
a = next(it)
|
893
|
+
for b in it:
|
894
|
+
yield int(b - a)
|
895
|
+
a = b
|
897
896
|
|
898
897
|
|
899
898
|
def gen_coords(
|
@@ -1505,14 +1504,8 @@ def span_ranges(
|
|
1505
1504
|
) -> tuple[Generator[int], Generator[int]]:
|
1506
1505
|
rng_from_r = 0 if span.from_r is None else span.from_r
|
1507
1506
|
rng_from_c = 0 if span.from_c is None else span.from_c
|
1508
|
-
if span.upto_r is None
|
1509
|
-
|
1510
|
-
else:
|
1511
|
-
rng_upto_r = span.upto_r
|
1512
|
-
if span.upto_c is None:
|
1513
|
-
rng_upto_c = totalcols() if isinstance(totalcols, Callable) else totalcols
|
1514
|
-
else:
|
1515
|
-
rng_upto_c = span.upto_c
|
1507
|
+
rng_upto_r = (totalrows() if isinstance(totalrows, Callable) else totalrows) if span.upto_r is None else span.upto_r
|
1508
|
+
rng_upto_c = (totalcols() if isinstance(totalcols, Callable) else totalcols) if span.upto_c is None else span.upto_c
|
1516
1509
|
return range(rng_from_r, rng_upto_r), range(rng_from_c, rng_upto_c)
|
1517
1510
|
|
1518
1511
|
|
@@ -1674,10 +1667,7 @@ def span_idxs_post_move(
|
|
1674
1667
|
newupto_colrange = newupto
|
1675
1668
|
else:
|
1676
1669
|
oldfrom = int(span[f"from_{axis}"])
|
1677
|
-
if not oldfrom
|
1678
|
-
newfrom = 0
|
1679
|
-
else:
|
1680
|
-
newfrom = full_new_idxs[oldfrom]
|
1670
|
+
newfrom = 0 if not oldfrom else full_new_idxs[oldfrom]
|
1681
1671
|
newupto = None
|
1682
1672
|
oldupto_colrange = total
|
1683
1673
|
newupto_colrange = oldupto_colrange
|