tksheet 7.3.3__py3-none-any.whl → 7.4.0__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/sheet.py CHANGED
@@ -2,36 +2,31 @@ from __future__ import annotations
2
2
 
3
3
  import tkinter as tk
4
4
  from bisect import bisect_left
5
- from collections import defaultdict, deque
6
- from collections.abc import (
7
- Callable,
8
- Generator,
9
- Hashable,
10
- Iterator,
11
- Sequence,
12
- )
13
- from functools import partial
14
- from itertools import (
15
- accumulate,
16
- chain,
17
- filterfalse,
18
- islice,
19
- product,
20
- repeat,
21
- )
5
+ from collections import deque
6
+ from collections.abc import Callable, Generator, Hashable, Iterator, Sequence
7
+ from itertools import accumulate, chain, filterfalse, islice, product, repeat
22
8
  from operator import attrgetter
23
9
  from timeit import default_timer
24
10
  from tkinter import ttk
25
11
  from typing import Literal
26
12
 
27
13
  from .column_headers import ColumnHeaders
14
+ from .constants import (
15
+ USER_OS,
16
+ align_value_error,
17
+ backwards_compatibility_keys,
18
+ emitted_events,
19
+ named_span_types,
20
+ rc_binding,
21
+ scrollbar_options_keys,
22
+ )
28
23
  from .functions import (
29
24
  add_highlight,
30
25
  add_to_options,
31
26
  alpha2idx,
27
+ bisect_in,
32
28
  consecutive_ranges,
33
29
  convert_align,
34
- data_to_displayed_idxs,
35
30
  del_from_options,
36
31
  del_named_span_options,
37
32
  del_named_span_options_nested,
@@ -48,12 +43,12 @@ from .functions import (
48
43
  key_to_span,
49
44
  new_tk_event,
50
45
  num2alpha,
51
- stored_event_dict,
52
46
  pop_positions,
53
47
  set_align,
54
48
  set_readonly,
55
49
  span_froms,
56
50
  span_ranges,
51
+ stored_event_dict,
57
52
  tksheet_type_error,
58
53
  unpack,
59
54
  )
@@ -70,9 +65,7 @@ from .other_classes import (
70
65
  Span,
71
66
  )
72
67
  from .row_index import RowIndex
73
- from .sheet_options import (
74
- new_sheet_options,
75
- )
68
+ from .sheet_options import new_sheet_options
76
69
  from .themes import (
77
70
  theme_black,
78
71
  theme_dark,
@@ -81,19 +74,8 @@ from .themes import (
81
74
  theme_light_blue,
82
75
  theme_light_green,
83
76
  )
77
+ from .tksheet_types import AnyIter, CellPropertyKey, CreateSpanTypes
84
78
  from .top_left_rectangle import TopLeftRectangle
85
- from .types import (
86
- AnyIter,
87
- CreateSpanTypes,
88
- )
89
- from .constants import (
90
- USER_OS,
91
- backwards_compatibility_keys,
92
- emitted_events,
93
- named_span_types,
94
- rc_binding,
95
- scrollbar_options_keys,
96
- )
97
79
 
98
80
 
99
81
  class Sheet(tk.Frame):
@@ -102,7 +84,7 @@ class Sheet(tk.Frame):
102
84
  parent: tk.Misc,
103
85
  name: str = "!sheet",
104
86
  show_table: bool = True,
105
- show_top_left: bool = True,
87
+ show_top_left: bool = False,
106
88
  show_row_index: bool = True,
107
89
  show_header: bool = True,
108
90
  show_x_scrollbar: bool = True,
@@ -135,10 +117,10 @@ class Sheet(tk.Frame):
135
117
  after_redraw_time_ms: int = 20,
136
118
  set_all_heights_and_widths: bool = False,
137
119
  zoom: int = 100,
138
- align: str = "w",
139
- header_align: str = "center",
120
+ align: str = "nw",
121
+ header_align: str = "n",
140
122
  row_index_align: str | None = None,
141
- index_align: str = "center",
123
+ index_align: str = "n",
142
124
  displayed_columns: list[int] = [],
143
125
  all_columns_displayed: bool = True,
144
126
  displayed_rows: list[int] = [],
@@ -197,9 +179,14 @@ class Sheet(tk.Frame):
197
179
  edit_cell_return: Literal["right", "down", ""] = "down",
198
180
  editor_del_key: Literal["forward", "backward", ""] = "forward",
199
181
  treeview: bool = False,
200
- treeview_indent: str | int = "5",
182
+ treeview_indent: str | int = "2",
201
183
  rounded_boxes: bool = True,
202
184
  alternate_color: str = "",
185
+ allow_cell_overflow: bool = False,
186
+ # "" no wrap, "w" word wrap, "c" char wrap
187
+ table_wrap: Literal["", "w", "c"] = "c",
188
+ index_wrap: Literal["", "w", "c"] = "c",
189
+ header_wrap: Literal["", "w", "c"] = "c",
203
190
  # colors
204
191
  outline_thickness: int = 0,
205
192
  theme: str = "light blue",
@@ -338,7 +325,7 @@ class Sheet(tk.Frame):
338
325
  self.name = name
339
326
  self.last_event_data = EventDataDict()
340
327
  self.bound_events = DotDict({k: [] for k in emitted_events})
341
- self.dropdown_class = Dropdown
328
+ self._dropdown_cls = Dropdown
342
329
  self.after_redraw_id = None
343
330
  self.after_redraw_time_ms = after_redraw_time_ms
344
331
  self.named_span_id = 0
@@ -354,22 +341,22 @@ class Sheet(tk.Frame):
354
341
  self.config(width=350)
355
342
  self.grid_columnconfigure(1, weight=1)
356
343
  self.grid_rowconfigure(1, weight=1)
357
- self.RI = RowIndex(
344
+ self.RI: RowIndex = RowIndex(
358
345
  parent=self,
359
346
  row_index_align=(
360
347
  convert_align(row_index_align) if row_index_align is not None else convert_align(index_align)
361
348
  ),
362
349
  )
363
- self.CH = ColumnHeaders(
350
+ self.CH: ColumnHeaders = ColumnHeaders(
364
351
  parent=self,
365
352
  header_align=convert_align(header_align),
366
353
  )
367
- self.MT = MainTable(
354
+ self.MT: MainTable = MainTable(
368
355
  parent=self,
369
- show_index=show_row_index,
370
- show_header=show_header,
371
356
  column_headers_canvas=self.CH,
372
357
  row_index_canvas=self.RI,
358
+ show_index=show_row_index,
359
+ show_header=show_header,
373
360
  headers=headers,
374
361
  header=header,
375
362
  data_reference=data if data_reference is None else data_reference,
@@ -384,7 +371,7 @@ class Sheet(tk.Frame):
384
371
  displayed_rows=displayed_rows,
385
372
  all_rows_displayed=all_rows_displayed,
386
373
  )
387
- self.TL = TopLeftRectangle(
374
+ self.TL: TopLeftRectangle = TopLeftRectangle(
388
375
  parent=self,
389
376
  main_canvas=self.MT,
390
377
  row_index_canvas=self.RI,
@@ -448,32 +435,19 @@ class Sheet(tk.Frame):
448
435
  orient="horizontal",
449
436
  style=f"Sheet{self.unique_id}.Horizontal.TScrollbar",
450
437
  )
451
- if show_top_left:
452
- self.TL.grid(row=0, column=0)
453
- if show_table:
454
- self.MT.grid(row=1, column=1, sticky="nswe")
455
- self.MT["xscrollcommand"] = self.xscroll.set
456
- self.MT["yscrollcommand"] = self.yscroll.set
457
- if show_row_index:
458
- self.RI.grid(row=1, column=0, sticky="nswe")
459
- self.RI["yscrollcommand"] = self.yscroll.set
460
- if show_header:
461
- self.CH.grid(row=0, column=1, sticky="nswe")
462
- self.CH["xscrollcommand"] = self.xscroll.set
463
- if show_x_scrollbar:
464
- self.xscroll.grid(row=2, column=0, columnspan=2, sticky="nswe")
465
- self.xscroll_showing = True
466
- self.xscroll_disabled = False
467
- else:
468
- self.xscroll_showing = False
469
- self.xscroll_disabled = True
470
- if show_y_scrollbar:
471
- self.yscroll.grid(row=0, column=2, rowspan=3, sticky="nswe")
472
- self.yscroll_showing = True
473
- self.yscroll_disabled = False
474
- else:
475
- self.yscroll_showing = False
476
- self.yscroll_disabled = True
438
+ self.show()
439
+ if not show_top_left and not (show_row_index and show_header):
440
+ self.hide("top_left")
441
+ if not show_row_index:
442
+ self.hide("row_index")
443
+ if not show_header:
444
+ self.hide("header")
445
+ if not show_table:
446
+ self.hide("table")
447
+ if not show_x_scrollbar:
448
+ self.hide("x_scrollbar")
449
+ if not show_y_scrollbar:
450
+ self.hide("y_scrollbar")
477
451
  self.update_idletasks()
478
452
  self.MT.update_idletasks()
479
453
  self.RI.update_idletasks()
@@ -652,6 +626,11 @@ class Sheet(tk.Frame):
652
626
  - "rc_delete_column"
653
627
  - "rc_insert_row"
654
628
  - "rc_delete_row"
629
+ - "sort_cells"
630
+ - "sort_row"
631
+ - "sort_column" / "sort_col"
632
+ - "sort_rows"
633
+ - "sort_columns" / "sort_cols"
655
634
  - "ctrl_click_select" / "ctrl_select"
656
635
  - "copy"
657
636
  - "cut"
@@ -718,6 +697,12 @@ class Sheet(tk.Frame):
718
697
  ) -> Sheet:
719
698
  """
720
699
  List of available bindings:
700
+ - "begin_sort_cells"
701
+ - "sort_cells", "end_sort_cells"
702
+ - "begin_sort_rows"
703
+ - "sort_rows", "end_sort_rows"
704
+ - "begin_sort_columns"
705
+ - "sort_columns", "end_sort_columns"
721
706
  - "begin_copy", "begin_ctrl_c"
722
707
  - "ctrl_c", "end_copy", "end_ctrl_c", "copy"
723
708
  - "begin_cut", "begin_ctrl_x"
@@ -793,6 +778,9 @@ class Sheet(tk.Frame):
793
778
  "bind_all",
794
779
  "unbind_all",
795
780
  ):
781
+ self.MT.extra_begin_sort_cells_func = f
782
+ self.CH.ch_extra_begin_sort_rows_func = f
783
+ self.RI.ri_extra_begin_sort_cols_func = f
796
784
  self.MT.extra_begin_ctrl_c_func = f
797
785
  self.MT.extra_begin_ctrl_x_func = f
798
786
  self.MT.extra_begin_ctrl_v_func = f
@@ -844,6 +832,9 @@ class Sheet(tk.Frame):
844
832
  "modified_events",
845
833
  "modified",
846
834
  ):
835
+ self.MT.extra_end_sort_cells_func = f
836
+ self.CH.ch_extra_end_sort_rows_func = f
837
+ self.RI.ri_extra_end_sort_cols_func = f
847
838
  self.MT.extra_end_ctrl_c_func = f
848
839
  self.MT.extra_end_ctrl_x_func = f
849
840
  self.MT.extra_end_ctrl_v_func = f
@@ -859,6 +850,24 @@ class Sheet(tk.Frame):
859
850
  self.CH.extra_end_edit_cell_func = f
860
851
  self.RI.extra_end_edit_cell_func = f
861
852
 
853
+ if b in ("begin_sort_cells",):
854
+ self.MT.extra_begin_sort_cells_func = f
855
+
856
+ if b in ("sort_cells", "end_sort_cells"):
857
+ self.MT.extra_end_sort_cells_func = f
858
+
859
+ if b in ("begin_sort_rows",):
860
+ self.CH.ch_extra_begin_sort_rows_func = f
861
+
862
+ if b in ("sort_rows", "end_sort_rows"):
863
+ self.CH.ch_extra_end_sort_rows_func = f
864
+
865
+ if b in ("begin_sort_columns",):
866
+ self.RI.ri_extra_begin_sort_cols_func = f
867
+
868
+ if b in ("sort_columns", "end_sort_columns"):
869
+ self.RI.ri_extra_end_sort_cols_func = f
870
+
862
871
  if b in (
863
872
  "begin_copy",
864
873
  "begin_ctrl_c",
@@ -1318,7 +1327,7 @@ class Sheet(tk.Frame):
1318
1327
  transposed: bool = False,
1319
1328
  ndim: int = 0,
1320
1329
  convert: object = None,
1321
- undo: bool = False,
1330
+ undo: bool = True,
1322
1331
  emit_event: bool = False,
1323
1332
  widget: object = None,
1324
1333
  expand: None | str = None,
@@ -1790,8 +1799,6 @@ class Sheet(tk.Frame):
1790
1799
 
1791
1800
  """
1792
1801
  span = self.span_from_key(*key)
1793
- if data is None:
1794
- data = []
1795
1802
  startr, startc = span_froms(span)
1796
1803
  table, index, header = span.table, span.index, span.header
1797
1804
  fmt_kw = span.kwargs if span.type_ == "format" and span.kwargs else None
@@ -2097,7 +2104,7 @@ class Sheet(tk.Frame):
2097
2104
  height: int | None = None,
2098
2105
  row_index: bool = False,
2099
2106
  fill: bool = True,
2100
- undo: bool = False,
2107
+ undo: bool = True,
2101
2108
  emit_event: bool = False,
2102
2109
  redraw: bool = True,
2103
2110
  ) -> EventDataDict:
@@ -2119,7 +2126,7 @@ class Sheet(tk.Frame):
2119
2126
  width: int | None = None,
2120
2127
  header: bool = False,
2121
2128
  fill: bool = True,
2122
- undo: bool = False,
2129
+ undo: bool = True,
2123
2130
  emit_event: bool = False,
2124
2131
  redraw: bool = True,
2125
2132
  ) -> EventDataDict:
@@ -2141,11 +2148,12 @@ class Sheet(tk.Frame):
2141
2148
  heights: list[int] | tuple[int] | None = None,
2142
2149
  row_index: bool = False,
2143
2150
  fill: bool = True,
2144
- undo: bool = False,
2151
+ undo: bool = True,
2145
2152
  emit_event: bool = False,
2146
2153
  create_selections: bool = True,
2147
2154
  add_column_widths: bool = True,
2148
2155
  push_ops: bool = True,
2156
+ tree: bool = True,
2149
2157
  redraw: bool = True,
2150
2158
  ) -> EventDataDict:
2151
2159
  total_cols = None
@@ -2205,14 +2213,10 @@ class Sheet(tk.Frame):
2205
2213
  row_index=row_index,
2206
2214
  ),
2207
2215
  add_col_positions=add_column_widths,
2208
- event_data=event_dict(
2209
- name="add_rows",
2210
- sheet=self.name,
2211
- boxes=self.MT.get_boxes(),
2212
- selected=self.MT.selected,
2213
- ),
2216
+ event_data=self.MT.new_event_dict("add_rows", state=True),
2214
2217
  create_selections=create_selections,
2215
2218
  push_ops=push_ops,
2219
+ tree=tree,
2216
2220
  )
2217
2221
  if undo:
2218
2222
  self.MT.undo_stack.append(stored_event_dict(event_data))
@@ -2228,7 +2232,7 @@ class Sheet(tk.Frame):
2228
2232
  widths: list[int] | tuple[int] | None = None,
2229
2233
  headers: bool = False,
2230
2234
  fill: bool = True,
2231
- undo: bool = False,
2235
+ undo: bool = True,
2232
2236
  emit_event: bool = False,
2233
2237
  create_selections: bool = True,
2234
2238
  add_row_heights: bool = True,
@@ -2300,12 +2304,7 @@ class Sheet(tk.Frame):
2300
2304
  headers=headers,
2301
2305
  ),
2302
2306
  add_row_positions=add_row_heights,
2303
- event_data=event_dict(
2304
- name="add_columns",
2305
- sheet=self.name,
2306
- boxes=self.MT.get_boxes(),
2307
- selected=self.MT.selected,
2308
- ),
2307
+ event_data=self.MT.new_event_dict("add_columns", state=True),
2309
2308
  create_selections=create_selections,
2310
2309
  push_ops=push_ops,
2311
2310
  )
@@ -2316,124 +2315,48 @@ class Sheet(tk.Frame):
2316
2315
  self.set_refresh_timer(redraw)
2317
2316
  return event_data
2318
2317
 
2319
- def del_row(
2320
- self,
2321
- idx: int = 0,
2322
- data_indexes: bool = True,
2323
- undo: bool = False,
2324
- emit_event: bool = False,
2325
- redraw: bool = True,
2326
- ) -> EventDataDict:
2327
- return self.del_rows(
2328
- rows=idx,
2329
- data_indexes=data_indexes,
2330
- undo=undo,
2331
- emit_event=emit_event,
2332
- redraw=redraw,
2333
- )
2334
-
2335
- delete_row = del_row
2336
-
2337
- def del_column(
2318
+ def del_rows(
2338
2319
  self,
2339
- idx: int = 0,
2320
+ rows: int | AnyIter[int],
2340
2321
  data_indexes: bool = True,
2341
- undo: bool = False,
2322
+ undo: bool = True,
2342
2323
  emit_event: bool = False,
2343
2324
  redraw: bool = True,
2344
2325
  ) -> EventDataDict:
2345
- return self.del_columns(
2346
- columns=idx,
2326
+ event_data = self.MT.delete_rows(
2327
+ rows=[rows] if isinstance(rows, int) else sorted(set(rows)),
2347
2328
  data_indexes=data_indexes,
2348
2329
  undo=undo,
2349
2330
  emit_event=emit_event,
2350
- redraw=redraw,
2351
- )
2352
-
2353
- delete_column = del_column
2354
-
2355
- def del_rows(
2356
- self,
2357
- rows: int | AnyIter[int],
2358
- data_indexes: bool = True,
2359
- undo: bool = False,
2360
- emit_event: bool = False,
2361
- redraw: bool = True,
2362
- ) -> EventDataDict:
2363
- self.MT.deselect("all", redraw=False)
2364
- rows = [rows] if isinstance(rows, int) else sorted(rows)
2365
- event_data = event_dict(
2366
- name="delete_rows",
2367
- sheet=self.name,
2368
- widget=self,
2369
- boxes=self.MT.get_boxes(),
2370
- selected=self.MT.selected,
2331
+ ext=True,
2371
2332
  )
2372
- if not data_indexes:
2373
- event_data = self.MT.delete_rows_displayed(rows, event_data)
2374
- event_data = self.MT.delete_rows_data(
2375
- rows if self.MT.all_rows_displayed else [self.MT.displayed_rows[r] for r in rows],
2376
- event_data,
2377
- )
2378
- else:
2379
- if self.MT.all_rows_displayed:
2380
- rows = rows
2381
- else:
2382
- rows = data_to_displayed_idxs(rows, self.MT.displayed_rows)
2383
- event_data = self.MT.delete_rows_data(rows, event_data)
2384
- event_data = self.MT.delete_rows_displayed(
2385
- rows,
2386
- event_data,
2387
- )
2388
- if undo:
2389
- self.MT.undo_stack.append(stored_event_dict(event_data))
2390
- if emit_event:
2391
- self.emit_event("<<SheetModified>>", event_data)
2392
2333
  self.set_refresh_timer(redraw)
2393
2334
  return event_data
2394
2335
 
2336
+ del_row = del_rows
2337
+ delete_row = del_rows
2395
2338
  delete_rows = del_rows
2396
2339
 
2397
2340
  def del_columns(
2398
2341
  self,
2399
2342
  columns: int | AnyIter[int],
2400
2343
  data_indexes: bool = True,
2401
- undo: bool = False,
2344
+ undo: bool = True,
2402
2345
  emit_event: bool = False,
2403
2346
  redraw: bool = True,
2404
2347
  ) -> EventDataDict:
2405
- self.MT.deselect("all", redraw=False)
2406
- columns = [columns] if isinstance(columns, int) else sorted(columns)
2407
- event_data = event_dict(
2408
- name="delete_columns",
2409
- sheet=self.name,
2410
- widget=self,
2411
- boxes=self.MT.get_boxes(),
2412
- selected=self.MT.selected,
2348
+ event_data = self.MT.delete_columns(
2349
+ columns=[columns] if isinstance(columns, int) else sorted(set(columns)),
2350
+ data_indexes=data_indexes,
2351
+ undo=undo,
2352
+ emit_event=emit_event,
2353
+ ext=True,
2413
2354
  )
2414
- if not data_indexes:
2415
- event_data = self.MT.delete_columns_displayed(columns, event_data)
2416
- event_data = self.MT.delete_columns_data(
2417
- columns if self.MT.all_columns_displayed else [self.MT.displayed_columns[c] for c in columns],
2418
- event_data,
2419
- )
2420
- else:
2421
- if self.MT.all_columns_displayed:
2422
- columns = columns
2423
- else:
2424
- columns = data_to_displayed_idxs(columns, self.MT.displayed_columns)
2425
- event_data = self.MT.delete_columns_data(columns, event_data)
2426
- event_data = self.MT.delete_columns_displayed(
2427
- columns,
2428
- event_data,
2429
- )
2430
- if undo:
2431
- self.MT.undo_stack.append(stored_event_dict(event_data))
2432
- if emit_event:
2433
- self.emit_event("<<SheetModified>>", event_data)
2434
2355
  self.set_refresh_timer(redraw)
2435
2356
  return event_data
2436
2357
 
2358
+ del_column = del_columns
2359
+ delete_column = del_columns
2437
2360
  delete_columns = del_columns
2438
2361
 
2439
2362
  def sheet_data_dimensions(
@@ -2495,11 +2418,11 @@ class Sheet(tk.Frame):
2495
2418
  self.MT.data_dimensions(total_columns=number)
2496
2419
  return self
2497
2420
 
2498
- def move_row(self, row: int, moveto: int) -> tuple[dict, dict, dict]:
2499
- return self.move_rows(moveto, row)
2421
+ def move_row(self, row: int, moveto: int) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
2422
+ return self.move_rows(moveto, [row])
2500
2423
 
2501
- def move_column(self, column: int, moveto: int) -> tuple[dict, dict, dict]:
2502
- return self.move_columns(moveto, column)
2424
+ def move_column(self, column: int, moveto: int) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
2425
+ return self.move_columns(moveto, [column])
2503
2426
 
2504
2427
  def move_rows(
2505
2428
  self,
@@ -2508,11 +2431,16 @@ class Sheet(tk.Frame):
2508
2431
  move_data: bool = True,
2509
2432
  data_indexes: bool = False,
2510
2433
  create_selections: bool = True,
2511
- undo: bool = False,
2434
+ undo: bool = True,
2512
2435
  emit_event: bool = False,
2513
2436
  move_heights: bool = True,
2437
+ event_data: EventDataDict | None = None,
2514
2438
  redraw: bool = True,
2515
- ) -> tuple[dict, dict, dict]:
2439
+ ) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
2440
+ if not event_data:
2441
+ event_data = self.MT.new_event_dict("move_rows", state=True)
2442
+ if not data_indexes:
2443
+ event_data.value = move_to
2516
2444
  data_idxs, disp_idxs, event_data = self.MT.move_rows_adjust_options_dict(
2517
2445
  *self.MT.get_args_for_move_rows(
2518
2446
  move_to=move_to,
@@ -2522,7 +2450,7 @@ class Sheet(tk.Frame):
2522
2450
  move_data=move_data,
2523
2451
  move_heights=move_heights,
2524
2452
  create_selections=create_selections,
2525
- data_indexes=data_indexes,
2453
+ event_data=event_data,
2526
2454
  )
2527
2455
  if undo:
2528
2456
  self.MT.undo_stack.append(stored_event_dict(event_data))
@@ -2538,11 +2466,16 @@ class Sheet(tk.Frame):
2538
2466
  move_data: bool = True,
2539
2467
  data_indexes: bool = False,
2540
2468
  create_selections: bool = True,
2541
- undo: bool = False,
2469
+ undo: bool = True,
2542
2470
  emit_event: bool = False,
2543
2471
  move_widths: bool = True,
2472
+ event_data: EventDataDict | None = None,
2544
2473
  redraw: bool = True,
2545
- ) -> tuple[dict, dict, dict]:
2474
+ ) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
2475
+ if not event_data:
2476
+ event_data = self.MT.new_event_dict("move_columns", state=True)
2477
+ if not data_indexes:
2478
+ event_data.value = move_to
2546
2479
  data_idxs, disp_idxs, event_data = self.MT.move_columns_adjust_options_dict(
2547
2480
  *self.MT.get_args_for_move_columns(
2548
2481
  move_to=move_to,
@@ -2552,7 +2485,7 @@ class Sheet(tk.Frame):
2552
2485
  move_data=move_data,
2553
2486
  move_widths=move_widths,
2554
2487
  create_selections=create_selections,
2555
- data_indexes=data_indexes,
2488
+ event_data=event_data,
2556
2489
  )
2557
2490
  if undo:
2558
2491
  self.MT.undo_stack.append(stored_event_dict(event_data))
@@ -2566,9 +2499,8 @@ class Sheet(tk.Frame):
2566
2499
  data_new_idxs: dict[int, int],
2567
2500
  disp_new_idxs: None | dict[int, int] = None,
2568
2501
  move_data: bool = True,
2569
- data_indexes: bool = False,
2570
2502
  create_selections: bool = True,
2571
- undo: bool = False,
2503
+ undo: bool = True,
2572
2504
  emit_event: bool = False,
2573
2505
  redraw: bool = True,
2574
2506
  ) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
@@ -2579,7 +2511,6 @@ class Sheet(tk.Frame):
2579
2511
  disp_new_idxs=disp_new_idxs,
2580
2512
  move_data=move_data,
2581
2513
  create_selections=create_selections,
2582
- data_indexes=data_indexes,
2583
2514
  )
2584
2515
  if undo:
2585
2516
  self.MT.undo_stack.append(stored_event_dict(event_data))
@@ -2593,10 +2524,10 @@ class Sheet(tk.Frame):
2593
2524
  data_new_idxs: dict[int, int],
2594
2525
  disp_new_idxs: None | dict[int, int] = None,
2595
2526
  move_data: bool = True,
2596
- data_indexes: bool = False,
2597
2527
  create_selections: bool = True,
2598
- undo: bool = False,
2528
+ undo: bool = True,
2599
2529
  emit_event: bool = False,
2530
+ event_data: EventDataDict | None = None,
2600
2531
  redraw: bool = True,
2601
2532
  ) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
2602
2533
  data_idxs, disp_idxs, event_data = self.MT.move_rows_adjust_options_dict(
@@ -2606,7 +2537,7 @@ class Sheet(tk.Frame):
2606
2537
  disp_new_idxs=disp_new_idxs,
2607
2538
  move_data=move_data,
2608
2539
  create_selections=create_selections,
2609
- data_indexes=data_indexes,
2540
+ event_data=event_data,
2610
2541
  )
2611
2542
  if undo:
2612
2543
  self.MT.undo_stack.append(stored_event_dict(event_data))
@@ -2647,6 +2578,102 @@ class Sheet(tk.Frame):
2647
2578
  data_idxs,
2648
2579
  )
2649
2580
 
2581
+ # Sorting
2582
+
2583
+ def sort(
2584
+ self,
2585
+ boxes: AnyIter[Sequence[int, int, int, int]] | Span | None = None,
2586
+ reverse: bool = False,
2587
+ row_wise: bool = False,
2588
+ validation: bool = True,
2589
+ key: Callable | None = None,
2590
+ undo: bool = True,
2591
+ ) -> EventDataDict:
2592
+ """
2593
+ Sort the data within specified box regions in the table.
2594
+
2595
+ This method sorts the data within one or multiple box regions defined by their coordinates. Each box's columns are sorted independently.
2596
+
2597
+ Args:
2598
+ boxes (AnyIter[Sequence[int, int, int, int]] | Span | None): An iterable of box coordinates. Each box is defined by:
2599
+ - From Row (inclusive)
2600
+ - From Column (inclusive)
2601
+ - Up To Row (exclusive)
2602
+ - Up To Column (exclusive)
2603
+ If None, it will sort the currently selected boxes or the entire table.
2604
+ If Span, it will use the span coordinates.
2605
+
2606
+ reverse (bool): If True, sorts in descending order. Default is False (ascending).
2607
+
2608
+ row_wise (bool): if True sorts cells row-wise. Default is column-wise
2609
+
2610
+ validation (bool): If True, checks if the new cell values are valid according to any restrictions
2611
+ (e.g., dropdown validations) before applying the sort. Default is True.
2612
+
2613
+ key (Callable | None): A function to extract a comparison key from each element in the columns.
2614
+ If None, a natural sorting key will be used, which can handle most Python objects, including:
2615
+ - None, booleans, numbers (int, float), datetime objects, strings, and unknown types.
2616
+ Note: Performance might be slow with the natural sort for very large datasets.
2617
+
2618
+ Returns:
2619
+ EventDataDict: A dictionary containing information about the sorting event, including changes made to the table.
2620
+
2621
+ Raises:
2622
+ ValueError: If the input boxes are not in the expected format or if the data cannot be sorted.
2623
+
2624
+ Note:
2625
+ - Sorting is performed column-wise within each box.
2626
+ - If validation is enabled, any cell content that fails validation will not be updated.
2627
+ - Any cell options attached to cells will not be moved. Event data can be used to re-locate them.
2628
+ """
2629
+ if isinstance(boxes, Span):
2630
+ boxes = [boxes.coords]
2631
+ return self.MT.sort_boxes(
2632
+ boxes=boxes, reverse=reverse, row_wise=row_wise, validation=validation, key=key, undo=undo
2633
+ )
2634
+
2635
+ def sort_rows(
2636
+ self,
2637
+ rows: AnyIter[int] | Span | None = None,
2638
+ reverse: bool = False,
2639
+ validation: bool = True,
2640
+ key: Callable | None = None,
2641
+ undo: bool = True,
2642
+ ) -> EventDataDict:
2643
+ if isinstance(rows, Span):
2644
+ rows = rows.rows
2645
+ return self.RI._sort_rows(rows=rows, reverse=reverse, validation=validation, key=key, undo=undo)
2646
+
2647
+ def sort_columns(
2648
+ self,
2649
+ columns: AnyIter[int] | Span | None = None,
2650
+ reverse: bool = False,
2651
+ validation: bool = True,
2652
+ key: Callable | None = None,
2653
+ undo: bool = True,
2654
+ ) -> EventDataDict:
2655
+ if isinstance(columns, Span):
2656
+ columns = columns.columns
2657
+ return self.CH._sort_columns(columns=columns, reverse=reverse, validation=validation, key=key, undo=undo)
2658
+
2659
+ def sort_rows_by_column(
2660
+ self,
2661
+ column: int | None = None,
2662
+ reverse: bool = False,
2663
+ key: Callable | None = None,
2664
+ undo: bool = True,
2665
+ ) -> EventDataDict:
2666
+ return self.CH._sort_rows_by_column(column=column, reverse=reverse, key=key, undo=undo)
2667
+
2668
+ def sort_columns_by_row(
2669
+ self,
2670
+ row: int | None = None,
2671
+ reverse: bool = False,
2672
+ key: Callable | None = None,
2673
+ undo: bool = True,
2674
+ ) -> EventDataDict:
2675
+ return self.RI._sort_columns_by_row(row=row, reverse=reverse, key=key, undo=undo)
2676
+
2650
2677
  # Highlighting Cells
2651
2678
 
2652
2679
  def highlight(
@@ -3085,16 +3112,17 @@ class Sheet(tk.Frame):
3085
3112
 
3086
3113
  # Text Font and Alignment
3087
3114
 
3088
- def font(
3089
- self,
3090
- newfont: tuple[str, int, str] | None = None,
3091
- reset_row_positions: bool = True,
3092
- ) -> tuple[str, int, str]:
3093
- return self.MT.set_table_font(newfont, reset_row_positions=reset_row_positions)
3115
+ def font(self, newfont: tuple[str, int, str] | None = None, **_) -> tuple[str, int, str]:
3116
+ return self.MT.set_table_font(newfont)
3117
+
3118
+ table_font = font
3094
3119
 
3095
3120
  def header_font(self, newfont: tuple[str, int, str] | None = None) -> tuple[str, int, str]:
3096
3121
  return self.MT.set_header_font(newfont)
3097
3122
 
3123
+ def index_font(self, newfont: tuple[str, int, str] | None = None) -> tuple[str, int, str]:
3124
+ return self.MT.set_index_font(newfont)
3125
+
3098
3126
  def table_align(
3099
3127
  self,
3100
3128
  align: str | None = None,
@@ -3105,7 +3133,7 @@ class Sheet(tk.Frame):
3105
3133
  elif convert_align(align):
3106
3134
  self.MT.align = convert_align(align)
3107
3135
  else:
3108
- raise ValueError("Align must be one of the following values: c, center, w, west, e, east")
3136
+ raise ValueError()
3109
3137
  return self.set_refresh_timer(redraw)
3110
3138
 
3111
3139
  def header_align(
@@ -3118,7 +3146,7 @@ class Sheet(tk.Frame):
3118
3146
  elif convert_align(align):
3119
3147
  self.CH.align = convert_align(align)
3120
3148
  else:
3121
- raise ValueError("Align must be one of the following values: c, center, w, west, e, east")
3149
+ raise ValueError(align_value_error)
3122
3150
  return self.set_refresh_timer(redraw)
3123
3151
 
3124
3152
  def row_index_align(
@@ -3131,7 +3159,7 @@ class Sheet(tk.Frame):
3131
3159
  elif convert_align(align):
3132
3160
  self.RI.align = convert_align(align)
3133
3161
  else:
3134
- raise ValueError("Align must be one of the following values: c, center, w, west, e, east")
3162
+ raise ValueError(align_value_error)
3135
3163
  return self.set_refresh_timer(redraw)
3136
3164
 
3137
3165
  index_align = row_index_align
@@ -3594,18 +3622,41 @@ class Sheet(tk.Frame):
3594
3622
  only_set_if_too_small: bool = False,
3595
3623
  redraw: bool = True,
3596
3624
  ) -> Sheet | int:
3597
- if column == "all" and width == "default":
3598
- self.MT.reset_col_positions()
3599
- elif column == "displayed" and width == "text":
3600
- for c in range(*self.MT.visible_text_columns):
3601
- self.CH.set_col_width(c)
3602
- elif width == "text" and isinstance(column, int):
3603
- self.CH.set_col_width(col=column, width=None, only_if_too_small=only_set_if_too_small)
3604
- elif isinstance(width, int) and isinstance(column, int):
3605
- self.CH.set_col_width(col=column, width=width, only_if_too_small=only_set_if_too_small)
3625
+ if column == "all":
3626
+ if width == "default":
3627
+ self.MT.reset_col_positions()
3628
+ elif width == "text":
3629
+ self.set_all_column_widths(only_set_if_too_small=only_set_if_too_small)
3630
+ elif isinstance(width, int):
3631
+ self.MT.reset_col_positions(width=width)
3632
+
3633
+ elif column == "displayed":
3634
+ if width == "default":
3635
+ for c in range(*self.MT.visible_text_columns):
3636
+ self.CH.set_col_width(
3637
+ c, width=self.ops.default_column_width, only_if_too_small=only_set_if_too_small
3638
+ )
3639
+ elif width == "text":
3640
+ for c in range(*self.MT.visible_text_columns):
3641
+ self.CH.set_col_width(c, only_if_too_small=only_set_if_too_small)
3642
+ elif isinstance(width, int):
3643
+ for c in range(*self.MT.visible_text_columns):
3644
+ self.CH.set_col_width(c, width=width, only_if_too_small=only_set_if_too_small)
3645
+
3606
3646
  elif isinstance(column, int):
3607
- return int(self.MT.col_positions[column + 1] - self.MT.col_positions[column])
3608
- return self.set_refresh_timer(redraw)
3647
+ if width == "default":
3648
+ self.CH.set_col_width(
3649
+ col=column, width=self.ops.default_column_width, only_if_too_small=only_set_if_too_small
3650
+ )
3651
+ elif width == "text":
3652
+ self.CH.set_col_width(col=column, only_if_too_small=only_set_if_too_small)
3653
+ elif isinstance(width, int):
3654
+ self.CH.set_col_width(col=column, width=width, only_if_too_small=only_set_if_too_small)
3655
+ elif width is None:
3656
+ return int(self.MT.col_positions[column + 1] - self.MT.col_positions[column])
3657
+
3658
+ else:
3659
+ return self.set_refresh_timer(redraw)
3609
3660
 
3610
3661
  def row_height(
3611
3662
  self,
@@ -3614,18 +3665,39 @@ class Sheet(tk.Frame):
3614
3665
  only_set_if_too_small: bool = False,
3615
3666
  redraw: bool = True,
3616
3667
  ) -> Sheet | int:
3617
- if row == "all" and height == "default":
3618
- self.MT.reset_row_positions()
3619
- elif row == "displayed" and height == "text":
3620
- for r in range(*self.MT.visible_text_rows):
3621
- self.RI.set_row_height(r)
3622
- elif height == "text" and isinstance(row, int):
3623
- self.RI.set_row_height(row=row, height=None, only_if_too_small=only_set_if_too_small)
3624
- elif isinstance(height, int) and isinstance(row, int):
3625
- self.RI.set_row_height(row=row, height=height, only_if_too_small=only_set_if_too_small)
3668
+ if row == "all":
3669
+ if height == "default":
3670
+ self.MT.reset_row_positions()
3671
+ elif height == "text":
3672
+ self.set_all_row_heights()
3673
+ elif isinstance(height, int):
3674
+ self.MT.reset_row_positions(height=height)
3675
+
3676
+ elif row == "displayed":
3677
+ if height == "default":
3678
+ height = self.MT.get_default_row_height()
3679
+ for r in range(*self.MT.visible_text_rows):
3680
+ self.RI.set_row_height(r, height=height, only_if_too_small=only_set_if_too_small)
3681
+ elif height == "text":
3682
+ for r in range(*self.MT.visible_text_rows):
3683
+ self.RI.set_row_height(r, only_if_too_small=only_set_if_too_small)
3684
+ elif isinstance(height, int):
3685
+ for r in range(*self.MT.visible_text_rows):
3686
+ self.RI.set_row_height(r, height=height, only_if_too_small=only_set_if_too_small)
3687
+
3626
3688
  elif isinstance(row, int):
3627
- return int(self.MT.row_positions[row + 1] - self.MT.row_positions[row])
3628
- return self.set_refresh_timer(redraw)
3689
+ if height == "default":
3690
+ height = self.MT.get_default_row_height()
3691
+ self.RI.set_row_height(row=row, height=height, only_if_too_small=only_set_if_too_small)
3692
+ elif height == "text":
3693
+ self.RI.set_row_height(row=row, only_if_too_small=only_set_if_too_small)
3694
+ elif isinstance(height, int):
3695
+ self.RI.set_row_height(row=row, height=height, only_if_too_small=only_set_if_too_small)
3696
+ elif height is None:
3697
+ return int(self.MT.row_positions[row + 1] - self.MT.row_positions[row])
3698
+
3699
+ else:
3700
+ return self.set_refresh_timer(redraw)
3629
3701
 
3630
3702
  def get_column_widths(self, canvas_positions: bool = False) -> list[float]:
3631
3703
  if canvas_positions:
@@ -3759,7 +3831,7 @@ class Sheet(tk.Frame):
3759
3831
  deselect_all: bool = False,
3760
3832
  redraw: bool = False,
3761
3833
  ) -> Sheet:
3762
- self.MT.insert_col_position(idx=idx, width=width, deselect_all=deselect_all)
3834
+ self.MT.insert_col_positions(idx=idx, widths=width, deselect_all=deselect_all)
3763
3835
  return self.set_refresh_timer(redraw)
3764
3836
 
3765
3837
  def insert_row_position(
@@ -3769,7 +3841,7 @@ class Sheet(tk.Frame):
3769
3841
  deselect_all: bool = False,
3770
3842
  redraw: bool = False,
3771
3843
  ) -> Sheet:
3772
- self.MT.insert_row_position(idx=idx, height=height, deselect_all=deselect_all)
3844
+ self.MT.insert_row_positions(idx=idx, heights=height, deselect_all=deselect_all)
3773
3845
  return self.set_refresh_timer(redraw)
3774
3846
 
3775
3847
  def insert_column_positions(
@@ -4101,11 +4173,12 @@ class Sheet(tk.Frame):
4101
4173
  if isinstance(columns, int):
4102
4174
  columns = [columns]
4103
4175
  cws = self.MT.get_column_widths()
4176
+ default_col_w = self.ops.default_column_width
4104
4177
  for column in columns:
4105
4178
  idx = bisect_left(self.MT.displayed_columns, column)
4106
4179
  if len(self.MT.displayed_columns) == idx or self.MT.displayed_columns[idx] != column:
4107
4180
  self.MT.displayed_columns.insert(idx, column)
4108
- cws.insert(idx, self.MT.saved_column_widths.pop(column, self.ops.default_column_width))
4181
+ cws.insert(idx, self.MT.saved_column_widths.pop(column, default_col_w))
4109
4182
  self.MT.set_col_positions(cws)
4110
4183
  if deselect_all:
4111
4184
  self.MT.deselect(redraw=False)
@@ -4234,12 +4307,13 @@ class Sheet(tk.Frame):
4234
4307
  return
4235
4308
  if isinstance(rows, int):
4236
4309
  rows = [rows]
4310
+ default_row_h = self.MT.get_default_row_height()
4237
4311
  rhs = self.MT.get_row_heights()
4238
4312
  for row in rows:
4239
4313
  idx = bisect_left(self.MT.displayed_rows, row)
4240
4314
  if len(self.MT.displayed_rows) == idx or self.MT.displayed_rows[idx] != row:
4241
4315
  self.MT.displayed_rows.insert(idx, row)
4242
- rhs.insert(idx, self.MT.saved_row_heights.pop(row, self.MT.get_default_row_height()))
4316
+ rhs.insert(idx, self.MT.saved_row_heights.pop(row, default_row_h))
4243
4317
  self.MT.set_row_positions(rhs)
4244
4318
  if deselect_all:
4245
4319
  self.MT.deselect(redraw=False)
@@ -4274,52 +4348,6 @@ class Sheet(tk.Frame):
4274
4348
 
4275
4349
  # Hiding Sheet Elements
4276
4350
 
4277
- def hide(
4278
- self,
4279
- canvas: Literal[
4280
- "all",
4281
- "row_index",
4282
- "header",
4283
- "top_left",
4284
- "x_scrollbar",
4285
- "y_scrollbar",
4286
- ] = "all",
4287
- ) -> Sheet:
4288
- if canvas.lower() == "all":
4289
- self.TL.grid_forget()
4290
- self.RI.grid_forget()
4291
- self.RI["yscrollcommand"] = 0
4292
- self.MT.show_index = False
4293
- self.CH.grid_forget()
4294
- self.CH["xscrollcommand"] = 0
4295
- self.MT.show_header = False
4296
- self.MT.grid_forget()
4297
- self.yscroll.grid_forget()
4298
- self.xscroll.grid_forget()
4299
- self.xscroll_showing = False
4300
- self.yscroll_showing = False
4301
- self.xscroll_disabled = True
4302
- self.yscroll_disabled = True
4303
- elif canvas.lower() == "row_index":
4304
- self.RI.grid_forget()
4305
- self.RI["yscrollcommand"] = 0
4306
- self.MT.show_index = False
4307
- elif canvas.lower() == "header":
4308
- self.CH.grid_forget()
4309
- self.CH["xscrollcommand"] = 0
4310
- self.MT.show_header = False
4311
- elif canvas.lower() == "top_left":
4312
- self.TL.grid_forget()
4313
- elif canvas.lower() == "x_scrollbar":
4314
- self.xscroll.grid_forget()
4315
- self.xscroll_showing = False
4316
- self.xscroll_disabled = True
4317
- elif canvas.lower() == "y_scrollbar":
4318
- self.yscroll.grid_forget()
4319
- self.yscroll_showing = False
4320
- self.yscroll_disabled = True
4321
- return self
4322
-
4323
4351
  def show(
4324
4352
  self,
4325
4353
  canvas: Literal[
@@ -4330,45 +4358,75 @@ class Sheet(tk.Frame):
4330
4358
  "top_left",
4331
4359
  "x_scrollbar",
4332
4360
  "y_scrollbar",
4361
+ "table",
4333
4362
  ] = "all",
4334
4363
  ) -> Sheet:
4335
- if canvas == "all":
4336
- self.hide()
4337
- self.TL.grid(row=0, column=0)
4338
- self.RI.grid(row=1, column=0, sticky="nswe")
4339
- self.CH.grid(row=0, column=1, sticky="nswe")
4364
+ if canvas in ("all", "table"):
4340
4365
  self.MT.grid(row=1, column=1, sticky="nswe")
4341
- self.yscroll.grid(row=0, column=2, rowspan=3, sticky="nswe")
4342
- self.xscroll.grid(row=2, column=0, columnspan=2, sticky="nswe")
4343
- self.MT["xscrollcommand"] = self.xscroll.set
4344
- self.CH["xscrollcommand"] = self.xscroll.set
4345
- self.MT["yscrollcommand"] = self.yscroll.set
4346
- self.RI["yscrollcommand"] = self.yscroll.set
4347
- self.xscroll_showing = True
4348
- self.yscroll_showing = True
4349
- self.xscroll_disabled = False
4350
- self.yscroll_disabled = False
4351
- elif canvas in ("row_index", "index"):
4366
+ if canvas in ("all", "row_index", "index"):
4352
4367
  self.RI.grid(row=1, column=0, sticky="nswe")
4353
4368
  self.MT["yscrollcommand"] = self.yscroll.set
4354
4369
  self.RI["yscrollcommand"] = self.yscroll.set
4355
4370
  self.MT.show_index = True
4356
- elif canvas == "header":
4371
+ if self.MT.show_header:
4372
+ self.show("top_left")
4373
+ if canvas in ("all", "header"):
4357
4374
  self.CH.grid(row=0, column=1, sticky="nswe")
4358
4375
  self.MT["xscrollcommand"] = self.xscroll.set
4359
4376
  self.CH["xscrollcommand"] = self.xscroll.set
4360
4377
  self.MT.show_header = True
4361
- elif canvas == "top_left":
4378
+ if self.MT.show_index:
4379
+ self.show("top_left")
4380
+ if canvas in ("all", "top_left"):
4362
4381
  self.TL.grid(row=0, column=0)
4363
- elif canvas == "x_scrollbar":
4382
+ if canvas in ("all", "x_scrollbar"):
4364
4383
  self.xscroll.grid(row=2, column=0, columnspan=2, sticky="nswe")
4365
4384
  self.xscroll_showing = True
4366
4385
  self.xscroll_disabled = False
4367
- elif canvas == "y_scrollbar":
4386
+ if canvas in ("all", "y_scrollbar"):
4368
4387
  self.yscroll.grid(row=0, column=2, rowspan=3, sticky="nswe")
4369
4388
  self.yscroll_showing = True
4370
4389
  self.yscroll_disabled = False
4371
- self.MT.update_idletasks()
4390
+ if self._startup_complete:
4391
+ self.MT.update_idletasks()
4392
+ return self
4393
+
4394
+ def hide(
4395
+ self,
4396
+ canvas: Literal[
4397
+ "all",
4398
+ "row_index",
4399
+ "header",
4400
+ "top_left",
4401
+ "x_scrollbar",
4402
+ "y_scrollbar",
4403
+ "table",
4404
+ ] = "all",
4405
+ ) -> Sheet:
4406
+ if canvas in ("all", "row_index"):
4407
+ self.RI.grid_remove()
4408
+ self.RI["yscrollcommand"] = 0
4409
+ self.MT.show_index = False
4410
+ if not self.ops.show_top_left:
4411
+ self.hide("top_left")
4412
+ if canvas in ("all", "header"):
4413
+ self.CH.grid_remove()
4414
+ self.CH["xscrollcommand"] = 0
4415
+ self.MT.show_header = False
4416
+ if not self.ops.show_top_left:
4417
+ self.hide("top_left")
4418
+ if canvas in ("all", "top_left"):
4419
+ self.TL.grid_remove()
4420
+ if canvas in ("all", "x_scrollbar"):
4421
+ self.xscroll.grid_remove()
4422
+ self.xscroll_showing = False
4423
+ self.xscroll_disabled = True
4424
+ if canvas in ("all", "y_scrollbar"):
4425
+ self.yscroll.grid_remove()
4426
+ self.yscroll_showing = False
4427
+ self.yscroll_disabled = True
4428
+ if canvas in ("all", "table"):
4429
+ self.MT.grid_remove()
4372
4430
  return self
4373
4431
 
4374
4432
  # Sheet Height and Width
@@ -4504,6 +4562,8 @@ class Sheet(tk.Frame):
4504
4562
  if "expand_sheet_if_paste_too_big" in kwargs:
4505
4563
  self.ops.paste_can_expand_x = kwargs["expand_sheet_if_paste_too_big"]
4506
4564
  self.ops.paste_can_expand_y = kwargs["expand_sheet_if_paste_too_big"]
4565
+ if "show_top_left" in kwargs:
4566
+ self.show("top_left")
4507
4567
  if "font" in kwargs:
4508
4568
  self.MT.set_table_font(kwargs["font"])
4509
4569
  elif "table_font" in kwargs:
@@ -4539,7 +4599,7 @@ class Sheet(tk.Frame):
4539
4599
  self.set_scrollbar_options()
4540
4600
  self.MT.create_rc_menus()
4541
4601
  if "treeview" in kwargs:
4542
- self.index_align("w", redraw=False)
4602
+ self.index_align("nw", redraw=False)
4543
4603
  return self.set_refresh_timer(redraw)
4544
4604
 
4545
4605
  def set_scrollbar_options(self) -> Sheet:
@@ -4596,15 +4656,7 @@ class Sheet(tk.Frame):
4596
4656
  self,
4597
4657
  row: int,
4598
4658
  column: int | str,
4599
- key: None
4600
- | Literal[
4601
- "format",
4602
- "highlight",
4603
- "dropdown",
4604
- "checkbox",
4605
- "readonly",
4606
- "align",
4607
- ] = None,
4659
+ key: None | CellPropertyKey = None,
4608
4660
  cellops: bool = True,
4609
4661
  rowops: bool = True,
4610
4662
  columnops: bool = True,
@@ -4638,15 +4690,7 @@ class Sheet(tk.Frame):
4638
4690
  def index_props(
4639
4691
  self,
4640
4692
  row: int,
4641
- key: None
4642
- | Literal[
4643
- "format",
4644
- "highlight",
4645
- "dropdown",
4646
- "checkbox",
4647
- "readonly",
4648
- "align",
4649
- ] = None,
4693
+ key: None | CellPropertyKey = None,
4650
4694
  ) -> dict:
4651
4695
  """
4652
4696
  Retrieve options (properties - props)
@@ -4662,15 +4706,7 @@ class Sheet(tk.Frame):
4662
4706
  def header_props(
4663
4707
  self,
4664
4708
  column: int | str,
4665
- key: None
4666
- | Literal[
4667
- "format",
4668
- "highlight",
4669
- "dropdown",
4670
- "checkbox",
4671
- "readonly",
4672
- "align",
4673
- ] = None,
4709
+ key: None | CellPropertyKey = None,
4674
4710
  ) -> dict:
4675
4711
  """
4676
4712
  Retrieve options (properties - props)
@@ -4687,7 +4723,7 @@ class Sheet(tk.Frame):
4687
4723
 
4688
4724
  def get_cell_options(
4689
4725
  self,
4690
- key: None | str = None,
4726
+ key: None | CellPropertyKey = None,
4691
4727
  canvas: Literal["table", "row_index", "index", "header"] = "table",
4692
4728
  ) -> dict:
4693
4729
  if canvas == "table":
@@ -4700,22 +4736,22 @@ class Sheet(tk.Frame):
4700
4736
  return target
4701
4737
  return {k: v[key] for k, v in target.items() if key in v}
4702
4738
 
4703
- def get_row_options(self, key: None | str = None) -> dict:
4739
+ def get_row_options(self, key: None | CellPropertyKey = None) -> dict:
4704
4740
  if key is None:
4705
4741
  return self.MT.row_options
4706
4742
  return {k: v[key] for k, v in self.MT.row_options.items() if key in v}
4707
4743
 
4708
- def get_column_options(self, key: None | str = None) -> dict:
4744
+ def get_column_options(self, key: None | CellPropertyKey = None) -> dict:
4709
4745
  if key is None:
4710
4746
  return self.MT.col_options
4711
4747
  return {k: v[key] for k, v in self.MT.col_options.items() if key in v}
4712
4748
 
4713
- def get_index_options(self, key: None | str = None) -> dict:
4749
+ def get_index_options(self, key: None | CellPropertyKey = None) -> dict:
4714
4750
  if key is None:
4715
4751
  return self.RI.cell_options
4716
4752
  return {k: v[key] for k, v in self.RI.cell_options.items() if key in v}
4717
4753
 
4718
- def get_header_options(self, key: None | str = None) -> dict:
4754
+ def get_header_options(self, key: None | CellPropertyKey = None) -> dict:
4719
4755
  if key is None:
4720
4756
  return self.CH.cell_options
4721
4757
  return {k: v[key] for k, v in self.CH.cell_options.items() if key in v}
@@ -4983,94 +5019,21 @@ class Sheet(tk.Frame):
4983
5019
  include_parent_column: bool = True,
4984
5020
  include_text_column: bool = True,
4985
5021
  ) -> Sheet:
4986
- self.reset(cell_options=False, column_widths=False, header=False, redraw=False)
4987
- if text_column is None:
4988
- text_column = iid_column
4989
- tally_of_ids = defaultdict(lambda: -1)
4990
- if not isinstance(ncols, int):
4991
- ncols = max(map(len, data), default=0)
4992
- for rn, row in enumerate(data):
4993
- if safety and ncols > (lnr := len(row)):
4994
- row += self.MT.get_empty_row_seq(rn, end=ncols, start=lnr)
4995
- if lower:
4996
- iid = row[iid_column].lower()
4997
- pid = row[parent_column].lower()
4998
- else:
4999
- iid = row[iid_column]
5000
- pid = row[parent_column]
5001
- if safety:
5002
- if not iid:
5003
- continue
5004
- tally_of_ids[iid] += 1
5005
- if tally_of_ids[iid]:
5006
- x = 1
5007
- while iid in tally_of_ids:
5008
- new = f"{row[iid_column]}_DUPLICATED_{x}"
5009
- iid = new.lower() if lower else new
5010
- x += 1
5011
- tally_of_ids[iid] += 1
5012
- row[iid_column] = new
5013
- if iid in self.RI.tree:
5014
- self.RI.tree[iid].text = row[text_column] if isinstance(text_column, int) else text_column[rn]
5015
- else:
5016
- self.RI.tree[iid] = Node(row[text_column] if isinstance(text_column, int) else text_column[rn], iid, "")
5017
- if safety and (iid == pid or self.RI.build_pid_causes_recursive_loop(iid, pid)):
5018
- row[parent_column] = ""
5019
- pid = ""
5020
- if pid:
5021
- if pid not in self.RI.tree:
5022
- self.RI.tree[pid] = Node(row[text_column] if isinstance(text_column, int) else text_column[rn], pid)
5023
- self.RI.tree[iid].parent = self.RI.tree[pid]
5024
- self.RI.tree[pid].children.append(self.RI.tree[iid])
5025
- else:
5026
- self.RI.tree[iid].parent = ""
5027
- self.RI.tree_rns[iid] = rn
5028
- if safety:
5029
- for n in self.RI.tree.values():
5030
- if n.parent is None:
5031
- n.parent = ""
5032
- newrow = self.MT.get_empty_row_seq(len(data), ncols)
5033
- newrow[iid_column] = n.iid
5034
- self.RI.tree_rns[n.iid] = len(data)
5035
- data.append(newrow)
5036
- insert_rows = partial(
5037
- self.insert_rows,
5038
- idx=0,
5039
- heights={} if row_heights is False else row_heights,
5040
- row_index=True,
5041
- create_selections=False,
5042
- fill=False,
5022
+ self.RI.tree_build(
5023
+ data=data,
5024
+ iid_column=iid_column,
5025
+ parent_column=parent_column,
5026
+ text_column=text_column,
5043
5027
  push_ops=push_ops,
5044
- redraw=False,
5028
+ row_heights=row_heights,
5029
+ open_ids=open_ids,
5030
+ safety=safety,
5031
+ ncols=ncols,
5032
+ lower=lower,
5033
+ include_iid_column=include_iid_column,
5034
+ include_parent_column=include_parent_column,
5035
+ include_text_column=include_text_column,
5045
5036
  )
5046
- exclude = set()
5047
- if not include_iid_column:
5048
- exclude.add(iid_column)
5049
- if not include_parent_column:
5050
- exclude.add(parent_column)
5051
- if isinstance(text_column, int) and not include_text_column:
5052
- exclude.add(text_column)
5053
- if exclude:
5054
- insert_rows(
5055
- rows=[
5056
- [self.RI.tree[iid]] + [e for i, e in enumerate(data[self.RI.tree_rns[iid]]) if i not in exclude]
5057
- for iid in self.get_nodes()
5058
- ]
5059
- )
5060
- else:
5061
- insert_rows(rows=[[self.RI.tree[iid]] + data[self.RI.tree_rns[iid]] for iid in self.get_nodes()])
5062
- self.MT.all_rows_displayed = False
5063
- self.MT.displayed_rows = list(range(len(self.MT._row_index)))
5064
- self.RI.tree_rns = {n.iid: i for i, n in enumerate(self.MT._row_index)}
5065
- if open_ids:
5066
- self.tree_set_open(open_ids=open_ids)
5067
- else:
5068
- self.hide_rows(
5069
- {self.RI.tree_rns[iid] for iid in self.get_children() if self.RI.tree[iid].parent},
5070
- deselect_all=False,
5071
- data_indexes=True,
5072
- row_heights=False if row_heights is False else True,
5073
- )
5074
5037
  return self
5075
5038
 
5076
5039
  def tree_reset(self) -> Sheet:
@@ -5178,6 +5141,7 @@ class Sheet(tk.Frame):
5178
5141
  text: None | str = None,
5179
5142
  values: None | list[object] = None,
5180
5143
  create_selections: bool = False,
5144
+ undo: bool = True,
5181
5145
  ) -> str:
5182
5146
  """
5183
5147
  Insert an item into the treeview
@@ -5186,9 +5150,7 @@ class Sheet(tk.Frame):
5186
5150
  str: new item iid
5187
5151
  """
5188
5152
  if not iid:
5189
- i = 0
5190
- while (iid := f"{num2alpha(i)}") in self.RI.tree:
5191
- i += 1
5153
+ iid = self.RI.new_iid()
5192
5154
  if iid in self.RI.tree:
5193
5155
  raise ValueError(f"iid '{iid}' already exists.")
5194
5156
  if iid == parent:
@@ -5197,18 +5159,37 @@ class Sheet(tk.Frame):
5197
5159
  raise ValueError(f"parent '{parent}' does not exist.")
5198
5160
  if text is None:
5199
5161
  text = iid
5200
- parent_node = self.RI.tree[parent] if parent else ""
5201
- self.RI.tree[iid] = Node(text, iid, parent_node)
5202
- if parent_node:
5162
+ new_node = Node(text, iid, parent)
5163
+ datarn = self._get_id_insert_row(index=index, parent=parent)
5164
+ self.insert_rows(
5165
+ rows=[[new_node] + ([] if values is None else values)],
5166
+ idx=datarn,
5167
+ row_index=True,
5168
+ create_selections=create_selections,
5169
+ fill=False,
5170
+ undo=undo,
5171
+ tree=True,
5172
+ )
5173
+ # only relevant here because in rc add rows they are visible and undo sets old open_ids
5174
+ if parent and (parent not in self.RI.tree_open_ids or not self.item_displayed(parent)):
5175
+ self.hide_rows(datarn, deselect_all=False, data_indexes=True)
5176
+ return iid
5177
+
5178
+ def _get_id_insert_row(self, index: int, parent: str) -> int:
5179
+ if parent:
5203
5180
  if isinstance(index, int):
5204
- datarn = self.RI.tree_rns[parent] + index + 1
5205
- datarn += sum(
5206
- sum(1 for _ in self.RI.get_iid_descendants(cid)) for cid in islice(self.get_children(parent), index)
5181
+ index = min(index, len(self.RI.tree[parent].children))
5182
+ datarn = (
5183
+ self.RI.tree_rns[parent]
5184
+ + index
5185
+ + 1
5186
+ + sum(
5187
+ sum(1 for _ in self.RI.get_iid_descendants(cid))
5188
+ for cid in islice(self.get_children(parent), index)
5189
+ )
5207
5190
  )
5208
- self.RI.tree[parent].children.insert(index, self.RI.tree[iid])
5209
5191
  else:
5210
5192
  datarn = self.RI.tree_rns[parent] + sum(1 for _ in self.RI.get_iid_descendants(parent)) + 1
5211
- self.RI.tree[parent].children.append(self.RI.tree[iid])
5212
5193
  else:
5213
5194
  if isinstance(index, int):
5214
5195
  datarn = index
@@ -5216,19 +5197,7 @@ class Sheet(tk.Frame):
5216
5197
  datarn = len(self.MT._row_index)
5217
5198
  else:
5218
5199
  datarn = len(self.MT._row_index)
5219
- if values is None:
5220
- values = []
5221
- self.insert_rows(
5222
- rows=[[self.RI.tree[iid]] + values],
5223
- idx=datarn,
5224
- row_index=True,
5225
- create_selections=create_selections,
5226
- fill=False,
5227
- )
5228
- self.RI.tree_rns[iid] = datarn
5229
- if parent and (parent not in self.RI.tree_open_ids or not self.item_displayed(parent)):
5230
- self.hide_rows(datarn, deselect_all=False, data_indexes=True)
5231
- return iid
5200
+ return datarn
5232
5201
 
5233
5202
  def bulk_insert(
5234
5203
  self,
@@ -5240,6 +5209,7 @@ class Sheet(tk.Frame):
5240
5209
  create_selections: bool = False,
5241
5210
  include_iid_column: bool = True,
5242
5211
  include_text_column: bool = True,
5212
+ undo: bool = True,
5243
5213
  ) -> dict[str, int]:
5244
5214
  """
5245
5215
  Insert multiple items into the treeview at once, under the same parent.
@@ -5250,52 +5220,29 @@ class Sheet(tk.Frame):
5250
5220
  - Values (int): row numbers
5251
5221
  """
5252
5222
  to_insert = []
5253
- pid = parent
5254
- if pid and pid not in self.RI.tree:
5223
+ if parent and parent not in self.RI.tree:
5255
5224
  raise ValueError(f"parent '{parent}' does not exist.")
5256
- parent_node = self.RI.tree[pid] if parent else ""
5257
- if parent_node:
5258
- if isinstance(index, int):
5259
- datarn = self.RI.tree_rns[pid] + index + 1
5260
- datarn += sum(
5261
- sum(1 for _ in self.RI.get_iid_descendants(cid)) for cid in islice(self.get_children(pid), index)
5262
- )
5263
- else:
5264
- datarn = self.RI.tree_rns[pid] + sum(1 for _ in self.RI.get_iid_descendants(pid)) + 1
5265
- else:
5266
- if isinstance(index, int):
5267
- datarn = index
5268
- if index and (datarn := self.top_index_row(datarn)) is None:
5269
- datarn = len(self.MT._row_index)
5270
- else:
5271
- datarn = len(self.MT._row_index)
5272
- i = 0
5225
+ datarn = self._get_id_insert_row(index=index, parent=parent)
5273
5226
  rns_to_add = {}
5274
5227
  for rn, r in enumerate(data, start=datarn):
5275
5228
  if iid_column is None:
5276
- while (iid := f"{num2alpha(i)}") in self.RI.tree:
5277
- i += 1
5229
+ iid = self.RI.new_iid()
5278
5230
  else:
5279
5231
  iid = r[iid_column]
5280
- self.RI.tree[iid] = Node(
5232
+ new_node = Node(
5281
5233
  r[text_column] if isinstance(text_column, int) else text_column if isinstance(text_column, str) else "",
5282
5234
  iid,
5283
- parent_node,
5235
+ parent,
5284
5236
  )
5285
- if parent_node:
5286
- if isinstance(index, int):
5287
- self.RI.tree[pid].children.insert(index, self.RI.tree[iid])
5288
- else:
5289
- self.RI.tree[pid].children.append(self.RI.tree[iid])
5290
5237
  exclude = set()
5291
5238
  if isinstance(iid_column, int) and not include_iid_column:
5292
5239
  exclude.add(iid_column)
5293
5240
  if isinstance(text_column, int) and not include_text_column:
5294
5241
  exclude.add(text_column)
5295
5242
  if exclude:
5296
- to_insert.append([self.RI.tree[iid]] + [e for c, e in enumerate(r) if c not in exclude])
5243
+ to_insert.append([new_node] + [e for c, e in enumerate(r) if c not in exclude])
5297
5244
  else:
5298
- to_insert.append([self.RI.tree[iid]] + r)
5245
+ to_insert.append([new_node] + r)
5299
5246
  rns_to_add[iid] = rn
5300
5247
  self.insert_rows(
5301
5248
  rows=to_insert,
@@ -5303,10 +5250,10 @@ class Sheet(tk.Frame):
5303
5250
  row_index=True,
5304
5251
  create_selections=create_selections,
5305
5252
  fill=False,
5253
+ undo=undo,
5254
+ tree=True,
5306
5255
  )
5307
- for iid, rn in rns_to_add.items():
5308
- self.RI.tree_rns[iid] = rn
5309
- if pid and (pid not in self.RI.tree_open_ids or not self.item_displayed(pid)):
5256
+ if parent and (parent not in self.RI.tree_open_ids or not self.item_displayed(parent)):
5310
5257
  self.hide_rows(range(datarn, datarn + len(to_insert)), deselect_all=False, data_indexes=True)
5311
5258
  return rns_to_add
5312
5259
 
@@ -5393,9 +5340,9 @@ class Sheet(tk.Frame):
5393
5340
  elif item == "":
5394
5341
  yield from map(attrgetter("iid"), self.RI.gen_top_nodes())
5395
5342
  else:
5396
- yield from (n.iid for n in self.RI.tree[item].children)
5343
+ yield from self.RI.tree[item].children
5397
5344
 
5398
- def get_nodes(self, item: None | str = None) -> Generator[str]:
5345
+ def get_iids(self, item: None | str = None) -> Generator[str]:
5399
5346
  if item is None:
5400
5347
  for n in self.RI.tree.values():
5401
5348
  if not n.parent:
@@ -5404,31 +5351,16 @@ class Sheet(tk.Frame):
5404
5351
  elif item == "":
5405
5352
  yield from (n.iid for n in self.RI.tree.values() if not n.parent)
5406
5353
  else:
5407
- yield from (n.iid for n in self.RI.tree[item].children)
5354
+ yield from self.RI.tree[item].children
5408
5355
 
5409
- def del_items(self, *items) -> Sheet:
5356
+ def del_items(self, *items, undo: bool = True) -> Sheet:
5410
5357
  """
5411
5358
  Also deletes all descendants of items
5412
5359
  """
5413
- rows_to_del = []
5414
- iids_to_del = []
5415
- for item in unpack(items):
5416
- if item not in self.RI.tree:
5417
- continue
5418
- rows_to_del.append(self.RI.tree_rns[item])
5419
- iids_to_del.append(item)
5420
- for did in self.RI.get_iid_descendants(item):
5421
- rows_to_del.append(self.RI.tree_rns[did])
5422
- iids_to_del.append(did)
5423
- for item in unpack(items):
5424
- self.RI.remove_node_from_parents_children(self.RI.tree[item])
5425
- self.del_rows(rows_to_del)
5426
- for iid in iids_to_del:
5427
- self.RI.tree_open_ids.discard(iid)
5428
- if self.RI.tree[iid].parent and len(self.RI.tree[iid].parent.children) == 1:
5429
- self.RI.tree_open_ids.discard(self.RI.tree[iid].parent.iid)
5430
- del self.RI.tree[iid]
5431
- return self.set_refresh_timer()
5360
+ rows = list(map(self.RI.tree_rns.get, filter(self.exists, unpack(items))))
5361
+ if rows:
5362
+ self.del_rows(rows, data_indexes=True, undo=undo)
5363
+ return self.set_refresh_timer(rows)
5432
5364
 
5433
5365
  def set_children(self, parent: str, *newchildren) -> Sheet:
5434
5366
  """
@@ -5444,123 +5376,42 @@ class Sheet(tk.Frame):
5444
5376
  except Exception:
5445
5377
  return None
5446
5378
 
5447
- def move(self, item: str, parent: str, index: int | None = None) -> Sheet:
5379
+ def move(
5380
+ self,
5381
+ item: str,
5382
+ parent: str,
5383
+ index: int | None = None,
5384
+ select: bool = True,
5385
+ undo: bool = True,
5386
+ emit_event: bool = False,
5387
+ ) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
5448
5388
  """
5449
5389
  Moves item to be under parent as child at index
5450
5390
  'parent' can be an empty str which will put the item at top level
5451
- Performance is not great
5452
5391
  """
5453
5392
  if item not in self.RI.tree:
5454
5393
  raise ValueError(f"Item '{item}' does not exist.")
5455
5394
  if parent and parent not in self.RI.tree:
5456
5395
  raise ValueError(f"Parent '{parent}' does not exist.")
5457
- mapping = {}
5458
- to_show = []
5459
- item_node = self.RI.tree[item]
5460
- item_r = self.RI.tree_rns[item]
5461
- item_descendants = tuple(self.RI.get_iid_descendants(item))
5462
- num_item_descendants = len(item_descendants)
5463
- if parent:
5464
- if self.RI.move_pid_causes_recursive_loop(item, parent):
5465
- raise ValueError(f"iid '{item}' causes a recursive loop with parent '{parent}'.")
5466
- parent_node = self.RI.tree[parent]
5467
- if parent_node.children:
5468
- if index is None or index >= len(parent_node.children):
5469
- index = len(parent_node.children)
5470
- new_r = self.RI.tree_rns[parent] + sum(1 for _ in self.RI.get_iid_descendants(parent))
5471
- # new parent has children
5472
- # index is on end
5473
- # item row is less than move to row
5474
- if item_r < new_r:
5475
- r_ctr = new_r - num_item_descendants
5476
-
5477
- # new parent has children
5478
- # index is on end
5479
- # item row is greater than move to row
5480
- else:
5481
- r_ctr = new_r + 1
5482
- else:
5483
- new_r = self.RI.tree_rns[parent_node.children[index].iid]
5484
- # new parent has children
5485
- # index is not end
5486
- # item row is less than move to row
5487
- if item_r < new_r:
5488
- if self.RI.items_parent(item) == parent:
5489
- r_ctr = (
5490
- new_r
5491
- + sum(1 for _ in self.RI.get_iid_descendants(parent_node.children[index].iid))
5492
- - num_item_descendants
5493
- )
5494
- else:
5495
- r_ctr = new_r - num_item_descendants - 1
5496
-
5497
- # new parent has children
5498
- # index is not end
5499
- # item row is greater than move to row
5500
- else:
5501
- r_ctr = new_r
5502
- else:
5503
- index = 0
5504
- new_r = self.RI.tree_rns[parent_node.iid]
5505
-
5506
- # new parent doesn't have children
5507
- # index always start
5508
- # item row is less than move to row
5509
- if item_r < new_r:
5510
- r_ctr = new_r - num_item_descendants
5511
-
5512
- # new parent doesn't have children
5513
- # index always start
5514
- # item row is greater than move to row
5515
- else:
5516
- r_ctr = new_r + 1
5517
- mapping[item_r] = r_ctr
5518
- if parent in self.RI.tree_open_ids and self.item_displayed(parent):
5519
- to_show.append(r_ctr)
5520
- r_ctr += 1
5521
- for did in item_descendants:
5522
- mapping[self.RI.tree_rns[did]] = r_ctr
5523
- if to_show and self.RI.ancestors_all_open(did, item_node.parent):
5524
- to_show.append(r_ctr)
5525
- r_ctr += 1
5526
- if parent == self.RI.items_parent(item):
5527
- pop_index = parent_node.children.index(item_node)
5528
- parent_node.children.insert(index, parent_node.children.pop(pop_index))
5529
- else:
5530
- self.RI.remove_node_from_parents_children(item_node)
5531
- item_node.parent = parent_node
5532
- parent_node.children.insert(index, item_node)
5533
- else:
5534
- if index is None or (new_r := self.top_index_row(index)) is None:
5535
- new_r = self.top_index_row(sum(1 for _ in self.RI.gen_top_nodes()) - 1)
5536
- if item_r < new_r:
5537
- r_ctr = (
5538
- new_r
5539
- + sum(1 for _ in self.RI.get_iid_descendants(self.rowitem(new_r, data_index=True)))
5540
- - num_item_descendants
5541
- )
5542
- else:
5543
- r_ctr = new_r
5544
- mapping[item_r] = r_ctr
5545
- to_show.append(r_ctr)
5546
- r_ctr += 1
5547
- for did in item_descendants:
5548
- mapping[self.RI.tree_rns[did]] = r_ctr
5549
- if to_show and self.RI.ancestors_all_open(did, item_node.parent):
5550
- to_show.append(r_ctr)
5551
- r_ctr += 1
5552
- self.RI.remove_node_from_parents_children(item_node)
5553
- self.RI.tree[item].parent = ""
5554
- self.mapping_move_rows(
5555
- data_new_idxs=mapping,
5556
- data_indexes=True,
5557
- create_selections=False,
5558
- redraw=False,
5396
+ if self.RI.move_pid_causes_recursive_loop(item, parent):
5397
+ raise ValueError(f"Item '{item}' causes recursive loop with parent '{parent}.")
5398
+ data_new_idxs, disp_new_idxs, event_data = self.MT.move_rows_adjust_options_dict(
5399
+ data_new_idxs={},
5400
+ data_old_idxs={},
5401
+ totalrows=None,
5402
+ disp_new_idxs={},
5403
+ move_data=True,
5404
+ move_heights=True,
5405
+ create_selections=select,
5406
+ event_data=None,
5407
+ node_change=(item, parent, index),
5559
5408
  )
5560
- if parent and (parent not in self.RI.tree_open_ids or not self.item_displayed(parent)):
5561
- self.hide_rows(set(mapping.values()), data_indexes=True)
5562
- self.show_rows(to_show)
5563
- return self.set_refresh_timer()
5409
+ if undo:
5410
+ self.MT.undo_stack.append(stored_event_dict(event_data))
5411
+ if emit_event:
5412
+ self.emit_event("<<SheetModified>>", event_data)
5413
+ self.set_refresh_timer()
5414
+ return data_new_idxs, disp_new_idxs, event_data
5564
5415
 
5565
5416
  reattach = move
5566
5417
 
@@ -5570,15 +5421,16 @@ class Sheet(tk.Frame):
5570
5421
  def parent(self, item: str) -> str:
5571
5422
  if item not in self.RI.tree:
5572
5423
  raise ValueError(f"Item '{item}' does not exist.")
5573
- return self.RI.tree[item].parent.iid if self.RI.tree[item].parent else self.RI.tree[item].parent
5424
+ return self.RI.tree[item].parent
5574
5425
 
5575
5426
  def index(self, item: str) -> int:
5576
5427
  if item not in self.RI.tree:
5577
5428
  raise ValueError(f"Item '{item}' does not exist.")
5578
- if not self.RI.tree[item].parent:
5429
+ elif self.RI.tree[item].parent:
5430
+ return self.RI.parent_node(item).children.index(item)
5431
+ else:
5579
5432
  find_node = self.RI.tree[item]
5580
5433
  return next(index for index, node in enumerate(self.RI.gen_top_nodes()) if node == find_node)
5581
- return self.RI.tree[item].parent.children.index(self.RI.tree[item])
5582
5434
 
5583
5435
  def item_displayed(self, item: str) -> bool:
5584
5436
  """
@@ -5587,7 +5439,7 @@ class Sheet(tk.Frame):
5587
5439
  """
5588
5440
  if item not in self.RI.tree:
5589
5441
  raise ValueError(f"Item '{item}' does not exist.")
5590
- return self.RI.tree_rns[item] in self.MT.displayed_rows
5442
+ return bisect_in(self.MT.displayed_rows, self.RI.tree_rns[item])
5591
5443
 
5592
5444
  def display_item(self, item: str, redraw: bool = False) -> Sheet:
5593
5445
  """
@@ -5996,7 +5848,7 @@ class Sheet(tk.Frame):
5996
5848
  def set_row_data(
5997
5849
  self,
5998
5850
  r: int,
5999
- values=tuple(),
5851
+ values: Sequence[object] = [],
6000
5852
  add_columns: bool = True,
6001
5853
  redraw: bool = True,
6002
5854
  keep_formatting: bool = True,
@@ -6014,7 +5866,7 @@ class Sheet(tk.Frame):
6014
5866
  if c > maxidx:
6015
5867
  self.MT.data[r].append(v)
6016
5868
  if self.MT.all_columns_displayed:
6017
- self.MT.insert_col_position("end")
5869
+ self.MT.insert_col_positions()
6018
5870
  else:
6019
5871
  self.set_cell_data(r=r, c=c, value=v, redraw=False, keep_formatting=keep_formatting)
6020
5872
  else:
@@ -6028,7 +5880,7 @@ class Sheet(tk.Frame):
6028
5880
  def set_column_data(
6029
5881
  self,
6030
5882
  c: int,
6031
- values=tuple(),
5883
+ values: Sequence[object] = [],
6032
5884
  add_rows: bool = True,
6033
5885
  redraw: bool = True,
6034
5886
  keep_formatting: bool = True,
@@ -6045,7 +5897,7 @@ class Sheet(tk.Frame):
6045
5897
  total_cols = self.MT.total_data_cols()
6046
5898
  self.MT.fix_data_len(rn, total_cols - 1)
6047
5899
  if self.MT.all_rows_displayed:
6048
- self.MT.insert_row_position("end", height=height)
5900
+ self.MT.insert_row_positions(heights=height)
6049
5901
  maxidx += 1
6050
5902
  if c >= len(self.MT.data[rn]):
6051
5903
  self.MT.fix_row_len(rn, c)
@@ -7043,7 +6895,6 @@ class Sheet(tk.Frame):
7043
6895
  self.RI.dropdown.window.values(values)
7044
6896
  if set_value is not None:
7045
6897
  self.MT.row_index(newindex=set_value, index=r_)
7046
- # here
7047
6898
  return self
7048
6899
 
7049
6900
  def get_dropdown_values(self, r: int = 0, c: int = 0) -> None | list:
@@ -7273,7 +7124,7 @@ class Dropdown(Sheet):
7273
7124
  modified_function: None | Callable = None,
7274
7125
  arrowkey_RIGHT: Callable | None = None,
7275
7126
  arrowkey_LEFT: Callable | None = None,
7276
- align: str = "w",
7127
+ align: str = "nw",
7277
7128
  # False for using r, c
7278
7129
  # "r" for r
7279
7130
  # "c" for c