tksheet 7.5.3__tar.gz → 7.5.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {tksheet-7.5.3/tksheet.egg-info → tksheet-7.5.5}/PKG-INFO +1 -1
  2. {tksheet-7.5.3 → tksheet-7.5.5}/pyproject.toml +1 -1
  3. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/__init__.py +1 -1
  4. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/functions.py +43 -34
  5. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/main_table.py +4 -6
  6. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/other_classes.py +4 -4
  7. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/row_index.py +39 -80
  8. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/sheet.py +37 -23
  9. {tksheet-7.5.3 → tksheet-7.5.5/tksheet.egg-info}/PKG-INFO +1 -1
  10. {tksheet-7.5.3 → tksheet-7.5.5}/LICENSE.txt +0 -0
  11. {tksheet-7.5.3 → tksheet-7.5.5}/README.md +0 -0
  12. {tksheet-7.5.3 → tksheet-7.5.5}/setup.cfg +0 -0
  13. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/colors.py +0 -0
  14. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/column_headers.py +0 -0
  15. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/constants.py +0 -0
  16. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/find_window.py +0 -0
  17. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/formatters.py +0 -0
  18. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/sheet_options.py +0 -0
  19. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/sorting.py +0 -0
  20. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/text_editor.py +0 -0
  21. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/themes.py +0 -0
  22. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/tksheet_types.py +0 -0
  23. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet/top_left_rectangle.py +0 -0
  24. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet.egg-info/SOURCES.txt +0 -0
  25. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet.egg-info/dependency_links.txt +0 -0
  26. {tksheet-7.5.3 → tksheet-7.5.5}/tksheet.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tksheet
3
- Version: 7.5.3
3
+ Version: 7.5.5
4
4
  Summary: Tkinter table / sheet and treeview widget
5
5
  Author-email: ragardner <github@ragardner.simplelogin.com>
6
6
  License: Copyright (c) 2019 ragardner and open source contributors
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
  name = "tksheet"
7
7
  description = "Tkinter table / sheet and treeview widget"
8
8
  readme = "README.md"
9
- version = "7.5.3"
9
+ version = "7.5.5"
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.5.3"
7
+ __version__ = "7.5.5"
8
8
 
9
9
  from .colors import (
10
10
  color_map,
@@ -765,6 +765,14 @@ def add_to_displayed(displayed: list[int], to_add: Iterable[int]) -> list[int]:
765
765
  return displayed
766
766
 
767
767
 
768
+ def push_displayed(displayed: list[int], to_add: Iterable[int]) -> list[int]:
769
+ # assumes to_add is sorted
770
+ for i in to_add:
771
+ ins = bisect_left(displayed, i)
772
+ displayed[ins:] = [e + 1 for e in islice(displayed, ins, None)]
773
+ return displayed
774
+
775
+
768
776
  def move_elements_by_mapping(
769
777
  seq: list[Any],
770
778
  new_idxs: dict[int, int],
@@ -853,18 +861,6 @@ def insert_items(
853
861
  return seq
854
862
 
855
863
 
856
- def del_placeholder_dict_key(
857
- d: dict[Hashable, Any],
858
- k: Hashable,
859
- v: Any,
860
- p: tuple = (),
861
- ) -> dict[Hashable, Any]:
862
- if p in d:
863
- del d[p]
864
- d[k] = v
865
- return d
866
-
867
-
868
864
  def data_to_displayed_idxs(
869
865
  to_convert: list[int],
870
866
  displayed: list[int],
@@ -1207,14 +1203,14 @@ PATTERN_ALL = re.compile(r"^:$") # ":"
1207
1203
 
1208
1204
  def span_a2i(a: str) -> int | None:
1209
1205
  n = 0
1210
- for c in a:
1206
+ for c in a.upper():
1211
1207
  n = n * 26 + ord(c) - ORD_A + 1
1212
1208
  return n - 1
1213
1209
 
1214
1210
 
1215
1211
  def span_a2n(a: str) -> int | None:
1216
1212
  n = 0
1217
- for c in a:
1213
+ for c in a.upper():
1218
1214
  n = n * 26 + ord(c) - ORD_A + 1
1219
1215
  return n
1220
1216
 
@@ -1251,7 +1247,7 @@ def key_to_span(
1251
1247
  return coords_to_span(widget=widget, from_r=None, from_c=None, upto_r=None, upto_c=None)
1252
1248
 
1253
1249
  # Validate input type
1254
- elif not isinstance(key, (str, int, slice, list, tuple)):
1250
+ elif not isinstance(key, (str, int, slice, tuple, list)):
1255
1251
  return f"Key type must be either str, int, list, tuple or slice, not '{type(key).__name__}'."
1256
1252
 
1257
1253
  try:
@@ -1277,22 +1273,34 @@ def key_to_span(
1277
1273
  )
1278
1274
 
1279
1275
  # Sequence key: various span formats
1280
- elif isinstance(key, (list, tuple)):
1281
- if (
1282
- len(key) == 2
1283
- and (isinstance(key[0], int) or key[0] is None)
1284
- and (isinstance(key[1], int) or key[1] is None)
1285
- ):
1286
- # Single cell or partial span: (row, col)
1287
- r_int = isinstance(key[0], int)
1288
- c_int = isinstance(key[1], int)
1289
- return span_dict(
1290
- from_r=key[0] if r_int else 0,
1291
- from_c=key[1] if c_int else 0,
1292
- upto_r=key[0] + 1 if r_int else None,
1293
- upto_c=key[1] + 1 if c_int else None,
1294
- widget=widget,
1295
- )
1276
+ elif isinstance(key, (tuple, list)):
1277
+ if len(key) == 2:
1278
+ if (isinstance(key[0], int) or key[0] is None) and (isinstance(key[1], int) or key[1] is None):
1279
+ # Single cell or partial span: (row, col)
1280
+ r_int = isinstance(key[0], int)
1281
+ c_int = isinstance(key[1], int)
1282
+ return span_dict(
1283
+ from_r=key[0] if r_int else 0,
1284
+ from_c=key[1] if c_int else 0,
1285
+ upto_r=key[0] + 1 if r_int else None,
1286
+ upto_c=key[1] + 1 if c_int else None,
1287
+ widget=widget,
1288
+ )
1289
+
1290
+ elif isinstance(key[0], int) and isinstance(key[1], str):
1291
+ # Single cell with column letter: (row 0, col A)
1292
+ c_int = span_a2i(key[1])
1293
+ return span_dict(
1294
+ from_r=key[0],
1295
+ from_c=c_int,
1296
+ upto_r=key[0] + 1,
1297
+ upto_c=c_int + 1,
1298
+ widget=widget,
1299
+ )
1300
+
1301
+ else:
1302
+ return f"'{key}' could not be converted to span."
1303
+
1296
1304
  elif len(key) == 4:
1297
1305
  # Full span coordinates: (from_r, from_c, upto_r, upto_c)
1298
1306
  return coords_to_span(
@@ -1302,7 +1310,7 @@ def key_to_span(
1302
1310
  upto_r=key[2],
1303
1311
  upto_c=key[3],
1304
1312
  )
1305
- elif len(key) == 2 and all(isinstance(k, (list, tuple)) for k in key):
1313
+ elif len(key) == 2 and all(isinstance(k, (tuple, list)) for k in key):
1306
1314
  # Start and end points: ((from_r, from_c), (upto_r, upto_c))
1307
1315
  return coords_to_span(
1308
1316
  widget=widget,
@@ -1339,11 +1347,12 @@ def key_to_span(
1339
1347
  widget=widget,
1340
1348
  )
1341
1349
  elif m := PATTERN_COL.match(key):
1350
+ c_int = span_a2i(m[1])
1342
1351
  return span_dict(
1343
1352
  from_r=None,
1344
- from_c=span_a2i(m[1]),
1353
+ from_c=c_int,
1345
1354
  upto_r=None,
1346
- upto_c=span_a2n(m[1]),
1355
+ upto_c=c_int + 1,
1347
1356
  widget=widget,
1348
1357
  )
1349
1358
  elif m := PATTERN_CELL.match(key):
@@ -1967,6 +1967,7 @@ class MainTable(tk.Canvas):
1967
1967
  )
1968
1968
  else:
1969
1969
  self.recreate_all_selection_boxes()
1970
+
1970
1971
  return data_new_idxs, disp_new_idxs, event_data
1971
1972
 
1972
1973
  def get_max_row_idx(self, maxidx: int | None = None) -> int:
@@ -2117,6 +2118,7 @@ class MainTable(tk.Canvas):
2117
2118
  event_data["selection_boxes"] = modification["selection_boxes"]
2118
2119
  event_data["selected"] = modification["selected"]
2119
2120
  saved_cells = False
2121
+
2120
2122
  if modification["added"]["rows"] or modification["added"]["columns"]:
2121
2123
  event_data = self.save_cells_using_modification(modification, event_data)
2122
2124
  saved_cells = True
@@ -5629,12 +5631,8 @@ class MainTable(tk.Canvas):
5629
5631
  return {
5630
5632
  "row_positions": list(self.row_positions),
5631
5633
  "col_positions": list(self.col_positions),
5632
- "displayed_rows": int(self.displayed_rows)
5633
- if isinstance(self.displayed_rows, int)
5634
- else list(self.displayed_rows),
5635
- "displayed_columns": int(self.displayed_columns)
5636
- if isinstance(self.displayed_columns, int)
5637
- else list(self.displayed_columns),
5634
+ "displayed_rows": list(self.displayed_rows),
5635
+ "displayed_columns": list(self.displayed_columns),
5638
5636
  "all_rows_displayed": bool(self.all_rows_displayed),
5639
5637
  "saved_row_heights": dict(self.saved_row_heights),
5640
5638
  "saved_column_widths": dict(self.saved_column_widths),
@@ -481,10 +481,10 @@ class Node:
481
481
  parent: str = "",
482
482
  children: list[str] | None = None,
483
483
  ) -> None:
484
- self.text = text
485
- self.iid = iid
486
- self.parent = parent
487
- self.children = children if children else []
484
+ self.text: str = text
485
+ self.iid: str = iid
486
+ self.parent: str = parent
487
+ self.children: list[str] = children if children else []
488
488
 
489
489
 
490
490
  class StorageBase:
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import tkinter as tk
4
4
  from collections import defaultdict
5
5
  from collections.abc import Callable, Generator, Hashable, Iterator, Sequence
6
- from contextlib import suppress
7
6
  from functools import partial
8
7
  from itertools import islice, repeat
9
8
  from math import ceil
@@ -22,7 +21,6 @@ from .formatters import is_bool_like, try_to_bool
22
21
  from .functions import (
23
22
  add_to_displayed,
24
23
  consecutive_ranges,
25
- del_placeholder_dict_key,
26
24
  event_dict,
27
25
  event_has_char_key,
28
26
  event_opens_dropdown_or_checkbox,
@@ -34,6 +32,7 @@ from .functions import (
34
32
  mod_event_val,
35
33
  new_tk_event,
36
34
  num2alpha,
35
+ push_displayed,
37
36
  rounded_box_coords,
38
37
  stored_event_dict,
39
38
  try_b_index,
@@ -839,7 +838,7 @@ class RowIndex(tk.Canvas):
839
838
  move_heights=self.ops.row_drag_and_drop_perform,
840
839
  event_data=event_data,
841
840
  )
842
- if data_new_idxs and disp_new_idxs:
841
+ if data_new_idxs:
843
842
  event_data["moved"]["rows"] = {
844
843
  "data": data_new_idxs,
845
844
  "displayed": disp_new_idxs,
@@ -2634,13 +2633,17 @@ class RowIndex(tk.Canvas):
2634
2633
 
2635
2634
  # handle displaying the new rows
2636
2635
  event_data["added"]["rows"]["row_heights"] = {}
2636
+ # parent exists and it's displayed and it's open
2637
2637
  if parent and self.PAR.item_displayed(parent) and parent in self.tree_open_ids:
2638
2638
  self.MT.displayed_rows = add_to_displayed(self.MT.displayed_rows, event_data["added"]["rows"]["index"])
2639
2639
  disp_idx = self.MT.disprn(self.rns[a_node.iid]) # first node, they're contiguous because not undo
2640
2640
  h = self.MT.get_default_row_height()
2641
2641
  for i in range(len(event_data["added"]["rows"]["index"])):
2642
2642
  event_data["added"]["rows"]["row_heights"][disp_idx + i] = h
2643
-
2643
+ # parent exists and either it's not displayed or not open
2644
+ elif parent:
2645
+ self.MT.displayed_rows = push_displayed(self.MT.displayed_rows, event_data["added"]["rows"]["index"])
2646
+ # no parent, top level
2644
2647
  elif not parent:
2645
2648
  self.MT.displayed_rows = add_to_displayed(self.MT.displayed_rows, event_data["added"]["rows"]["index"])
2646
2649
  h = self.MT.get_default_row_height()
@@ -2675,34 +2678,38 @@ class RowIndex(tk.Canvas):
2675
2678
  moved_rows = [self.rns[item]]
2676
2679
  new_parent = node_change[1]
2677
2680
  move_to_index = node_change[2]
2681
+ # new parent exists
2678
2682
  if new_parent:
2683
+ new_par_cn = self.iid_children(new_parent)
2684
+ # determine index
2679
2685
  if isinstance(move_to_index, int):
2680
- move_to_index = min(move_to_index, len(self.iid_children(new_parent)))
2686
+ move_to_index = min(move_to_index, len(new_par_cn))
2681
2687
  else:
2682
- move_to_index = len(self.iid_children(new_parent))
2683
- move_to_row = self.rns[new_parent]
2684
- _find = move_to_index + 1 if new_parent == self.iid_parent(item) else move_to_index
2685
- move_to_row += _find + sum(
2686
- self.num_descendants(cid) for cid in islice(self.iid_children(new_parent), _find)
2687
- )
2688
- insert_row = move_to_row + 1
2688
+ move_to_index = len(new_par_cn)
2689
+ # determine insert row
2690
+ if not new_par_cn:
2691
+ insert_row = self.rns[new_parent] + 1
2692
+ elif move_to_index >= len(new_par_cn):
2693
+ insert_row = self.rns[new_par_cn[-1]] + self.num_descendants(new_par_cn[-1]) + 1
2694
+ elif new_parent == self.iid_parent(item):
2695
+ if move_to_index <= self.PAR.index(item):
2696
+ # Insert before the sibling at move_to_index
2697
+ insert_row = self.rns[new_par_cn[move_to_index]]
2698
+ else:
2699
+ # Insert after the sibling at move_to_index
2700
+ insert_row = self.rns[new_par_cn[move_to_index]]
2701
+ insert_row += 1 + self.num_descendants(new_par_cn[move_to_index])
2702
+ else:
2703
+ insert_row = self.rns[new_par_cn[move_to_index]]
2704
+ # no new parent
2689
2705
  else:
2690
- num_top_nodes = sum(1 for _ in self.gen_top_nodes())
2691
- if move_to_index is None:
2692
- move_to_row = self.PAR.top_index_row(num_top_nodes - 1)
2693
- move_to_index = num_top_nodes
2694
- insert_row = move_to_row
2706
+ # determine index
2707
+ if isinstance(move_to_index, int):
2708
+ move_to_index = min(move_to_index, sum(1 for _ in self.gen_top_nodes()))
2695
2709
  else:
2696
- move_to_row = self.PAR.top_index_row(move_to_index)
2697
- insert_row = move_to_row
2698
- if move_to_row is None:
2699
- move_to_row = self.PAR.top_index_row(num_top_nodes - 1)
2700
- move_to_index = num_top_nodes
2701
- insert_row = move_to_row + 1
2702
-
2703
- move_to_iid = self.MT._row_index[move_to_row].iid
2704
- disp_insert_row = None
2705
-
2710
+ move_to_index = sum(1 for _ in self.gen_top_nodes())
2711
+ # determine insert row
2712
+ insert_row = self.PAR._get_id_insert_row(move_to_index, new_parent)
2706
2713
  else:
2707
2714
  iids = {self.MT._row_index[r].iid for r in event_data["moved"]["rows"]["data"]}
2708
2715
  iids_descendants = {iid: set(self.get_iid_descendants(iid)) for iid in iids}
@@ -2713,21 +2720,18 @@ class RowIndex(tk.Canvas):
2713
2720
  item = self.MT._row_index[moved_rows[0]].iid
2714
2721
 
2715
2722
  if isinstance(event_data.value, int):
2716
- disp_insert_row = event_data.value
2717
- if disp_insert_row >= len(self.MT.displayed_rows):
2723
+ if event_data.value >= len(self.MT.displayed_rows):
2718
2724
  insert_row = len(self.MT._row_index)
2719
2725
  else:
2720
- insert_row = self.MT.datarn(disp_insert_row)
2726
+ insert_row = self.MT.datarn(event_data.value)
2721
2727
  move_to_iid = self.MT._row_index[min(insert_row, len(self.MT._row_index) - 1)].iid
2722
2728
 
2723
2729
  else:
2724
- disp_insert_row = None
2725
2730
  min_from = min(event_data["moved"]["rows"]["data"])
2726
2731
  # max_from = max(event_data.moved.rows)
2727
2732
  min_to = min(event_data["moved"]["rows"]["data"].values())
2728
2733
  max_to = max(event_data["moved"]["rows"]["data"].values())
2729
2734
  insert_row = max_to if min_from <= min_to else min_to
2730
- move_to_row = insert_row
2731
2735
  move_to_iid = self.MT._row_index[insert_row].iid
2732
2736
 
2733
2737
  move_to_index = self.PAR.index(move_to_iid)
@@ -2738,19 +2742,7 @@ class RowIndex(tk.Canvas):
2738
2742
  new_loc_is_displayed = not new_parent or (
2739
2743
  new_parent and new_parent in self.tree_open_ids and self.PAR.item_displayed(new_parent)
2740
2744
  )
2741
- # deal with displayed mapping
2742
2745
  event_data["moved"]["rows"]["displayed"] = {}
2743
- with suppress(Exception):
2744
- if new_loc_is_displayed:
2745
- if disp_insert_row is None:
2746
- if new_parent or insert_row > move_to_row:
2747
- disp_insert_row = self.MT.disprn(self.rns[move_to_iid]) + 1
2748
- else:
2749
- disp_insert_row = self.MT.disprn(self.rns[move_to_iid])
2750
- if (disp_from_row := self.MT.try_disprn(self.rns[item])) is not None:
2751
- event_data["moved"]["rows"]["displayed"] = {disp_from_row: disp_insert_row}
2752
- else:
2753
- event_data["moved"]["rows"]["displayed"] = {(): disp_insert_row}
2754
2746
 
2755
2747
  if any(self.move_pid_causes_recursive_loop(self.MT._row_index[r].iid, new_parent) for r in moved_rows):
2756
2748
  event_data["moved"]["rows"] = {}
@@ -2772,15 +2764,6 @@ class RowIndex(tk.Canvas):
2772
2764
  )
2773
2765
  data_new_idxs = event_data["moved"]["rows"]["data"]
2774
2766
  data_old_idxs = {v: k for k, v in data_new_idxs.items()}
2775
-
2776
- if () in event_data["moved"]["rows"]["displayed"]:
2777
- del event_data["moved"]["rows"]["displayed"][()]
2778
-
2779
- if event_data["moved"]["rows"]["displayed"]:
2780
- event_data["moved"]["rows"]["displayed"] = get_new_indexes(
2781
- disp_insert_row,
2782
- event_data["moved"]["rows"]["displayed"],
2783
- )
2784
2767
  disp_new_idxs = event_data["moved"]["rows"]["displayed"]
2785
2768
 
2786
2769
  if data_new_idxs:
@@ -2817,8 +2800,7 @@ class RowIndex(tk.Canvas):
2817
2800
  # its the same parent, we're just moving index
2818
2801
  if parent == item_node.parent:
2819
2802
  event_data = self.copy_nodes((item, parent), event_data)
2820
- pop_index = parent_node.children.index(item)
2821
- parent_node.children.insert(index, parent_node.children.pop(pop_index))
2803
+ parent_node.children.insert(index, parent_node.children.pop(parent_node.children.index(item)))
2822
2804
 
2823
2805
  else:
2824
2806
  if item_node.parent:
@@ -2842,29 +2824,16 @@ class RowIndex(tk.Canvas):
2842
2824
  mapping = event_data["moved"]["rows"]["data"]
2843
2825
  row_ctr = next(reversed(mapping.values())) + 1
2844
2826
 
2845
- if disp_mapping := event_data["moved"]["rows"]["displayed"]:
2846
- if () in disp_mapping:
2847
- disp_row_ctr = next(reversed(disp_mapping.values()))
2848
- else:
2849
- disp_row_ctr = next(reversed(disp_mapping.values())) + 1
2850
-
2851
2827
  rn = self.rns[item]
2852
2828
  if rn not in mapping:
2853
2829
  mapping[rn] = row_ctr
2854
2830
  row_ctr += 1
2855
- if disp_mapping and (disp_from := self.MT.try_disprn(rn)) is not None:
2856
- disp_mapping = del_placeholder_dict_key(disp_mapping, disp_from, disp_row_ctr)
2857
- disp_row_ctr += 1
2858
2831
 
2859
2832
  for did in self.get_iid_descendants(item):
2860
2833
  mapping[self.rns[did]] = row_ctr
2861
2834
  row_ctr += 1
2862
- if disp_mapping and (disp_from := self.MT.try_disprn(self.rns[did])) is not None:
2863
- disp_mapping = del_placeholder_dict_key(disp_mapping, disp_from, disp_row_ctr)
2864
- disp_row_ctr += 1
2865
2835
 
2866
2836
  event_data["moved"]["rows"]["data"] = mapping
2867
- event_data["moved"]["rows"]["displayed"] = disp_mapping
2868
2837
 
2869
2838
  return event_data
2870
2839
 
@@ -2872,21 +2841,11 @@ class RowIndex(tk.Canvas):
2872
2841
  for iid, node in event_data["treeview"]["nodes"].items():
2873
2842
  self.MT._row_index[self.rns[iid]] = node
2874
2843
 
2875
- def copy_node(self, item: str) -> Node:
2876
- n = self.iid_node(item)
2877
- return Node(
2878
- text=n.text,
2879
- iid=n.iid,
2880
- parent=n.parent,
2881
- children=n.children.copy(),
2882
- )
2883
-
2884
2844
  def copy_nodes(self, items: Iterator[str], event_data: EventDataDict) -> EventDataDict:
2885
- nodes = event_data["treeview"]["nodes"]
2886
2845
  for iid in items:
2887
- if iid not in nodes:
2846
+ if iid not in event_data["treeview"]["nodes"]:
2888
2847
  n = self.iid_node(iid)
2889
- nodes[iid] = Node(
2848
+ event_data["treeview"]["nodes"][iid] = Node(
2890
2849
  text=n.text,
2891
2850
  iid=n.iid,
2892
2851
  parent=n.parent,
@@ -4744,26 +4744,31 @@ class Sheet(tk.Frame):
4744
4744
  self.hide_rows(datarn, deselect_all=False, data_indexes=True)
4745
4745
  return iid
4746
4746
 
4747
- def _get_id_insert_row(self, index: int, parent: str) -> int:
4747
+ def _get_id_insert_row(self, index: int | None, parent: str) -> int:
4748
4748
  if parent:
4749
+ chn = self.RI.iid_children(parent)
4749
4750
  if isinstance(index, int):
4750
- index = min(index, len(self.RI.iid_children(parent)))
4751
- datarn = (
4752
- self.RI.rns[parent]
4753
- + index
4754
- + 1
4755
- + sum(self.RI.num_descendants(cid) for cid in islice(self.get_children(parent), index))
4756
- )
4751
+ index = min(index, len(chn))
4752
+ if index == 0:
4753
+ return self.RI.rns[parent] + 1
4754
+ else:
4755
+ prev_chld = chn[index - 1]
4756
+ return self.RI.rns[prev_chld] + self.RI.num_descendants(prev_chld) + 1
4757
4757
  else:
4758
- datarn = self.RI.rns[parent] + self.RI.num_descendants(parent) + 1
4758
+ if chn:
4759
+ last_chld = chn[-1]
4760
+ last_chld_rn = self.RI.rns[last_chld]
4761
+ return last_chld_rn + self.RI.num_descendants(last_chld) + 1
4762
+ else:
4763
+ return self.RI.rns[parent] + 1
4759
4764
  else:
4760
4765
  if isinstance(index, int):
4761
- datarn = index
4762
- if index and (datarn := self.top_index_row(datarn)) is None:
4763
- datarn = len(self.MT._row_index)
4766
+ if index == 0:
4767
+ return 0
4768
+ datarn = self.top_index_row(index)
4769
+ return len(self.MT._row_index) if datarn is None else datarn
4764
4770
  else:
4765
- datarn = len(self.MT._row_index)
4766
- return datarn
4771
+ return len(self.MT._row_index)
4767
4772
 
4768
4773
  def bulk_insert(
4769
4774
  self,
@@ -4792,6 +4797,8 @@ class Sheet(tk.Frame):
4792
4797
  rns_to_add = {}
4793
4798
  for rn, r in enumerate(data, start=datarn):
4794
4799
  iid = self.RI.new_iid() if iid_column is None else r[iid_column]
4800
+ if iid in self.RI.rns:
4801
+ raise ValueError(f"iid '{iid}' already exists.")
4795
4802
  new_node = Node(
4796
4803
  r[text_column] if isinstance(text_column, int) else text_column if isinstance(text_column, str) else "",
4797
4804
  iid,
@@ -4995,11 +5002,24 @@ class Sheet(tk.Frame):
4995
5002
  raise ValueError(f"Item '{item}' does not exist.")
4996
5003
  return self.RI.iid_parent(item)
4997
5004
 
4998
- def index(self, item: str) -> int:
5005
+ def index(self, item: str, safety: bool = False) -> int:
5006
+ """
5007
+ Finds the index of an item amongst it's siblings in the
5008
+ treeview.
5009
+
5010
+ 'safety' is only necessary when the internal row number dict
5011
+ is not able to provide row numbers, e.g. when modifying the
5012
+ tree and before the action is complete.
5013
+
5014
+ When 'True' the fn uses list.index() instead.
5015
+ """
4999
5016
  if item not in self.RI.rns:
5000
5017
  raise ValueError(f"Item '{item}' does not exist.")
5001
- elif self.RI.iid_parent(item):
5002
- return self.RI.parent_node(item).children.index(item)
5018
+ elif par := self.RI.iid_parent(item):
5019
+ if not safety:
5020
+ return self.RI.rns[item] - self.RI.rns[par] - 1
5021
+ else:
5022
+ return self.RI.parent_node(item).children.index(item)
5003
5023
  else:
5004
5024
  return next(index for index, iid in enumerate(self.get_children("")) if iid == item)
5005
5025
 
@@ -6349,20 +6369,14 @@ class Sheet(tk.Frame):
6349
6369
  **{k: v["dropdown"] for k, v in self.MT.row_options.items() if "dropdown" in v},
6350
6370
  **{k: v["dropdown"] for k, v in self.MT.col_options.items() if "dropdown" in v},
6351
6371
  }
6352
- if "dropdown" in self.MT.options:
6353
- return {**d, "dropdown": self.MT.options["dropdown"]}
6354
6372
  return d
6355
6373
 
6356
6374
  def get_header_dropdowns(self) -> dict:
6357
6375
  d = {k: v["dropdown"] for k, v in self.CH.cell_options.items() if "dropdown" in v}
6358
- if "dropdown" in self.CH.options:
6359
- return {**d, "dropdown": self.CH.options["dropdown"]}
6360
6376
  return d
6361
6377
 
6362
6378
  def get_index_dropdowns(self) -> dict:
6363
6379
  d = {k: v["dropdown"] for k, v in self.RI.cell_options.items() if "dropdown" in v}
6364
- if "dropdown" in self.RI.options:
6365
- return {**d, "dropdown": self.RI.options["dropdown"]}
6366
6380
  return d
6367
6381
 
6368
6382
  def set_dropdown_values(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tksheet
3
- Version: 7.5.3
3
+ Version: 7.5.5
4
4
  Summary: Tkinter table / sheet and treeview widget
5
5
  Author-email: ragardner <github@ragardner.simplelogin.com>
6
6
  License: Copyright (c) 2019 ragardner and open source contributors
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