tksheet 7.4.8__py3-none-any.whl → 7.4.10__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/row_index.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import tkinter as tk
4
4
  from collections import defaultdict
5
- from collections.abc import Callable, Generator, Hashable, Sequence
5
+ from collections.abc import Callable, Generator, Hashable, Iterator, Sequence
6
6
  from functools import partial
7
7
  from itertools import chain, cycle, islice, repeat
8
8
  from math import ceil, floor
@@ -19,6 +19,7 @@ from .constants import (
19
19
  )
20
20
  from .formatters import is_bool_like, try_to_bool
21
21
  from .functions import (
22
+ add_to_displayed,
22
23
  consecutive_ranges,
23
24
  del_placeholder_dict_key,
24
25
  event_dict,
@@ -40,7 +41,6 @@ from .functions import (
40
41
  from .other_classes import DotDict, DraggedRowColumn, DropdownStorage, EventDataDict, Node, TextEditorStorage
41
42
  from .sorting import sort_columns_by_row, sort_row
42
43
  from .text_editor import TextEditor
43
- from .tksheet_types import AnyIter
44
44
 
45
45
 
46
46
  class RowIndex(tk.Canvas):
@@ -891,7 +891,7 @@ class RowIndex(tk.Canvas):
891
891
  def _sort_rows(
892
892
  self,
893
893
  event: tk.Event | None = None,
894
- rows: AnyIter[int] | None = None,
894
+ rows: Iterator[int] | None = None,
895
895
  reverse: bool = False,
896
896
  validation: bool = True,
897
897
  key: Callable | None = None,
@@ -947,7 +947,7 @@ class RowIndex(tk.Canvas):
947
947
  if row is None:
948
948
  if not self.MT.selected:
949
949
  return event_data
950
- row = self.MT.selected.row
950
+ row = self.MT.datarn(self.MT.selected.row)
951
951
  if try_binding(self.ri_extra_begin_sort_cols_func, event_data, "begin_move_columns"):
952
952
  if key is None:
953
953
  key = self.PAR.ops.sort_key
@@ -1012,7 +1012,7 @@ class RowIndex(tk.Canvas):
1012
1012
 
1013
1013
  def select_row(
1014
1014
  self,
1015
- r: int | AnyIter[int],
1015
+ r: int | Iterator[int],
1016
1016
  redraw: bool = False,
1017
1017
  run_binding_func: bool = True,
1018
1018
  ext: bool = False,
@@ -1100,7 +1100,7 @@ class RowIndex(tk.Canvas):
1100
1100
  self.itemconfig(item, state="hidden")
1101
1101
 
1102
1102
  def get_cell_dimensions(self, datarn: int) -> tuple[int, int]:
1103
- txt = self.get_valid_cell_data_as_str(datarn, fix=False)
1103
+ txt = self.cell_str(datarn, fix=False)
1104
1104
  if txt:
1105
1105
  self.MT.txt_measure_canvas.itemconfig(self.MT.txt_measure_canvas_text, text=txt, font=self.ops.index_font)
1106
1106
  b = self.MT.txt_measure_canvas.bbox(self.MT.txt_measure_canvas_text)
@@ -1127,7 +1127,7 @@ class RowIndex(tk.Canvas):
1127
1127
  sum(
1128
1128
  1
1129
1129
  for _ in wrap_text(
1130
- text=self.get_valid_cell_data_as_str(datarn, fix=False),
1130
+ text=self.cell_str(datarn, fix=False),
1131
1131
  max_width=self.current_width,
1132
1132
  max_lines=float("inf"),
1133
1133
  char_width_fn=self.wrap_get_char_w,
@@ -1207,7 +1207,7 @@ class RowIndex(tk.Canvas):
1207
1207
 
1208
1208
  def get_index_text_width(
1209
1209
  self,
1210
- only_rows: AnyIter[int] | None = None,
1210
+ only_rows: Iterator[int] | None = None,
1211
1211
  ) -> int:
1212
1212
  self.fix_index()
1213
1213
  w = self.ops.default_row_index_width
@@ -1770,13 +1770,13 @@ class RowIndex(tk.Canvas):
1770
1770
  fill=tree_arrow_fg,
1771
1771
  tag="ta",
1772
1772
  indent=indent,
1773
- has_children=bool(self.tree[iid].children),
1773
+ has_children=bool(self.MT._row_index[datarn].children),
1774
1774
  open_=self.MT._row_index[datarn].iid in self.tree_open_ids,
1775
1775
  level=level,
1776
1776
  )
1777
1777
  if max_width <= 1:
1778
1778
  continue
1779
- text = self.get_valid_cell_data_as_str(datarn, fix=False)
1779
+ text = self.cell_str(datarn, fix=False)
1780
1780
  if not text:
1781
1781
  continue
1782
1782
  start_line = max(0, int((scrollpos_top - rtopgridln) / self.MT.index_txt_height))
@@ -2457,7 +2457,7 @@ class RowIndex(tk.Canvas):
2457
2457
  redirect_int: bool = False,
2458
2458
  ) -> Any:
2459
2459
  if get_displayed:
2460
- return self.get_valid_cell_data_as_str(datarn, fix=False)
2460
+ return self.cell_str(datarn, fix=False)
2461
2461
  if redirect_int and isinstance(self.MT._row_index, int): # internal use
2462
2462
  return self.MT.get_cell_data(datarn, self.MT._row_index, none_to_empty_str=True)
2463
2463
  if (
@@ -2471,7 +2471,7 @@ class RowIndex(tk.Canvas):
2471
2471
  return self.MT._row_index[datarn].text
2472
2472
  return self.MT._row_index[datarn]
2473
2473
 
2474
- def get_valid_cell_data_as_str(self, datarn: int, fix: bool = True) -> str:
2474
+ def cell_str(self, datarn: int, fix: bool = True) -> str:
2475
2475
  kwargs = self.get_cell_kwargs(datarn, key="dropdown")
2476
2476
  if kwargs:
2477
2477
  if kwargs["text"] is not None:
@@ -2481,7 +2481,7 @@ class RowIndex(tk.Canvas):
2481
2481
  if kwargs:
2482
2482
  return f"{kwargs['text']}"
2483
2483
  if isinstance(self.MT._row_index, int):
2484
- return self.MT.get_valid_cell_data_as_str(datarn, self.MT._row_index, get_displayed=True)
2484
+ return self.MT.cell_str(datarn, self.MT._row_index, get_displayed=True)
2485
2485
  if fix:
2486
2486
  self.fix_index(datarn)
2487
2487
  try:
@@ -2584,9 +2584,8 @@ class RowIndex(tk.Canvas):
2584
2584
  # Treeview Mode
2585
2585
 
2586
2586
  def tree_reset(self) -> None:
2587
- self.tree: dict[str, Node] = {}
2588
2587
  self.tree_open_ids = set()
2589
- self.tree_rns = {}
2588
+ self.rns = {}
2590
2589
  if self.MT:
2591
2590
  self.MT.displayed_rows = []
2592
2591
  self.MT._row_index = []
@@ -2596,7 +2595,7 @@ class RowIndex(tk.Canvas):
2596
2595
 
2597
2596
  def new_iid(self) -> str:
2598
2597
  self.new_iid_ctr += 1
2599
- while (iid := f"{num2alpha(self.new_iid_ctr)}") in self.tree:
2598
+ while (iid := f"{num2alpha(self.new_iid_ctr)}") in self.rns:
2600
2599
  self.new_iid_ctr += 1
2601
2600
  return iid
2602
2601
 
@@ -2608,7 +2607,7 @@ class RowIndex(tk.Canvas):
2608
2607
 
2609
2608
  def tree_del_rows(self, event_data: EventDataDict) -> EventDataDict:
2610
2609
  event_data["treeview"]["nodes"] = {}
2611
- for node in reversed(event_data["deleted"]["index"].values()):
2610
+ for node in event_data["deleted"]["index"].values():
2612
2611
  iid = node.iid
2613
2612
  if parent_node := self.parent_node(iid):
2614
2613
  if parent_node.iid not in event_data["treeview"]["nodes"]:
@@ -2619,38 +2618,52 @@ class RowIndex(tk.Canvas):
2619
2618
  children=parent_node.children.copy(),
2620
2619
  )
2621
2620
  self.remove_iid_from_parents_children(iid)
2622
- for node in reversed(event_data["deleted"]["index"].values()):
2621
+ for node in event_data["deleted"]["index"].values():
2623
2622
  iid = node.iid
2624
2623
  for did in self.get_iid_descendants(iid):
2625
2624
  self.tree_open_ids.discard(did)
2626
- del self.tree[did]
2627
2625
  self.tree_open_ids.discard(iid)
2628
- del self.tree[iid]
2626
+
2627
+ for datarn in reversed(event_data["deleted"]["index"]):
2628
+ del self.MT._row_index[datarn]
2629
+
2629
2630
  return event_data
2630
2631
 
2631
2632
  def tree_add_rows(self, event_data: EventDataDict) -> EventDataDict:
2632
2633
  for rn, node in event_data["added"]["rows"]["index"].items():
2633
- self.tree[node.iid] = node
2634
- self.tree_rns[node.iid] = rn
2634
+ self.rns[node.iid] = rn
2635
+
2635
2636
  if event_data["treeview"]["nodes"]:
2636
2637
  self.restore_nodes(event_data=event_data)
2637
2638
  else:
2638
- row, a_node = next(reversed(event_data["added"]["rows"]["index"].items()))
2639
+ row, a_node = next(iter(event_data["added"]["rows"]["index"].items()))
2639
2640
  if parent := a_node.parent:
2640
- if self.tree[parent].children:
2641
+ if self.iid_children(parent):
2641
2642
  index = next(
2642
- (i for i, cid in enumerate(self.tree[parent].children) if self.tree_rns[cid] >= row),
2643
- len(self.tree[parent].children),
2643
+ (i for i, cid in enumerate(self.iid_children(parent)) if self.rns[cid] >= row),
2644
+ len(self.iid_children(parent)),
2644
2645
  )
2645
- self.tree[parent].children[index:index] = [
2646
- n.iid for n in reversed(event_data["added"]["rows"]["index"].values())
2646
+ self.iid_children(parent)[index:index] = [
2647
+ n.iid for n in event_data["added"]["rows"]["index"].values()
2647
2648
  ]
2648
2649
  else:
2649
- self.tree[parent].children.extend(
2650
- n.iid for n in reversed(event_data["added"]["rows"]["index"].values())
2651
- )
2652
- if not self.PAR.item_displayed(parent) or parent not in self.tree_open_ids:
2653
- self.PAR.hide_rows(event_data["added"]["rows"]["index"], data_indexes=True)
2650
+ self.iid_children(parent).extend(n.iid for n in event_data["added"]["rows"]["index"].values())
2651
+
2652
+ # handle displaying the new rows
2653
+ event_data["added"]["rows"]["row_heights"] = {}
2654
+ if parent and self.PAR.item_displayed(parent) and parent in self.tree_open_ids:
2655
+ self.MT.displayed_rows = add_to_displayed(self.MT.displayed_rows, event_data["added"]["rows"]["index"])
2656
+ disp_idx = self.MT.disprn(self.rns[a_node.iid]) # first node, they're contiguous because not undo
2657
+ h = self.MT.get_default_row_height()
2658
+ for i in range(len(event_data["added"]["rows"]["index"])):
2659
+ event_data["added"]["rows"]["row_heights"][disp_idx + i] = h
2660
+
2661
+ elif not parent:
2662
+ self.MT.displayed_rows = add_to_displayed(self.MT.displayed_rows, event_data["added"]["rows"]["index"])
2663
+ h = self.MT.get_default_row_height()
2664
+ for datarn in event_data["added"]["rows"]["index"]:
2665
+ event_data["added"]["rows"]["row_heights"][self.MT.disprn(datarn)] = h
2666
+
2654
2667
  return event_data
2655
2668
 
2656
2669
  def move_rows_mod_nodes(
@@ -2676,18 +2689,18 @@ class RowIndex(tk.Canvas):
2676
2689
  elif event_data["moved"]["rows"]:
2677
2690
  if node_change:
2678
2691
  item = node_change[0]
2679
- moved_rows = [self.tree_rns[item]]
2692
+ moved_rows = [self.rns[item]]
2680
2693
  new_parent = node_change[1]
2681
2694
  move_to_index = node_change[2]
2682
2695
  if new_parent:
2683
2696
  if isinstance(move_to_index, int):
2684
- move_to_index = min(move_to_index, len(self.tree[new_parent].children))
2697
+ move_to_index = min(move_to_index, len(self.iid_children(new_parent)))
2685
2698
  else:
2686
- move_to_index = len(self.tree[new_parent].children)
2687
- move_to_row = self.tree_rns[new_parent]
2688
- _find = move_to_index + 1 if new_parent == self.tree[item].parent else move_to_index
2699
+ move_to_index = len(self.iid_children(new_parent))
2700
+ move_to_row = self.rns[new_parent]
2701
+ _find = move_to_index + 1 if new_parent == self.iid_parent(item) else move_to_index
2689
2702
  move_to_row += _find + sum(
2690
- self.num_descendants(cid) for cid in islice(self.tree[new_parent].children, _find)
2703
+ self.num_descendants(cid) for cid in islice(self.iid_children(new_parent), _find)
2691
2704
  )
2692
2705
  insert_row = move_to_row + 1
2693
2706
  else:
@@ -2713,7 +2726,7 @@ class RowIndex(tk.Canvas):
2713
2726
 
2714
2727
  # remove descendants in iids to move
2715
2728
  iids -= set.union(*iids_descendants.values()) & iids
2716
- moved_rows = sorted(map(self.tree_rns.__getitem__, iids))
2729
+ moved_rows = sorted(map(self.rns.__getitem__, iids))
2717
2730
  item = self.MT._row_index[moved_rows[0]].iid
2718
2731
 
2719
2732
  if isinstance(event_data.value, int):
@@ -2735,7 +2748,7 @@ class RowIndex(tk.Canvas):
2735
2748
  move_to_iid = self.MT._row_index[insert_row].iid
2736
2749
 
2737
2750
  move_to_index = self.PAR.index(move_to_iid)
2738
- new_parent = self.items_parent(move_to_iid)
2751
+ new_parent = self.iid_parent(move_to_iid)
2739
2752
 
2740
2753
  event_data["moved"]["rows"]["data"] = {moved_rows[0]: insert_row}
2741
2754
 
@@ -2747,10 +2760,10 @@ class RowIndex(tk.Canvas):
2747
2760
  if new_loc_is_displayed:
2748
2761
  if disp_insert_row is None:
2749
2762
  if new_parent or insert_row > move_to_row:
2750
- disp_insert_row = self.MT.disprn(self.tree_rns[move_to_iid]) + 1
2763
+ disp_insert_row = self.MT.disprn(self.rns[move_to_iid]) + 1
2751
2764
  else:
2752
- disp_insert_row = self.MT.disprn(self.tree_rns[move_to_iid])
2753
- if (disp_from_row := self.MT.try_disprn(self.tree_rns[item])) is not None:
2765
+ disp_insert_row = self.MT.disprn(self.rns[move_to_iid])
2766
+ if (disp_from_row := self.MT.try_disprn(self.rns[item])) is not None:
2754
2767
  event_data["moved"]["rows"]["displayed"] = {disp_from_row: disp_insert_row}
2755
2768
  else:
2756
2769
  event_data["moved"]["rows"]["displayed"] = {(): disp_insert_row}
@@ -2810,13 +2823,13 @@ class RowIndex(tk.Canvas):
2810
2823
  ) -> EventDataDict:
2811
2824
  # also backs up nodes
2812
2825
  if parent is None:
2813
- parent = self.items_parent(item)
2826
+ parent = self.iid_parent(item)
2814
2827
 
2815
- item_node = self.tree[item]
2828
+ item_node = self.iid_node(item)
2816
2829
 
2817
2830
  # new parent is an item
2818
2831
  if parent:
2819
- parent_node = self.tree[parent]
2832
+ parent_node = self.iid_node(parent)
2820
2833
  # its the same parent, we're just moving index
2821
2834
  if parent == item_node.parent:
2822
2835
  event_data = self.copy_nodes((item, parent), event_data)
@@ -2839,7 +2852,7 @@ class RowIndex(tk.Canvas):
2839
2852
  else:
2840
2853
  event_data = self.copy_nodes((item,), event_data)
2841
2854
  self.remove_iid_from_parents_children(item)
2842
- self.tree[item].parent = ""
2855
+ self.MT._row_index[self.rns[item]].parent = ""
2843
2856
 
2844
2857
  # last row in mapping is where to start from +1
2845
2858
  mapping = event_data["moved"]["rows"]["data"]
@@ -2851,7 +2864,7 @@ class RowIndex(tk.Canvas):
2851
2864
  else:
2852
2865
  disp_row_ctr = next(reversed(disp_mapping.values())) + 1
2853
2866
 
2854
- rn = self.tree_rns[item]
2867
+ rn = self.rns[item]
2855
2868
  if rn not in mapping:
2856
2869
  mapping[rn] = row_ctr
2857
2870
  row_ctr += 1
@@ -2860,9 +2873,9 @@ class RowIndex(tk.Canvas):
2860
2873
  disp_row_ctr += 1
2861
2874
 
2862
2875
  for did in self.get_iid_descendants(item):
2863
- mapping[self.tree_rns[did]] = row_ctr
2876
+ mapping[self.rns[did]] = row_ctr
2864
2877
  row_ctr += 1
2865
- if disp_mapping and (disp_from := self.MT.try_disprn(self.tree_rns[did])) is not None:
2878
+ if disp_mapping and (disp_from := self.MT.try_disprn(self.rns[did])) is not None:
2866
2879
  disp_mapping = del_placeholder_dict_key(disp_mapping, disp_from, disp_row_ctr)
2867
2880
  disp_row_ctr += 1
2868
2881
 
@@ -2873,11 +2886,10 @@ class RowIndex(tk.Canvas):
2873
2886
 
2874
2887
  def restore_nodes(self, event_data: EventDataDict) -> None:
2875
2888
  for iid, node in event_data["treeview"]["nodes"].items():
2876
- self.MT._row_index[self.tree_rns[iid]] = node
2877
- self.tree[iid] = node
2889
+ self.MT._row_index[self.rns[iid]] = node
2878
2890
 
2879
2891
  def copy_node(self, item: str) -> Node:
2880
- n = self.tree[item]
2892
+ n = self.iid_node(item)
2881
2893
  return Node(
2882
2894
  text=n.text,
2883
2895
  iid=n.iid,
@@ -2885,11 +2897,11 @@ class RowIndex(tk.Canvas):
2885
2897
  children=n.children.copy(),
2886
2898
  )
2887
2899
 
2888
- def copy_nodes(self, items: AnyIter[str], event_data: EventDataDict) -> EventDataDict:
2900
+ def copy_nodes(self, items: Iterator[str], event_data: EventDataDict) -> EventDataDict:
2889
2901
  nodes = event_data["treeview"]["nodes"]
2890
2902
  for iid in items:
2891
2903
  if iid not in nodes:
2892
- n = self.tree[iid]
2904
+ n = self.iid_node(iid)
2893
2905
  nodes[iid] = Node(
2894
2906
  text=n.text,
2895
2907
  iid=n.iid,
@@ -2898,10 +2910,12 @@ class RowIndex(tk.Canvas):
2898
2910
  )
2899
2911
  return event_data
2900
2912
 
2901
- def get_node_level(self, node: Node, level: int = 0) -> Generator[int]:
2902
- yield level
2903
- if node.parent:
2904
- yield from self.get_node_level(self.tree[node.parent], level + 1)
2913
+ def get_node_level(self, node: Node) -> int:
2914
+ level = 0
2915
+ while node.parent:
2916
+ level += 1
2917
+ node = self.MT._row_index[self.rns[node.parent]]
2918
+ return level
2905
2919
 
2906
2920
  def ancestors_all_open(self, iid: str, stop_at: str = "") -> bool:
2907
2921
  if stop_at:
@@ -2915,48 +2929,68 @@ class RowIndex(tk.Canvas):
2915
2929
 
2916
2930
  def get_iid_ancestors(self, iid: str) -> Generator[str]:
2917
2931
  current_iid = iid
2918
- while self.tree[current_iid].parent:
2919
- parent_iid = self.tree[current_iid].parent
2932
+ while self.MT._row_index[self.rns[current_iid]].parent:
2933
+ parent_iid = self.MT._row_index[self.rns[current_iid]].parent
2920
2934
  yield parent_iid
2921
2935
  current_iid = parent_iid
2922
2936
 
2923
2937
  def get_iid_descendants(self, iid: str, check_open: bool = False) -> Generator[str]:
2924
- tree = self.tree
2925
- stack = [iter(tree[iid].children)]
2938
+ index = self.MT._row_index
2939
+ stack = [iter(index[self.rns[iid]].children)]
2926
2940
  while stack:
2927
2941
  top_iterator = stack[-1]
2928
2942
  try:
2929
2943
  ciid = next(top_iterator)
2930
2944
  yield ciid
2931
- if tree[ciid].children and (not check_open or ciid in self.tree_open_ids):
2932
- stack.append(iter(tree[ciid].children))
2945
+ if index[self.rns[ciid]].children and (not check_open or ciid in self.tree_open_ids):
2946
+ stack.append(iter(index[self.rns[ciid]].children))
2933
2947
  except StopIteration:
2934
2948
  stack.pop()
2935
2949
 
2936
2950
  def num_descendants(self, iid: str) -> int:
2937
- tree = self.tree
2938
- stack = [iter(tree[iid].children)]
2951
+ index = self.MT._row_index
2952
+ stack = [iter(index[self.rns[iid]].children)]
2939
2953
  num = 0
2940
2954
  while stack:
2941
2955
  top_iterator = stack[-1]
2942
2956
  try:
2943
2957
  ciid = next(top_iterator)
2944
2958
  num += 1
2945
- if tree[ciid].children:
2946
- stack.append(iter(tree[ciid].children))
2959
+ if index[self.rns[ciid]].children:
2960
+ stack.append(iter(index[self.rns[ciid]].children))
2947
2961
  except StopIteration:
2948
2962
  stack.pop()
2949
2963
  return num
2950
2964
 
2951
- def items_parent(self, iid: str) -> str:
2952
- if self.tree[iid].parent:
2953
- return self.tree[iid].parent
2954
- return ""
2965
+ def iid_node(self, iid: str) -> Node:
2966
+ return self.MT._row_index[self.rns[iid]]
2967
+
2968
+ def iid_parent(self, iid: str) -> str:
2969
+ return self.MT._row_index[self.rns[iid]].parent
2955
2970
 
2956
- def parent_node(self, iid: str) -> Node:
2957
- if self.tree[iid].parent:
2958
- return self.tree[self.tree[iid].parent]
2959
- return ""
2971
+ def iid_children(self, iid: str) -> list[str]:
2972
+ return self.MT._row_index[self.rns[iid]].children
2973
+
2974
+ def parent_node(self, iid: str) -> Node | Literal[""]:
2975
+ if self.MT._row_index[self.rns[iid]].parent:
2976
+ return self.MT._row_index[self.rns[self.MT._row_index[self.rns[iid]].parent]]
2977
+ else:
2978
+ return ""
2979
+
2980
+ def rename_iid(self, old: str, new: str, event_data: EventDataDict) -> EventDataDict:
2981
+ event_data.treeview.renamed[old] = new
2982
+ for ciid in self.iid_children(old):
2983
+ self.MT._row_index[self.rns[ciid]].parent = new
2984
+ if self.iid_parent(old):
2985
+ parent_node = self.parent_node(old)
2986
+ item_index = parent_node.children.index(old)
2987
+ parent_node.children[item_index] = new
2988
+ self.MT._row_index[self.rns[old]].iid = new
2989
+ self.rns[new] = self.rns.pop(old)
2990
+ if old in self.tree_open_ids:
2991
+ self.tree_open_ids.discard(old)
2992
+ self.tree_open_ids.add(new)
2993
+ return event_data
2960
2994
 
2961
2995
  def gen_top_nodes(self) -> Generator[Node]:
2962
2996
  yield from (node for node in self.MT._row_index if node.parent == "")
@@ -2966,14 +3000,14 @@ class RowIndex(tk.Canvas):
2966
3000
  indent = self.MT.index_txt_width * int(self.ops.treeview_indent)
2967
3001
  else:
2968
3002
  indent = self.ops.treeview_indent
2969
- return indent * max(self.get_node_level(self.tree[iid]))
3003
+ return indent * self.get_node_level(self.iid_node(iid))
2970
3004
 
2971
3005
  def get_iid_level_indent(self, iid: str) -> tuple[int, int]:
2972
3006
  if isinstance(self.ops.treeview_indent, str):
2973
3007
  indent = self.MT.index_txt_width * int(self.ops.treeview_indent)
2974
3008
  else:
2975
3009
  indent = self.ops.treeview_indent
2976
- level = max(self.get_node_level(self.tree[iid]))
3010
+ level = self.get_node_level(self.iid_node(iid))
2977
3011
  return level, indent * level
2978
3012
 
2979
3013
  def remove_iid_from_parents_children(self, iid: str) -> None:
@@ -3004,80 +3038,182 @@ class RowIndex(tk.Canvas):
3004
3038
  text_column: None | int | list[str] = None,
3005
3039
  push_ops: bool = False,
3006
3040
  row_heights: Sequence[int] | None | False = None,
3007
- open_ids: AnyIter[str] | None = None,
3008
- safety: bool = True,
3041
+ open_ids: Iterator[str] | None = None,
3009
3042
  ncols: int | None = None,
3010
3043
  lower: bool = False,
3011
3044
  include_iid_column: bool = True,
3012
3045
  include_parent_column: bool = True,
3013
3046
  include_text_column: bool = True,
3014
3047
  ) -> None:
3015
- self.PAR.reset(cell_options=False, column_widths=False, header=False, redraw=False)
3048
+ index = self.MT._row_index
3049
+ data_rns = {}
3016
3050
  if text_column is None:
3017
3051
  text_column = iid_column
3018
- tally_of_ids = defaultdict(lambda: -1)
3019
3052
  if not isinstance(ncols, int):
3020
3053
  ncols = max(map(len, data), default=0)
3021
3054
  for rn, row in enumerate(data):
3022
- if safety and ncols > (lnr := len(row)):
3023
- row += self.MT.get_empty_row_seq(rn, end=ncols, start=lnr)
3024
3055
  if lower:
3025
3056
  iid = row[iid_column].lower()
3026
3057
  pid = row[parent_column].lower()
3027
3058
  else:
3028
3059
  iid = row[iid_column]
3029
3060
  pid = row[parent_column]
3030
- if safety:
3031
- if not iid:
3032
- continue
3033
- tally_of_ids[iid] += 1
3034
- if tally_of_ids[iid]:
3035
- x = 1
3036
- while iid in tally_of_ids:
3037
- new = f"{row[iid_column]}_DUPLICATED_{x}"
3038
- iid = new.lower() if lower else new
3039
- x += 1
3040
- tally_of_ids[iid] += 1
3041
- row[iid_column] = new
3042
- if iid in self.tree:
3043
- self.tree[iid].text = row[text_column] if isinstance(text_column, int) else text_column[rn]
3061
+ if iid in self.rns:
3062
+ index[self.rns[iid]].text = row[text_column] if isinstance(text_column, int) else text_column[rn]
3044
3063
  else:
3045
- self.tree[iid] = Node(row[text_column] if isinstance(text_column, int) else text_column[rn], iid, "")
3046
- if safety and (iid == pid or self.build_pid_causes_recursive_loop(iid, pid)):
3047
- row[parent_column] = ""
3048
- pid = ""
3064
+ index.append(
3065
+ Node(
3066
+ text=row[text_column] if isinstance(text_column, int) else text_column[rn],
3067
+ iid=iid,
3068
+ parent="",
3069
+ )
3070
+ )
3071
+ self.rns[iid] = len(index) - 1
3072
+ data_rns[iid] = rn
3049
3073
  if pid:
3050
- if pid in self.tree:
3051
- self.tree[pid].children.append(iid)
3074
+ if pid in self.rns:
3075
+ index[self.rns[pid]].children.append(iid)
3052
3076
  else:
3053
- self.tree[pid] = Node(
3054
- text=row[text_column] if isinstance(text_column, int) else text_column[rn],
3055
- iid=pid,
3056
- children=[iid],
3077
+ index.append(
3078
+ Node(
3079
+ text=pid,
3080
+ iid=pid,
3081
+ children=[iid],
3082
+ )
3057
3083
  )
3058
- self.tree[iid].parent = pid
3084
+ self.rns[pid] = len(index) - 1
3085
+ index[self.rns[iid]].parent = pid
3059
3086
  else:
3060
- self.tree[iid].parent = ""
3061
- self.tree_rns[iid] = rn
3062
- if safety:
3063
- for n in self.tree.values():
3064
- if n.parent is None:
3065
- n.parent = ""
3066
- newrow = self.MT.get_empty_row_seq(len(data), ncols)
3067
- newrow[iid_column] = n.iid
3068
- self.tree_rns[n.iid] = len(data)
3069
- data.append(newrow)
3070
- insert_rows = partial(
3071
- self.PAR.insert_rows,
3087
+ index[self.rns[iid]].parent = ""
3088
+ exclude = set()
3089
+ if not include_iid_column:
3090
+ exclude.add(iid_column)
3091
+ if not include_parent_column:
3092
+ exclude.add(parent_column)
3093
+ if isinstance(text_column, int) and not include_text_column:
3094
+ exclude.add(text_column)
3095
+ rows = []
3096
+ if exclude:
3097
+ for iid in self.PAR.tree_traverse():
3098
+ row = [index[self.rns[iid]]]
3099
+ row.extend(e for i, e in enumerate(data[data_rns[iid]]) if i not in exclude)
3100
+ rows.append(row)
3101
+ else:
3102
+ for iid in self.PAR.tree_traverse():
3103
+ row = [index[self.rns[iid]]]
3104
+ row.extend(data[data_rns[iid]])
3105
+ rows.append(row)
3106
+ self.MT._row_index = []
3107
+ self.rns = {}
3108
+ self.PAR.insert_rows(
3109
+ rows=rows,
3072
3110
  idx=0,
3073
- heights={} if row_heights is False else row_heights,
3111
+ heights=[] if row_heights is False else row_heights,
3074
3112
  row_index=True,
3075
3113
  create_selections=False,
3076
3114
  fill=False,
3077
3115
  undo=False,
3078
3116
  push_ops=push_ops,
3079
3117
  redraw=False,
3118
+ tree=False,
3080
3119
  )
3120
+ self.MT.all_rows_displayed = False
3121
+ self.MT.displayed_rows = list(range(len(self.MT._row_index)))
3122
+ self.rns = {n.iid: i for i, n in enumerate(self.MT._row_index)}
3123
+ if open_ids:
3124
+ self.PAR.tree_set_open(open_ids=open_ids)
3125
+ else:
3126
+ index = self.MT._row_index
3127
+ self.PAR.hide_rows(
3128
+ {self.rns[iid] for iid in self.PAR.get_children() if index[self.rns[iid]].parent},
3129
+ deselect_all=False,
3130
+ data_indexes=True,
3131
+ row_heights=row_heights is not False,
3132
+ )
3133
+
3134
+ def safe_tree_build(
3135
+ self,
3136
+ data: list[list[Any]],
3137
+ iid_column: int,
3138
+ parent_column: int,
3139
+ text_column: None | int | list[str] = None,
3140
+ push_ops: bool = False,
3141
+ row_heights: Sequence[int] | None | False = None,
3142
+ open_ids: Iterator[str] | None = None,
3143
+ ncols: int | None = None,
3144
+ lower: bool = False,
3145
+ include_iid_column: bool = True,
3146
+ include_parent_column: bool = True,
3147
+ include_text_column: bool = True,
3148
+ ) -> None:
3149
+ index = self.MT._row_index
3150
+ data_rns = {}
3151
+ iids_missing_rows = set()
3152
+ if text_column is None:
3153
+ text_column = iid_column
3154
+ tally_of_ids = defaultdict(lambda: -1)
3155
+ if not isinstance(ncols, int):
3156
+ ncols = max(map(len, data), default=0)
3157
+ for rn, row in enumerate(data):
3158
+ if ncols > (lnr := len(row)):
3159
+ row += self.MT.get_empty_row_seq(rn, end=ncols, start=lnr)
3160
+ if lower:
3161
+ iid = row[iid_column].lower()
3162
+ pid = row[parent_column].lower()
3163
+ else:
3164
+ iid = row[iid_column]
3165
+ pid = row[parent_column]
3166
+ if not iid:
3167
+ continue
3168
+ tally_of_ids[iid] += 1
3169
+ if tally_of_ids[iid]:
3170
+ x = 1
3171
+ while iid in tally_of_ids:
3172
+ new = f"{row[iid_column]}_DUPLICATED_{x}"
3173
+ iid = new.lower() if lower else new
3174
+ x += 1
3175
+ tally_of_ids[iid] += 1
3176
+ row[iid_column] = new
3177
+ if iid in self.rns:
3178
+ index[self.rns[iid]].text = row[text_column] if isinstance(text_column, int) else text_column[rn]
3179
+ else:
3180
+ index.append(
3181
+ Node(
3182
+ text=row[text_column] if isinstance(text_column, int) else text_column[rn],
3183
+ iid=iid,
3184
+ parent="",
3185
+ )
3186
+ )
3187
+ self.rns[iid] = len(index) - 1
3188
+ if iid in iids_missing_rows:
3189
+ iids_missing_rows.discard(iid)
3190
+ data_rns[iid] = rn
3191
+ if iid == pid or self.build_pid_causes_recursive_loop(iid, pid):
3192
+ row[parent_column] = ""
3193
+ pid = ""
3194
+ if pid:
3195
+ if pid in self.rns:
3196
+ index[self.rns[pid]].children.append(iid)
3197
+ else:
3198
+ index.append(
3199
+ Node(
3200
+ text=pid,
3201
+ iid=pid,
3202
+ children=[iid],
3203
+ )
3204
+ )
3205
+ iids_missing_rows.add(pid)
3206
+ self.rns[pid] = len(index) - 1
3207
+ index[self.rns[iid]].parent = pid
3208
+ else:
3209
+ index[self.rns[iid]].parent = ""
3210
+ empty_rows = {}
3211
+ for iid in iids_missing_rows:
3212
+ node = index[self.rns[iid]]
3213
+ node.parent = ""
3214
+ newrow = self.MT.get_empty_row_seq(len(data), ncols)
3215
+ newrow[iid_column] = node.iid
3216
+ empty_rows[node.iid] = newrow
3081
3217
  exclude = set()
3082
3218
  if not include_iid_column:
3083
3219
  exclude.add(iid_column)
@@ -3085,27 +3221,46 @@ class RowIndex(tk.Canvas):
3085
3221
  exclude.add(parent_column)
3086
3222
  if isinstance(text_column, int) and not include_text_column:
3087
3223
  exclude.add(text_column)
3224
+ rows = []
3088
3225
  if exclude:
3089
- insert_rows(
3090
- rows=[
3091
- [self.tree[iid]] + [e for i, e in enumerate(data[self.tree_rns[iid]]) if i not in exclude]
3092
- for iid in self.PAR.get_iids()
3093
- ],
3094
- tree=False,
3095
- )
3226
+ for iid in self.PAR.tree_traverse():
3227
+ row = [index[self.rns[iid]]]
3228
+ if iid in empty_rows:
3229
+ row.extend(e for i, e in enumerate(empty_rows[iid]) if i not in exclude)
3230
+ else:
3231
+ row.extend(e for i, e in enumerate(data[data_rns[iid]]) if i not in exclude)
3232
+ rows.append(row)
3096
3233
  else:
3097
- insert_rows(
3098
- rows=[[self.tree[iid]] + data[self.tree_rns[iid]] for iid in self.PAR.get_iids()],
3099
- tree=False,
3100
- )
3234
+ for iid in self.PAR.tree_traverse():
3235
+ row = [index[self.rns[iid]]]
3236
+ if iid in empty_rows:
3237
+ row.extend(empty_rows[iid])
3238
+ else:
3239
+ row.extend(data[data_rns[iid]])
3240
+ rows.append(row)
3241
+ self.MT._row_index = []
3242
+ self.rns = {}
3243
+ self.PAR.insert_rows(
3244
+ rows=rows,
3245
+ idx=0,
3246
+ heights=[] if row_heights is False else row_heights,
3247
+ row_index=True,
3248
+ create_selections=False,
3249
+ fill=False,
3250
+ undo=False,
3251
+ push_ops=push_ops,
3252
+ redraw=False,
3253
+ tree=False,
3254
+ )
3101
3255
  self.MT.all_rows_displayed = False
3102
3256
  self.MT.displayed_rows = list(range(len(self.MT._row_index)))
3103
- self.tree_rns = {n.iid: i for i, n in enumerate(self.MT._row_index)}
3257
+ self.rns = {n.iid: i for i, n in enumerate(self.MT._row_index)}
3104
3258
  if open_ids:
3105
3259
  self.PAR.tree_set_open(open_ids=open_ids)
3106
3260
  else:
3261
+ index = self.MT._row_index
3107
3262
  self.PAR.hide_rows(
3108
- {self.tree_rns[iid] for iid in self.PAR.get_children() if self.tree[iid].parent},
3263
+ {self.rns[iid] for iid in self.PAR.get_children() if index[self.rns[iid]].parent},
3109
3264
  deselect_all=False,
3110
3265
  data_indexes=True,
3111
3266
  row_heights=row_heights is not False,