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.
Files changed (24) hide show
  1. {tksheet-7.2.17/tksheet.egg-info → tksheet-7.2.18}/PKG-INFO +1 -1
  2. {tksheet-7.2.17 → tksheet-7.2.18}/pyproject.toml +1 -1
  3. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/__init__.py +1 -1
  4. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/functions.py +16 -2
  5. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/main_table.py +13 -23
  6. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/other_classes.py +0 -1
  7. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/row_index.py +5 -3
  8. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/sheet.py +180 -98
  9. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/themes.py +2 -2
  10. {tksheet-7.2.17 → tksheet-7.2.18/tksheet.egg-info}/PKG-INFO +1 -1
  11. {tksheet-7.2.17 → tksheet-7.2.18}/LICENSE.txt +0 -0
  12. {tksheet-7.2.17 → tksheet-7.2.18}/README.md +0 -0
  13. {tksheet-7.2.17 → tksheet-7.2.18}/setup.cfg +0 -0
  14. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/colors.py +0 -0
  15. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/column_headers.py +0 -0
  16. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/formatters.py +0 -0
  17. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/sheet_options.py +0 -0
  18. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/text_editor.py +0 -0
  19. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/top_left_rectangle.py +0 -0
  20. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/types.py +0 -0
  21. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet/vars.py +0 -0
  22. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet.egg-info/SOURCES.txt +0 -0
  23. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet.egg-info/dependency_links.txt +0 -0
  24. {tksheet-7.2.17 → tksheet-7.2.18}/tksheet.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tksheet
3
- Version: 7.2.17
3
+ Version: 7.2.18
4
4
  Summary: Tkinter table / sheet widget
5
5
  Author-email: ragardner <github@ragardner.simplelogin.com>
6
6
  License: Copyright (c) 2019 ragardner and open source contributors
@@ -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.17"
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"}
@@ -4,7 +4,7 @@
4
4
  tksheet - A Python tkinter table widget
5
5
  """
6
6
 
7
- __version__ = "7.2.17"
7
+ __version__ = "7.2.18"
8
8
 
9
9
  from .colors import (
10
10
  color_map,
@@ -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: str = "numbers") -> str:
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
- return f"{num2alpha(n)} {n + 1}"
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
- # push displayed indexes by one for every inserted column
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 = int(displayed_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
- # push displayed indexes by one for every inserted row
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 = int(displayed_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:
@@ -6,7 +6,6 @@ from collections.abc import Callable, Generator, Hashable, Iterator
6
6
  from functools import partial
7
7
  from typing import Literal
8
8
 
9
-
10
9
  pickle_obj = partial(pickle.dumps, protocol=pickle.HIGHEST_PROTOCOL)
11
10
 
12
11
  FontTuple = namedtuple("FontTuple", "family size style")
@@ -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
- iid, pid = row[iid_column].lower(), row[parent_column].lower()
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
- for n in self.RI.tree.values():
4759
- if n.parent is None:
4760
- n.parent = ""
4761
- newrow = self.MT.get_empty_row_seq(len(data), ncols)
4762
- newrow[iid_column] = n.iid
4763
- self.RI.tree_rns[n.iid] = len(data)
4764
- data.append(newrow)
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 and include_parent_column:
4778
- insert_rows(rows=[[self.RI.tree[iid]] + data[self.RI.tree_rns[iid]] for iid in self.get_children()])
4779
-
4780
- elif include_iid_column and not include_parent_column:
4781
- exclude = {parent_column}
4782
- insert_rows(
4783
- rows=[
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.get_children()
4795
+ for iid in self.get_nodes()
4795
4796
  ]
4796
4797
  )
4797
-
4798
- elif not include_iid_column and not include_parent_column:
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, map(str.lower, open_ids)))
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
- if iid is None:
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 == pid:
4937
- raise ValueError(f"iid '{iid}' cannot be equal to parent '{pid}'.")
4938
- if pid and pid not in self.RI.tree:
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[pid] if parent else ""
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
- idx = self.RI.tree_rns[pid] + index + 1
4947
- for count, cid in enumerate(self.get_children(pid)):
4948
- if count >= index:
4949
- break
4950
- idx += sum(1 for _ in self.RI.get_iid_descendants(cid))
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
- idx = self.RI.tree_rns[pid] + sum(1 for _ in self.RI.get_iid_descendants(pid)) + 1
4954
- self.RI.tree[pid].children.append(self.RI.tree[iid])
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
- idx = index
4958
- if index:
4959
- for count, cid in enumerate(self.get_children("")):
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
- idx = len(self.MT._row_index)
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] = idx
4975
- if pid and (pid not in self.RI.tree_open_ids or not self.item_displayed(pid)):
4976
- self.hide_rows(idx, deselect_all=False, data_indexes=True)
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 not (item := item.lower()) or item not in self.RI.tree:
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.lower()]
5106
+ return self.RI.tree_rns[item]
5044
5107
  except Exception:
5045
- raise ValueError(f"item '{item.lower()}' does not exist.")
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
- for iid in self.RI.get_iid_descendants(n.iid):
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 (item := item.lower()) and item not in self.RI.tree:
5183
+ if item not in self.RI.tree:
5113
5184
  raise ValueError(f"Item '{item}' does not exist.")
5114
- if (parent := parent.lower()) and parent not in self.RI.tree:
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((sum(1 for _ in self.RI.gen_top_nodes()) - 1))
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.lower() in self.RI.tree
5296
+ return item in self.RI.tree
5229
5297
 
5230
5298
  def parent(self, item: str) -> str:
5231
- if (item := item.lower()) not in self.RI.tree:
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 (item := item.lower()) not in self.RI.tree:
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 (item := item.lower()) not in self.RI.tree:
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 (item := item.lower()) not in self.RI.tree:
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 (item := item.lower()) not in self.RI.tree:
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
- def selection_set(self, *items, redraw: bool = True) -> Sheet:
5292
- if any(item.lower() in self.RI.tree for item in unpack(items)):
5293
- boxes_to_hide = tuple(self.MT.selection_boxes)
5294
- self.selection_add(*items, redraw=False)
5295
- for iid in boxes_to_hide:
5296
- self.MT.hide_selection_box(iid)
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[(item := item.lower())] not in quick_displayed_check and self.RI.tree[item].parent:
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.lower()],
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
- self.MT.run_selection_binding("rows")
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 (item := item.lower()) not in self.RI.tree:
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 (item := item.lower()) in self.RI.tree:
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": "#C7C7C7",
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": "#b7b7b7",
100
+ "top_left_fg": "#d9d9d9",
101
101
  "top_left_fg_highlight": "#5f6368",
102
102
  "table_bg": "#FFFFFF",
103
103
  "table_grid_fg": "#bfbfbf",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tksheet
3
- Version: 7.2.17
3
+ Version: 7.2.18
4
4
  Summary: Tkinter table / sheet widget
5
5
  Author-email: ragardner <github@ragardner.simplelogin.com>
6
6
  License: Copyright (c) 2019 ragardner and open source contributors
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes