tksheet 7.2.17__tar.gz → 7.2.18__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.2.17/tksheet.egg-info → tksheet-7.2.18}/PKG-INFO +1 -1
- {tksheet-7.2.17 → tksheet-7.2.18}/pyproject.toml +1 -1
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/__init__.py +1 -1
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/functions.py +16 -2
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/main_table.py +13 -23
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/other_classes.py +0 -1
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/row_index.py +5 -3
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/sheet.py +180 -98
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/themes.py +2 -2
- {tksheet-7.2.17 → tksheet-7.2.18/tksheet.egg-info}/PKG-INFO +1 -1
- {tksheet-7.2.17 → tksheet-7.2.18}/LICENSE.txt +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/README.md +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/setup.cfg +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/colors.py +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/column_headers.py +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/formatters.py +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/sheet_options.py +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/text_editor.py +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/top_left_rectangle.py +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/types.py +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/vars.py +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet.egg-info/SOURCES.txt +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet.egg-info/dependency_links.txt +0 -0
- {tksheet-7.2.17 → tksheet-7.2.18}/tksheet.egg-info/top_level.txt +0 -0
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
6
6
|
name = "tksheet"
|
7
7
|
description = "Tkinter table / sheet widget"
|
8
8
|
readme = "README.md"
|
9
|
-
version = "7.2.
|
9
|
+
version = "7.2.18"
|
10
10
|
authors = [{ name = "ragardner", email = "github@ragardner.simplelogin.com" }]
|
11
11
|
requires-python = ">=3.8"
|
12
12
|
license = {file = "LICENSE.txt"}
|
@@ -18,6 +18,7 @@ from collections.abc import (
|
|
18
18
|
)
|
19
19
|
from functools import partial
|
20
20
|
from itertools import islice, repeat
|
21
|
+
from typing import Literal
|
21
22
|
|
22
23
|
from .formatters import (
|
23
24
|
to_bool,
|
@@ -363,12 +364,15 @@ def idx_param_to_int(idx: str | int | None) -> int | None:
|
|
363
364
|
return alpha2idx(idx)
|
364
365
|
|
365
366
|
|
366
|
-
def get_n2a(n: int = 0, _type:
|
367
|
+
def get_n2a(n: int = 0, _type: Literal["letters", "numbers", "both"] | None = "numbers") -> str:
|
367
368
|
if _type == "letters":
|
368
369
|
return num2alpha(n)
|
369
370
|
elif _type == "numbers":
|
370
371
|
return f"{n + 1}"
|
371
|
-
|
372
|
+
elif _type == "both":
|
373
|
+
return f"{num2alpha(n)} {n + 1}"
|
374
|
+
elif _type is None:
|
375
|
+
return ""
|
372
376
|
|
373
377
|
|
374
378
|
def get_index_of_gap_in_sorted_integer_seq_forward(
|
@@ -514,6 +518,16 @@ def index_exists(seq: Sequence[object], index: int) -> bool:
|
|
514
518
|
return False
|
515
519
|
|
516
520
|
|
521
|
+
def add_to_displayed(displayed: list[int], to_add: Iterator[int]) -> list[int]:
|
522
|
+
# assumes to_add is sorted in reverse
|
523
|
+
for i in reversed(to_add):
|
524
|
+
ins = bisect_left(displayed, i)
|
525
|
+
displayed[ins:] = [e + 1 for e in islice(displayed, ins, None)]
|
526
|
+
for i in reversed(to_add):
|
527
|
+
displayed.insert(bisect_left(displayed, i), i)
|
528
|
+
return displayed
|
529
|
+
|
530
|
+
|
517
531
|
def move_elements_by_mapping(
|
518
532
|
seq: list[object],
|
519
533
|
new_idxs: dict[int, int],
|
@@ -49,7 +49,9 @@ from .formatters import (
|
|
49
49
|
try_to_bool,
|
50
50
|
)
|
51
51
|
from .functions import (
|
52
|
+
add_to_displayed,
|
52
53
|
b_index,
|
54
|
+
cell_right_within_box,
|
53
55
|
consecutive_ranges,
|
54
56
|
decompress_load,
|
55
57
|
diff_gen,
|
@@ -73,7 +75,6 @@ from .functions import (
|
|
73
75
|
new_tk_event,
|
74
76
|
pickle_obj,
|
75
77
|
pickled_event_dict,
|
76
|
-
cell_right_within_box,
|
77
78
|
rounded_box_coords,
|
78
79
|
span_idxs_post_move,
|
79
80
|
try_binding,
|
@@ -4317,16 +4318,7 @@ class MainTable(tk.Canvas):
|
|
4317
4318
|
if isinstance(displayed_columns, list):
|
4318
4319
|
self.displayed_columns = displayed_columns
|
4319
4320
|
elif not self.all_columns_displayed:
|
4320
|
-
|
4321
|
-
self.displayed_columns.sort()
|
4322
|
-
# highest index is first in columns
|
4323
|
-
up_to = len(self.displayed_columns)
|
4324
|
-
for cn in columns:
|
4325
|
-
self.displayed_columns.insert((last_ins := bisect_left(self.displayed_columns, cn)), cn)
|
4326
|
-
self.displayed_columns[last_ins + 1 : up_to] = [
|
4327
|
-
i + 1 for i in islice(self.displayed_columns, last_ins + 1, up_to)
|
4328
|
-
]
|
4329
|
-
up_to = last_ins
|
4321
|
+
self.displayed_columns = add_to_displayed(self.displayed_columns, columns)
|
4330
4322
|
cws = self.get_column_widths()
|
4331
4323
|
if column_widths and next(reversed(column_widths)) > len(cws):
|
4332
4324
|
for i in reversed(range(len(cws), len(cws) + next(reversed(column_widths)) - len(cws))):
|
@@ -4417,7 +4409,7 @@ class MainTable(tk.Canvas):
|
|
4417
4409
|
else:
|
4418
4410
|
numcols = 1
|
4419
4411
|
displayed_ins_col = len(self.col_positions) - 1
|
4420
|
-
data_ins_col =
|
4412
|
+
data_ins_col = self.total_data_cols()
|
4421
4413
|
if (
|
4422
4414
|
isinstance(self.PAR.ops.paste_insert_column_limit, int)
|
4423
4415
|
and self.PAR.ops.paste_insert_column_limit < displayed_ins_col + numcols
|
@@ -4462,16 +4454,7 @@ class MainTable(tk.Canvas):
|
|
4462
4454
|
if isinstance(displayed_rows, list):
|
4463
4455
|
self.displayed_rows = displayed_rows
|
4464
4456
|
elif not self.all_rows_displayed:
|
4465
|
-
|
4466
|
-
self.displayed_rows.sort()
|
4467
|
-
# highest index is first in rows
|
4468
|
-
up_to = len(self.displayed_rows)
|
4469
|
-
for rn in rows:
|
4470
|
-
self.displayed_rows.insert((last_ins := bisect_left(self.displayed_rows, rn)), rn)
|
4471
|
-
self.displayed_rows[last_ins + 1 : up_to] = [
|
4472
|
-
i + 1 for i in islice(self.displayed_rows, last_ins + 1, up_to)
|
4473
|
-
]
|
4474
|
-
up_to = last_ins
|
4457
|
+
self.displayed_rows = add_to_displayed(self.displayed_rows, rows)
|
4475
4458
|
rhs = self.get_row_heights()
|
4476
4459
|
if row_heights and next(reversed(row_heights)) > len(rhs):
|
4477
4460
|
default_row_height = self.get_default_row_height()
|
@@ -4561,7 +4544,7 @@ class MainTable(tk.Canvas):
|
|
4561
4544
|
else:
|
4562
4545
|
numrows = 1
|
4563
4546
|
displayed_ins_row = len(self.row_positions) - 1
|
4564
|
-
data_ins_row =
|
4547
|
+
data_ins_row = self.total_data_rows()
|
4565
4548
|
if (
|
4566
4549
|
isinstance(self.PAR.ops.paste_insert_row_limit, int)
|
4567
4550
|
and self.PAR.ops.paste_insert_row_limit < displayed_ins_row + numrows
|
@@ -5847,6 +5830,7 @@ class MainTable(tk.Canvas):
|
|
5847
5830
|
c: int | None = None,
|
5848
5831
|
item: int | None = None,
|
5849
5832
|
box: tuple[int, int, int, int] | None = None,
|
5833
|
+
run_binding: bool = True,
|
5850
5834
|
) -> None:
|
5851
5835
|
if isinstance(item, int) and item in self.selection_boxes:
|
5852
5836
|
selection_box = self.selection_boxes[item]
|
@@ -5862,6 +5846,8 @@ class MainTable(tk.Canvas):
|
|
5862
5846
|
selection_box.type_,
|
5863
5847
|
selection_box.fill_iid,
|
5864
5848
|
)
|
5849
|
+
if run_binding:
|
5850
|
+
self.run_selection_binding(selection_box.type_)
|
5865
5851
|
return
|
5866
5852
|
# currently selected is pointed at any selection box with "box" coordinates
|
5867
5853
|
if isinstance(box, tuple):
|
@@ -5878,6 +5864,8 @@ class MainTable(tk.Canvas):
|
|
5878
5864
|
selection_box.type_,
|
5879
5865
|
selection_box.fill_iid,
|
5880
5866
|
)
|
5867
|
+
if run_binding:
|
5868
|
+
self.run_selection_binding(selection_box.type_)
|
5881
5869
|
return
|
5882
5870
|
# currently selected is just pointed at a coordinate
|
5883
5871
|
# find the top most box there, requires r and c
|
@@ -5891,6 +5879,8 @@ class MainTable(tk.Canvas):
|
|
5891
5879
|
selection_box.type_,
|
5892
5880
|
selection_box.fill_iid,
|
5893
5881
|
)
|
5882
|
+
if run_binding:
|
5883
|
+
self.run_selection_binding(selection_box.type_)
|
5894
5884
|
return
|
5895
5885
|
# wasn't provided an item and couldn't find a box at coords so select cell
|
5896
5886
|
if r < len(self.row_positions) - 1 and c < len(self.col_positions) - 1:
|
@@ -1206,6 +1206,8 @@ class RowIndex(tk.Canvas):
|
|
1206
1206
|
new_w = self.MT.get_txt_w(f"{end_row}") + 20
|
1207
1207
|
elif self.PAR.ops.default_row_index == "both":
|
1208
1208
|
new_w = self.MT.get_txt_w(f"{end_row + 1} {num2alpha(end_row)}") + 20
|
1209
|
+
elif self.PAR.ops.default_row_index is None:
|
1210
|
+
new_w = 20
|
1209
1211
|
elif self.PAR.ops.auto_resize_row_index is True:
|
1210
1212
|
new_w = self.get_index_text_width(only_rows=only_rows)
|
1211
1213
|
else:
|
@@ -1380,7 +1382,7 @@ class RowIndex(tk.Canvas):
|
|
1380
1382
|
# x1 + 5 + indent + small_mod + small_mod,
|
1381
1383
|
# y1 + mid_y + small_mod + small_mod,
|
1382
1384
|
# )
|
1383
|
-
|
1385
|
+
|
1384
1386
|
# POINTS FOR A LINE THAT STOPS AT ROW LINE
|
1385
1387
|
# points = (
|
1386
1388
|
# # the upper point
|
@@ -1390,7 +1392,7 @@ class RowIndex(tk.Canvas):
|
|
1390
1392
|
# x1 + 5 + indent + small_mod + small_mod,
|
1391
1393
|
# y2 - mid_y + small_mod + small_mod,
|
1392
1394
|
# )
|
1393
|
-
|
1395
|
+
|
1394
1396
|
# POINTS FOR A HORIZONTAL LINE
|
1395
1397
|
points = (
|
1396
1398
|
# the left point
|
@@ -1400,7 +1402,7 @@ class RowIndex(tk.Canvas):
|
|
1400
1402
|
x1 + 5 + indent + small_mod + small_mod + small_mod + small_mod,
|
1401
1403
|
y1 + mid_y,
|
1402
1404
|
)
|
1403
|
-
|
1405
|
+
|
1404
1406
|
if self.hidd_tree_arrow:
|
1405
1407
|
t, sh = self.hidd_tree_arrow.popitem()
|
1406
1408
|
self.coords(t, points)
|
@@ -19,6 +19,7 @@ from itertools import (
|
|
19
19
|
product,
|
20
20
|
repeat,
|
21
21
|
)
|
22
|
+
from operator import attrgetter
|
22
23
|
from timeit import default_timer
|
23
24
|
from tkinter import ttk
|
24
25
|
from typing import Literal
|
@@ -110,8 +111,8 @@ class Sheet(tk.Frame):
|
|
110
111
|
header: None | list[object] = None,
|
111
112
|
row_index: None | list[object] = None,
|
112
113
|
index: None | list[object] = None,
|
113
|
-
default_header: Literal["letters", "numbers", "both"] = "letters",
|
114
|
-
default_row_index: Literal["letters", "numbers", "both"] = "numbers",
|
114
|
+
default_header: Literal["letters", "numbers", "both"] | None = "letters",
|
115
|
+
default_row_index: Literal["letters", "numbers", "both"] | None = "numbers",
|
115
116
|
data_reference: None | Sequence[Sequence[object]] = None,
|
116
117
|
data: None | Sequence[Sequence[object]] = None,
|
117
118
|
# either (start row, end row, "rows"), (start column, end column, "rows") or
|
@@ -4715,8 +4716,10 @@ class Sheet(tk.Frame):
|
|
4715
4716
|
open_ids: Iterator[str] | None = None,
|
4716
4717
|
safety: bool = True,
|
4717
4718
|
ncols: int | None = None,
|
4719
|
+
lower: bool = False,
|
4718
4720
|
include_iid_column: bool = True,
|
4719
4721
|
include_parent_column: bool = True,
|
4722
|
+
include_text_column: bool = True,
|
4720
4723
|
) -> Sheet:
|
4721
4724
|
self.reset(cell_options=False, column_widths=False, header=False, redraw=False)
|
4722
4725
|
if text_column is None:
|
@@ -4727,7 +4730,12 @@ class Sheet(tk.Frame):
|
|
4727
4730
|
for rn, row in enumerate(data):
|
4728
4731
|
if safety and ncols > (lnr := len(row)):
|
4729
4732
|
row += self.MT.get_empty_row_seq(rn, end=ncols, start=lnr)
|
4730
|
-
|
4733
|
+
if lower:
|
4734
|
+
iid = row[iid_column].lower()
|
4735
|
+
pid = row[parent_column].lower()
|
4736
|
+
else:
|
4737
|
+
iid = row[iid_column]
|
4738
|
+
pid = row[parent_column]
|
4731
4739
|
if safety:
|
4732
4740
|
if not iid:
|
4733
4741
|
continue
|
@@ -4736,7 +4744,7 @@ class Sheet(tk.Frame):
|
|
4736
4744
|
x = 1
|
4737
4745
|
while iid in tally_of_ids:
|
4738
4746
|
new = f"{row[iid_column]}_DUPLICATED_{x}"
|
4739
|
-
iid = new.lower()
|
4747
|
+
iid = new.lower() if lower else new
|
4740
4748
|
x += 1
|
4741
4749
|
tally_of_ids[iid] += 1
|
4742
4750
|
row[iid_column] = new
|
@@ -4755,14 +4763,14 @@ class Sheet(tk.Frame):
|
|
4755
4763
|
else:
|
4756
4764
|
self.RI.tree[iid].parent = ""
|
4757
4765
|
self.RI.tree_rns[iid] = rn
|
4758
|
-
|
4759
|
-
|
4760
|
-
n.parent
|
4761
|
-
|
4762
|
-
|
4763
|
-
|
4764
|
-
|
4765
|
-
|
4766
|
+
if safety:
|
4767
|
+
for n in self.RI.tree.values():
|
4768
|
+
if n.parent is None:
|
4769
|
+
n.parent = ""
|
4770
|
+
newrow = self.MT.get_empty_row_seq(len(data), ncols)
|
4771
|
+
newrow[iid_column] = n.iid
|
4772
|
+
self.RI.tree_rns[n.iid] = len(data)
|
4773
|
+
data.append(newrow)
|
4766
4774
|
insert_rows = partial(
|
4767
4775
|
self.insert_rows,
|
4768
4776
|
idx=0,
|
@@ -4773,37 +4781,22 @@ class Sheet(tk.Frame):
|
|
4773
4781
|
push_ops=push_ops,
|
4774
4782
|
redraw=False,
|
4775
4783
|
)
|
4776
|
-
|
4777
|
-
if include_iid_column
|
4778
|
-
|
4779
|
-
|
4780
|
-
|
4781
|
-
|
4782
|
-
|
4783
|
-
|
4784
|
-
[self.RI.tree[iid]] + [e for i, e in enumerate(data[self.RI.tree_rns[iid]]) if i not in exclude]
|
4785
|
-
for iid in self.get_children()
|
4786
|
-
]
|
4787
|
-
)
|
4788
|
-
|
4789
|
-
elif include_parent_column and not include_iid_column:
|
4790
|
-
exclude = {iid_column}
|
4784
|
+
exclude = set()
|
4785
|
+
if not include_iid_column:
|
4786
|
+
exclude.add(iid_column)
|
4787
|
+
if not include_parent_column:
|
4788
|
+
exclude.add(parent_column)
|
4789
|
+
if isinstance(text_column, int) and not include_text_column:
|
4790
|
+
exclude.add(text_column)
|
4791
|
+
if exclude:
|
4791
4792
|
insert_rows(
|
4792
4793
|
rows=[
|
4793
4794
|
[self.RI.tree[iid]] + [e for i, e in enumerate(data[self.RI.tree_rns[iid]]) if i not in exclude]
|
4794
|
-
for iid in self.
|
4795
|
+
for iid in self.get_nodes()
|
4795
4796
|
]
|
4796
4797
|
)
|
4797
|
-
|
4798
|
-
|
4799
|
-
exclude = {iid_column, parent_column}
|
4800
|
-
insert_rows(
|
4801
|
-
rows=[
|
4802
|
-
[self.RI.tree[iid]] + [e for i, e in enumerate(data[self.RI.tree_rns[iid]]) if i not in exclude]
|
4803
|
-
for iid in self.get_children()
|
4804
|
-
]
|
4805
|
-
)
|
4806
|
-
|
4798
|
+
else:
|
4799
|
+
insert_rows(rows=[[self.RI.tree[iid]] + data[self.RI.tree_rns[iid]] for iid in self.get_nodes()])
|
4807
4800
|
self.MT.all_rows_displayed = False
|
4808
4801
|
self.MT.displayed_rows = list(range(len(self.MT._row_index)))
|
4809
4802
|
self.RI.tree_rns = {n.iid: i for i, n in enumerate(self.MT._row_index)}
|
@@ -4840,7 +4833,7 @@ class Sheet(tk.Frame):
|
|
4840
4833
|
deselect_all=False,
|
4841
4834
|
data_indexes=True,
|
4842
4835
|
)
|
4843
|
-
open_ids = set(filter(self.exists,
|
4836
|
+
open_ids = set(filter(self.exists, open_ids))
|
4844
4837
|
self.RI.tree_open_ids = set()
|
4845
4838
|
if open_ids:
|
4846
4839
|
self.show_rows(
|
@@ -4924,58 +4917,129 @@ class Sheet(tk.Frame):
|
|
4924
4917
|
values: None | list[object] = None,
|
4925
4918
|
create_selections: bool = False,
|
4926
4919
|
) -> str:
|
4927
|
-
|
4920
|
+
"""
|
4921
|
+
Insert an item into the treeview
|
4922
|
+
"""
|
4923
|
+
if not iid:
|
4928
4924
|
i = 0
|
4929
4925
|
while (iid := f"{num2alpha(i)}") in self.RI.tree:
|
4930
4926
|
i += 1
|
4931
|
-
iid, pid = iid.lower(), parent.lower()
|
4932
|
-
if not iid:
|
4933
|
-
raise ValueError("iid cannot be empty string.")
|
4934
4927
|
if iid in self.RI.tree:
|
4935
4928
|
raise ValueError(f"iid '{iid}' already exists.")
|
4936
|
-
if iid ==
|
4937
|
-
raise ValueError(f"iid '{iid}' cannot be equal to parent '{
|
4938
|
-
if
|
4929
|
+
if iid == parent:
|
4930
|
+
raise ValueError(f"iid '{iid}' cannot be equal to parent '{parent}'.")
|
4931
|
+
if parent and parent not in self.RI.tree:
|
4939
4932
|
raise ValueError(f"parent '{parent}' does not exist.")
|
4940
4933
|
if text is None:
|
4941
4934
|
text = iid
|
4942
|
-
parent_node = self.RI.tree[
|
4935
|
+
parent_node = self.RI.tree[parent] if parent else ""
|
4943
4936
|
self.RI.tree[iid] = Node(text, iid, parent_node)
|
4944
4937
|
if parent_node:
|
4945
4938
|
if isinstance(index, int):
|
4946
|
-
|
4947
|
-
|
4948
|
-
|
4949
|
-
|
4950
|
-
|
4951
|
-
self.RI.tree[pid].children.insert(index, self.RI.tree[iid])
|
4939
|
+
datarn = self.RI.tree_rns[parent] + index + 1
|
4940
|
+
datarn += sum(
|
4941
|
+
sum(1 for _ in self.RI.get_iid_descendants(cid)) for cid in islice(self.get_children(parent), index)
|
4942
|
+
)
|
4943
|
+
self.RI.tree[parent].children.insert(index, self.RI.tree[iid])
|
4952
4944
|
else:
|
4953
|
-
|
4954
|
-
self.RI.tree[
|
4945
|
+
datarn = self.RI.tree_rns[parent] + sum(1 for _ in self.RI.get_iid_descendants(parent)) + 1
|
4946
|
+
self.RI.tree[parent].children.append(self.RI.tree[iid])
|
4955
4947
|
else:
|
4956
4948
|
if isinstance(index, int):
|
4957
|
-
|
4958
|
-
if index:
|
4959
|
-
|
4960
|
-
if count >= index:
|
4961
|
-
break
|
4962
|
-
idx += sum(1 for _ in self.RI.get_iid_descendants(cid)) + 1
|
4949
|
+
datarn = index
|
4950
|
+
if index and (datarn := self.top_index_row(datarn)) is None:
|
4951
|
+
datarn = len(self.MT._row_index)
|
4963
4952
|
else:
|
4964
|
-
|
4953
|
+
datarn = len(self.MT._row_index)
|
4965
4954
|
if values is None:
|
4966
4955
|
values = []
|
4967
4956
|
self.insert_rows(
|
4968
|
-
idx=idx,
|
4969
4957
|
rows=[[self.RI.tree[iid]] + values],
|
4958
|
+
idx=datarn,
|
4970
4959
|
row_index=True,
|
4971
4960
|
create_selections=create_selections,
|
4972
4961
|
fill=False,
|
4973
4962
|
)
|
4974
|
-
self.RI.tree_rns[iid] =
|
4975
|
-
if
|
4976
|
-
self.hide_rows(
|
4963
|
+
self.RI.tree_rns[iid] = datarn
|
4964
|
+
if parent and (parent not in self.RI.tree_open_ids or not self.item_displayed(parent)):
|
4965
|
+
self.hide_rows(datarn, deselect_all=False, data_indexes=True)
|
4977
4966
|
return iid
|
4978
4967
|
|
4968
|
+
def bulk_insert(
|
4969
|
+
self,
|
4970
|
+
data: list[list[object]],
|
4971
|
+
parent: str = "",
|
4972
|
+
index: None | int | Literal["end"] = None,
|
4973
|
+
iid_column: int | None = None,
|
4974
|
+
text_column: int | None | str = None,
|
4975
|
+
create_selections: bool = False,
|
4976
|
+
include_iid_column: bool = True,
|
4977
|
+
include_text_column: bool = True,
|
4978
|
+
) -> dict[str, int]:
|
4979
|
+
"""
|
4980
|
+
Insert multiple items into the treeview at once, under the same parent
|
4981
|
+
"""
|
4982
|
+
to_insert = []
|
4983
|
+
pid = parent
|
4984
|
+
if pid and pid not in self.RI.tree:
|
4985
|
+
raise ValueError(f"parent '{parent}' does not exist.")
|
4986
|
+
parent_node = self.RI.tree[pid] if parent else ""
|
4987
|
+
if parent_node:
|
4988
|
+
if isinstance(index, int):
|
4989
|
+
datarn = self.RI.tree_rns[pid] + index + 1
|
4990
|
+
datarn += sum(
|
4991
|
+
sum(1 for _ in self.RI.get_iid_descendants(cid)) for cid in islice(self.get_children(pid), index)
|
4992
|
+
)
|
4993
|
+
else:
|
4994
|
+
datarn = self.RI.tree_rns[pid] + sum(1 for _ in self.RI.get_iid_descendants(pid)) + 1
|
4995
|
+
else:
|
4996
|
+
if isinstance(index, int):
|
4997
|
+
datarn = index
|
4998
|
+
if index and (datarn := self.top_index_row(datarn)) is None:
|
4999
|
+
datarn = len(self.MT._row_index)
|
5000
|
+
else:
|
5001
|
+
datarn = len(self.MT._row_index)
|
5002
|
+
i = 0
|
5003
|
+
rns_to_add = {}
|
5004
|
+
for rn, r in enumerate(data, start=datarn):
|
5005
|
+
if iid_column is None:
|
5006
|
+
while (iid := f"{num2alpha(i)}") in self.RI.tree:
|
5007
|
+
i += 1
|
5008
|
+
else:
|
5009
|
+
iid = r[iid_column]
|
5010
|
+
self.RI.tree[iid] = Node(
|
5011
|
+
r[text_column] if isinstance(text_column, int) else text_column if isinstance(text_column, str) else "",
|
5012
|
+
iid,
|
5013
|
+
parent_node,
|
5014
|
+
)
|
5015
|
+
if parent_node:
|
5016
|
+
if isinstance(index, int):
|
5017
|
+
self.RI.tree[pid].children.insert(index, self.RI.tree[iid])
|
5018
|
+
else:
|
5019
|
+
self.RI.tree[pid].children.append(self.RI.tree[iid])
|
5020
|
+
exclude = set()
|
5021
|
+
if isinstance(iid_column, int) and not include_iid_column:
|
5022
|
+
exclude.add(iid_column)
|
5023
|
+
if isinstance(text_column, int) and not include_text_column:
|
5024
|
+
exclude.add(text_column)
|
5025
|
+
if exclude:
|
5026
|
+
to_insert.append([self.RI.tree[iid]] + [e for c, e in enumerate(r) if c not in exclude])
|
5027
|
+
else:
|
5028
|
+
to_insert.append([self.RI.tree[iid]] + r)
|
5029
|
+
rns_to_add[iid] = rn
|
5030
|
+
self.insert_rows(
|
5031
|
+
rows=to_insert,
|
5032
|
+
idx=datarn,
|
5033
|
+
row_index=True,
|
5034
|
+
create_selections=create_selections,
|
5035
|
+
fill=False,
|
5036
|
+
)
|
5037
|
+
for iid, rn in rns_to_add.items():
|
5038
|
+
self.RI.tree_rns[iid] = rn
|
5039
|
+
if pid and (pid not in self.RI.tree_open_ids or not self.item_displayed(pid)):
|
5040
|
+
self.hide_rows(range(datarn, datarn + len(to_insert)), deselect_all=False, data_indexes=True)
|
5041
|
+
return rns_to_add
|
5042
|
+
|
4979
5043
|
def item(
|
4980
5044
|
self,
|
4981
5045
|
item: str,
|
@@ -4990,12 +5054,11 @@ class Sheet(tk.Frame):
|
|
4990
5054
|
If no options are set then returns DotDict of options for item
|
4991
5055
|
Else returns Sheet
|
4992
5056
|
"""
|
4993
|
-
if
|
5057
|
+
if item not in self.RI.tree:
|
4994
5058
|
raise ValueError(f"Item '{item}' does not exist.")
|
4995
5059
|
if isinstance(iid, str):
|
4996
5060
|
if iid in self.RI.tree:
|
4997
5061
|
raise ValueError(f"Cannot rename '{iid}', it already exists.")
|
4998
|
-
iid = iid.lower()
|
4999
5062
|
self.RI.tree[item].iid = iid
|
5000
5063
|
self.RI.tree[iid] = self.RI.tree.pop(item)
|
5001
5064
|
self.RI.tree_rns[iid] = self.RI.tree_rns.pop(item)
|
@@ -5040,9 +5103,9 @@ class Sheet(tk.Frame):
|
|
5040
5103
|
|
5041
5104
|
def itemrow(self, item: str) -> int:
|
5042
5105
|
try:
|
5043
|
-
return self.RI.tree_rns[item
|
5106
|
+
return self.RI.tree_rns[item]
|
5044
5107
|
except Exception:
|
5045
|
-
raise ValueError(f"item '{item
|
5108
|
+
raise ValueError(f"item '{item}' does not exist.")
|
5046
5109
|
|
5047
5110
|
def rowitem(self, row: int, data_index: bool = False) -> str | None:
|
5048
5111
|
try:
|
@@ -5053,12 +5116,21 @@ class Sheet(tk.Frame):
|
|
5053
5116
|
return None
|
5054
5117
|
|
5055
5118
|
def get_children(self, item: None | str = None) -> Generator[str]:
|
5119
|
+
if item is None:
|
5120
|
+
for iid in self.get_children(""):
|
5121
|
+
yield iid
|
5122
|
+
yield from self.RI.get_iid_descendants(iid)
|
5123
|
+
elif item == "":
|
5124
|
+
yield from map(attrgetter("iid"), self.RI.gen_top_nodes())
|
5125
|
+
else:
|
5126
|
+
yield from (n.iid for n in self.RI.tree[item].children)
|
5127
|
+
|
5128
|
+
def get_nodes(self, item: None | str = None) -> Generator[str]:
|
5056
5129
|
if item is None:
|
5057
5130
|
for n in self.RI.tree.values():
|
5058
5131
|
if not n.parent:
|
5059
5132
|
yield n.iid
|
5060
|
-
|
5061
|
-
yield iid
|
5133
|
+
yield from self.RI.get_iid_descendants(n.iid)
|
5062
5134
|
elif item == "":
|
5063
5135
|
yield from (n.iid for n in self.RI.tree.values() if not n.parent)
|
5064
5136
|
else:
|
@@ -5071,7 +5143,6 @@ class Sheet(tk.Frame):
|
|
5071
5143
|
rows_to_del = []
|
5072
5144
|
iids_to_del = []
|
5073
5145
|
for item in unpack(items):
|
5074
|
-
item = item.lower()
|
5075
5146
|
if item not in self.RI.tree:
|
5076
5147
|
continue
|
5077
5148
|
rows_to_del.append(self.RI.tree_rns[item])
|
@@ -5109,9 +5180,9 @@ class Sheet(tk.Frame):
|
|
5109
5180
|
'parent' can be an empty str which will put the item at top level
|
5110
5181
|
Performance is not great
|
5111
5182
|
"""
|
5112
|
-
if
|
5183
|
+
if item not in self.RI.tree:
|
5113
5184
|
raise ValueError(f"Item '{item}' does not exist.")
|
5114
|
-
if
|
5185
|
+
if parent and parent not in self.RI.tree:
|
5115
5186
|
raise ValueError(f"Parent '{parent}' does not exist.")
|
5116
5187
|
mapping = {}
|
5117
5188
|
to_show = []
|
@@ -5188,11 +5259,8 @@ class Sheet(tk.Frame):
|
|
5188
5259
|
item_node.parent = parent_node
|
5189
5260
|
parent_node.children.insert(index, item_node)
|
5190
5261
|
else:
|
5191
|
-
if index is None:
|
5192
|
-
new_r = self.top_index_row(
|
5193
|
-
else:
|
5194
|
-
if (new_r := self.top_index_row(index)) is None:
|
5195
|
-
new_r = self.top_index_row((sum(1 for _ in self.RI.gen_top_nodes()) - 1))
|
5262
|
+
if index is None or (new_r := self.top_index_row(index)) is None:
|
5263
|
+
new_r = self.top_index_row(sum(1 for _ in self.RI.gen_top_nodes()) - 1)
|
5196
5264
|
if item_r < new_r:
|
5197
5265
|
r_ctr = (
|
5198
5266
|
new_r
|
@@ -5225,15 +5293,15 @@ class Sheet(tk.Frame):
|
|
5225
5293
|
reattach = move
|
5226
5294
|
|
5227
5295
|
def exists(self, item: str) -> bool:
|
5228
|
-
return item
|
5296
|
+
return item in self.RI.tree
|
5229
5297
|
|
5230
5298
|
def parent(self, item: str) -> str:
|
5231
|
-
if
|
5299
|
+
if item not in self.RI.tree:
|
5232
5300
|
raise ValueError(f"Item '{item}' does not exist.")
|
5233
5301
|
return self.RI.tree[item].parent.iid if self.RI.tree[item].parent else self.RI.tree[item].parent
|
5234
5302
|
|
5235
5303
|
def index(self, item: str) -> int:
|
5236
|
-
if
|
5304
|
+
if item not in self.RI.tree:
|
5237
5305
|
raise ValueError(f"Item '{item}' does not exist.")
|
5238
5306
|
if not self.RI.tree[item].parent:
|
5239
5307
|
find_node = self.RI.tree[item]
|
@@ -5245,7 +5313,7 @@ class Sheet(tk.Frame):
|
|
5245
5313
|
Check if an item (row) is currently displayed on the sheet
|
5246
5314
|
- Does not check if the item is visible to the user
|
5247
5315
|
"""
|
5248
|
-
if
|
5316
|
+
if item not in self.RI.tree:
|
5249
5317
|
raise ValueError(f"Item '{item}' does not exist.")
|
5250
5318
|
return self.RI.tree_rns[item] in self.MT.displayed_rows
|
5251
5319
|
|
@@ -5256,7 +5324,7 @@ class Sheet(tk.Frame):
|
|
5256
5324
|
- Unlike the ttk treeview 'see' function
|
5257
5325
|
this function does **NOT** scroll to the item
|
5258
5326
|
"""
|
5259
|
-
if
|
5327
|
+
if item not in self.RI.tree:
|
5260
5328
|
raise ValueError(f"Item '{item}' does not exist.")
|
5261
5329
|
if self.RI.tree[item].parent:
|
5262
5330
|
self.show_rows(
|
@@ -5270,7 +5338,7 @@ class Sheet(tk.Frame):
|
|
5270
5338
|
"""
|
5271
5339
|
Scrolls to an item and ensures that it is displayed
|
5272
5340
|
"""
|
5273
|
-
if
|
5341
|
+
if item not in self.RI.tree:
|
5274
5342
|
raise ValueError(f"Item '{item}' does not exist.")
|
5275
5343
|
self.display_item(item, redraw=False)
|
5276
5344
|
self.see(
|
@@ -5288,19 +5356,29 @@ class Sheet(tk.Frame):
|
|
5288
5356
|
self.MT._row_index[self.MT.displayed_rows[rn]].iid for rn in self.get_selected_rows(get_cells_as_rows=cells)
|
5289
5357
|
]
|
5290
5358
|
|
5291
|
-
|
5292
|
-
|
5293
|
-
|
5294
|
-
|
5295
|
-
|
5296
|
-
|
5359
|
+
@property
|
5360
|
+
def tree_selected(self) -> str | None:
|
5361
|
+
"""
|
5362
|
+
Get the iid at the currently selected box
|
5363
|
+
"""
|
5364
|
+
if selected := self.selected:
|
5365
|
+
return self.rowitem(selected.row)
|
5366
|
+
return None
|
5367
|
+
|
5368
|
+
def selection_set(self, *items, run_binding: bool = True, redraw: bool = True) -> Sheet:
|
5369
|
+
boxes_to_hide = tuple(self.MT.selection_boxes)
|
5370
|
+
self.selection_add(*items, run_binding=False, redraw=False)
|
5371
|
+
for iid in boxes_to_hide:
|
5372
|
+
self.MT.hide_selection_box(iid)
|
5373
|
+
if run_binding:
|
5374
|
+
self.MT.run_selection_binding("rows")
|
5297
5375
|
return self.set_refresh_timer(redraw)
|
5298
5376
|
|
5299
|
-
def selection_add(self, *items, redraw: bool = True) -> Sheet:
|
5377
|
+
def selection_add(self, *items, run_binding: bool = True, redraw: bool = True) -> Sheet:
|
5300
5378
|
to_open = []
|
5301
5379
|
quick_displayed_check = set(self.MT.displayed_rows)
|
5302
5380
|
for item in unpack(items):
|
5303
|
-
if self.RI.tree_rns[
|
5381
|
+
if self.RI.tree_rns[item] not in quick_displayed_check and self.RI.tree[item].parent:
|
5304
5382
|
to_open.extend(list(self.RI.get_iid_ancestors(item)))
|
5305
5383
|
if to_open:
|
5306
5384
|
self.show_rows(
|
@@ -5312,7 +5390,7 @@ class Sheet(tk.Frame):
|
|
5312
5390
|
sorted(
|
5313
5391
|
bisect_left(
|
5314
5392
|
self.MT.displayed_rows,
|
5315
|
-
self.RI.tree_rns[item
|
5393
|
+
self.RI.tree_rns[item],
|
5316
5394
|
)
|
5317
5395
|
for item in unpack(items)
|
5318
5396
|
)
|
@@ -5326,12 +5404,13 @@ class Sheet(tk.Frame):
|
|
5326
5404
|
set_current=True,
|
5327
5405
|
ext=True,
|
5328
5406
|
)
|
5329
|
-
|
5407
|
+
if run_binding:
|
5408
|
+
self.MT.run_selection_binding("rows")
|
5330
5409
|
return self.set_refresh_timer(redraw)
|
5331
5410
|
|
5332
5411
|
def selection_remove(self, *items, redraw: bool = True) -> Sheet:
|
5333
5412
|
for item in unpack(items):
|
5334
|
-
if
|
5413
|
+
if item not in self.RI.tree:
|
5335
5414
|
continue
|
5336
5415
|
try:
|
5337
5416
|
self.deselect(bisect_left(self.MT.displayed_rows, self.RI.tree_rns[item]), redraw=False)
|
@@ -5344,7 +5423,7 @@ class Sheet(tk.Frame):
|
|
5344
5423
|
add = []
|
5345
5424
|
remove = []
|
5346
5425
|
for item in unpack(items):
|
5347
|
-
if
|
5426
|
+
if item in self.RI.tree:
|
5348
5427
|
if item in selected:
|
5349
5428
|
remove.append(item)
|
5350
5429
|
else:
|
@@ -5353,6 +5432,9 @@ class Sheet(tk.Frame):
|
|
5353
5432
|
self.selection_add(*add, redraw=False)
|
5354
5433
|
return self.set_refresh_timer(redraw)
|
5355
5434
|
|
5435
|
+
def descendants(self, item: str, check_open: bool = False) -> Generator[str]:
|
5436
|
+
return self.RI.get_iid_descendants(item, check_open=check_open)
|
5437
|
+
|
5356
5438
|
# Functions not in docs
|
5357
5439
|
|
5358
5440
|
def event_generate(self, *args, **kwargs) -> None:
|
@@ -22,7 +22,7 @@ theme_light_blue: dict[str, str] = DotDict({
|
|
22
22
|
"index_selected_cells_bg": "#D3E3FD",
|
23
23
|
"index_selected_cells_fg": "black",
|
24
24
|
"top_left_bg": "#F9FBFD",
|
25
|
-
"top_left_fg": "#
|
25
|
+
"top_left_fg": "#d9d9d9",
|
26
26
|
"top_left_fg_highlight": "#747775",
|
27
27
|
"table_bg": "#FFFFFF",
|
28
28
|
"table_grid_fg": "#E1E1E1",
|
@@ -97,7 +97,7 @@ theme_light_green: dict[str, str] = DotDict({
|
|
97
97
|
"index_selected_cells_bg": "#CAEAD8",
|
98
98
|
"index_selected_cells_fg": "#107C41",
|
99
99
|
"top_left_bg": "#F5F5F5",
|
100
|
-
"top_left_fg": "#
|
100
|
+
"top_left_fg": "#d9d9d9",
|
101
101
|
"top_left_fg_highlight": "#5f6368",
|
102
102
|
"table_bg": "#FFFFFF",
|
103
103
|
"table_grid_fg": "#bfbfbf",
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|