tksheet 7.3.4__py3-none-any.whl → 7.4.1__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,9 +2,8 @@ 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
5
+ from collections import deque
6
6
  from collections.abc import Callable, Generator, Hashable, Iterator, Sequence
7
- from functools import partial
8
7
  from itertools import accumulate, chain, filterfalse, islice, product, repeat
9
8
  from operator import attrgetter
10
9
  from timeit import default_timer
@@ -14,6 +13,7 @@ from typing import Literal
14
13
  from .column_headers import ColumnHeaders
15
14
  from .constants import (
16
15
  USER_OS,
16
+ align_value_error,
17
17
  backwards_compatibility_keys,
18
18
  emitted_events,
19
19
  named_span_types,
@@ -24,9 +24,9 @@ from .functions import (
24
24
  add_highlight,
25
25
  add_to_options,
26
26
  alpha2idx,
27
+ bisect_in,
27
28
  consecutive_ranges,
28
29
  convert_align,
29
- data_to_displayed_idxs,
30
30
  del_from_options,
31
31
  del_named_span_options,
32
32
  del_named_span_options_nested,
@@ -66,9 +66,16 @@ from .other_classes import (
66
66
  )
67
67
  from .row_index import RowIndex
68
68
  from .sheet_options import new_sheet_options
69
- from .themes import theme_black, theme_dark, theme_dark_blue, theme_dark_green, theme_light_blue, theme_light_green
69
+ from .themes import (
70
+ theme_black,
71
+ theme_dark,
72
+ theme_dark_blue,
73
+ theme_dark_green,
74
+ theme_light_blue,
75
+ theme_light_green,
76
+ )
77
+ from .tksheet_types import AnyIter, CellPropertyKey, CreateSpanTypes
70
78
  from .top_left_rectangle import TopLeftRectangle
71
- from .types import AnyIter, CreateSpanTypes
72
79
 
73
80
 
74
81
  class Sheet(tk.Frame):
@@ -77,7 +84,7 @@ class Sheet(tk.Frame):
77
84
  parent: tk.Misc,
78
85
  name: str = "!sheet",
79
86
  show_table: bool = True,
80
- show_top_left: bool = True,
87
+ show_top_left: bool = False,
81
88
  show_row_index: bool = True,
82
89
  show_header: bool = True,
83
90
  show_x_scrollbar: bool = True,
@@ -110,10 +117,10 @@ class Sheet(tk.Frame):
110
117
  after_redraw_time_ms: int = 20,
111
118
  set_all_heights_and_widths: bool = False,
112
119
  zoom: int = 100,
113
- align: str = "w",
114
- header_align: str = "center",
120
+ align: str = "nw",
121
+ header_align: str = "n",
115
122
  row_index_align: str | None = None,
116
- index_align: str = "center",
123
+ index_align: str = "n",
117
124
  displayed_columns: list[int] = [],
118
125
  all_columns_displayed: bool = True,
119
126
  displayed_rows: list[int] = [],
@@ -172,9 +179,14 @@ class Sheet(tk.Frame):
172
179
  edit_cell_return: Literal["right", "down", ""] = "down",
173
180
  editor_del_key: Literal["forward", "backward", ""] = "forward",
174
181
  treeview: bool = False,
175
- treeview_indent: str | int = "5",
182
+ treeview_indent: str | int = "2",
176
183
  rounded_boxes: bool = True,
177
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",
178
190
  # colors
179
191
  outline_thickness: int = 0,
180
192
  theme: str = "light blue",
@@ -329,22 +341,22 @@ class Sheet(tk.Frame):
329
341
  self.config(width=350)
330
342
  self.grid_columnconfigure(1, weight=1)
331
343
  self.grid_rowconfigure(1, weight=1)
332
- self.RI = RowIndex(
344
+ self.RI: RowIndex = RowIndex(
333
345
  parent=self,
334
346
  row_index_align=(
335
347
  convert_align(row_index_align) if row_index_align is not None else convert_align(index_align)
336
348
  ),
337
349
  )
338
- self.CH = ColumnHeaders(
350
+ self.CH: ColumnHeaders = ColumnHeaders(
339
351
  parent=self,
340
352
  header_align=convert_align(header_align),
341
353
  )
342
- self.MT = MainTable(
354
+ self.MT: MainTable = MainTable(
343
355
  parent=self,
344
- show_index=show_row_index,
345
- show_header=show_header,
346
356
  column_headers_canvas=self.CH,
347
357
  row_index_canvas=self.RI,
358
+ show_index=show_row_index,
359
+ show_header=show_header,
348
360
  headers=headers,
349
361
  header=header,
350
362
  data_reference=data if data_reference is None else data_reference,
@@ -359,7 +371,7 @@ class Sheet(tk.Frame):
359
371
  displayed_rows=displayed_rows,
360
372
  all_rows_displayed=all_rows_displayed,
361
373
  )
362
- self.TL = TopLeftRectangle(
374
+ self.TL: TopLeftRectangle = TopLeftRectangle(
363
375
  parent=self,
364
376
  main_canvas=self.MT,
365
377
  row_index_canvas=self.RI,
@@ -423,32 +435,19 @@ class Sheet(tk.Frame):
423
435
  orient="horizontal",
424
436
  style=f"Sheet{self.unique_id}.Horizontal.TScrollbar",
425
437
  )
426
- if show_top_left:
427
- self.TL.grid(row=0, column=0)
428
- if show_table:
429
- self.MT.grid(row=1, column=1, sticky="nswe")
430
- self.MT["xscrollcommand"] = self.xscroll.set
431
- self.MT["yscrollcommand"] = self.yscroll.set
432
- if show_row_index:
433
- self.RI.grid(row=1, column=0, sticky="nswe")
434
- self.RI["yscrollcommand"] = self.yscroll.set
435
- if show_header:
436
- self.CH.grid(row=0, column=1, sticky="nswe")
437
- self.CH["xscrollcommand"] = self.xscroll.set
438
- if show_x_scrollbar:
439
- self.xscroll.grid(row=2, column=0, columnspan=2, sticky="nswe")
440
- self.xscroll_showing = True
441
- self.xscroll_disabled = False
442
- else:
443
- self.xscroll_showing = False
444
- self.xscroll_disabled = True
445
- if show_y_scrollbar:
446
- self.yscroll.grid(row=0, column=2, rowspan=3, sticky="nswe")
447
- self.yscroll_showing = True
448
- self.yscroll_disabled = False
449
- else:
450
- self.yscroll_showing = False
451
- 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")
452
451
  self.update_idletasks()
453
452
  self.MT.update_idletasks()
454
453
  self.RI.update_idletasks()
@@ -627,6 +626,11 @@ class Sheet(tk.Frame):
627
626
  - "rc_delete_column"
628
627
  - "rc_insert_row"
629
628
  - "rc_delete_row"
629
+ - "sort_cells"
630
+ - "sort_row"
631
+ - "sort_column" / "sort_col"
632
+ - "sort_rows"
633
+ - "sort_columns" / "sort_cols"
630
634
  - "ctrl_click_select" / "ctrl_select"
631
635
  - "copy"
632
636
  - "cut"
@@ -693,6 +697,12 @@ class Sheet(tk.Frame):
693
697
  ) -> Sheet:
694
698
  """
695
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"
696
706
  - "begin_copy", "begin_ctrl_c"
697
707
  - "ctrl_c", "end_copy", "end_ctrl_c", "copy"
698
708
  - "begin_cut", "begin_ctrl_x"
@@ -768,6 +778,9 @@ class Sheet(tk.Frame):
768
778
  "bind_all",
769
779
  "unbind_all",
770
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
771
784
  self.MT.extra_begin_ctrl_c_func = f
772
785
  self.MT.extra_begin_ctrl_x_func = f
773
786
  self.MT.extra_begin_ctrl_v_func = f
@@ -819,6 +832,9 @@ class Sheet(tk.Frame):
819
832
  "modified_events",
820
833
  "modified",
821
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
822
838
  self.MT.extra_end_ctrl_c_func = f
823
839
  self.MT.extra_end_ctrl_x_func = f
824
840
  self.MT.extra_end_ctrl_v_func = f
@@ -834,6 +850,24 @@ class Sheet(tk.Frame):
834
850
  self.CH.extra_end_edit_cell_func = f
835
851
  self.RI.extra_end_edit_cell_func = f
836
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
+
837
871
  if b in (
838
872
  "begin_copy",
839
873
  "begin_ctrl_c",
@@ -1293,7 +1327,7 @@ class Sheet(tk.Frame):
1293
1327
  transposed: bool = False,
1294
1328
  ndim: int = 0,
1295
1329
  convert: object = None,
1296
- undo: bool = False,
1330
+ undo: bool = True,
1297
1331
  emit_event: bool = False,
1298
1332
  widget: object = None,
1299
1333
  expand: None | str = None,
@@ -1765,8 +1799,6 @@ class Sheet(tk.Frame):
1765
1799
 
1766
1800
  """
1767
1801
  span = self.span_from_key(*key)
1768
- if data is None:
1769
- data = []
1770
1802
  startr, startc = span_froms(span)
1771
1803
  table, index, header = span.table, span.index, span.header
1772
1804
  fmt_kw = span.kwargs if span.type_ == "format" and span.kwargs else None
@@ -2072,7 +2104,7 @@ class Sheet(tk.Frame):
2072
2104
  height: int | None = None,
2073
2105
  row_index: bool = False,
2074
2106
  fill: bool = True,
2075
- undo: bool = False,
2107
+ undo: bool = True,
2076
2108
  emit_event: bool = False,
2077
2109
  redraw: bool = True,
2078
2110
  ) -> EventDataDict:
@@ -2094,7 +2126,7 @@ class Sheet(tk.Frame):
2094
2126
  width: int | None = None,
2095
2127
  header: bool = False,
2096
2128
  fill: bool = True,
2097
- undo: bool = False,
2129
+ undo: bool = True,
2098
2130
  emit_event: bool = False,
2099
2131
  redraw: bool = True,
2100
2132
  ) -> EventDataDict:
@@ -2116,11 +2148,12 @@ class Sheet(tk.Frame):
2116
2148
  heights: list[int] | tuple[int] | None = None,
2117
2149
  row_index: bool = False,
2118
2150
  fill: bool = True,
2119
- undo: bool = False,
2151
+ undo: bool = True,
2120
2152
  emit_event: bool = False,
2121
2153
  create_selections: bool = True,
2122
2154
  add_column_widths: bool = True,
2123
2155
  push_ops: bool = True,
2156
+ tree: bool = True,
2124
2157
  redraw: bool = True,
2125
2158
  ) -> EventDataDict:
2126
2159
  total_cols = None
@@ -2180,14 +2213,10 @@ class Sheet(tk.Frame):
2180
2213
  row_index=row_index,
2181
2214
  ),
2182
2215
  add_col_positions=add_column_widths,
2183
- event_data=event_dict(
2184
- name="add_rows",
2185
- sheet=self.name,
2186
- boxes=self.MT.get_boxes(),
2187
- selected=self.MT.selected,
2188
- ),
2216
+ event_data=self.MT.new_event_dict("add_rows", state=True),
2189
2217
  create_selections=create_selections,
2190
2218
  push_ops=push_ops,
2219
+ tree=tree,
2191
2220
  )
2192
2221
  if undo:
2193
2222
  self.MT.undo_stack.append(stored_event_dict(event_data))
@@ -2203,7 +2232,7 @@ class Sheet(tk.Frame):
2203
2232
  widths: list[int] | tuple[int] | None = None,
2204
2233
  headers: bool = False,
2205
2234
  fill: bool = True,
2206
- undo: bool = False,
2235
+ undo: bool = True,
2207
2236
  emit_event: bool = False,
2208
2237
  create_selections: bool = True,
2209
2238
  add_row_heights: bool = True,
@@ -2275,12 +2304,7 @@ class Sheet(tk.Frame):
2275
2304
  headers=headers,
2276
2305
  ),
2277
2306
  add_row_positions=add_row_heights,
2278
- event_data=event_dict(
2279
- name="add_columns",
2280
- sheet=self.name,
2281
- boxes=self.MT.get_boxes(),
2282
- selected=self.MT.selected,
2283
- ),
2307
+ event_data=self.MT.new_event_dict("add_columns", state=True),
2284
2308
  create_selections=create_selections,
2285
2309
  push_ops=push_ops,
2286
2310
  )
@@ -2291,124 +2315,48 @@ class Sheet(tk.Frame):
2291
2315
  self.set_refresh_timer(redraw)
2292
2316
  return event_data
2293
2317
 
2294
- def del_row(
2295
- self,
2296
- idx: int = 0,
2297
- data_indexes: bool = True,
2298
- undo: bool = False,
2299
- emit_event: bool = False,
2300
- redraw: bool = True,
2301
- ) -> EventDataDict:
2302
- return self.del_rows(
2303
- rows=idx,
2304
- data_indexes=data_indexes,
2305
- undo=undo,
2306
- emit_event=emit_event,
2307
- redraw=redraw,
2308
- )
2309
-
2310
- delete_row = del_row
2311
-
2312
- def del_column(
2318
+ def del_rows(
2313
2319
  self,
2314
- idx: int = 0,
2320
+ rows: int | AnyIter[int],
2315
2321
  data_indexes: bool = True,
2316
- undo: bool = False,
2322
+ undo: bool = True,
2317
2323
  emit_event: bool = False,
2318
2324
  redraw: bool = True,
2319
2325
  ) -> EventDataDict:
2320
- return self.del_columns(
2321
- columns=idx,
2326
+ event_data = self.MT.delete_rows(
2327
+ rows=[rows] if isinstance(rows, int) else sorted(set(rows)),
2322
2328
  data_indexes=data_indexes,
2323
2329
  undo=undo,
2324
2330
  emit_event=emit_event,
2325
- redraw=redraw,
2326
- )
2327
-
2328
- delete_column = del_column
2329
-
2330
- def del_rows(
2331
- self,
2332
- rows: int | AnyIter[int],
2333
- data_indexes: bool = True,
2334
- undo: bool = False,
2335
- emit_event: bool = False,
2336
- redraw: bool = True,
2337
- ) -> EventDataDict:
2338
- self.MT.deselect("all", redraw=False)
2339
- rows = [rows] if isinstance(rows, int) else sorted(rows)
2340
- event_data = event_dict(
2341
- name="delete_rows",
2342
- sheet=self.name,
2343
- widget=self,
2344
- boxes=self.MT.get_boxes(),
2345
- selected=self.MT.selected,
2331
+ ext=True,
2346
2332
  )
2347
- if not data_indexes:
2348
- event_data = self.MT.delete_rows_displayed(rows, event_data)
2349
- event_data = self.MT.delete_rows_data(
2350
- rows if self.MT.all_rows_displayed else [self.MT.displayed_rows[r] for r in rows],
2351
- event_data,
2352
- )
2353
- else:
2354
- if self.MT.all_rows_displayed:
2355
- rows = rows
2356
- else:
2357
- rows = data_to_displayed_idxs(rows, self.MT.displayed_rows)
2358
- event_data = self.MT.delete_rows_data(rows, event_data)
2359
- event_data = self.MT.delete_rows_displayed(
2360
- rows,
2361
- event_data,
2362
- )
2363
- if undo:
2364
- self.MT.undo_stack.append(stored_event_dict(event_data))
2365
- if emit_event:
2366
- self.emit_event("<<SheetModified>>", event_data)
2367
2333
  self.set_refresh_timer(redraw)
2368
2334
  return event_data
2369
2335
 
2336
+ del_row = del_rows
2337
+ delete_row = del_rows
2370
2338
  delete_rows = del_rows
2371
2339
 
2372
2340
  def del_columns(
2373
2341
  self,
2374
2342
  columns: int | AnyIter[int],
2375
2343
  data_indexes: bool = True,
2376
- undo: bool = False,
2344
+ undo: bool = True,
2377
2345
  emit_event: bool = False,
2378
2346
  redraw: bool = True,
2379
2347
  ) -> EventDataDict:
2380
- self.MT.deselect("all", redraw=False)
2381
- columns = [columns] if isinstance(columns, int) else sorted(columns)
2382
- event_data = event_dict(
2383
- name="delete_columns",
2384
- sheet=self.name,
2385
- widget=self,
2386
- boxes=self.MT.get_boxes(),
2387
- 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,
2388
2354
  )
2389
- if not data_indexes:
2390
- event_data = self.MT.delete_columns_displayed(columns, event_data)
2391
- event_data = self.MT.delete_columns_data(
2392
- columns if self.MT.all_columns_displayed else [self.MT.displayed_columns[c] for c in columns],
2393
- event_data,
2394
- )
2395
- else:
2396
- if self.MT.all_columns_displayed:
2397
- columns = columns
2398
- else:
2399
- columns = data_to_displayed_idxs(columns, self.MT.displayed_columns)
2400
- event_data = self.MT.delete_columns_data(columns, event_data)
2401
- event_data = self.MT.delete_columns_displayed(
2402
- columns,
2403
- event_data,
2404
- )
2405
- if undo:
2406
- self.MT.undo_stack.append(stored_event_dict(event_data))
2407
- if emit_event:
2408
- self.emit_event("<<SheetModified>>", event_data)
2409
2355
  self.set_refresh_timer(redraw)
2410
2356
  return event_data
2411
2357
 
2358
+ del_column = del_columns
2359
+ delete_column = del_columns
2412
2360
  delete_columns = del_columns
2413
2361
 
2414
2362
  def sheet_data_dimensions(
@@ -2470,11 +2418,11 @@ class Sheet(tk.Frame):
2470
2418
  self.MT.data_dimensions(total_columns=number)
2471
2419
  return self
2472
2420
 
2473
- def move_row(self, row: int, moveto: int) -> tuple[dict, dict, dict]:
2474
- 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])
2475
2423
 
2476
- def move_column(self, column: int, moveto: int) -> tuple[dict, dict, dict]:
2477
- 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])
2478
2426
 
2479
2427
  def move_rows(
2480
2428
  self,
@@ -2483,11 +2431,16 @@ class Sheet(tk.Frame):
2483
2431
  move_data: bool = True,
2484
2432
  data_indexes: bool = False,
2485
2433
  create_selections: bool = True,
2486
- undo: bool = False,
2434
+ undo: bool = True,
2487
2435
  emit_event: bool = False,
2488
2436
  move_heights: bool = True,
2437
+ event_data: EventDataDict | None = None,
2489
2438
  redraw: bool = True,
2490
- ) -> 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
2491
2444
  data_idxs, disp_idxs, event_data = self.MT.move_rows_adjust_options_dict(
2492
2445
  *self.MT.get_args_for_move_rows(
2493
2446
  move_to=move_to,
@@ -2497,7 +2450,7 @@ class Sheet(tk.Frame):
2497
2450
  move_data=move_data,
2498
2451
  move_heights=move_heights,
2499
2452
  create_selections=create_selections,
2500
- data_indexes=data_indexes,
2453
+ event_data=event_data,
2501
2454
  )
2502
2455
  if undo:
2503
2456
  self.MT.undo_stack.append(stored_event_dict(event_data))
@@ -2513,11 +2466,16 @@ class Sheet(tk.Frame):
2513
2466
  move_data: bool = True,
2514
2467
  data_indexes: bool = False,
2515
2468
  create_selections: bool = True,
2516
- undo: bool = False,
2469
+ undo: bool = True,
2517
2470
  emit_event: bool = False,
2518
2471
  move_widths: bool = True,
2472
+ event_data: EventDataDict | None = None,
2519
2473
  redraw: bool = True,
2520
- ) -> 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
2521
2479
  data_idxs, disp_idxs, event_data = self.MT.move_columns_adjust_options_dict(
2522
2480
  *self.MT.get_args_for_move_columns(
2523
2481
  move_to=move_to,
@@ -2527,7 +2485,7 @@ class Sheet(tk.Frame):
2527
2485
  move_data=move_data,
2528
2486
  move_widths=move_widths,
2529
2487
  create_selections=create_selections,
2530
- data_indexes=data_indexes,
2488
+ event_data=event_data,
2531
2489
  )
2532
2490
  if undo:
2533
2491
  self.MT.undo_stack.append(stored_event_dict(event_data))
@@ -2541,9 +2499,8 @@ class Sheet(tk.Frame):
2541
2499
  data_new_idxs: dict[int, int],
2542
2500
  disp_new_idxs: None | dict[int, int] = None,
2543
2501
  move_data: bool = True,
2544
- data_indexes: bool = False,
2545
2502
  create_selections: bool = True,
2546
- undo: bool = False,
2503
+ undo: bool = True,
2547
2504
  emit_event: bool = False,
2548
2505
  redraw: bool = True,
2549
2506
  ) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
@@ -2554,7 +2511,6 @@ class Sheet(tk.Frame):
2554
2511
  disp_new_idxs=disp_new_idxs,
2555
2512
  move_data=move_data,
2556
2513
  create_selections=create_selections,
2557
- data_indexes=data_indexes,
2558
2514
  )
2559
2515
  if undo:
2560
2516
  self.MT.undo_stack.append(stored_event_dict(event_data))
@@ -2568,10 +2524,10 @@ class Sheet(tk.Frame):
2568
2524
  data_new_idxs: dict[int, int],
2569
2525
  disp_new_idxs: None | dict[int, int] = None,
2570
2526
  move_data: bool = True,
2571
- data_indexes: bool = False,
2572
2527
  create_selections: bool = True,
2573
- undo: bool = False,
2528
+ undo: bool = True,
2574
2529
  emit_event: bool = False,
2530
+ event_data: EventDataDict | None = None,
2575
2531
  redraw: bool = True,
2576
2532
  ) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
2577
2533
  data_idxs, disp_idxs, event_data = self.MT.move_rows_adjust_options_dict(
@@ -2581,7 +2537,7 @@ class Sheet(tk.Frame):
2581
2537
  disp_new_idxs=disp_new_idxs,
2582
2538
  move_data=move_data,
2583
2539
  create_selections=create_selections,
2584
- data_indexes=data_indexes,
2540
+ event_data=event_data,
2585
2541
  )
2586
2542
  if undo:
2587
2543
  self.MT.undo_stack.append(stored_event_dict(event_data))
@@ -2622,6 +2578,102 @@ class Sheet(tk.Frame):
2622
2578
  data_idxs,
2623
2579
  )
2624
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
+
2625
2677
  # Highlighting Cells
2626
2678
 
2627
2679
  def highlight(
@@ -3060,16 +3112,17 @@ class Sheet(tk.Frame):
3060
3112
 
3061
3113
  # Text Font and Alignment
3062
3114
 
3063
- def font(
3064
- self,
3065
- newfont: tuple[str, int, str] | None = None,
3066
- reset_row_positions: bool = True,
3067
- ) -> tuple[str, int, str]:
3068
- 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
3069
3119
 
3070
3120
  def header_font(self, newfont: tuple[str, int, str] | None = None) -> tuple[str, int, str]:
3071
3121
  return self.MT.set_header_font(newfont)
3072
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
+
3073
3126
  def table_align(
3074
3127
  self,
3075
3128
  align: str | None = None,
@@ -3080,7 +3133,7 @@ class Sheet(tk.Frame):
3080
3133
  elif convert_align(align):
3081
3134
  self.MT.align = convert_align(align)
3082
3135
  else:
3083
- raise ValueError("Align must be one of the following values: c, center, w, west, e, east")
3136
+ raise ValueError()
3084
3137
  return self.set_refresh_timer(redraw)
3085
3138
 
3086
3139
  def header_align(
@@ -3093,7 +3146,7 @@ class Sheet(tk.Frame):
3093
3146
  elif convert_align(align):
3094
3147
  self.CH.align = convert_align(align)
3095
3148
  else:
3096
- raise ValueError("Align must be one of the following values: c, center, w, west, e, east")
3149
+ raise ValueError(align_value_error)
3097
3150
  return self.set_refresh_timer(redraw)
3098
3151
 
3099
3152
  def row_index_align(
@@ -3106,7 +3159,7 @@ class Sheet(tk.Frame):
3106
3159
  elif convert_align(align):
3107
3160
  self.RI.align = convert_align(align)
3108
3161
  else:
3109
- raise ValueError("Align must be one of the following values: c, center, w, west, e, east")
3162
+ raise ValueError(align_value_error)
3110
3163
  return self.set_refresh_timer(redraw)
3111
3164
 
3112
3165
  index_align = row_index_align
@@ -3569,18 +3622,41 @@ class Sheet(tk.Frame):
3569
3622
  only_set_if_too_small: bool = False,
3570
3623
  redraw: bool = True,
3571
3624
  ) -> Sheet | int:
3572
- if column == "all" and width == "default":
3573
- self.MT.reset_col_positions()
3574
- elif column == "displayed" and width == "text":
3575
- for c in range(*self.MT.visible_text_columns):
3576
- self.CH.set_col_width(c)
3577
- elif width == "text" and isinstance(column, int):
3578
- self.CH.set_col_width(col=column, width=None, only_if_too_small=only_set_if_too_small)
3579
- elif isinstance(width, int) and isinstance(column, int):
3580
- 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
+
3581
3646
  elif isinstance(column, int):
3582
- return int(self.MT.col_positions[column + 1] - self.MT.col_positions[column])
3583
- 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)
3584
3660
 
3585
3661
  def row_height(
3586
3662
  self,
@@ -3589,18 +3665,39 @@ class Sheet(tk.Frame):
3589
3665
  only_set_if_too_small: bool = False,
3590
3666
  redraw: bool = True,
3591
3667
  ) -> Sheet | int:
3592
- if row == "all" and height == "default":
3593
- self.MT.reset_row_positions()
3594
- elif row == "displayed" and height == "text":
3595
- for r in range(*self.MT.visible_text_rows):
3596
- self.RI.set_row_height(r)
3597
- elif height == "text" and isinstance(row, int):
3598
- self.RI.set_row_height(row=row, height=None, only_if_too_small=only_set_if_too_small)
3599
- elif isinstance(height, int) and isinstance(row, int):
3600
- 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
+
3601
3688
  elif isinstance(row, int):
3602
- return int(self.MT.row_positions[row + 1] - self.MT.row_positions[row])
3603
- 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)
3604
3701
 
3605
3702
  def get_column_widths(self, canvas_positions: bool = False) -> list[float]:
3606
3703
  if canvas_positions:
@@ -3734,7 +3831,7 @@ class Sheet(tk.Frame):
3734
3831
  deselect_all: bool = False,
3735
3832
  redraw: bool = False,
3736
3833
  ) -> Sheet:
3737
- 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)
3738
3835
  return self.set_refresh_timer(redraw)
3739
3836
 
3740
3837
  def insert_row_position(
@@ -3744,7 +3841,7 @@ class Sheet(tk.Frame):
3744
3841
  deselect_all: bool = False,
3745
3842
  redraw: bool = False,
3746
3843
  ) -> Sheet:
3747
- 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)
3748
3845
  return self.set_refresh_timer(redraw)
3749
3846
 
3750
3847
  def insert_column_positions(
@@ -4076,11 +4173,12 @@ class Sheet(tk.Frame):
4076
4173
  if isinstance(columns, int):
4077
4174
  columns = [columns]
4078
4175
  cws = self.MT.get_column_widths()
4176
+ default_col_w = self.ops.default_column_width
4079
4177
  for column in columns:
4080
4178
  idx = bisect_left(self.MT.displayed_columns, column)
4081
4179
  if len(self.MT.displayed_columns) == idx or self.MT.displayed_columns[idx] != column:
4082
4180
  self.MT.displayed_columns.insert(idx, column)
4083
- 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))
4084
4182
  self.MT.set_col_positions(cws)
4085
4183
  if deselect_all:
4086
4184
  self.MT.deselect(redraw=False)
@@ -4209,12 +4307,13 @@ class Sheet(tk.Frame):
4209
4307
  return
4210
4308
  if isinstance(rows, int):
4211
4309
  rows = [rows]
4310
+ default_row_h = self.MT.get_default_row_height()
4212
4311
  rhs = self.MT.get_row_heights()
4213
4312
  for row in rows:
4214
4313
  idx = bisect_left(self.MT.displayed_rows, row)
4215
4314
  if len(self.MT.displayed_rows) == idx or self.MT.displayed_rows[idx] != row:
4216
4315
  self.MT.displayed_rows.insert(idx, row)
4217
- 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))
4218
4317
  self.MT.set_row_positions(rhs)
4219
4318
  if deselect_all:
4220
4319
  self.MT.deselect(redraw=False)
@@ -4249,52 +4348,6 @@ class Sheet(tk.Frame):
4249
4348
 
4250
4349
  # Hiding Sheet Elements
4251
4350
 
4252
- def hide(
4253
- self,
4254
- canvas: Literal[
4255
- "all",
4256
- "row_index",
4257
- "header",
4258
- "top_left",
4259
- "x_scrollbar",
4260
- "y_scrollbar",
4261
- ] = "all",
4262
- ) -> Sheet:
4263
- if canvas.lower() == "all":
4264
- self.TL.grid_forget()
4265
- self.RI.grid_forget()
4266
- self.RI["yscrollcommand"] = 0
4267
- self.MT.show_index = False
4268
- self.CH.grid_forget()
4269
- self.CH["xscrollcommand"] = 0
4270
- self.MT.show_header = False
4271
- self.MT.grid_forget()
4272
- self.yscroll.grid_forget()
4273
- self.xscroll.grid_forget()
4274
- self.xscroll_showing = False
4275
- self.yscroll_showing = False
4276
- self.xscroll_disabled = True
4277
- self.yscroll_disabled = True
4278
- elif canvas.lower() == "row_index":
4279
- self.RI.grid_forget()
4280
- self.RI["yscrollcommand"] = 0
4281
- self.MT.show_index = False
4282
- elif canvas.lower() == "header":
4283
- self.CH.grid_forget()
4284
- self.CH["xscrollcommand"] = 0
4285
- self.MT.show_header = False
4286
- elif canvas.lower() == "top_left":
4287
- self.TL.grid_forget()
4288
- elif canvas.lower() == "x_scrollbar":
4289
- self.xscroll.grid_forget()
4290
- self.xscroll_showing = False
4291
- self.xscroll_disabled = True
4292
- elif canvas.lower() == "y_scrollbar":
4293
- self.yscroll.grid_forget()
4294
- self.yscroll_showing = False
4295
- self.yscroll_disabled = True
4296
- return self
4297
-
4298
4351
  def show(
4299
4352
  self,
4300
4353
  canvas: Literal[
@@ -4305,45 +4358,75 @@ class Sheet(tk.Frame):
4305
4358
  "top_left",
4306
4359
  "x_scrollbar",
4307
4360
  "y_scrollbar",
4361
+ "table",
4308
4362
  ] = "all",
4309
4363
  ) -> Sheet:
4310
- if canvas == "all":
4311
- self.hide()
4312
- self.TL.grid(row=0, column=0)
4313
- self.RI.grid(row=1, column=0, sticky="nswe")
4314
- self.CH.grid(row=0, column=1, sticky="nswe")
4364
+ if canvas in ("all", "table"):
4315
4365
  self.MT.grid(row=1, column=1, sticky="nswe")
4316
- self.yscroll.grid(row=0, column=2, rowspan=3, sticky="nswe")
4317
- self.xscroll.grid(row=2, column=0, columnspan=2, sticky="nswe")
4318
- self.MT["xscrollcommand"] = self.xscroll.set
4319
- self.CH["xscrollcommand"] = self.xscroll.set
4320
- self.MT["yscrollcommand"] = self.yscroll.set
4321
- self.RI["yscrollcommand"] = self.yscroll.set
4322
- self.xscroll_showing = True
4323
- self.yscroll_showing = True
4324
- self.xscroll_disabled = False
4325
- self.yscroll_disabled = False
4326
- elif canvas in ("row_index", "index"):
4366
+ if canvas in ("all", "row_index", "index"):
4327
4367
  self.RI.grid(row=1, column=0, sticky="nswe")
4328
4368
  self.MT["yscrollcommand"] = self.yscroll.set
4329
4369
  self.RI["yscrollcommand"] = self.yscroll.set
4330
4370
  self.MT.show_index = True
4331
- elif canvas == "header":
4371
+ if self.MT.show_header:
4372
+ self.show("top_left")
4373
+ if canvas in ("all", "header"):
4332
4374
  self.CH.grid(row=0, column=1, sticky="nswe")
4333
4375
  self.MT["xscrollcommand"] = self.xscroll.set
4334
4376
  self.CH["xscrollcommand"] = self.xscroll.set
4335
4377
  self.MT.show_header = True
4336
- elif canvas == "top_left":
4378
+ if self.MT.show_index:
4379
+ self.show("top_left")
4380
+ if canvas in ("all", "top_left"):
4337
4381
  self.TL.grid(row=0, column=0)
4338
- elif canvas == "x_scrollbar":
4382
+ if canvas in ("all", "x_scrollbar"):
4339
4383
  self.xscroll.grid(row=2, column=0, columnspan=2, sticky="nswe")
4340
4384
  self.xscroll_showing = True
4341
4385
  self.xscroll_disabled = False
4342
- elif canvas == "y_scrollbar":
4386
+ if canvas in ("all", "y_scrollbar"):
4343
4387
  self.yscroll.grid(row=0, column=2, rowspan=3, sticky="nswe")
4344
4388
  self.yscroll_showing = True
4345
4389
  self.yscroll_disabled = False
4346
- 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()
4347
4430
  return self
4348
4431
 
4349
4432
  # Sheet Height and Width
@@ -4479,6 +4562,8 @@ class Sheet(tk.Frame):
4479
4562
  if "expand_sheet_if_paste_too_big" in kwargs:
4480
4563
  self.ops.paste_can_expand_x = kwargs["expand_sheet_if_paste_too_big"]
4481
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")
4482
4567
  if "font" in kwargs:
4483
4568
  self.MT.set_table_font(kwargs["font"])
4484
4569
  elif "table_font" in kwargs:
@@ -4514,7 +4599,7 @@ class Sheet(tk.Frame):
4514
4599
  self.set_scrollbar_options()
4515
4600
  self.MT.create_rc_menus()
4516
4601
  if "treeview" in kwargs:
4517
- self.index_align("w", redraw=False)
4602
+ self.index_align("nw", redraw=False)
4518
4603
  return self.set_refresh_timer(redraw)
4519
4604
 
4520
4605
  def set_scrollbar_options(self) -> Sheet:
@@ -4571,15 +4656,7 @@ class Sheet(tk.Frame):
4571
4656
  self,
4572
4657
  row: int,
4573
4658
  column: int | str,
4574
- key: None
4575
- | Literal[
4576
- "format",
4577
- "highlight",
4578
- "dropdown",
4579
- "checkbox",
4580
- "readonly",
4581
- "align",
4582
- ] = None,
4659
+ key: None | CellPropertyKey = None,
4583
4660
  cellops: bool = True,
4584
4661
  rowops: bool = True,
4585
4662
  columnops: bool = True,
@@ -4613,15 +4690,7 @@ class Sheet(tk.Frame):
4613
4690
  def index_props(
4614
4691
  self,
4615
4692
  row: int,
4616
- key: None
4617
- | Literal[
4618
- "format",
4619
- "highlight",
4620
- "dropdown",
4621
- "checkbox",
4622
- "readonly",
4623
- "align",
4624
- ] = None,
4693
+ key: None | CellPropertyKey = None,
4625
4694
  ) -> dict:
4626
4695
  """
4627
4696
  Retrieve options (properties - props)
@@ -4637,15 +4706,7 @@ class Sheet(tk.Frame):
4637
4706
  def header_props(
4638
4707
  self,
4639
4708
  column: int | str,
4640
- key: None
4641
- | Literal[
4642
- "format",
4643
- "highlight",
4644
- "dropdown",
4645
- "checkbox",
4646
- "readonly",
4647
- "align",
4648
- ] = None,
4709
+ key: None | CellPropertyKey = None,
4649
4710
  ) -> dict:
4650
4711
  """
4651
4712
  Retrieve options (properties - props)
@@ -4662,7 +4723,7 @@ class Sheet(tk.Frame):
4662
4723
 
4663
4724
  def get_cell_options(
4664
4725
  self,
4665
- key: None | str = None,
4726
+ key: None | CellPropertyKey = None,
4666
4727
  canvas: Literal["table", "row_index", "index", "header"] = "table",
4667
4728
  ) -> dict:
4668
4729
  if canvas == "table":
@@ -4675,22 +4736,22 @@ class Sheet(tk.Frame):
4675
4736
  return target
4676
4737
  return {k: v[key] for k, v in target.items() if key in v}
4677
4738
 
4678
- def get_row_options(self, key: None | str = None) -> dict:
4739
+ def get_row_options(self, key: None | CellPropertyKey = None) -> dict:
4679
4740
  if key is None:
4680
4741
  return self.MT.row_options
4681
4742
  return {k: v[key] for k, v in self.MT.row_options.items() if key in v}
4682
4743
 
4683
- def get_column_options(self, key: None | str = None) -> dict:
4744
+ def get_column_options(self, key: None | CellPropertyKey = None) -> dict:
4684
4745
  if key is None:
4685
4746
  return self.MT.col_options
4686
4747
  return {k: v[key] for k, v in self.MT.col_options.items() if key in v}
4687
4748
 
4688
- def get_index_options(self, key: None | str = None) -> dict:
4749
+ def get_index_options(self, key: None | CellPropertyKey = None) -> dict:
4689
4750
  if key is None:
4690
4751
  return self.RI.cell_options
4691
4752
  return {k: v[key] for k, v in self.RI.cell_options.items() if key in v}
4692
4753
 
4693
- def get_header_options(self, key: None | str = None) -> dict:
4754
+ def get_header_options(self, key: None | CellPropertyKey = None) -> dict:
4694
4755
  if key is None:
4695
4756
  return self.CH.cell_options
4696
4757
  return {k: v[key] for k, v in self.CH.cell_options.items() if key in v}
@@ -4958,94 +5019,21 @@ class Sheet(tk.Frame):
4958
5019
  include_parent_column: bool = True,
4959
5020
  include_text_column: bool = True,
4960
5021
  ) -> Sheet:
4961
- self.reset(cell_options=False, column_widths=False, header=False, redraw=False)
4962
- if text_column is None:
4963
- text_column = iid_column
4964
- tally_of_ids = defaultdict(lambda: -1)
4965
- if not isinstance(ncols, int):
4966
- ncols = max(map(len, data), default=0)
4967
- for rn, row in enumerate(data):
4968
- if safety and ncols > (lnr := len(row)):
4969
- row += self.MT.get_empty_row_seq(rn, end=ncols, start=lnr)
4970
- if lower:
4971
- iid = row[iid_column].lower()
4972
- pid = row[parent_column].lower()
4973
- else:
4974
- iid = row[iid_column]
4975
- pid = row[parent_column]
4976
- if safety:
4977
- if not iid:
4978
- continue
4979
- tally_of_ids[iid] += 1
4980
- if tally_of_ids[iid]:
4981
- x = 1
4982
- while iid in tally_of_ids:
4983
- new = f"{row[iid_column]}_DUPLICATED_{x}"
4984
- iid = new.lower() if lower else new
4985
- x += 1
4986
- tally_of_ids[iid] += 1
4987
- row[iid_column] = new
4988
- if iid in self.RI.tree:
4989
- self.RI.tree[iid].text = row[text_column] if isinstance(text_column, int) else text_column[rn]
4990
- else:
4991
- self.RI.tree[iid] = Node(row[text_column] if isinstance(text_column, int) else text_column[rn], iid, "")
4992
- if safety and (iid == pid or self.RI.build_pid_causes_recursive_loop(iid, pid)):
4993
- row[parent_column] = ""
4994
- pid = ""
4995
- if pid:
4996
- if pid not in self.RI.tree:
4997
- self.RI.tree[pid] = Node(row[text_column] if isinstance(text_column, int) else text_column[rn], pid)
4998
- self.RI.tree[iid].parent = self.RI.tree[pid]
4999
- self.RI.tree[pid].children.append(self.RI.tree[iid])
5000
- else:
5001
- self.RI.tree[iid].parent = ""
5002
- self.RI.tree_rns[iid] = rn
5003
- if safety:
5004
- for n in self.RI.tree.values():
5005
- if n.parent is None:
5006
- n.parent = ""
5007
- newrow = self.MT.get_empty_row_seq(len(data), ncols)
5008
- newrow[iid_column] = n.iid
5009
- self.RI.tree_rns[n.iid] = len(data)
5010
- data.append(newrow)
5011
- insert_rows = partial(
5012
- self.insert_rows,
5013
- idx=0,
5014
- heights={} if row_heights is False else row_heights,
5015
- row_index=True,
5016
- create_selections=False,
5017
- 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,
5018
5027
  push_ops=push_ops,
5019
- 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,
5020
5036
  )
5021
- exclude = set()
5022
- if not include_iid_column:
5023
- exclude.add(iid_column)
5024
- if not include_parent_column:
5025
- exclude.add(parent_column)
5026
- if isinstance(text_column, int) and not include_text_column:
5027
- exclude.add(text_column)
5028
- if exclude:
5029
- insert_rows(
5030
- rows=[
5031
- [self.RI.tree[iid]] + [e for i, e in enumerate(data[self.RI.tree_rns[iid]]) if i not in exclude]
5032
- for iid in self.get_nodes()
5033
- ]
5034
- )
5035
- else:
5036
- insert_rows(rows=[[self.RI.tree[iid]] + data[self.RI.tree_rns[iid]] for iid in self.get_nodes()])
5037
- self.MT.all_rows_displayed = False
5038
- self.MT.displayed_rows = list(range(len(self.MT._row_index)))
5039
- self.RI.tree_rns = {n.iid: i for i, n in enumerate(self.MT._row_index)}
5040
- if open_ids:
5041
- self.tree_set_open(open_ids=open_ids)
5042
- else:
5043
- self.hide_rows(
5044
- {self.RI.tree_rns[iid] for iid in self.get_children() if self.RI.tree[iid].parent},
5045
- deselect_all=False,
5046
- data_indexes=True,
5047
- row_heights=False if row_heights is False else True,
5048
- )
5049
5037
  return self
5050
5038
 
5051
5039
  def tree_reset(self) -> Sheet:
@@ -5153,6 +5141,7 @@ class Sheet(tk.Frame):
5153
5141
  text: None | str = None,
5154
5142
  values: None | list[object] = None,
5155
5143
  create_selections: bool = False,
5144
+ undo: bool = True,
5156
5145
  ) -> str:
5157
5146
  """
5158
5147
  Insert an item into the treeview
@@ -5161,9 +5150,7 @@ class Sheet(tk.Frame):
5161
5150
  str: new item iid
5162
5151
  """
5163
5152
  if not iid:
5164
- i = 0
5165
- while (iid := f"{num2alpha(i)}") in self.RI.tree:
5166
- i += 1
5153
+ iid = self.RI.new_iid()
5167
5154
  if iid in self.RI.tree:
5168
5155
  raise ValueError(f"iid '{iid}' already exists.")
5169
5156
  if iid == parent:
@@ -5172,18 +5159,37 @@ class Sheet(tk.Frame):
5172
5159
  raise ValueError(f"parent '{parent}' does not exist.")
5173
5160
  if text is None:
5174
5161
  text = iid
5175
- parent_node = self.RI.tree[parent] if parent else ""
5176
- self.RI.tree[iid] = Node(text, iid, parent_node)
5177
- 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:
5178
5180
  if isinstance(index, int):
5179
- datarn = self.RI.tree_rns[parent] + index + 1
5180
- datarn += sum(
5181
- 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
+ )
5182
5190
  )
5183
- self.RI.tree[parent].children.insert(index, self.RI.tree[iid])
5184
5191
  else:
5185
5192
  datarn = self.RI.tree_rns[parent] + sum(1 for _ in self.RI.get_iid_descendants(parent)) + 1
5186
- self.RI.tree[parent].children.append(self.RI.tree[iid])
5187
5193
  else:
5188
5194
  if isinstance(index, int):
5189
5195
  datarn = index
@@ -5191,19 +5197,7 @@ class Sheet(tk.Frame):
5191
5197
  datarn = len(self.MT._row_index)
5192
5198
  else:
5193
5199
  datarn = len(self.MT._row_index)
5194
- if values is None:
5195
- values = []
5196
- self.insert_rows(
5197
- rows=[[self.RI.tree[iid]] + values],
5198
- idx=datarn,
5199
- row_index=True,
5200
- create_selections=create_selections,
5201
- fill=False,
5202
- )
5203
- self.RI.tree_rns[iid] = datarn
5204
- if parent and (parent not in self.RI.tree_open_ids or not self.item_displayed(parent)):
5205
- self.hide_rows(datarn, deselect_all=False, data_indexes=True)
5206
- return iid
5200
+ return datarn
5207
5201
 
5208
5202
  def bulk_insert(
5209
5203
  self,
@@ -5215,6 +5209,7 @@ class Sheet(tk.Frame):
5215
5209
  create_selections: bool = False,
5216
5210
  include_iid_column: bool = True,
5217
5211
  include_text_column: bool = True,
5212
+ undo: bool = True,
5218
5213
  ) -> dict[str, int]:
5219
5214
  """
5220
5215
  Insert multiple items into the treeview at once, under the same parent.
@@ -5225,52 +5220,29 @@ class Sheet(tk.Frame):
5225
5220
  - Values (int): row numbers
5226
5221
  """
5227
5222
  to_insert = []
5228
- pid = parent
5229
- if pid and pid not in self.RI.tree:
5223
+ if parent and parent not in self.RI.tree:
5230
5224
  raise ValueError(f"parent '{parent}' does not exist.")
5231
- parent_node = self.RI.tree[pid] if parent else ""
5232
- if parent_node:
5233
- if isinstance(index, int):
5234
- datarn = self.RI.tree_rns[pid] + index + 1
5235
- datarn += sum(
5236
- sum(1 for _ in self.RI.get_iid_descendants(cid)) for cid in islice(self.get_children(pid), index)
5237
- )
5238
- else:
5239
- datarn = self.RI.tree_rns[pid] + sum(1 for _ in self.RI.get_iid_descendants(pid)) + 1
5240
- else:
5241
- if isinstance(index, int):
5242
- datarn = index
5243
- if index and (datarn := self.top_index_row(datarn)) is None:
5244
- datarn = len(self.MT._row_index)
5245
- else:
5246
- datarn = len(self.MT._row_index)
5247
- i = 0
5225
+ datarn = self._get_id_insert_row(index=index, parent=parent)
5248
5226
  rns_to_add = {}
5249
5227
  for rn, r in enumerate(data, start=datarn):
5250
5228
  if iid_column is None:
5251
- while (iid := f"{num2alpha(i)}") in self.RI.tree:
5252
- i += 1
5229
+ iid = self.RI.new_iid()
5253
5230
  else:
5254
5231
  iid = r[iid_column]
5255
- self.RI.tree[iid] = Node(
5232
+ new_node = Node(
5256
5233
  r[text_column] if isinstance(text_column, int) else text_column if isinstance(text_column, str) else "",
5257
5234
  iid,
5258
- parent_node,
5235
+ parent,
5259
5236
  )
5260
- if parent_node:
5261
- if isinstance(index, int):
5262
- self.RI.tree[pid].children.insert(index, self.RI.tree[iid])
5263
- else:
5264
- self.RI.tree[pid].children.append(self.RI.tree[iid])
5265
5237
  exclude = set()
5266
5238
  if isinstance(iid_column, int) and not include_iid_column:
5267
5239
  exclude.add(iid_column)
5268
5240
  if isinstance(text_column, int) and not include_text_column:
5269
5241
  exclude.add(text_column)
5270
5242
  if exclude:
5271
- 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])
5272
5244
  else:
5273
- to_insert.append([self.RI.tree[iid]] + r)
5245
+ to_insert.append([new_node] + r)
5274
5246
  rns_to_add[iid] = rn
5275
5247
  self.insert_rows(
5276
5248
  rows=to_insert,
@@ -5278,10 +5250,10 @@ class Sheet(tk.Frame):
5278
5250
  row_index=True,
5279
5251
  create_selections=create_selections,
5280
5252
  fill=False,
5253
+ undo=undo,
5254
+ tree=True,
5281
5255
  )
5282
- for iid, rn in rns_to_add.items():
5283
- self.RI.tree_rns[iid] = rn
5284
- 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)):
5285
5257
  self.hide_rows(range(datarn, datarn + len(to_insert)), deselect_all=False, data_indexes=True)
5286
5258
  return rns_to_add
5287
5259
 
@@ -5368,9 +5340,9 @@ class Sheet(tk.Frame):
5368
5340
  elif item == "":
5369
5341
  yield from map(attrgetter("iid"), self.RI.gen_top_nodes())
5370
5342
  else:
5371
- yield from (n.iid for n in self.RI.tree[item].children)
5343
+ yield from self.RI.tree[item].children
5372
5344
 
5373
- def get_nodes(self, item: None | str = None) -> Generator[str]:
5345
+ def get_iids(self, item: None | str = None) -> Generator[str]:
5374
5346
  if item is None:
5375
5347
  for n in self.RI.tree.values():
5376
5348
  if not n.parent:
@@ -5379,31 +5351,16 @@ class Sheet(tk.Frame):
5379
5351
  elif item == "":
5380
5352
  yield from (n.iid for n in self.RI.tree.values() if not n.parent)
5381
5353
  else:
5382
- yield from (n.iid for n in self.RI.tree[item].children)
5354
+ yield from self.RI.tree[item].children
5383
5355
 
5384
- def del_items(self, *items) -> Sheet:
5356
+ def del_items(self, *items, undo: bool = True) -> Sheet:
5385
5357
  """
5386
5358
  Also deletes all descendants of items
5387
5359
  """
5388
- rows_to_del = []
5389
- iids_to_del = []
5390
- for item in unpack(items):
5391
- if item not in self.RI.tree:
5392
- continue
5393
- rows_to_del.append(self.RI.tree_rns[item])
5394
- iids_to_del.append(item)
5395
- for did in self.RI.get_iid_descendants(item):
5396
- rows_to_del.append(self.RI.tree_rns[did])
5397
- iids_to_del.append(did)
5398
- for item in unpack(items):
5399
- self.RI.remove_node_from_parents_children(self.RI.tree[item])
5400
- self.del_rows(rows_to_del)
5401
- for iid in iids_to_del:
5402
- self.RI.tree_open_ids.discard(iid)
5403
- if self.RI.tree[iid].parent and len(self.RI.tree[iid].parent.children) == 1:
5404
- self.RI.tree_open_ids.discard(self.RI.tree[iid].parent.iid)
5405
- del self.RI.tree[iid]
5406
- 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)
5407
5364
 
5408
5365
  def set_children(self, parent: str, *newchildren) -> Sheet:
5409
5366
  """
@@ -5419,123 +5376,42 @@ class Sheet(tk.Frame):
5419
5376
  except Exception:
5420
5377
  return None
5421
5378
 
5422
- 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]:
5423
5388
  """
5424
5389
  Moves item to be under parent as child at index
5425
5390
  'parent' can be an empty str which will put the item at top level
5426
- Performance is not great
5427
5391
  """
5428
5392
  if item not in self.RI.tree:
5429
5393
  raise ValueError(f"Item '{item}' does not exist.")
5430
5394
  if parent and parent not in self.RI.tree:
5431
5395
  raise ValueError(f"Parent '{parent}' does not exist.")
5432
- mapping = {}
5433
- to_show = []
5434
- item_node = self.RI.tree[item]
5435
- item_r = self.RI.tree_rns[item]
5436
- item_descendants = tuple(self.RI.get_iid_descendants(item))
5437
- num_item_descendants = len(item_descendants)
5438
- if parent:
5439
- if self.RI.move_pid_causes_recursive_loop(item, parent):
5440
- raise ValueError(f"iid '{item}' causes a recursive loop with parent '{parent}'.")
5441
- parent_node = self.RI.tree[parent]
5442
- if parent_node.children:
5443
- if index is None or index >= len(parent_node.children):
5444
- index = len(parent_node.children)
5445
- new_r = self.RI.tree_rns[parent] + sum(1 for _ in self.RI.get_iid_descendants(parent))
5446
- # new parent has children
5447
- # index is on end
5448
- # item row is less than move to row
5449
- if item_r < new_r:
5450
- r_ctr = new_r - num_item_descendants
5451
-
5452
- # new parent has children
5453
- # index is on end
5454
- # item row is greater than move to row
5455
- else:
5456
- r_ctr = new_r + 1
5457
- else:
5458
- new_r = self.RI.tree_rns[parent_node.children[index].iid]
5459
- # new parent has children
5460
- # index is not end
5461
- # item row is less than move to row
5462
- if item_r < new_r:
5463
- if self.RI.items_parent(item) == parent:
5464
- r_ctr = (
5465
- new_r
5466
- + sum(1 for _ in self.RI.get_iid_descendants(parent_node.children[index].iid))
5467
- - num_item_descendants
5468
- )
5469
- else:
5470
- r_ctr = new_r - num_item_descendants - 1
5471
-
5472
- # new parent has children
5473
- # index is not end
5474
- # item row is greater than move to row
5475
- else:
5476
- r_ctr = new_r
5477
- else:
5478
- index = 0
5479
- new_r = self.RI.tree_rns[parent_node.iid]
5480
-
5481
- # new parent doesn't have children
5482
- # index always start
5483
- # item row is less than move to row
5484
- if item_r < new_r:
5485
- r_ctr = new_r - num_item_descendants
5486
-
5487
- # new parent doesn't have children
5488
- # index always start
5489
- # item row is greater than move to row
5490
- else:
5491
- r_ctr = new_r + 1
5492
- mapping[item_r] = r_ctr
5493
- if parent in self.RI.tree_open_ids and self.item_displayed(parent):
5494
- to_show.append(r_ctr)
5495
- r_ctr += 1
5496
- for did in item_descendants:
5497
- mapping[self.RI.tree_rns[did]] = r_ctr
5498
- if to_show and self.RI.ancestors_all_open(did, item_node.parent):
5499
- to_show.append(r_ctr)
5500
- r_ctr += 1
5501
- if parent == self.RI.items_parent(item):
5502
- pop_index = parent_node.children.index(item_node)
5503
- parent_node.children.insert(index, parent_node.children.pop(pop_index))
5504
- else:
5505
- self.RI.remove_node_from_parents_children(item_node)
5506
- item_node.parent = parent_node
5507
- parent_node.children.insert(index, item_node)
5508
- else:
5509
- if index is None or (new_r := self.top_index_row(index)) is None:
5510
- new_r = self.top_index_row(sum(1 for _ in self.RI.gen_top_nodes()) - 1)
5511
- if item_r < new_r:
5512
- r_ctr = (
5513
- new_r
5514
- + sum(1 for _ in self.RI.get_iid_descendants(self.rowitem(new_r, data_index=True)))
5515
- - num_item_descendants
5516
- )
5517
- else:
5518
- r_ctr = new_r
5519
- mapping[item_r] = r_ctr
5520
- to_show.append(r_ctr)
5521
- r_ctr += 1
5522
- for did in item_descendants:
5523
- mapping[self.RI.tree_rns[did]] = r_ctr
5524
- if to_show and self.RI.ancestors_all_open(did, item_node.parent):
5525
- to_show.append(r_ctr)
5526
- r_ctr += 1
5527
- self.RI.remove_node_from_parents_children(item_node)
5528
- self.RI.tree[item].parent = ""
5529
- self.mapping_move_rows(
5530
- data_new_idxs=mapping,
5531
- data_indexes=True,
5532
- create_selections=False,
5533
- 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),
5534
5408
  )
5535
- if parent and (parent not in self.RI.tree_open_ids or not self.item_displayed(parent)):
5536
- self.hide_rows(set(mapping.values()), data_indexes=True)
5537
- self.show_rows(to_show)
5538
- 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
5539
5415
 
5540
5416
  reattach = move
5541
5417
 
@@ -5545,15 +5421,16 @@ class Sheet(tk.Frame):
5545
5421
  def parent(self, item: str) -> str:
5546
5422
  if item not in self.RI.tree:
5547
5423
  raise ValueError(f"Item '{item}' does not exist.")
5548
- 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
5549
5425
 
5550
5426
  def index(self, item: str) -> int:
5551
5427
  if item not in self.RI.tree:
5552
5428
  raise ValueError(f"Item '{item}' does not exist.")
5553
- 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:
5554
5432
  find_node = self.RI.tree[item]
5555
5433
  return next(index for index, node in enumerate(self.RI.gen_top_nodes()) if node == find_node)
5556
- return self.RI.tree[item].parent.children.index(self.RI.tree[item])
5557
5434
 
5558
5435
  def item_displayed(self, item: str) -> bool:
5559
5436
  """
@@ -5562,7 +5439,7 @@ class Sheet(tk.Frame):
5562
5439
  """
5563
5440
  if item not in self.RI.tree:
5564
5441
  raise ValueError(f"Item '{item}' does not exist.")
5565
- return self.RI.tree_rns[item] in self.MT.displayed_rows
5442
+ return bisect_in(self.MT.displayed_rows, self.RI.tree_rns[item])
5566
5443
 
5567
5444
  def display_item(self, item: str, redraw: bool = False) -> Sheet:
5568
5445
  """
@@ -5971,7 +5848,7 @@ class Sheet(tk.Frame):
5971
5848
  def set_row_data(
5972
5849
  self,
5973
5850
  r: int,
5974
- values=tuple(),
5851
+ values: Sequence[object] = [],
5975
5852
  add_columns: bool = True,
5976
5853
  redraw: bool = True,
5977
5854
  keep_formatting: bool = True,
@@ -5989,7 +5866,7 @@ class Sheet(tk.Frame):
5989
5866
  if c > maxidx:
5990
5867
  self.MT.data[r].append(v)
5991
5868
  if self.MT.all_columns_displayed:
5992
- self.MT.insert_col_position("end")
5869
+ self.MT.insert_col_positions()
5993
5870
  else:
5994
5871
  self.set_cell_data(r=r, c=c, value=v, redraw=False, keep_formatting=keep_formatting)
5995
5872
  else:
@@ -6003,7 +5880,7 @@ class Sheet(tk.Frame):
6003
5880
  def set_column_data(
6004
5881
  self,
6005
5882
  c: int,
6006
- values=tuple(),
5883
+ values: Sequence[object] = [],
6007
5884
  add_rows: bool = True,
6008
5885
  redraw: bool = True,
6009
5886
  keep_formatting: bool = True,
@@ -6020,7 +5897,7 @@ class Sheet(tk.Frame):
6020
5897
  total_cols = self.MT.total_data_cols()
6021
5898
  self.MT.fix_data_len(rn, total_cols - 1)
6022
5899
  if self.MT.all_rows_displayed:
6023
- self.MT.insert_row_position("end", height=height)
5900
+ self.MT.insert_row_positions(heights=height)
6024
5901
  maxidx += 1
6025
5902
  if c >= len(self.MT.data[rn]):
6026
5903
  self.MT.fix_row_len(rn, c)
@@ -7018,7 +6895,6 @@ class Sheet(tk.Frame):
7018
6895
  self.RI.dropdown.window.values(values)
7019
6896
  if set_value is not None:
7020
6897
  self.MT.row_index(newindex=set_value, index=r_)
7021
- # here
7022
6898
  return self
7023
6899
 
7024
6900
  def get_dropdown_values(self, r: int = 0, c: int = 0) -> None | list:
@@ -7248,7 +7124,7 @@ class Dropdown(Sheet):
7248
7124
  modified_function: None | Callable = None,
7249
7125
  arrowkey_RIGHT: Callable | None = None,
7250
7126
  arrowkey_LEFT: Callable | None = None,
7251
- align: str = "w",
7127
+ align: str = "nw",
7252
7128
  # False for using r, c
7253
7129
  # "r" for r
7254
7130
  # "c" for c