tksheet 7.4.3__tar.gz → 7.4.4__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 (27) hide show
  1. {tksheet-7.4.3/tksheet.egg-info → tksheet-7.4.4}/PKG-INFO +1 -1
  2. {tksheet-7.4.3 → tksheet-7.4.4}/pyproject.toml +1 -1
  3. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/__init__.py +2 -2
  4. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/column_headers.py +21 -14
  5. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/functions.py +26 -26
  6. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/main_table.py +56 -44
  7. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/row_index.py +39 -28
  8. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/sheet.py +34 -47
  9. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/sheet_options.py +2 -0
  10. tksheet-7.4.4/tksheet/sorting.py +531 -0
  11. {tksheet-7.4.3 → tksheet-7.4.4/tksheet.egg-info}/PKG-INFO +1 -1
  12. tksheet-7.4.3/tksheet/sorting.py +0 -318
  13. {tksheet-7.4.3 → tksheet-7.4.4}/LICENSE.txt +0 -0
  14. {tksheet-7.4.3 → tksheet-7.4.4}/README.md +0 -0
  15. {tksheet-7.4.3 → tksheet-7.4.4}/setup.cfg +0 -0
  16. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/colors.py +0 -0
  17. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/constants.py +0 -0
  18. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/find_window.py +0 -0
  19. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/formatters.py +0 -0
  20. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/other_classes.py +0 -0
  21. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/text_editor.py +0 -0
  22. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/themes.py +0 -0
  23. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/tksheet_types.py +0 -0
  24. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet/top_left_rectangle.py +0 -0
  25. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet.egg-info/SOURCES.txt +0 -0
  26. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet.egg-info/dependency_links.txt +0 -0
  27. {tksheet-7.4.3 → tksheet-7.4.4}/tksheet.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tksheet
3
- Version: 7.4.3
3
+ Version: 7.4.4
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.4.3"
9
+ version = "7.4.4"
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.4.3"
7
+ __version__ = "7.4.4"
8
8
 
9
9
  from .colors import (
10
10
  color_map,
@@ -93,7 +93,7 @@ from .other_classes import (
93
93
  from .row_index import RowIndex
94
94
  from .sheet import Dropdown, Sheet
95
95
  from .sheet_options import new_sheet_options
96
- from .sorting import natural_sort_key
96
+ from .sorting import fast_sort_key, natural_sort_key, version_sort_key
97
97
  from .text_editor import (
98
98
  TextEditor,
99
99
  TextEditorTkText,
@@ -874,6 +874,8 @@ class ColumnHeaders(tk.Canvas):
874
874
  columns = list(range(0, len(self.MT.col_positions) - 1))
875
875
  event_data = self.MT.new_event_dict("edit_table")
876
876
  try_binding(self.MT.extra_begin_sort_cells_func, event_data)
877
+ if key is None:
878
+ key = self.PAR.ops.sort_key
877
879
  for c in columns:
878
880
  datacn = self.MT.datacn(c)
879
881
  for r, val in enumerate(
@@ -923,6 +925,8 @@ class ColumnHeaders(tk.Canvas):
923
925
  return event_data
924
926
  column = self.MT.selected.column
925
927
  if try_binding(self.ch_extra_begin_sort_rows_func, event_data, "begin_move_rows"):
928
+ if key is None:
929
+ key = self.PAR.ops.sort_key
926
930
  disp_new_idxs, disp_row_ctr = {}, 0
927
931
  if self.ops.treeview:
928
932
  new_nodes_order, data_new_idxs = sort_tree_view(
@@ -2239,16 +2243,17 @@ class ColumnHeaders(tk.Canvas):
2239
2243
  self.MT._headers[datacn] = value
2240
2244
 
2241
2245
  def input_valid_for_cell(self, datacn: int, value: object, check_readonly: bool = True) -> bool:
2242
- if check_readonly and self.get_cell_kwargs(datacn, key="readonly"):
2246
+ kwargs = self.get_cell_kwargs(datacn, key=None)
2247
+ if check_readonly and "readonly" in kwargs:
2243
2248
  return False
2244
- if self.get_cell_kwargs(datacn, key="checkbox"):
2249
+ elif "checkbox" in kwargs:
2245
2250
  return is_bool_like(value)
2246
- if self.cell_equal_to(datacn, value):
2251
+ elif self.cell_equal_to(datacn, value):
2247
2252
  return False
2248
- kwargs = self.get_cell_kwargs(datacn, key="dropdown")
2249
- if kwargs and kwargs["validate_input"] and value not in kwargs["values"]:
2253
+ elif (kwargs := kwargs.get("dropdown", {})) and kwargs["validate_input"] and value not in kwargs["values"]:
2250
2254
  return False
2251
- return True
2255
+ else:
2256
+ return True
2252
2257
 
2253
2258
  def cell_equal_to(self, datacn: int, value: object) -> bool:
2254
2259
  self.fix_header(datacn)
@@ -2303,12 +2308,13 @@ class ColumnHeaders(tk.Canvas):
2303
2308
  return value
2304
2309
 
2305
2310
  def get_value_for_empty_cell(self, datacn: int, c_ops: bool = True) -> object:
2306
- if self.get_cell_kwargs(datacn, key="checkbox", cell=c_ops):
2311
+ kwargs = self.get_cell_kwargs(datacn, key=None, cell=c_ops)
2312
+ if "checkbox" in kwargs:
2307
2313
  return False
2308
- kwargs = self.get_cell_kwargs(datacn, key="dropdown", cell=c_ops)
2309
- if kwargs and kwargs["validate_input"] and kwargs["values"]:
2314
+ elif (kwargs := kwargs.get("dropdown", {})) and kwargs["validate_input"] and kwargs["values"]:
2310
2315
  return kwargs["values"][0]
2311
- return ""
2316
+ else:
2317
+ return ""
2312
2318
 
2313
2319
  def get_empty_header_seq(self, end: int, start: int = 0, c_ops: bool = True) -> list[object]:
2314
2320
  return [self.get_value_for_empty_cell(datacn, c_ops=c_ops) for datacn in range(start, end)]
@@ -2376,7 +2382,8 @@ class ColumnHeaders(tk.Canvas):
2376
2382
  if redraw:
2377
2383
  self.MT.refresh()
2378
2384
 
2379
- def get_cell_kwargs(self, datacn: int, key: Hashable = "dropdown", cell: bool = True) -> dict:
2380
- if cell and datacn in self.cell_options and key in self.cell_options[datacn]:
2381
- return self.cell_options[datacn][key]
2382
- return {}
2385
+ def get_cell_kwargs(self, datacn: int, key: Hashable | None = "dropdown", cell: bool = True) -> dict:
2386
+ if cell and datacn in self.cell_options:
2387
+ return self.cell_options[datacn] if key is None else self.cell_options[datacn].get(key, {})
2388
+ else:
2389
+ return {}
@@ -15,7 +15,7 @@ from typing import Literal
15
15
  from .colors import color_map
16
16
  from .constants import align_value_error, symbols_set
17
17
  from .formatters import to_bool
18
- from .other_classes import Box_nt, DotDict, EventDataDict, Highlight, Loc, Span
18
+ from .other_classes import DotDict, EventDataDict, Highlight, Loc, Span
19
19
  from .tksheet_types import AnyIter
20
20
 
21
21
  unpickle_obj = pickle.loads
@@ -279,10 +279,6 @@ def float_to_int(f: int | float) -> int | float:
279
279
  return int(f)
280
280
 
281
281
 
282
- def selection_box_tup_to_dict(box: tuple) -> dict:
283
- return {Box_nt(*box[:-1]): box[-1]}
284
-
285
-
286
282
  def event_dict(
287
283
  name: str = None,
288
284
  sheet: object = None,
@@ -334,9 +330,7 @@ def event_dict(
334
330
  ),
335
331
  named_spans=DotDict() if named_spans is None else named_spans,
336
332
  options=DotDict(),
337
- selection_boxes=(
338
- {} if boxes is None else selection_box_tup_to_dict(boxes) if isinstance(boxes, tuple) else boxes
339
- ),
333
+ selection_boxes={} if boxes is None else boxes,
340
334
  selected=tuple() if selected is None else selected,
341
335
  being_selected=tuple() if being_selected is None else being_selected,
342
336
  data=[] if data is None else data,
@@ -753,6 +747,17 @@ def move_elements_by_mapping(
753
747
  return [seq[old_idxs[i]] if i in old_idxs else next(remaining_values) for i in range(len(seq))]
754
748
 
755
749
 
750
+ def move_elements_by_mapping_gen(
751
+ seq: list[object],
752
+ new_idxs: dict[int, int],
753
+ old_idxs: dict[int, int] | None = None,
754
+ ) -> Generator[object]:
755
+ if old_idxs is None:
756
+ old_idxs = dict(zip(new_idxs.values(), new_idxs))
757
+ remaining_values = (e for i, e in enumerate(seq) if i not in new_idxs)
758
+ return (seq[old_idxs[i]] if i in old_idxs else next(remaining_values) for i in range(len(seq)))
759
+
760
+
756
761
  def move_elements_to(
757
762
  seq: list[object],
758
763
  move_to: int,
@@ -770,10 +775,12 @@ def move_elements_to(
770
775
 
771
776
  def get_new_indexes(
772
777
  move_to: int,
773
- to_move: list[int],
778
+ to_move: AnyIter[int],
774
779
  get_inverse: bool = False,
775
780
  ) -> tuple[dict]:
776
781
  """
782
+ move_to: A positive int, could possibly be the same as an element of to_move
783
+ to_move: A sorted list[int], ints always positive and unique, list never empty.
777
784
  returns {old idx: new idx, ...}
778
785
  """
779
786
  offset = sum(1 for i in to_move if i < move_to)
@@ -789,6 +796,11 @@ def insert_items(
789
796
  to_insert: dict[int, object],
790
797
  seq_len_func: Callable | None = None,
791
798
  ) -> list[object]:
799
+ """
800
+ seq: list[object]
801
+ to_insert: keys are ints sorted in reverse, representing list indexes to insert items.
802
+ Values are any object, e.g. {1: 200, 0: 200}
803
+ """
792
804
  if to_insert:
793
805
  if seq_len_func and next(iter(to_insert)) >= len(seq) + len(to_insert):
794
806
  seq_len_func(next(iter(to_insert)) - len(to_insert))
@@ -876,24 +888,12 @@ def rounded_box_coords(
876
888
  )
877
889
 
878
890
 
879
- def diff_list(seq: list[float]) -> list[int]:
880
- return [
881
- int(b - a)
882
- for a, b in zip(
883
- seq,
884
- islice(seq, 1, None),
885
- )
886
- ]
887
-
888
-
889
891
  def diff_gen(seq: list[float]) -> Generator[int]:
890
- return (
891
- int(b - a)
892
- for a, b in zip(
893
- seq,
894
- islice(seq, 1, None),
895
- )
896
- )
892
+ it = iter(seq)
893
+ a = next(it)
894
+ for b in it:
895
+ yield int(b - a)
896
+ a = b
897
897
 
898
898
 
899
899
  def gen_coords(
@@ -50,7 +50,6 @@ from .functions import (
50
50
  consecutive_ranges,
51
51
  data_to_displayed_idxs,
52
52
  diff_gen,
53
- diff_list,
54
53
  down_cell_within_box,
55
54
  event_dict,
56
55
  event_has_char_key,
@@ -72,6 +71,7 @@ from .functions import (
72
71
  mod_span,
73
72
  mod_span_widget,
74
73
  move_elements_by_mapping,
74
+ move_elements_by_mapping_gen,
75
75
  new_tk_event,
76
76
  next_cell,
77
77
  push_n,
@@ -932,19 +932,21 @@ class MainTable(tk.Canvas):
932
932
  def sort_boxes(
933
933
  self,
934
934
  event: tk.Event | None = None,
935
- boxes: AnyIter[int, int, int, int] | None = None,
935
+ boxes: dict[tuple[int, int, int, int], Literal["cells", "rows", "columns"]] | None = None,
936
936
  reverse: bool = False,
937
937
  row_wise: bool = False,
938
938
  validation: bool = True,
939
939
  key: Callable | None = None,
940
940
  undo: bool = True,
941
941
  ) -> EventDataDict:
942
- if boxes is None:
942
+ if not boxes:
943
943
  boxes = self.get_boxes()
944
944
  if not boxes:
945
- boxes = [(0, 0, len(self.row_positions) - 1, len(self.col_positions) - 1)]
945
+ boxes = {Box_nt(0, 0, len(self.row_positions) - 1, len(self.col_positions) - 1): "cells"}
946
946
  event_data = self.new_event_dict("edit_table", boxes=boxes)
947
947
  try_binding(self.extra_begin_sort_cells_func, event_data)
948
+ if key is None:
949
+ key = self.PAR.ops.sort_key
948
950
  for r1, c1, r2, c2 in boxes:
949
951
  data = sort_selection(
950
952
  [[self.get_cell_data(self.datarn(r), self.datacn(c)) for c in range(c1, c2)] for r in range(r1, r2)],
@@ -1287,26 +1289,36 @@ class MainTable(tk.Canvas):
1287
1289
  data_indexes: bool = False,
1288
1290
  ) -> tuple[dict[int, int], dict[int, int], int, dict[int, int]]:
1289
1291
  if not data_indexes or self.all_columns_displayed:
1290
- disp_new_idxs = get_new_indexes(move_to=move_to, to_move=to_move)
1292
+ disp_new_idxs = get_new_indexes(
1293
+ move_to=move_to,
1294
+ to_move=to_move,
1295
+ )
1296
+ data_new_idxs = dict(disp_new_idxs)
1291
1297
  else:
1292
1298
  disp_new_idxs = {}
1299
+ data_new_idxs = get_new_indexes(
1300
+ move_to=move_to,
1301
+ to_move=to_move,
1302
+ )
1293
1303
  # at_least_cols should not be len in this case as move_to can be len
1294
1304
  fix_len = (move_to - 1) if move_to else move_to
1295
1305
  if not self.all_columns_displayed and not data_indexes:
1296
1306
  fix_len = self.datacn(fix_len)
1297
1307
  totalcols = self.equalize_data_row_lengths(at_least_cols=fix_len)
1298
- data_new_idxs = get_new_indexes(move_to=move_to, to_move=to_move)
1299
1308
  if not self.all_columns_displayed and not data_indexes:
1300
- data_new_idxs = dict(
1301
- zip(
1302
- move_elements_by_mapping(
1309
+ keep = set(map(self.datacn, to_move))
1310
+ data_new_idxs = {
1311
+ k: v
1312
+ for k, v in zip(
1313
+ move_elements_by_mapping_gen(
1303
1314
  self.displayed_columns,
1304
1315
  data_new_idxs,
1305
1316
  dict(zip(data_new_idxs.values(), data_new_idxs)),
1306
1317
  ),
1307
1318
  self.displayed_columns,
1308
- ),
1309
- )
1319
+ )
1320
+ if k in keep
1321
+ }
1310
1322
  return data_new_idxs, dict(zip(data_new_idxs.values(), data_new_idxs)), totalcols, disp_new_idxs
1311
1323
 
1312
1324
  def move_columns_adjust_options_dict(
@@ -1338,7 +1350,7 @@ class MainTable(tk.Canvas):
1338
1350
 
1339
1351
  if move_widths and disp_new_idxs:
1340
1352
  self.set_col_positions(
1341
- itr=move_elements_by_mapping(
1353
+ itr=move_elements_by_mapping_gen(
1342
1354
  self.get_column_widths(),
1343
1355
  disp_new_idxs,
1344
1356
  dict(
@@ -1537,7 +1549,7 @@ class MainTable(tk.Canvas):
1537
1549
  data_new_idxs = {
1538
1550
  k: v
1539
1551
  for k, v in zip(
1540
- move_elements_by_mapping(
1552
+ move_elements_by_mapping_gen(
1541
1553
  self.displayed_rows,
1542
1554
  data_new_idxs,
1543
1555
  dict(zip(data_new_idxs.values(), data_new_idxs)),
@@ -1732,7 +1744,7 @@ class MainTable(tk.Canvas):
1732
1744
 
1733
1745
  if move_heights and disp_new_idxs:
1734
1746
  self.set_row_positions(
1735
- itr=move_elements_by_mapping(
1747
+ itr=move_elements_by_mapping_gen(
1736
1748
  self.get_row_heights(),
1737
1749
  disp_new_idxs,
1738
1750
  dict(
@@ -1788,7 +1800,7 @@ class MainTable(tk.Canvas):
1788
1800
  old_idxs = dict(zip(new_idxs.values(), new_idxs))
1789
1801
  return dict(
1790
1802
  zip(
1791
- move_elements_by_mapping(tuple(range(max_idx + 1)), new_idxs, old_idxs),
1803
+ move_elements_by_mapping_gen(tuple(range(max_idx + 1)), new_idxs, old_idxs),
1792
1804
  range(max_idx + 1),
1793
1805
  )
1794
1806
  )
@@ -4489,10 +4501,10 @@ class MainTable(tk.Canvas):
4489
4501
  self.set_row_positions(itr=(h for i, h in enumerate(self.gen_row_heights()) if i not in idxs))
4490
4502
 
4491
4503
  def get_column_widths(self) -> list[int]:
4492
- return diff_list(self.col_positions)
4504
+ return list(diff_gen(self.col_positions))
4493
4505
 
4494
4506
  def get_row_heights(self) -> list[int]:
4495
- return diff_list(self.row_positions)
4507
+ return list(diff_gen(self.row_positions))
4496
4508
 
4497
4509
  def gen_column_widths(self) -> Generator[int]:
4498
4510
  return diff_gen(self.col_positions)
@@ -7893,26 +7905,20 @@ class MainTable(tk.Canvas):
7893
7905
  self.data[datarn][datacn] = value
7894
7906
 
7895
7907
  def get_value_for_empty_cell(self, datarn: int, datacn: int, r_ops: bool = True, c_ops: bool = True) -> object:
7896
- if self.get_cell_kwargs(
7897
- datarn,
7898
- datacn,
7899
- key="checkbox",
7900
- cell=r_ops and c_ops,
7901
- row=r_ops,
7902
- column=c_ops,
7903
- ):
7904
- return False
7905
7908
  kwargs = self.get_cell_kwargs(
7906
7909
  datarn,
7907
7910
  datacn,
7908
- key="dropdown",
7911
+ key=None,
7909
7912
  cell=r_ops and c_ops,
7910
7913
  row=r_ops,
7911
7914
  column=c_ops,
7912
7915
  )
7913
- if kwargs and kwargs["validate_input"] and kwargs["values"]:
7916
+ if "checkbox" in kwargs:
7917
+ return False
7918
+ elif (kwargs := kwargs.get("dropdown", {})) and kwargs["validate_input"] and kwargs["values"]:
7914
7919
  return kwargs["values"][0]
7915
- return ""
7920
+ else:
7921
+ return ""
7916
7922
 
7917
7923
  def get_empty_row_seq(
7918
7924
  self,
@@ -8072,18 +8078,21 @@ class MainTable(tk.Canvas):
8072
8078
  check_readonly: bool = True,
8073
8079
  ignore_empty: bool = False,
8074
8080
  ) -> bool:
8075
- if check_readonly and self.get_cell_kwargs(datarn, datacn, key="readonly"):
8081
+ kwargs = self.get_cell_kwargs(datarn, datacn, key=None)
8082
+ if check_readonly and "readonly" in kwargs:
8076
8083
  return False
8077
- if self.get_cell_kwargs(datarn, datacn, key="format"):
8084
+ elif "format" in kwargs:
8078
8085
  return True
8079
- if self.cell_equal_to(datarn, datacn, value, ignore_empty=ignore_empty):
8086
+ elif self.cell_equal_to(datarn, datacn, value, ignore_empty=ignore_empty):
8080
8087
  return False
8081
- kwargs = self.get_cell_kwargs(datarn, datacn, key="dropdown")
8082
- if kwargs and kwargs["validate_input"] and value not in kwargs["values"]:
8088
+ elif (
8089
+ (dropdown := kwargs.get("dropdown", {})) and dropdown["validate_input"] and value not in dropdown["values"]
8090
+ ):
8083
8091
  return False
8084
- if self.get_cell_kwargs(datarn, datacn, key="checkbox"):
8092
+ elif "checkbox" in kwargs:
8085
8093
  return is_bool_like(value)
8086
- return True
8094
+ else:
8095
+ return True
8087
8096
 
8088
8097
  def cell_equal_to(self, datarn: int, datacn: int, value: object, ignore_empty: bool = False, **kwargs) -> bool:
8089
8098
  v = self.get_cell_data(datarn, datacn)
@@ -8118,18 +8127,21 @@ class MainTable(tk.Canvas):
8118
8127
  self,
8119
8128
  datarn: int,
8120
8129
  datacn: int,
8121
- key: Hashable = "format",
8130
+ key: Hashable | None = "format",
8122
8131
  cell: bool = True,
8123
8132
  row: bool = True,
8124
8133
  column: bool = True,
8125
8134
  ) -> dict:
8126
- if cell and (datarn, datacn) in self.cell_options and key in self.cell_options[(datarn, datacn)]:
8127
- return self.cell_options[(datarn, datacn)][key]
8128
- if row and datarn in self.row_options and key in self.row_options[datarn]:
8129
- return self.row_options[datarn][key]
8130
- if column and datacn in self.col_options and key in self.col_options[datacn]:
8131
- return self.col_options[datacn][key]
8132
- return {}
8135
+ if cell and (datarn, datacn) in self.cell_options:
8136
+ return (
8137
+ self.cell_options[(datarn, datacn)] if key is None else self.cell_options[(datarn, datacn)].get(key, {})
8138
+ )
8139
+ elif row and datarn in self.row_options:
8140
+ return self.row_options[datarn] if key is None else self.row_options[datarn].get(key, {})
8141
+ elif column and datacn in self.col_options:
8142
+ return self.col_options[datacn] if key is None else self.col_options[datacn].get(key, {})
8143
+ else:
8144
+ return {}
8133
8145
 
8134
8146
  def datacn(self, c: int) -> int:
8135
8147
  return c if self.all_columns_displayed else self.displayed_columns[c]
@@ -900,6 +900,8 @@ class RowIndex(tk.Canvas):
900
900
  rows = list(range(0, len(self.MT.row_positions) - 1))
901
901
  event_data = self.MT.new_event_dict("edit_table")
902
902
  try_binding(self.MT.extra_begin_sort_cells_func, event_data)
903
+ if key is None:
904
+ key = self.PAR.ops.sort_key
903
905
  for r in rows:
904
906
  datarn = self.MT.datarn(r)
905
907
  for c, val in enumerate(sort_row(self.MT.data[datarn], reverse=reverse, key=key)):
@@ -943,6 +945,8 @@ class RowIndex(tk.Canvas):
943
945
  return event_data
944
946
  row = self.MT.selected.row
945
947
  if try_binding(self.ri_extra_begin_sort_cols_func, event_data, "begin_move_columns"):
948
+ if key is None:
949
+ key = self.PAR.ops.sort_key
946
950
  sorted_indices, data_new_idxs = sort_columns_by_row(self.MT.data, row=row, reverse=reverse, key=key)
947
951
  disp_new_idxs = {}
948
952
  if self.MT.all_columns_displayed:
@@ -2424,16 +2428,17 @@ class RowIndex(tk.Canvas):
2424
2428
  self.MT._row_index[datarn] = value
2425
2429
 
2426
2430
  def input_valid_for_cell(self, datarn: int, value: object, check_readonly: bool = True) -> bool:
2427
- if check_readonly and self.get_cell_kwargs(datarn, key="readonly"):
2431
+ kwargs = self.get_cell_kwargs(datarn, key=None)
2432
+ if check_readonly and "readonly" in kwargs:
2428
2433
  return False
2429
- if self.get_cell_kwargs(datarn, key="checkbox"):
2434
+ elif "checkbox" in kwargs:
2430
2435
  return is_bool_like(value)
2431
- if self.cell_equal_to(datarn, value):
2436
+ elif self.cell_equal_to(datarn, value):
2432
2437
  return False
2433
- kwargs = self.get_cell_kwargs(datarn, key="dropdown")
2434
- if kwargs and kwargs["validate_input"] and value not in kwargs["values"]:
2438
+ elif (kwargs := kwargs.get("dropdown", {})) and kwargs["validate_input"] and value not in kwargs["values"]:
2435
2439
  return False
2436
- return True
2440
+ else:
2441
+ return True
2437
2442
 
2438
2443
  def cell_equal_to(self, datarn: int, value: object) -> bool:
2439
2444
  self.fix_index(datarn)
@@ -2495,12 +2500,13 @@ class RowIndex(tk.Canvas):
2495
2500
  if self.ops.treeview:
2496
2501
  iid = self.new_iid()
2497
2502
  return Node(text=iid, iid=iid, parent=self.get_row_parent(datarn))
2498
- if self.get_cell_kwargs(datarn, key="checkbox", cell=r_ops):
2503
+ kwargs = self.get_cell_kwargs(datarn, key=None, cell=r_ops)
2504
+ if "checkbox" in kwargs:
2499
2505
  return False
2500
- kwargs = self.get_cell_kwargs(datarn, key="dropdown", cell=r_ops)
2501
- if kwargs and kwargs["validate_input"] and kwargs["values"]:
2506
+ elif (kwargs := kwargs.get("dropdown", {})) and kwargs["validate_input"] and kwargs["values"]:
2502
2507
  return kwargs["values"][0]
2503
- return ""
2508
+ else:
2509
+ return ""
2504
2510
 
2505
2511
  def get_empty_index_seq(self, end: int, start: int = 0, r_ops: bool = True) -> list[object]:
2506
2512
  return [self.get_value_for_empty_cell(datarn, r_ops=r_ops) for datarn in range(start, end)]
@@ -2567,10 +2573,11 @@ class RowIndex(tk.Canvas):
2567
2573
  if redraw:
2568
2574
  self.MT.refresh()
2569
2575
 
2570
- def get_cell_kwargs(self, datarn: int, key: Hashable = "dropdown", cell: bool = True) -> dict:
2571
- if cell and datarn in self.cell_options and key in self.cell_options[datarn]:
2572
- return self.cell_options[datarn][key]
2573
- return {}
2576
+ def get_cell_kwargs(self, datarn: int, key: Hashable | None = "dropdown", cell: bool = True) -> dict:
2577
+ if cell and datarn in self.cell_options:
2578
+ return self.cell_options[datarn] if key is None else self.cell_options[datarn].get(key, {})
2579
+ else:
2580
+ return {}
2574
2581
 
2575
2582
  # Treeview Mode
2576
2583
 
@@ -2671,25 +2678,33 @@ class RowIndex(tk.Canvas):
2671
2678
  new_parent = node_change[1]
2672
2679
  move_to_index = node_change[2]
2673
2680
  if new_parent:
2674
- move_to_index = (
2675
- move_to_index if isinstance(move_to_index, int) else len(self.tree[new_parent].children)
2676
- )
2677
- move_to_row = self.tree_rns[new_parent] + max(
2678
- 0, min(move_to_index, len(self.tree[new_parent].children))
2679
- )
2681
+ if isinstance(move_to_index, int):
2682
+ move_to_index = min(move_to_index, len(self.tree[new_parent].children))
2683
+ else:
2684
+ move_to_index = len(self.tree[new_parent].children)
2685
+ move_to_row = self.tree_rns[new_parent]
2686
+ if new_parent == self.tree[item].parent:
2687
+ move_to_index += 1
2688
+ for i, ciid in enumerate(self.tree[new_parent].children):
2689
+ if i == move_to_index:
2690
+ break
2691
+ move_to_row += sum(1 for _ in self.get_iid_descendants(ciid)) + 1
2692
+ insert_row = move_to_row + 1
2680
2693
  else:
2681
2694
  num_top_nodes = sum(1 for _ in self.gen_top_nodes())
2682
2695
  if move_to_index is None:
2683
2696
  move_to_row = self.PAR.top_index_row(num_top_nodes - 1)
2684
2697
  move_to_index = num_top_nodes
2698
+ insert_row = move_to_row
2685
2699
  else:
2686
2700
  move_to_row = self.PAR.top_index_row(move_to_index)
2701
+ insert_row = move_to_row
2687
2702
  if move_to_row is None:
2688
2703
  move_to_row = self.PAR.top_index_row(num_top_nodes - 1)
2689
2704
  move_to_index = num_top_nodes
2705
+ insert_row = move_to_row + 1
2690
2706
 
2691
2707
  move_to_iid = self.MT._row_index[move_to_row].iid
2692
- insert_row = move_to_row + 1
2693
2708
  disp_insert_row = None
2694
2709
 
2695
2710
  else:
@@ -2733,10 +2748,10 @@ class RowIndex(tk.Canvas):
2733
2748
  event_data["moved"]["rows"]["displayed"] = {}
2734
2749
  if new_loc_is_displayed:
2735
2750
  if disp_insert_row is None:
2736
- if new_parent == self.tree[item].parent:
2751
+ if new_parent or insert_row > move_to_row:
2737
2752
  disp_insert_row = self.MT.disprn(self.tree_rns[move_to_iid]) + 1
2738
2753
  else:
2739
- disp_insert_row = self.MT.disprn(self.tree_rns[move_to_iid]) + 1
2754
+ disp_insert_row = self.MT.disprn(self.tree_rns[move_to_iid])
2740
2755
  if (disp_from_row := self.MT.try_disprn(self.tree_rns[item])) is not None:
2741
2756
  event_data["moved"]["rows"]["displayed"] = {disp_from_row: disp_insert_row}
2742
2757
  else:
@@ -2756,7 +2771,6 @@ class RowIndex(tk.Canvas):
2756
2771
  index=move_to_index,
2757
2772
  )
2758
2773
  move_to_index += 1
2759
-
2760
2774
  event_data["moved"]["rows"]["data"] = get_new_indexes(
2761
2775
  insert_row,
2762
2776
  event_data["moved"]["rows"]["data"],
@@ -2782,7 +2796,6 @@ class RowIndex(tk.Canvas):
2782
2796
  if not undo_modification and data_new_idxs:
2783
2797
  if new_parent and (not self.PAR.item_displayed(new_parent) or new_parent not in self.tree_open_ids):
2784
2798
  self.PAR.hide_rows(set(data_new_idxs.values()), data_indexes=True)
2785
-
2786
2799
  if new_loc_is_displayed:
2787
2800
  self.PAR.show_rows(
2788
2801
  (r for r in data_new_idxs.values() if self.ancestors_all_open(self.MT._row_index[r].iid))
@@ -2959,9 +2972,7 @@ class RowIndex(tk.Canvas):
2959
2972
  def move_pid_causes_recursive_loop(self, to_move_iid: str, move_to_parent: str) -> bool:
2960
2973
  # if the parent the item is being moved under is one of the item's descendants
2961
2974
  # then it is a recursive loop
2962
- return to_move_iid == move_to_parent or any(
2963
- move_to_parent == diid for diid in self.get_iid_descendants(to_move_iid)
2964
- )
2975
+ return any(move_to_parent == diid for diid in self.get_iid_descendants(to_move_iid))
2965
2976
 
2966
2977
  def tree_build(
2967
2978
  self,