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/main_table.py CHANGED
@@ -14,13 +14,16 @@ from tkinter import TclError
14
14
  from typing import Literal
15
15
 
16
16
  from .colors import color_map
17
+ from .column_headers import ColumnHeaders
17
18
  from .constants import (
18
19
  USER_OS,
20
+ _test_str,
19
21
  bind_add_columns,
20
22
  bind_add_rows,
21
23
  bind_del_columns,
22
24
  bind_del_rows,
23
25
  ctrl_key,
26
+ font_value_error,
24
27
  rc_binding,
25
28
  text_editor_close_bindings,
26
29
  text_editor_newline_bindings,
@@ -45,6 +48,7 @@ from .functions import (
45
48
  cell_right_within_box,
46
49
  color_tup,
47
50
  consecutive_ranges,
51
+ data_to_displayed_idxs,
48
52
  diff_gen,
49
53
  diff_list,
50
54
  down_cell_within_box,
@@ -70,11 +74,13 @@ from .functions import (
70
74
  move_elements_by_mapping,
71
75
  new_tk_event,
72
76
  next_cell,
77
+ push_n,
73
78
  rounded_box_coords,
74
79
  span_idxs_post_move,
75
80
  stored_event_dict,
76
81
  try_binding,
77
82
  unpickle_obj,
83
+ wrap_text,
78
84
  )
79
85
  from .other_classes import (
80
86
  Box_nt,
@@ -92,20 +98,30 @@ from .other_classes import (
92
98
  SelectionBox,
93
99
  TextEditorStorage,
94
100
  )
101
+ from .row_index import RowIndex
102
+ from .sorting import sort_selection
95
103
  from .text_editor import TextEditor
96
- from .types import AnyIter
104
+ from .tksheet_types import AnyIter
97
105
 
98
106
 
99
107
  class MainTable(tk.Canvas):
100
- def __init__(self, *args, **kwargs):
108
+ def __init__(
109
+ self,
110
+ parent,
111
+ row_index_canvas: RowIndex,
112
+ column_headers_canvas: ColumnHeaders,
113
+ **kwargs,
114
+ ):
101
115
  super().__init__(
102
- kwargs["parent"],
103
- background=kwargs["parent"].ops.table_bg,
116
+ parent,
117
+ background=parent.ops.table_bg,
104
118
  highlightthickness=0,
105
119
  )
106
- self.PAR = kwargs["parent"]
120
+ self.PAR = parent
107
121
  self.PAR_width = 0
108
122
  self.PAR_height = 0
123
+ self.cells_cache = None
124
+ self.table_txt_height, self.index_txt_height, self.header_txt_height = 0, 0, 0
109
125
  self.scrollregion = tuple()
110
126
  self.current_cursor = ""
111
127
  self.ctrl_b1_pressed = False
@@ -176,6 +192,9 @@ class MainTable(tk.Canvas):
176
192
 
177
193
  self.edit_validation_func = None
178
194
 
195
+ self.extra_begin_sort_cells_func = None
196
+ self.extra_end_sort_cells_func = None
197
+
179
198
  self.extra_begin_ctrl_c_func = None
180
199
  self.extra_end_ctrl_c_func = None
181
200
 
@@ -239,50 +258,48 @@ class MainTable(tk.Canvas):
239
258
  self.rc_insert_column_enabled = False
240
259
  self.rc_delete_row_enabled = False
241
260
  self.rc_insert_row_enabled = False
261
+ self.rc_sort_cells_enabled = False
262
+ self.rc_sort_row_enabled = False
263
+ self.rc_sort_column_enabled = False
264
+ self.rc_sort_rows_enabled = False
265
+ self.rc_sort_columns_enabled = False
242
266
  self.rc_popup_menus_enabled = False
243
267
  self.edit_cell_enabled = False
244
- self.CH = kwargs["column_headers_canvas"]
268
+ self.CH = column_headers_canvas
245
269
  self.CH.MT = self
246
- self.CH.RI = kwargs["row_index_canvas"]
247
- self.RI = kwargs["row_index_canvas"]
270
+ self.CH.RI = row_index_canvas
271
+ self.RI = row_index_canvas
248
272
  self.RI.MT = self
249
- self.RI.CH = kwargs["column_headers_canvas"]
273
+ self.RI.CH = column_headers_canvas
250
274
  self.TL = None # is set from within TopLeftRectangle() __init__
251
275
  self.all_columns_displayed = True
252
276
  self.all_rows_displayed = True
253
277
  self.align = kwargs["align"]
254
-
255
- self.PAR.ops.table_font = [
278
+ self.PAR.ops.table_font = FontTuple(
256
279
  self.PAR.ops.table_font[0],
257
- int(self.PAR.ops.table_font[1] * kwargs["zoom"] / 100),
280
+ max(1, int(self.PAR.ops.table_font[1] * kwargs["zoom"] / 100)),
258
281
  self.PAR.ops.table_font[2],
259
- ]
260
-
261
- self.PAR.ops.index_font = [
282
+ )
283
+ self.PAR.ops.index_font = FontTuple(
262
284
  self.PAR.ops.index_font[0],
263
- int(self.PAR.ops.index_font[1] * kwargs["zoom"] / 100),
285
+ max(1, int(self.PAR.ops.index_font[1] * kwargs["zoom"] / 100)),
264
286
  self.PAR.ops.index_font[2],
265
- ]
266
-
267
- self.PAR.ops.header_font = [
287
+ )
288
+ self.PAR.ops.header_font = FontTuple(
268
289
  self.PAR.ops.header_font[0],
269
- int(self.PAR.ops.header_font[1] * kwargs["zoom"] / 100),
290
+ max(1, int(self.PAR.ops.header_font[1] * kwargs["zoom"] / 100)),
270
291
  self.PAR.ops.header_font[2],
271
- ]
272
- for fnt in (self.PAR.ops.table_font, self.PAR.ops.index_font, self.PAR.ops.header_font):
273
- if fnt[1] < 1:
274
- fnt[1] = 1
275
- self.PAR.ops.table_font = FontTuple(*self.PAR.ops.table_font)
276
- self.PAR.ops.index_font = FontTuple(*self.PAR.ops.index_font)
277
- self.PAR.ops.header_font = FontTuple(*self.PAR.ops.header_font)
278
-
292
+ )
279
293
  self.txt_measure_canvas = tk.Canvas(self)
280
294
  self.txt_measure_canvas_text = self.txt_measure_canvas.create_text(0, 0, text="", font=self.PAR.ops.table_font)
281
295
 
282
296
  self.RI.set_width(self.PAR.ops.default_row_index_width)
297
+
298
+ self.char_widths = {}
283
299
  self.set_table_font_help()
284
300
  self.set_header_font_help()
285
301
  self.set_index_font_help()
302
+
286
303
  self.data = kwargs["data_reference"]
287
304
  if isinstance(self.data, (list, tuple)):
288
305
  self.data = kwargs["data_reference"]
@@ -344,7 +361,7 @@ class MainTable(tk.Canvas):
344
361
  super().event_generate(*args, **kwargs)
345
362
 
346
363
  def refresh(self, event: object = None) -> None:
347
- self.main_table_redraw_grid_and_text(True, True)
364
+ self.PAR.set_refresh_timer()
348
365
 
349
366
  def window_configured(self, event: object) -> None:
350
367
  w = self.PAR.winfo_width()
@@ -450,7 +467,7 @@ class MainTable(tk.Canvas):
450
467
  dash=dash,
451
468
  width=3,
452
469
  outline=self.PAR.ops.resizing_line_fg if outline is None else outline,
453
- tag="ctrl",
470
+ tags="ctrl",
454
471
  )
455
472
  if delete_on_timer:
456
473
  self.after(1500, self.delete_ctrl_outlines)
@@ -711,13 +728,13 @@ class MainTable(tk.Canvas):
711
728
  dash: tuple[int, int],
712
729
  width: int,
713
730
  outline: str,
714
- tag: str | tuple[str, ...],
731
+ tags: str | tuple[str, ...],
715
732
  ) -> None:
716
733
  if self.hidd_ctrl_outline:
717
734
  t, sh = self.hidd_ctrl_outline.popitem()
718
735
  self.coords(t, x1, y1, x2, y2)
719
736
  if sh:
720
- self.itemconfig(t, fill=fill, dash=dash, width=width, outline=outline, tag=tag)
737
+ self.itemconfig(t, fill=fill, dash=dash, width=width, outline=outline, tags=tags)
721
738
  else:
722
739
  self.itemconfig(
723
740
  t,
@@ -725,7 +742,7 @@ class MainTable(tk.Canvas):
725
742
  dash=dash,
726
743
  width=width,
727
744
  outline=outline,
728
- tag=tag,
745
+ tags=tags,
729
746
  state="normal",
730
747
  )
731
748
  self.lift(t)
@@ -739,7 +756,7 @@ class MainTable(tk.Canvas):
739
756
  dash=dash,
740
757
  width=width,
741
758
  outline=outline,
742
- tag=tag,
759
+ tags=tags,
743
760
  )
744
761
  self.disp_ctrl_outline[t] = True
745
762
 
@@ -778,15 +795,20 @@ class MainTable(tk.Canvas):
778
795
  )
779
796
  return s, writer
780
797
 
781
- def ctrl_c(self, event=None) -> None | EventDataDict:
782
- if not self.selected:
783
- return
784
- event_data = event_dict(
785
- name="begin_ctrl_c",
798
+ def new_event_dict(self, name: str, boxes: dict | None = None, state: bool = False) -> EventDataDict:
799
+ return event_dict(
800
+ name=name,
786
801
  sheet=self.PAR.name,
787
802
  widget=self,
803
+ boxes=self.get_boxes() if boxes is None else boxes,
788
804
  selected=self.selected,
805
+ sheet_state=self.copy_sheet_state() if state else None,
789
806
  )
807
+
808
+ def ctrl_c(self, event=None) -> None | EventDataDict:
809
+ if not self.selected:
810
+ return
811
+ event_data = self.new_event_dict("begin_ctrl_c")
790
812
  boxes, maxrows = self.get_ctrl_x_c_boxes()
791
813
  event_data["selection_boxes"] = boxes
792
814
  s, writer = self.io_csv_writer()
@@ -831,12 +853,7 @@ class MainTable(tk.Canvas):
831
853
  def ctrl_x(self, event=None, validation: bool = True) -> None | EventDataDict:
832
854
  if not self.selected:
833
855
  return
834
- event_data = event_dict(
835
- name="edit_table",
836
- sheet=self.PAR.name,
837
- widget=self,
838
- selected=self.selected,
839
- )
856
+ event_data = self.new_event_dict("edit_table")
840
857
  boxes, maxrows = self.get_ctrl_x_c_boxes()
841
858
  event_data["selection_boxes"] = boxes
842
859
  s, writer = self.io_csv_writer()
@@ -912,17 +929,64 @@ class MainTable(tk.Canvas):
912
929
  self.PAR.emit_event("<<Cut>>", event_data)
913
930
  return event_data
914
931
 
932
+ def sort_boxes(
933
+ self,
934
+ event: tk.Event | None = None,
935
+ boxes: AnyIter[int, int, int, int] | None = None,
936
+ reverse: bool = False,
937
+ row_wise: bool = False,
938
+ validation: bool = True,
939
+ key: Callable | None = None,
940
+ undo: bool = True,
941
+ ) -> EventDataDict:
942
+ if boxes is None:
943
+ boxes = self.get_boxes()
944
+ if not boxes:
945
+ boxes = [(0, 0, len(self.row_positions) - 1, len(self.col_positions) - 1)]
946
+ event_data = self.new_event_dict("edit_table", boxes=boxes)
947
+ try_binding(self.extra_begin_sort_cells_func, event_data)
948
+ for r1, c1, r2, c2 in boxes:
949
+ data = sort_selection(
950
+ [[self.get_cell_data(self.datarn(r), self.datacn(c)) for c in range(c1, c2)] for r in range(r1, r2)],
951
+ reverse=reverse,
952
+ key=key,
953
+ row_wise=row_wise,
954
+ )
955
+ for ir, r in enumerate(range(r1, r2)):
956
+ data_r = self.datarn(r)
957
+ for ic, c in enumerate(range(c1, c2)):
958
+ data_c = self.datacn(c)
959
+ val = data[ir][ic]
960
+ if (
961
+ not self.edit_validation_func
962
+ or not validation
963
+ or (
964
+ self.edit_validation_func
965
+ and (val := self.edit_validation_func(mod_event_val(event_data, val, (data_r, data_c))))
966
+ is not None
967
+ )
968
+ ):
969
+ event_data = self.event_data_set_cell(
970
+ datarn=data_r,
971
+ datacn=data_c,
972
+ value=val,
973
+ event_data=event_data,
974
+ )
975
+ if event_data["cells"]["table"]:
976
+ if undo and self.undo_enabled:
977
+ self.undo_stack.append(stored_event_dict(event_data))
978
+ try_binding(self.extra_end_sort_cells_func, event_data, "end_edit_table")
979
+ self.sheet_modified(event_data)
980
+ self.PAR.emit_event("<<SheetModified>>", event_data)
981
+ self.refresh()
982
+ return event_data
983
+
915
984
  def ctrl_v(self, event: object = None, validation: bool = True) -> None | EventDataDict:
916
985
  if not self.PAR.ops.paste_can_expand_x and len(self.col_positions) == 1:
917
986
  return
918
987
  if not self.PAR.ops.paste_can_expand_y and len(self.row_positions) == 1:
919
988
  return
920
- event_data = event_dict(
921
- name="edit_table",
922
- sheet=self.PAR.name,
923
- widget=self,
924
- selected=self.selected,
925
- )
989
+ event_data = self.new_event_dict("edit_table", state=True)
926
990
  if self.selected:
927
991
  selected_r = self.selected.box.from_r
928
992
  selected_c = self.selected.box.from_c
@@ -1081,7 +1145,7 @@ class MainTable(tk.Canvas):
1081
1145
  if ctr:
1082
1146
  event_data = self.add_rows(
1083
1147
  rows=rows,
1084
- index=index,
1148
+ index=index if isinstance(self._row_index, list) and self._row_index else {},
1085
1149
  row_heights=row_heights,
1086
1150
  event_data=event_data,
1087
1151
  mod_event_boxes=False,
@@ -1131,7 +1195,7 @@ class MainTable(tk.Canvas):
1131
1195
  if ctr:
1132
1196
  event_data = self.add_columns(
1133
1197
  columns=columns,
1134
- header=headers,
1198
+ header=headers if isinstance(self._headers, list) and self._headers else {},
1135
1199
  column_widths=column_widths,
1136
1200
  event_data=event_data,
1137
1201
  mod_event_boxes=False,
@@ -1179,12 +1243,7 @@ class MainTable(tk.Canvas):
1179
1243
  def delete_key(self, event: object = None, validation: bool = True) -> None | EventDataDict:
1180
1244
  if not self.selected:
1181
1245
  return
1182
- event_data = event_dict(
1183
- name="edit_table",
1184
- sheet=self.PAR.name,
1185
- widget=self,
1186
- selected=self.selected,
1187
- )
1246
+ event_data = self.new_event_dict("edit_table")
1188
1247
  boxes = self.get_boxes()
1189
1248
  event_data["selection_boxes"] = boxes
1190
1249
  if not try_binding(self.extra_begin_delete_key_func, event_data, "begin_delete"):
@@ -1259,7 +1318,6 @@ class MainTable(tk.Canvas):
1259
1318
  move_data: bool = True,
1260
1319
  move_widths: bool = True,
1261
1320
  create_selections: bool = True,
1262
- data_indexes: bool = False,
1263
1321
  event_data: EventDataDict | None = None,
1264
1322
  ) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
1265
1323
  self.saved_column_widths = {}
@@ -1268,22 +1326,17 @@ class MainTable(tk.Canvas):
1268
1326
  if totalcols:
1269
1327
  totalcols += 1
1270
1328
  totalcols = self.equalize_data_row_lengths(at_least_cols=totalcols)
1271
- if event_data is None:
1272
- event_data = event_dict(
1273
- name="move_columns",
1274
- sheet=self.PAR.name,
1275
- widget=self,
1276
- boxes=self.get_boxes(),
1277
- selected=self.selected,
1278
- )
1329
+ if not event_data:
1330
+ event_data = self.new_event_dict("move_columns", state=True)
1331
+ if not event_data["moved"]["columns"]:
1279
1332
  event_data["moved"]["columns"] = {
1280
1333
  "data": data_new_idxs,
1281
1334
  "displayed": {} if disp_new_idxs is None else disp_new_idxs,
1282
1335
  }
1283
1336
  event_data["options"] = self.copy_options()
1284
1337
  event_data["named_spans"] = {k: span.pickle_self() for k, span in self.named_spans.items()}
1285
- if move_widths and disp_new_idxs and (not data_indexes or self.all_columns_displayed):
1286
- self.deselect("all", run_binding=False, redraw=False)
1338
+
1339
+ if move_widths and disp_new_idxs:
1287
1340
  self.set_col_positions(
1288
1341
  itr=move_elements_by_mapping(
1289
1342
  self.get_column_widths(),
@@ -1297,6 +1350,7 @@ class MainTable(tk.Canvas):
1297
1350
  )
1298
1351
  )
1299
1352
  if create_selections:
1353
+ self.deselect("all", run_binding=False, redraw=False)
1300
1354
  for boxst, boxend in consecutive_ranges(sorted(disp_new_idxs.values())):
1301
1355
  self.create_selection_box(
1302
1356
  0,
@@ -1306,6 +1360,8 @@ class MainTable(tk.Canvas):
1306
1360
  "columns",
1307
1361
  run_binding=True,
1308
1362
  )
1363
+ else:
1364
+ self.recreate_all_selection_boxes()
1309
1365
  if move_data:
1310
1366
  self.data = list(
1311
1367
  map(
@@ -1459,29 +1515,54 @@ class MainTable(tk.Canvas):
1459
1515
  data_indexes: bool = False,
1460
1516
  ) -> tuple[dict[int, int], dict[int, int], int, dict[int, int]]:
1461
1517
  if not data_indexes or self.all_rows_displayed:
1462
- disp_new_idxs = get_new_indexes(move_to=move_to, to_move=to_move)
1518
+ disp_new_idxs = get_new_indexes(
1519
+ move_to=move_to,
1520
+ to_move=to_move,
1521
+ )
1522
+ data_new_idxs = dict(disp_new_idxs)
1463
1523
  else:
1464
1524
  disp_new_idxs = {}
1525
+ data_new_idxs = get_new_indexes(
1526
+ move_to=move_to,
1527
+ to_move=to_move,
1528
+ )
1465
1529
  # move_to can be len and fix_data_len() takes index so - 1
1466
1530
  fix_len = (move_to - 1) if move_to else move_to
1467
1531
  if not self.all_rows_displayed and not data_indexes:
1468
1532
  fix_len = self.datarn(fix_len)
1469
1533
  self.fix_data_len(fix_len)
1470
1534
  totalrows = max(self.total_data_rows(), len(self.row_positions) - 1)
1471
- data_new_idxs = get_new_indexes(move_to=move_to, to_move=to_move)
1472
1535
  if not self.all_rows_displayed and not data_indexes:
1473
- data_new_idxs = dict(
1474
- zip(
1536
+ keep = set(map(self.datarn, to_move))
1537
+ data_new_idxs = {
1538
+ k: v
1539
+ for k, v in zip(
1475
1540
  move_elements_by_mapping(
1476
1541
  self.displayed_rows,
1477
1542
  data_new_idxs,
1478
1543
  dict(zip(data_new_idxs.values(), data_new_idxs)),
1479
1544
  ),
1480
1545
  self.displayed_rows,
1481
- ),
1482
- )
1546
+ )
1547
+ if k in keep
1548
+ }
1483
1549
  return data_new_idxs, dict(zip(data_new_idxs.values(), data_new_idxs)), totalrows, disp_new_idxs
1484
1550
 
1551
+ def move_rows_data(
1552
+ self,
1553
+ data_new_idxs: dict[int, int],
1554
+ data_old_idxs: dict[int, int],
1555
+ maxidx: int,
1556
+ ) -> None:
1557
+ self.data = move_elements_by_mapping(
1558
+ self.data,
1559
+ data_new_idxs,
1560
+ data_old_idxs,
1561
+ )
1562
+ self.RI.fix_index(maxidx)
1563
+ if isinstance(self._row_index, list) and self._row_index:
1564
+ self._row_index = move_elements_by_mapping(self._row_index, data_new_idxs, data_old_idxs)
1565
+
1485
1566
  def move_rows_adjust_options_dict(
1486
1567
  self,
1487
1568
  data_new_idxs: dict[int, int],
@@ -1491,8 +1572,9 @@ class MainTable(tk.Canvas):
1491
1572
  move_data: bool = True,
1492
1573
  move_heights: bool = True,
1493
1574
  create_selections: bool = True,
1494
- data_indexes: bool = False,
1495
1575
  event_data: EventDataDict | None = None,
1576
+ undo_modification: EventDataDict | None = None,
1577
+ node_change: None | tuple[str, str, int] = None,
1496
1578
  ) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
1497
1579
  self.saved_row_heights = {}
1498
1580
  if not isinstance(totalrows, int):
@@ -1502,54 +1584,33 @@ class MainTable(tk.Canvas):
1502
1584
  max(data_new_idxs.values(), default=0),
1503
1585
  )
1504
1586
  self.fix_data_len(totalrows - 1)
1505
- if event_data is None:
1506
- event_data = event_dict(
1507
- name="move_rows",
1508
- sheet=self.PAR.name,
1509
- widget=self,
1510
- boxes=self.get_boxes(),
1511
- selected=self.selected,
1512
- )
1587
+ if not event_data:
1588
+ event_data = self.new_event_dict("move_rows", state=True)
1589
+ if not event_data["moved"]["rows"]:
1513
1590
  event_data["moved"]["rows"] = {
1514
1591
  "data": data_new_idxs,
1515
1592
  "displayed": {} if disp_new_idxs is None else disp_new_idxs,
1516
1593
  }
1517
1594
  event_data["options"] = self.copy_options()
1518
1595
  event_data["named_spans"] = {k: span.pickle_self() for k, span in self.named_spans.items()}
1519
- if move_heights and disp_new_idxs and (not data_indexes or self.all_rows_displayed):
1520
- self.deselect("all", run_binding=False, redraw=False)
1521
- self.set_row_positions(
1522
- itr=move_elements_by_mapping(
1523
- self.get_row_heights(),
1524
- disp_new_idxs,
1525
- dict(
1526
- zip(
1527
- disp_new_idxs.values(),
1528
- disp_new_idxs,
1529
- )
1530
- ),
1531
- )
1532
- )
1533
- if create_selections:
1534
- for boxst, boxend in consecutive_ranges(sorted(disp_new_idxs.values())):
1535
- self.create_selection_box(
1536
- boxst,
1537
- 0,
1538
- boxend,
1539
- len(self.col_positions) - 1,
1540
- "rows",
1541
- run_binding=True,
1542
- )
1596
+
1543
1597
  if move_data:
1544
- self.data = move_elements_by_mapping(
1545
- self.data,
1546
- data_new_idxs,
1547
- data_old_idxs,
1548
- )
1549
1598
  maxidx = len_to_idx(totalrows)
1550
- self.RI.fix_index(maxidx)
1551
- if isinstance(self._row_index, list) and self._row_index:
1552
- self._row_index = move_elements_by_mapping(self._row_index, data_new_idxs, data_old_idxs)
1599
+ if self.PAR.ops.treeview:
1600
+ two_step_move = self.RI.move_rows_mod_nodes(
1601
+ data_new_idxs=data_new_idxs,
1602
+ data_old_idxs=data_old_idxs,
1603
+ disp_new_idxs=disp_new_idxs,
1604
+ maxidx=maxidx,
1605
+ event_data=event_data,
1606
+ undo_modification=undo_modification,
1607
+ node_change=node_change,
1608
+ )
1609
+ data_new_idxs, data_old_idxs, disp_new_idxs, event_data = next(two_step_move)
1610
+ if not data_new_idxs and not disp_new_idxs:
1611
+ return data_new_idxs, disp_new_idxs, event_data
1612
+ else:
1613
+ self.move_rows_data(data_new_idxs, data_old_idxs, maxidx)
1553
1614
  maxidx = self.get_max_row_idx(maxidx)
1554
1615
  full_new_idxs = self.get_full_new_idxs(
1555
1616
  max_idx=maxidx,
@@ -1567,6 +1628,9 @@ class MainTable(tk.Canvas):
1567
1628
  self.RI.cell_options = {full_new_idxs[k]: v for k, v in self.RI.cell_options.items()}
1568
1629
  self.RI.tree_rns = {v: full_new_idxs[k] for v, k in self.RI.tree_rns.items()}
1569
1630
  self.displayed_rows = sorted(full_new_idxs[k] for k in self.displayed_rows)
1631
+ if self.PAR.ops.treeview:
1632
+ next(two_step_move)
1633
+
1570
1634
  if self.named_spans:
1571
1635
  totalcols = self.total_data_cols()
1572
1636
  new_ops = self.PAR.create_options_from_span
@@ -1665,6 +1729,33 @@ class MainTable(tk.Canvas):
1665
1729
  del self.cell_options[(full_new_idxs[k], c)][span["type_"]]
1666
1730
  # finally, change the span coords
1667
1731
  span["from_r"], span["upto_r"] = newfrom, newupto
1732
+
1733
+ if move_heights and disp_new_idxs:
1734
+ self.set_row_positions(
1735
+ itr=move_elements_by_mapping(
1736
+ self.get_row_heights(),
1737
+ disp_new_idxs,
1738
+ dict(
1739
+ zip(
1740
+ disp_new_idxs.values(),
1741
+ disp_new_idxs,
1742
+ )
1743
+ ),
1744
+ )
1745
+ )
1746
+ if create_selections:
1747
+ self.deselect("all", run_binding=False, redraw=False)
1748
+ for boxst, boxend in consecutive_ranges(sorted(disp_new_idxs.values())):
1749
+ self.create_selection_box(
1750
+ boxst,
1751
+ 0,
1752
+ boxend,
1753
+ len(self.col_positions) - 1,
1754
+ "rows",
1755
+ run_binding=True,
1756
+ )
1757
+ else:
1758
+ self.recreate_all_selection_boxes()
1668
1759
  return data_new_idxs, disp_new_idxs, event_data
1669
1760
 
1670
1761
  def get_max_row_idx(self, maxidx: int | None = None) -> int:
@@ -1781,7 +1872,7 @@ class MainTable(tk.Canvas):
1781
1872
  event_data["cells"]["table"][k] = self.get_cell_data(k[0], k[1])
1782
1873
  return event_data
1783
1874
 
1784
- def restore_options_named_spans(self, modification: EventDataDict) -> None:
1875
+ def restore_sheet_state(self, modification: EventDataDict) -> None:
1785
1876
  if "cell_options" in modification["options"]:
1786
1877
  self.cell_options = modification["options"]["cell_options"]
1787
1878
  if "column_options" in modification["options"]:
@@ -1798,17 +1889,25 @@ class MainTable(tk.Canvas):
1798
1889
  self.tagged_rows = modification["options"]["tagged_rows"]
1799
1890
  if "tagged_columns" in modification["options"]:
1800
1891
  self.tagged_columns = modification["options"]["tagged_columns"]
1801
- self.named_spans = {
1802
- k: mod_span_widget(unpickle_obj(v), self.PAR) for k, v in modification["named_spans"].items()
1803
- }
1892
+ if modification["named_spans"]:
1893
+ self.named_spans = {
1894
+ k: mod_span_widget(unpickle_obj(v), self.PAR) for k, v in modification["named_spans"].items()
1895
+ }
1896
+ if modification["sheet_state"]:
1897
+ self.RI.tree_open_ids = modification["sheet_state"]["tree_open_ids"]
1898
+ self.row_positions = modification["sheet_state"]["row_positions"]
1899
+ self.col_positions = modification["sheet_state"]["col_positions"]
1900
+ self.displayed_rows = modification["sheet_state"]["displayed_rows"]
1901
+ self.displayed_columns = modification["sheet_state"]["displayed_columns"]
1902
+ self.all_rows_displayed = modification["sheet_state"]["all_rows_displayed"]
1903
+ self.all_columns_displayed = modification["sheet_state"]["all_columns_displayed"]
1904
+ self.saved_row_heights = modification["sheet_state"]["saved_row_heights"]
1905
+ self.saved_column_widths = modification["sheet_state"]["saved_column_widths"]
1906
+ self.recreate_all_selection_boxes()
1804
1907
 
1805
1908
  def undo_modification_invert_event(self, modification: EventDataDict, name: str = "undo") -> EventDataDict:
1806
1909
  self.deselect("all", redraw=False)
1807
- event_data = event_dict(
1808
- name=modification["eventname"],
1809
- sheet=self.PAR.name,
1810
- widget=self,
1811
- )
1910
+ event_data = self.new_event_dict(modification["eventname"], state=True)
1812
1911
  event_data["selection_boxes"] = modification["selection_boxes"]
1813
1912
  event_data["selected"] = modification["selected"]
1814
1913
  saved_cells = False
@@ -1839,7 +1938,6 @@ class MainTable(tk.Canvas):
1839
1938
  "data": data_new_idxs,
1840
1939
  "displayed": disp_new_idxs,
1841
1940
  }
1842
- self.restore_options_named_spans(modification)
1843
1941
 
1844
1942
  if modification["moved"]["rows"]:
1845
1943
  totalrows = max(self.total_data_rows(), max(modification["moved"]["rows"]["data"].values()))
@@ -1859,12 +1957,12 @@ class MainTable(tk.Canvas):
1859
1957
  )
1860
1958
  ),
1861
1959
  event_data=event_data,
1960
+ undo_modification=modification,
1862
1961
  )
1863
1962
  event_data["moved"]["rows"] = {
1864
1963
  "data": data_new_idxs,
1865
1964
  "displayed": disp_new_idxs,
1866
1965
  }
1867
- self.restore_options_named_spans(modification)
1868
1966
 
1869
1967
  if modification["added"]["rows"]:
1870
1968
  self.deselect("all", run_binding=False, redraw=False)
@@ -1875,8 +1973,8 @@ class MainTable(tk.Canvas):
1875
1973
  event_data = self.delete_rows_displayed(
1876
1974
  rows=tuple(reversed(modification["added"]["rows"]["row_heights"])),
1877
1975
  event_data=event_data,
1976
+ restored_state=True,
1878
1977
  )
1879
- self.displayed_rows = modification["added"]["rows"]["displayed_rows"]
1880
1978
 
1881
1979
  if modification["added"]["columns"]:
1882
1980
  self.deselect("all", run_binding=False, redraw=False)
@@ -1887,21 +1985,21 @@ class MainTable(tk.Canvas):
1887
1985
  event_data = self.delete_columns_displayed(
1888
1986
  cols=tuple(reversed(modification["added"]["columns"]["column_widths"])),
1889
1987
  event_data=event_data,
1988
+ restored_state=True,
1890
1989
  )
1891
- self.displayed_columns = modification["added"]["columns"]["displayed_columns"]
1892
1990
 
1893
1991
  if modification["deleted"]["rows"] or modification["deleted"]["row_heights"]:
1992
+ event_data["treeview"] = modification["treeview"]
1894
1993
  self.add_rows(
1895
1994
  rows=modification["deleted"]["rows"],
1896
1995
  index=modification["deleted"]["index"],
1897
1996
  row_heights=modification["deleted"]["row_heights"],
1898
1997
  event_data=event_data,
1899
- displayed_rows=modification["deleted"]["displayed_rows"],
1900
1998
  create_ops=False,
1901
- create_selections=not modification["eventname"].startswith("edit"),
1999
+ create_selections=False,
1902
2000
  add_col_positions=False,
2001
+ restored_state=True,
1903
2002
  )
1904
- self.restore_options_named_spans(modification)
1905
2003
 
1906
2004
  if modification["deleted"]["columns"] or modification["deleted"]["column_widths"]:
1907
2005
  self.add_columns(
@@ -1909,12 +2007,11 @@ class MainTable(tk.Canvas):
1909
2007
  header=modification["deleted"]["header"],
1910
2008
  column_widths=modification["deleted"]["column_widths"],
1911
2009
  event_data=event_data,
1912
- displayed_columns=modification["deleted"]["displayed_columns"],
1913
2010
  create_ops=False,
1914
- create_selections=not modification["eventname"].startswith("edit"),
2011
+ create_selections=False,
1915
2012
  add_row_positions=False,
2013
+ restored_state=True,
1916
2014
  )
1917
- self.restore_options_named_spans(modification)
1918
2015
 
1919
2016
  if modification["eventname"].startswith(("edit", "move")):
1920
2017
  if not saved_cells:
@@ -1927,6 +2024,8 @@ class MainTable(tk.Canvas):
1927
2024
  elif modification["eventname"].startswith("delete"):
1928
2025
  event_data["eventname"] = modification["eventname"].replace("delete", "add")
1929
2026
 
2027
+ self.restore_sheet_state(modification)
2028
+
1930
2029
  if not modification["eventname"].startswith("move") and (
1931
2030
  len(self.row_positions) > 1 or len(self.col_positions) > 1
1932
2031
  ):
@@ -2819,7 +2918,7 @@ class MainTable(tk.Canvas):
2819
2918
  self.menu_add_command(
2820
2919
  self.CH.ch_rc_popup_menu,
2821
2920
  label=self.PAR.ops.delete_columns_label,
2822
- command=self.rc_delete_columns,
2921
+ command=self.delete_columns,
2823
2922
  **mnkwgs,
2824
2923
  )
2825
2924
  if self.rc_insert_column_enabled:
@@ -2845,7 +2944,7 @@ class MainTable(tk.Canvas):
2845
2944
  self.menu_add_command(
2846
2945
  self.RI.ri_rc_popup_menu,
2847
2946
  label=self.PAR.ops.delete_rows_label,
2848
- command=self.rc_delete_rows,
2947
+ command=self.delete_rows,
2849
2948
  **mnkwgs,
2850
2949
  )
2851
2950
  if self.rc_insert_row_enabled:
@@ -2867,6 +2966,99 @@ class MainTable(tk.Canvas):
2867
2966
  command=lambda: self.rc_add_rows("below"),
2868
2967
  **mnkwgs,
2869
2968
  )
2969
+ if self.rc_sort_cells_enabled:
2970
+ self.menu_add_command(
2971
+ self.rc_popup_menu,
2972
+ label=self.PAR.ops.sort_cells_label,
2973
+ accelerator=self.PAR.ops.sort_cells_accelerator,
2974
+ command=self.sort_boxes,
2975
+ **mnkwgs,
2976
+ )
2977
+ self.menu_add_command(
2978
+ self.rc_popup_menu,
2979
+ label=self.PAR.ops.sort_cells_reverse_label,
2980
+ accelerator=self.PAR.ops.sort_cells_reverse_accelerator,
2981
+ command=lambda: self.sort_boxes(reverse=True),
2982
+ **mnkwgs,
2983
+ )
2984
+ self.menu_add_command(
2985
+ self.rc_popup_menu,
2986
+ label=self.PAR.ops.sort_cells_x_label,
2987
+ accelerator=self.PAR.ops.sort_cells_x_accelerator,
2988
+ command=lambda: self.sort_boxes(row_wise=True),
2989
+ **mnkwgs,
2990
+ )
2991
+ self.menu_add_command(
2992
+ self.rc_popup_menu,
2993
+ label=self.PAR.ops.sort_cells_x_reverse_label,
2994
+ accelerator=self.PAR.ops.sort_cells_x_reverse_accelerator,
2995
+ command=lambda: self.sort_boxes(reverse=True, row_wise=True),
2996
+ **mnkwgs,
2997
+ )
2998
+ # row index sort rows cells
2999
+ if self.rc_sort_row_enabled:
3000
+ self.menu_add_command(
3001
+ self.RI.ri_rc_popup_menu,
3002
+ label=self.PAR.ops.sort_row_label,
3003
+ accelerator=self.PAR.ops.sort_row_accelerator,
3004
+ command=self.RI._sort_rows,
3005
+ **mnkwgs,
3006
+ )
3007
+ self.menu_add_command(
3008
+ self.RI.ri_rc_popup_menu,
3009
+ label=self.PAR.ops.sort_row_reverse_label,
3010
+ accelerator=self.PAR.ops.sort_row_reverse_accelerator,
3011
+ command=lambda: self.RI._sort_rows(reverse=True),
3012
+ **mnkwgs,
3013
+ )
3014
+ # header sort columns cells
3015
+ if self.rc_sort_column_enabled:
3016
+ self.menu_add_command(
3017
+ self.CH.ch_rc_popup_menu,
3018
+ label=self.PAR.ops.sort_column_label,
3019
+ accelerator=self.PAR.ops.sort_column_accelerator,
3020
+ command=self.CH._sort_columns,
3021
+ **mnkwgs,
3022
+ )
3023
+ self.menu_add_command(
3024
+ self.CH.ch_rc_popup_menu,
3025
+ label=self.PAR.ops.sort_column_reverse_label,
3026
+ accelerator=self.PAR.ops.sort_column_reverse_accelerator,
3027
+ command=lambda: self.CH._sort_columns(reverse=True),
3028
+ **mnkwgs,
3029
+ )
3030
+ # row index sort columns by row
3031
+ if self.rc_sort_columns_enabled:
3032
+ self.menu_add_command(
3033
+ self.RI.ri_rc_popup_menu,
3034
+ label=self.PAR.ops.sort_columns_label,
3035
+ accelerator=self.PAR.ops.sort_columns_accelerator,
3036
+ command=self.RI._sort_columns_by_row,
3037
+ **mnkwgs,
3038
+ )
3039
+ self.menu_add_command(
3040
+ self.RI.ri_rc_popup_menu,
3041
+ label=self.PAR.ops.sort_columns_reverse_label,
3042
+ accelerator=self.PAR.ops.sort_columns_reverse_accelerator,
3043
+ command=lambda: self.RI._sort_columns_by_row(reverse=True),
3044
+ **mnkwgs,
3045
+ )
3046
+ # header sort rows by column
3047
+ if self.rc_sort_rows_enabled:
3048
+ self.menu_add_command(
3049
+ self.CH.ch_rc_popup_menu,
3050
+ label=self.PAR.ops.sort_rows_label,
3051
+ accelerator=self.PAR.ops.sort_rows_accelerator,
3052
+ command=self.CH._sort_rows_by_column,
3053
+ **mnkwgs,
3054
+ )
3055
+ self.menu_add_command(
3056
+ self.CH.ch_rc_popup_menu,
3057
+ label=self.PAR.ops.sort_rows_reverse_label,
3058
+ accelerator=self.PAR.ops.sort_rows_reverse_accelerator,
3059
+ command=lambda: self.CH._sort_rows_by_column(reverse=True),
3060
+ **mnkwgs,
3061
+ )
2870
3062
  for label, func in self.extra_table_rc_menu_funcs.items():
2871
3063
  self.menu_add_command(
2872
3064
  self.rc_popup_menu,
@@ -3037,6 +3229,16 @@ class MainTable(tk.Canvas):
3037
3229
  self.rc_insert_row_enabled = True
3038
3230
  self.rc_popup_menus_enabled = True
3039
3231
  self.rc_select_enabled = True
3232
+ if binding in ("all", "sort_cells"):
3233
+ self.rc_sort_cells_enabled = True
3234
+ if binding in ("all", "sort_row"):
3235
+ self.rc_sort_row_enabled = True
3236
+ if binding in ("all", "sort_column", "sort_col"):
3237
+ self.rc_sort_column_enabled = True
3238
+ if binding in ("all", "sort_columns", "sort_cols"):
3239
+ self.rc_sort_columns_enabled = True
3240
+ if binding in ("all", "sort_rows"):
3241
+ self.rc_sort_rows_enabled = True
3040
3242
  if binding in ("all", "right_click_popup_menu", "rc_popup_menu", "rc_menu"):
3041
3243
  self.rc_popup_menus_enabled = True
3042
3244
  self.rc_select_enabled = True
@@ -3110,6 +3312,16 @@ class MainTable(tk.Canvas):
3110
3312
  self.rc_insert_column_enabled = False
3111
3313
  if binding in bind_add_rows:
3112
3314
  self.rc_insert_row_enabled = False
3315
+ if binding in ("all", "sort_cells"):
3316
+ self.rc_sort_cells_enabled = False
3317
+ if binding in ("all", "sort_row"):
3318
+ self.rc_sort_row_enabled = False
3319
+ if binding in ("all", "sort_column", "sort_col"):
3320
+ self.rc_sort_column_enabled = False
3321
+ if binding in ("all", "sort_columns", "sort_cols"):
3322
+ self.rc_sort_columns_enabled = False
3323
+ if binding in ("all", "sort_rows"):
3324
+ self.rc_sort_rows_enabled = False
3113
3325
  if binding in ("all", "right_click_popup_menu", "rc_popup_menu", "rc_menu"):
3114
3326
  self.rc_popup_menus_enabled = False
3115
3327
  if binding in ("all", "right_click_select", "rc_select"):
@@ -3255,12 +3467,12 @@ class MainTable(tk.Canvas):
3255
3467
  t, sh = self.hidd_resize_lines.popitem()
3256
3468
  self.coords(t, x1, y1, x2, y2)
3257
3469
  if sh:
3258
- self.itemconfig(t, width=width, fill=fill, tag=tag)
3470
+ self.itemconfig(t, width=width, fill=fill, tags=tag)
3259
3471
  else:
3260
- self.itemconfig(t, width=width, fill=fill, tag=tag, state="normal")
3472
+ self.itemconfig(t, width=width, fill=fill, tags=tag, state="normal")
3261
3473
  self.lift(t)
3262
3474
  else:
3263
- t = self.create_line(x1, y1, x2, y2, width=width, fill=fill, tag=tag)
3475
+ t = self.create_line(x1, y1, x2, y2, width=width, fill=fill, tags=tag)
3264
3476
  self.disp_resize_lines[t] = True
3265
3477
 
3266
3478
  def delete_resize_lines(self):
@@ -3768,38 +3980,14 @@ class MainTable(tk.Canvas):
3768
3980
  c_pc = 0.0
3769
3981
  old_min_row_height = int(self.min_row_height)
3770
3982
  old_default_row_height = int(self.get_default_row_height())
3771
- self.set_table_font(
3772
- table_font,
3773
- reset_row_positions=False,
3774
- )
3775
- self.set_index_font(index_font)
3983
+ self.set_table_font(table_font, row_heights=False)
3984
+ self.set_index_font(index_font, row_heights=False)
3776
3985
  self.set_header_font(header_font)
3777
3986
  if self.PAR.ops.set_cell_sizes_on_zoom:
3778
3987
  self.set_all_cell_sizes_to_text()
3779
3988
  self.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
3780
3989
  elif not self.PAR.ops.set_cell_sizes_on_zoom:
3781
- default_row_height = self.get_default_row_height()
3782
- self.row_positions = list(
3783
- accumulate(
3784
- chain(
3785
- [0],
3786
- (
3787
- (
3788
- self.min_row_height
3789
- if h == old_min_row_height
3790
- else (
3791
- default_row_height
3792
- if h == old_default_row_height
3793
- else self.min_row_height
3794
- if h < self.min_row_height
3795
- else h
3796
- )
3797
- )
3798
- for h in self.gen_row_heights()
3799
- ),
3800
- )
3801
- )
3802
- )
3990
+ self.change_font_manage_row_heights(old_min_row_height, old_default_row_height)
3803
3991
  self.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
3804
3992
  self.recreate_all_selection_boxes()
3805
3993
  self.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
@@ -3843,13 +4031,20 @@ class MainTable(tk.Canvas):
3843
4031
  return b[2] - b[0], b[3] - b[1]
3844
4032
 
3845
4033
  def get_lines_cell_height(self, n: int, font: None | FontTuple = None) -> int:
3846
- return (
3847
- self.get_txt_h(
4034
+ if font == self.PAR.ops.table_font:
4035
+ return 3 + (n * self.table_txt_height)
4036
+
4037
+ elif font == self.PAR.ops.index_font:
4038
+ return 3 + (n * self.index_txt_height)
4039
+
4040
+ elif font == self.PAR.ops.header_font:
4041
+ return 3 + (n * self.header_txt_height)
4042
+
4043
+ else:
4044
+ return 3 + self.get_txt_h(
3848
4045
  txt="\n".join("|" for _ in range(n)) if n > 1 else "|",
3849
- font=self.PAR.ops.table_font if font is None else font,
4046
+ font=font,
3850
4047
  )
3851
- + 5
3852
- )
3853
4048
 
3854
4049
  def set_min_column_width(self, width: int) -> None:
3855
4050
  if width:
@@ -3880,62 +4075,95 @@ class MainTable(tk.Canvas):
3880
4075
  )
3881
4076
  return self.PAR.ops.default_header_height
3882
4077
 
3883
- def set_table_font(self, newfont: tuple | None = None, reset_row_positions: bool = False) -> tuple[str, int, str]:
4078
+ def check_font(self, newfont: tuple) -> None:
4079
+ if (
4080
+ not isinstance(newfont, tuple)
4081
+ or len(newfont) != 3
4082
+ or not isinstance(newfont[0], str)
4083
+ or not isinstance(newfont[1], int)
4084
+ or not isinstance(newfont[2], str)
4085
+ ):
4086
+ raise ValueError(font_value_error)
4087
+
4088
+ def set_table_font(self, newfont: tuple | None = None, row_heights: bool = True) -> tuple[str, int, str]:
3884
4089
  if newfont:
3885
- if not isinstance(newfont, tuple):
3886
- raise ValueError("Argument must be tuple e.g. ('Carlito', 12, 'normal')")
3887
- if len(newfont) != 3:
3888
- raise ValueError("Argument must be three-tuple")
3889
- if not isinstance(newfont[0], str) or not isinstance(newfont[1], int) or not isinstance(newfont[2], str):
3890
- raise ValueError(
3891
- "Argument must be font, size and 'normal', 'bold' or'italic' e.g. ('Carlito',12,'normal')"
3892
- )
4090
+ self.check_font(newfont)
3893
4091
  self.PAR.ops.table_font = FontTuple(*newfont)
4092
+ old_min_row_height = int(self.min_row_height)
4093
+ old_default_row_height = int(self.get_default_row_height())
3894
4094
  self.set_table_font_help()
3895
- if reset_row_positions:
3896
- if isinstance(reset_row_positions, bool):
3897
- self.reset_row_positions()
3898
- else:
3899
- self.set_row_positions(itr=reset_row_positions)
4095
+ if row_heights:
4096
+ self.change_font_manage_row_heights(old_min_row_height, old_default_row_height)
3900
4097
  self.recreate_all_selection_boxes()
3901
4098
  return self.PAR.ops.table_font
3902
4099
 
3903
4100
  def set_table_font_help(self):
4101
+ self.table_font = self.PAR.ops.table_font
4102
+ if self.PAR.ops.table_font not in self.char_widths:
4103
+ self.char_widths[self.PAR.ops.table_font] = {}
4104
+ self.table_test_str_w = self.get_txt_w(_test_str)
3904
4105
  self.table_txt_width, self.table_txt_height = self.get_txt_dimensions("|", self.PAR.ops.table_font)
3905
- self.table_half_txt_height = ceil(self.table_txt_height / 2)
3906
- if not self.table_half_txt_height % 2:
3907
- self.table_first_ln_ins = self.table_half_txt_height + 2
3908
- else:
3909
- self.table_first_ln_ins = self.table_half_txt_height + 3
3910
- self.min_row_height = int(self.table_first_ln_ins * 2.22)
3911
- self.table_xtra_lines_increment = int(self.table_txt_height)
3912
- if self.min_row_height < 12:
3913
- self.min_row_height = 12
4106
+ self.min_row_height = max(6, self.table_txt_height, self.index_txt_height) + 6
3914
4107
 
3915
- def set_header_font(self, newfont: tuple | None = None) -> tuple[str, int, str]:
4108
+ def set_index_font(self, newfont: tuple | None = None, row_heights: bool = True) -> tuple[str, int, str]:
3916
4109
  if newfont:
3917
- if not isinstance(newfont, tuple):
3918
- raise ValueError("Argument must be tuple e.g. ('Carlito', 12, 'normal')")
3919
- if len(newfont) != 3:
3920
- raise ValueError("Argument must be three-tuple")
3921
- if not isinstance(newfont[0], str) or not isinstance(newfont[1], int) or not isinstance(newfont[2], str):
3922
- raise ValueError(
3923
- "Argument must be font, size and 'normal', 'bold' or'italic' e.g. ('Carlito',12,'normal')"
4110
+ self.check_font(newfont)
4111
+ self.PAR.ops.index_font = FontTuple(*newfont)
4112
+ old_min_row_height = int(self.min_row_height)
4113
+ old_default_row_height = int(self.get_default_row_height())
4114
+ self.set_index_font_help()
4115
+ if row_heights:
4116
+ self.change_font_manage_row_heights(old_min_row_height, old_default_row_height)
4117
+ self.recreate_all_selection_boxes()
4118
+ return self.PAR.ops.index_font
4119
+
4120
+ def set_index_font_help(self):
4121
+ self.RI.index_font = self.PAR.ops.index_font
4122
+ if self.PAR.ops.index_font not in self.char_widths:
4123
+ self.char_widths[self.PAR.ops.index_font] = {}
4124
+ self.RI.index_test_str_w = self.get_txt_w(_test_str, self.PAR.ops.index_font)
4125
+ self.index_txt_width, self.index_txt_height = self.get_txt_dimensions("|", self.PAR.ops.index_font)
4126
+ self.min_row_height = max(6, self.table_txt_height, self.index_txt_height) + 6
4127
+
4128
+ def change_font_manage_row_heights(self, old_min_row_height: int, old_default_row_height: int) -> None:
4129
+ default_row_height = self.get_default_row_height()
4130
+ self.row_positions = list(
4131
+ accumulate(
4132
+ chain(
4133
+ [0],
4134
+ (
4135
+ (
4136
+ self.min_row_height
4137
+ if h == old_min_row_height
4138
+ else (
4139
+ default_row_height
4140
+ if h == old_default_row_height
4141
+ else self.min_row_height
4142
+ if h < self.min_row_height
4143
+ else h
4144
+ )
4145
+ )
4146
+ for h in self.gen_row_heights()
4147
+ ),
3924
4148
  )
4149
+ )
4150
+ )
4151
+
4152
+ def set_header_font(self, newfont: tuple | None = None) -> tuple[str, int, str]:
4153
+ if newfont:
4154
+ self.check_font(newfont)
3925
4155
  self.PAR.ops.header_font = FontTuple(*newfont)
3926
4156
  self.set_header_font_help()
3927
4157
  self.recreate_all_selection_boxes()
3928
4158
  return self.PAR.ops.header_font
3929
4159
 
3930
4160
  def set_header_font_help(self):
4161
+ self.CH.header_font = self.PAR.ops.header_font
4162
+ if self.PAR.ops.header_font not in self.char_widths:
4163
+ self.char_widths[self.PAR.ops.header_font] = {}
4164
+ self.CH.header_test_str_w = self.get_txt_w(_test_str, self.PAR.ops.header_font)
3931
4165
  self.header_txt_width, self.header_txt_height = self.get_txt_dimensions("|", self.PAR.ops.header_font)
3932
- self.header_half_txt_height = ceil(self.header_txt_height / 2)
3933
- if not self.header_half_txt_height % 2:
3934
- self.header_first_ln_ins = self.header_half_txt_height + 2
3935
- else:
3936
- self.header_first_ln_ins = self.header_half_txt_height + 3
3937
- self.header_xtra_lines_increment = self.header_txt_height
3938
- self.min_header_height = int(self.header_first_ln_ins * 2.22)
4166
+ self.min_header_height = self.header_txt_height + 6
3939
4167
  if (
3940
4168
  isinstance(self.PAR.ops.default_header_height, int)
3941
4169
  and self.PAR.ops.default_header_height < self.min_header_height
@@ -3943,30 +4171,6 @@ class MainTable(tk.Canvas):
3943
4171
  self.PAR.ops.default_header_height = int(self.min_header_height)
3944
4172
  self.CH.set_height(self.get_default_header_height(), set_TL=True)
3945
4173
 
3946
- def set_index_font(self, newfont: tuple | None = None) -> tuple[str, int, str]:
3947
- if newfont:
3948
- if not isinstance(newfont, tuple):
3949
- raise ValueError("Argument must be tuple e.g. ('Carlito', 12, 'normal')")
3950
- if len(newfont) != 3:
3951
- raise ValueError("Argument must be three-tuple")
3952
- if not isinstance(newfont[0], str) or not isinstance(newfont[1], int) or not isinstance(newfont[2], str):
3953
- raise ValueError(
3954
- "Argument must be font, size and 'normal', 'bold' or'italic' e.g. ('Carlito',12,'normal')"
3955
- )
3956
- self.PAR.ops.index_font = FontTuple(*newfont)
3957
- self.set_index_font_help()
3958
- return self.PAR.ops.index_font
3959
-
3960
- def set_index_font_help(self):
3961
- self.index_txt_width, self.index_txt_height = self.get_txt_dimensions("|", self.PAR.ops.index_font)
3962
- self.index_half_txt_height = ceil(self.index_txt_height / 2)
3963
- if not self.index_half_txt_height % 2:
3964
- self.index_first_ln_ins = self.index_half_txt_height + 2
3965
- else:
3966
- self.index_first_ln_ins = self.index_half_txt_height + 3
3967
- self.index_xtra_lines_increment = self.index_txt_height
3968
- self.min_index_width = 5
3969
-
3970
4174
  def purge_undo_and_redo_stack(self):
3971
4175
  self.undo_stack = deque(maxlen=self.PAR.ops.max_undos)
3972
4176
  self.redo_stack = deque(maxlen=self.PAR.ops.max_undos)
@@ -4088,6 +4292,58 @@ class MainTable(tk.Canvas):
4088
4292
  else:
4089
4293
  return False
4090
4294
 
4295
+ def get_cell_max_width(self, datarn: int, dispcn: int) -> int:
4296
+ datacn = self.datacn(dispcn)
4297
+ col_width = self.col_positions[dispcn + 1] - self.col_positions[dispcn]
4298
+ if kwargs := self.get_cell_kwargs(datarn, datacn, "dropdown"):
4299
+ max_width = col_width - self.table_txt_height - 5
4300
+ else:
4301
+ max_width = col_width - 2
4302
+ if (kwargs := self.get_cell_kwargs(datarn, datacn, "dropdown")) and max_width > self.table_txt_height + 1:
4303
+ box_w = self.table_txt_height + 1
4304
+ max_width -= box_w + 4
4305
+ if self.PAR.ops.allow_cell_overflow and not kwargs:
4306
+ if self.cells_cache is None:
4307
+ disprn = self.disprn(datarn)
4308
+ self.cells_cache = self._redraw_precache_cells(disprn, disprn + 1, 0, len(self.col_positions) - 1)
4309
+ if not (align := self.get_cell_kwargs(datarn, datacn, key="align")):
4310
+ align = self.align
4311
+ if align.endswith("w"):
4312
+ max_width += sum(
4313
+ self._overflow(
4314
+ self.cells_cache,
4315
+ range(dispcn + 1, len(self.col_positions) - 1),
4316
+ datarn,
4317
+ )
4318
+ )
4319
+ elif align.endswith("e"):
4320
+ max_width += sum(
4321
+ self._overflow(
4322
+ self.cells_cache,
4323
+ reversed(range(0, dispcn)),
4324
+ datarn,
4325
+ )
4326
+ )
4327
+ return max_width
4328
+
4329
+ def get_wrapped_cell_height(self, datarn: int, datacn: int) -> int:
4330
+ dispcn = self.dispcn(datacn)
4331
+ n_lines = max(
4332
+ 1,
4333
+ sum(
4334
+ 1
4335
+ for _ in wrap_text(
4336
+ text=self.get_valid_cell_data_as_str(datarn, datacn, get_displayed=True),
4337
+ max_width=self.get_cell_max_width(datarn, dispcn),
4338
+ max_lines=float("inf"),
4339
+ char_width_fn=self.wrap_get_char_w,
4340
+ widths=self.char_widths[self.table_font],
4341
+ wrap=self.PAR.ops.table_wrap,
4342
+ )
4343
+ ),
4344
+ )
4345
+ return 3 + (n_lines * self.table_txt_height)
4346
+
4091
4347
  def set_all_cell_sizes_to_text(
4092
4348
  self,
4093
4349
  width: int | None = None,
@@ -4171,26 +4427,28 @@ class MainTable(tk.Canvas):
4171
4427
  def set_col_positions(self, itr: AnyIter[float]) -> None:
4172
4428
  self.col_positions = list(accumulate(chain([0], itr)))
4173
4429
 
4174
- def reset_col_positions(self, ncols: int | None = None):
4175
- colpos = self.PAR.ops.default_column_width
4430
+ def reset_col_positions(self, ncols: int | None = None, width: int | None = None) -> None:
4431
+ if width is None:
4432
+ width = self.PAR.ops.default_column_width
4176
4433
  if isinstance(ncols, int):
4177
- self.set_col_positions(itr=repeat(colpos, ncols))
4434
+ self.set_col_positions(itr=repeat(width, ncols))
4178
4435
  elif self.all_columns_displayed:
4179
- self.set_col_positions(itr=repeat(colpos, self.total_data_cols()))
4436
+ self.set_col_positions(itr=repeat(width, self.total_data_cols()))
4180
4437
  else:
4181
- self.set_col_positions(itr=repeat(colpos, len(self.displayed_columns)))
4438
+ self.set_col_positions(itr=repeat(width, len(self.displayed_columns)))
4182
4439
 
4183
4440
  def set_row_positions(self, itr: AnyIter[float]) -> None:
4184
4441
  self.row_positions = list(accumulate(chain([0], itr)))
4185
4442
 
4186
- def reset_row_positions(self, nrows: int | None = None):
4187
- rowpos = self.get_default_row_height()
4443
+ def reset_row_positions(self, nrows: int | None = None, height: int | None = None) -> None:
4444
+ if height is None:
4445
+ height = self.get_default_row_height()
4188
4446
  if isinstance(nrows, int):
4189
- self.set_row_positions(itr=repeat(rowpos, nrows))
4447
+ self.set_row_positions(itr=repeat(height, nrows))
4190
4448
  elif self.all_rows_displayed:
4191
- self.set_row_positions(itr=repeat(rowpos, self.total_data_rows()))
4449
+ self.set_row_positions(itr=repeat(height, self.total_data_rows()))
4192
4450
  else:
4193
- self.set_row_positions(itr=repeat(rowpos, len(self.displayed_rows)))
4451
+ self.set_row_positions(itr=repeat(height, len(self.displayed_rows)))
4194
4452
 
4195
4453
  def del_col_position(self, idx: int, deselect_all: bool = False):
4196
4454
  if deselect_all:
@@ -4242,46 +4500,6 @@ class MainTable(tk.Canvas):
4242
4500
  def gen_row_heights(self) -> Generator[int]:
4243
4501
  return diff_gen(self.row_positions)
4244
4502
 
4245
- def insert_col_position(
4246
- self,
4247
- idx: Literal["end"] | int = "end",
4248
- width: int | None = None,
4249
- deselect_all: bool = False,
4250
- ) -> None:
4251
- if deselect_all:
4252
- self.deselect("all", redraw=False)
4253
- if width is None:
4254
- w = self.PAR.ops.default_column_width
4255
- else:
4256
- w = width
4257
- if idx == "end" or len(self.col_positions) == idx + 1:
4258
- self.col_positions.append(self.col_positions[-1] + w)
4259
- else:
4260
- idx += 1
4261
- self.col_positions.insert(idx, self.col_positions[idx - 1] + w)
4262
- idx += 1
4263
- self.col_positions[idx:] = [e + w for e in islice(self.col_positions, idx, len(self.col_positions))]
4264
-
4265
- def insert_row_position(
4266
- self,
4267
- idx: Literal["end"] | int = "end",
4268
- height: int | None = None,
4269
- deselect_all: bool = False,
4270
- ) -> None:
4271
- if deselect_all:
4272
- self.deselect("all", redraw=False)
4273
- if height is None:
4274
- h = self.get_default_row_height()
4275
- else:
4276
- h = height
4277
- if idx == "end" or len(self.row_positions) == idx + 1:
4278
- self.row_positions.append(self.row_positions[-1] + h)
4279
- else:
4280
- idx += 1
4281
- self.row_positions.insert(idx, self.row_positions[idx - 1] + h)
4282
- idx += 1
4283
- self.row_positions[idx:] = [e + h for e in islice(self.row_positions, idx, len(self.row_positions))]
4284
-
4285
4503
  def insert_col_positions(
4286
4504
  self,
4287
4505
  idx: Literal["end"] | int = "end",
@@ -4368,25 +4586,13 @@ class MainTable(tk.Canvas):
4368
4586
  create_ops: bool = True,
4369
4587
  ) -> None:
4370
4588
  self.tagged_cells = {
4371
- tags: {(r, c if not (num := bisect_right(cols, c)) else c + num) for (r, c) in tagged}
4372
- for tags, tagged in self.tagged_cells.items()
4373
- }
4374
- self.cell_options = {
4375
- (r, c if not (num := bisect_right(cols, c)) else c + num): v for (r, c), v in self.cell_options.items()
4376
- }
4377
- self.progress_bars = {
4378
- (r, c if not (num := bisect_right(cols, c)) else c + num): v for (r, c), v in self.progress_bars.items()
4379
- }
4380
- self.tagged_columns = {
4381
- tags: {c if not (num := bisect_right(cols, c)) else c + num for c in tagged}
4382
- for tags, tagged in self.tagged_columns.items()
4383
- }
4384
- self.col_options = {
4385
- c if not (num := bisect_right(cols, c)) else c + num: v for c, v in self.col_options.items()
4386
- }
4387
- self.CH.cell_options = {
4388
- c if not (num := bisect_right(cols, c)) else c + num: v for c, v in self.CH.cell_options.items()
4589
+ tags: {(r, push_n(c, cols)) for (r, c) in tagged} for tags, tagged in self.tagged_cells.items()
4389
4590
  }
4591
+ self.cell_options = {(r, push_n(c, cols)): v for (r, c), v in self.cell_options.items()}
4592
+ self.progress_bars = {(r, push_n(c, cols)): v for (r, c), v in self.progress_bars.items()}
4593
+ self.tagged_columns = {tags: {push_n(c, cols) for c in tagged} for tags, tagged in self.tagged_columns.items()}
4594
+ self.col_options = {push_n(c, cols): v for c, v in self.col_options.items()}
4595
+ self.CH.cell_options = {push_n(c, cols): v for c, v in self.CH.cell_options.items()}
4390
4596
  # if there are named spans where columns were added
4391
4597
  # add options to gap which was created by adding columns
4392
4598
  totalrows = None
@@ -4436,32 +4642,18 @@ class MainTable(tk.Canvas):
4436
4642
 
4437
4643
  def adjust_options_post_add_rows(
4438
4644
  self,
4439
- rows: list | tuple,
4645
+ rows: list[int] | tuple[int],
4440
4646
  create_ops: bool = True,
4441
4647
  ) -> None:
4442
4648
  self.tagged_cells = {
4443
- tags: {(r if not (num := bisect_right(rows, r)) else r + num, c) for (r, c) in tagged}
4444
- for tags, tagged in self.tagged_cells.items()
4445
- }
4446
- self.cell_options = {
4447
- (r if not (num := bisect_right(rows, r)) else r + num, c): v for (r, c), v in self.cell_options.items()
4448
- }
4449
- self.progress_bars = {
4450
- (r if not (num := bisect_right(rows, r)) else r + num, c): v for (r, c), v in self.progress_bars.items()
4451
- }
4452
- self.tagged_rows = {
4453
- tags: {r if not (num := bisect_right(rows, r)) else r + num for r in tagged}
4454
- for tags, tagged in self.tagged_rows.items()
4455
- }
4456
- self.row_options = {
4457
- r if not (num := bisect_right(rows, r)) else r + num: v for r, v in self.row_options.items()
4458
- }
4459
- self.RI.cell_options = {
4460
- r if not (num := bisect_right(rows, r)) else r + num: v for r, v in self.RI.cell_options.items()
4461
- }
4462
- self.RI.tree_rns = {
4463
- v: r if not (num := bisect_right(rows, r)) else r + num for v, r in self.RI.tree_rns.items()
4649
+ tags: {(push_n(r, rows), c) for (r, c) in tagged} for tags, tagged in self.tagged_cells.items()
4464
4650
  }
4651
+ self.cell_options = {(push_n(r, rows), c): v for (r, c), v in self.cell_options.items()}
4652
+ self.progress_bars = {(push_n(r, rows), c): v for (r, c), v in self.progress_bars.items()}
4653
+ self.tagged_rows = {tags: {push_n(r, rows) for r in tagged} for tags, tagged in self.tagged_rows.items()}
4654
+ self.row_options = {push_n(r, rows): v for r, v in self.row_options.items()}
4655
+ self.RI.cell_options = {push_n(r, rows): v for r, v in self.RI.cell_options.items()}
4656
+ self.RI.tree_rns = {k: push_n(r, rows) for k, r in self.RI.tree_rns.items()}
4465
4657
  # if there are named spans where rows were added
4466
4658
  # add options to gap which was created by adding rows
4467
4659
  totalcols = None
@@ -4696,29 +4888,27 @@ class MainTable(tk.Canvas):
4696
4888
  header: dict,
4697
4889
  column_widths: dict,
4698
4890
  event_data: dict,
4699
- displayed_columns: None | list[int] = None,
4700
4891
  create_ops: bool = True,
4701
4892
  create_selections: bool = True,
4702
4893
  add_row_positions: bool = True,
4703
4894
  push_ops: bool = True,
4704
4895
  mod_event_boxes: bool = True,
4896
+ restored_state: bool = False,
4705
4897
  ) -> EventDataDict:
4706
4898
  self.saved_column_widths = {}
4707
- saved_displayed_columns = list(self.displayed_columns)
4708
- if isinstance(displayed_columns, list):
4709
- self.displayed_columns = displayed_columns
4710
- elif not self.all_columns_displayed:
4899
+ if not restored_state and not self.all_columns_displayed:
4711
4900
  self.displayed_columns = add_to_displayed(self.displayed_columns, columns)
4712
4901
  cws = self.get_column_widths()
4713
4902
  if column_widths and next(reversed(column_widths)) > len(cws):
4714
4903
  for i in reversed(range(len(cws), len(cws) + next(reversed(column_widths)) - len(cws))):
4715
4904
  column_widths[i] = self.PAR.ops.default_column_width
4716
- self.set_col_positions(
4717
- itr=insert_items(
4718
- cws,
4719
- column_widths,
4905
+ if not restored_state:
4906
+ self.set_col_positions(
4907
+ itr=insert_items(
4908
+ cws,
4909
+ column_widths,
4910
+ )
4720
4911
  )
4721
- )
4722
4912
  # rn needed for indexing but cn insert
4723
4913
  maxrn = 0
4724
4914
  for cn, rowdict in reversed(columns.items()):
@@ -4737,14 +4927,14 @@ class MainTable(tk.Canvas):
4737
4927
  "table": {},
4738
4928
  "index": {},
4739
4929
  "row_heights": {rn: default_height for rn in range(len(self.row_positions) - 1, maxrn + 1)},
4740
- "displayed_rows": self.displayed_rows,
4741
4930
  }
4742
- self.set_row_positions(
4743
- itr=chain(
4744
- self.gen_row_heights(),
4745
- repeat(default_height, maxrn + 1 - (len(self.row_positions) - 1)),
4931
+ if not restored_state:
4932
+ self.set_row_positions(
4933
+ itr=chain(
4934
+ self.gen_row_heights(),
4935
+ repeat(default_height, maxrn + 1 - (len(self.row_positions) - 1)),
4936
+ )
4746
4937
  )
4747
- )
4748
4938
  if isinstance(self._headers, list) and header:
4749
4939
  self._headers = insert_items(self._headers, header, self.CH.fix_header)
4750
4940
  if push_ops:
@@ -4770,7 +4960,6 @@ class MainTable(tk.Canvas):
4770
4960
  "table": columns,
4771
4961
  "header": header,
4772
4962
  "column_widths": column_widths,
4773
- "displayed_columns": saved_displayed_columns,
4774
4963
  }
4775
4964
  return event_data
4776
4965
 
@@ -4807,17 +4996,14 @@ class MainTable(tk.Canvas):
4807
4996
  numcols = self.PAR.ops.paste_insert_column_limit - len(self.col_positions) - 1
4808
4997
  if numcols < 1:
4809
4998
  return
4810
- event_data = event_dict(
4811
- name="add_columns",
4812
- sheet=self.PAR.name,
4813
- widget=self,
4814
- boxes=self.get_boxes(),
4815
- selected=self.selected,
4816
- )
4999
+ event_data = self.new_event_dict("add_columns", state=True)
4817
5000
  if not try_binding(self.extra_begin_insert_cols_rc_func, event_data, "begin_add_columns"):
4818
5001
  return
5002
+ columns, headers, widths = self.get_args_for_add_columns(data_ins_col, displayed_ins_col, numcols)
4819
5003
  event_data = self.add_columns(
4820
- *self.get_args_for_add_columns(data_ins_col, displayed_ins_col, numcols),
5004
+ columns=columns,
5005
+ header=headers if isinstance(self._headers, list) and self._headers else {},
5006
+ column_widths=widths,
4821
5007
  event_data=event_data,
4822
5008
  )
4823
5009
  if self.undo_enabled:
@@ -4828,34 +5014,33 @@ class MainTable(tk.Canvas):
4828
5014
 
4829
5015
  def add_rows(
4830
5016
  self,
4831
- rows: dict,
4832
- index: dict,
4833
- row_heights: dict,
4834
- event_data: dict,
4835
- displayed_rows: None | list[int] = None,
5017
+ rows: dict[int, list[object]],
5018
+ index: dict[int, object],
5019
+ row_heights: dict[int, float | int],
5020
+ event_data: EventDataDict,
4836
5021
  create_ops: bool = True,
4837
5022
  create_selections: bool = True,
4838
5023
  add_col_positions: bool = True,
4839
5024
  push_ops: bool = True,
5025
+ tree: bool = True,
4840
5026
  mod_event_boxes: bool = True,
5027
+ restored_state: bool = False,
4841
5028
  ) -> EventDataDict:
4842
5029
  self.saved_row_heights = {}
4843
- saved_displayed_rows = list(self.displayed_rows)
4844
- if isinstance(displayed_rows, list):
4845
- self.displayed_rows = displayed_rows
4846
- elif not self.all_rows_displayed:
5030
+ if not restored_state and not self.all_rows_displayed:
4847
5031
  self.displayed_rows = add_to_displayed(self.displayed_rows, rows)
4848
5032
  rhs = self.get_row_heights()
4849
5033
  if row_heights and next(reversed(row_heights)) > len(rhs):
4850
5034
  default_row_height = self.get_default_row_height()
4851
5035
  for i in reversed(range(len(rhs), len(rhs) + next(reversed(row_heights)) - len(rhs))):
4852
5036
  row_heights[i] = default_row_height
4853
- self.set_row_positions(
4854
- itr=insert_items(
4855
- rhs,
4856
- row_heights,
5037
+ if not restored_state:
5038
+ self.set_row_positions(
5039
+ itr=insert_items(
5040
+ rhs,
5041
+ row_heights,
5042
+ )
4857
5043
  )
4858
- )
4859
5044
  maxcn = 0
4860
5045
  # rn needed for insert but cn indexing
4861
5046
  for rn, row in reversed(rows.items()):
@@ -4874,14 +5059,14 @@ class MainTable(tk.Canvas):
4874
5059
  "table": {},
4875
5060
  "header": {},
4876
5061
  "column_widths": {cn: default_width for cn in range(len(self.col_positions) - 1, maxcn + 1)},
4877
- "displayed_columns": self.displayed_columns,
4878
5062
  }
4879
- self.set_col_positions(
4880
- itr=chain(
4881
- self.gen_column_widths(),
4882
- repeat(default_width, maxcn + 1 - (len(self.col_positions) - 1)),
5063
+ if not restored_state:
5064
+ self.set_col_positions(
5065
+ itr=chain(
5066
+ self.gen_column_widths(),
5067
+ repeat(default_width, maxcn + 1 - (len(self.col_positions) - 1)),
5068
+ )
4883
5069
  )
4884
- )
4885
5070
  if push_ops:
4886
5071
  self.adjust_options_post_add_rows(
4887
5072
  rows=tuple(reversed(rows)),
@@ -4905,8 +5090,9 @@ class MainTable(tk.Canvas):
4905
5090
  "table": rows,
4906
5091
  "index": index,
4907
5092
  "row_heights": row_heights,
4908
- "displayed_rows": saved_displayed_rows,
4909
5093
  }
5094
+ if tree and self.PAR.ops.treeview:
5095
+ event_data = self.RI.tree_add_rows(event_data=event_data)
4910
5096
  return event_data
4911
5097
 
4912
5098
  def rc_add_rows(self, event: object = None):
@@ -4942,17 +5128,14 @@ class MainTable(tk.Canvas):
4942
5128
  numrows = self.PAR.ops.paste_insert_row_limit - len(self.row_positions) - 1
4943
5129
  if numrows < 1:
4944
5130
  return
4945
- event_data = event_dict(
4946
- name="add_rows",
4947
- sheet=self.PAR.name,
4948
- widget=self,
4949
- boxes=self.get_boxes(),
4950
- selected=self.selected,
4951
- )
5131
+ event_data = self.new_event_dict("add_rows", state=True)
4952
5132
  if not try_binding(self.extra_begin_insert_rows_rc_func, event_data, "begin_add_rows"):
4953
5133
  return
5134
+ rows, index, heights = self.get_args_for_add_rows(data_ins_row, displayed_ins_row, numrows)
4954
5135
  event_data = self.add_rows(
4955
- *self.get_args_for_add_rows(data_ins_row, displayed_ins_row, numrows),
5136
+ rows=rows,
5137
+ index=index if isinstance(self._row_index, list) and self._row_index else {},
5138
+ row_heights=heights,
4956
5139
  event_data=event_data,
4957
5140
  )
4958
5141
  if self.undo_enabled:
@@ -5029,10 +5212,14 @@ class MainTable(tk.Canvas):
5029
5212
  for datarn, v in zip(reversed(range(data_ins_row, data_ins_row + numrows)), reversed(rows))
5030
5213
  }
5031
5214
  else:
5032
- index_data = {
5033
- datarn: self.RI.get_value_for_empty_cell(datarn, r_ops=False)
5034
- for datarn in reversed(range(data_ins_row, data_ins_row + numrows))
5035
- }
5215
+ if self.PAR.ops.treeview:
5216
+ nodes = [self.RI.get_value_for_empty_cell(data_ins_row, r_ops=False) for _ in range(numrows)]
5217
+ index_data = dict(zip(reversed(range(data_ins_row, data_ins_row + numrows)), reversed(nodes)))
5218
+ else:
5219
+ index_data = {
5220
+ datarn: self.RI.get_value_for_empty_cell(datarn, r_ops=False)
5221
+ for datarn in reversed(range(data_ins_row, data_ins_row + numrows))
5222
+ }
5036
5223
  if rows is None:
5037
5224
  if total_data_cols is None:
5038
5225
  total_data_cols = self.total_data_cols()
@@ -5072,11 +5259,27 @@ class MainTable(tk.Canvas):
5072
5259
  "tagged_columns": {f"{tag}": set(s) for tag, s in self.tagged_columns.items()},
5073
5260
  }
5074
5261
 
5075
- def delete_columns_data(self, cols: list, event_data: dict) -> EventDataDict:
5262
+ def copy_sheet_state(self) -> dict:
5263
+ return {
5264
+ "row_positions": list(self.row_positions),
5265
+ "col_positions": list(self.col_positions),
5266
+ "displayed_rows": int(self.displayed_rows)
5267
+ if isinstance(self.displayed_rows, int)
5268
+ else list(self.displayed_rows),
5269
+ "displayed_columns": int(self.displayed_columns)
5270
+ if isinstance(self.displayed_columns, int)
5271
+ else list(self.displayed_columns),
5272
+ "all_rows_displayed": bool(self.all_rows_displayed),
5273
+ "saved_row_heights": dict(self.saved_row_heights),
5274
+ "saved_column_widths": dict(self.saved_column_widths),
5275
+ "all_columns_displayed": bool(self.all_columns_displayed),
5276
+ "tree_open_ids": set(self.RI.tree_open_ids),
5277
+ }
5278
+
5279
+ def delete_columns_data(self, cols: list[int], event_data: EventDataDict | None = None) -> EventDataDict:
5076
5280
  self.mouseclick_outside_editor_or_dropdown_all_canvases()
5077
- event_data["deleted"]["displayed_columns"] = (
5078
- list(self.displayed_columns) if not isinstance(self.displayed_columns, int) else int(self.displayed_columns)
5079
- )
5281
+ if not event_data:
5282
+ event_data = self.new_event_dict("delete_columns", state=True)
5080
5283
  event_data["options"] = self.copy_options()
5081
5284
  event_data["named_spans"] = {k: span.pickle_self() for k, span in self.named_spans.items()}
5082
5285
  for datacn in reversed(cols):
@@ -5104,41 +5307,72 @@ class MainTable(tk.Canvas):
5104
5307
  ]
5105
5308
  return event_data
5106
5309
 
5107
- def delete_columns_displayed(self, cols: list, event_data: dict) -> EventDataDict:
5310
+ def delete_columns_displayed(
5311
+ self,
5312
+ cols: list[int],
5313
+ event_data: EventDataDict | None = None,
5314
+ restored_state: bool = False,
5315
+ ) -> EventDataDict:
5316
+ if not event_data:
5317
+ event_data = self.new_event_dict("delete_columns", state=True)
5108
5318
  self.saved_column_widths = {}
5109
- cols_set = set(cols)
5110
- for c in reversed(cols):
5111
- event_data["deleted"]["column_widths"][c] = self.col_positions[c + 1] - self.col_positions[c]
5112
- self.set_col_positions(itr=(width for c, width in enumerate(self.gen_column_widths()) if c not in cols_set))
5319
+ if cols:
5320
+ for c in reversed(cols):
5321
+ if len(self.col_positions) > c + 1:
5322
+ event_data["deleted"]["column_widths"][c] = self.col_positions[c + 1] - self.col_positions[c]
5323
+ if not restored_state:
5324
+ cols_set = set(cols)
5325
+ self.set_col_positions(
5326
+ itr=(width for c, width in enumerate(self.gen_column_widths()) if c not in cols_set)
5327
+ )
5113
5328
  return event_data
5114
5329
 
5115
- def rc_delete_columns(self, event: object = None):
5116
- selected = sorted(self.get_selected_cols())
5117
- if not self.selected:
5330
+ def delete_columns(
5331
+ self,
5332
+ event: object = None,
5333
+ columns: list[int] | None = None,
5334
+ data_indexes: bool = False,
5335
+ undo: bool = True,
5336
+ emit_event: bool = True,
5337
+ ext: bool = False,
5338
+ ) -> EventDataDict:
5339
+ event_data = self.new_event_dict("delete_columns", state=True)
5340
+ if not columns:
5341
+ if not (columns := sorted(self.get_selected_cols())):
5342
+ return event_data
5343
+ if not ext and not try_binding(self.extra_begin_del_cols_rc_func, event_data, "begin_delete_columns"):
5118
5344
  return
5119
- event_data = event_dict(
5120
- name="delete_columns",
5121
- sheet=self.PAR.name,
5122
- widget=self,
5123
- boxes=self.get_boxes(),
5124
- selected=self.selected,
5345
+ if self.all_columns_displayed:
5346
+ data_columns = columns
5347
+ disp_columns = columns
5348
+ else:
5349
+ if data_indexes:
5350
+ data_columns = columns
5351
+ disp_columns = data_to_displayed_idxs(data_columns, self.displayed_columns)
5352
+ else:
5353
+ data_columns = [self.displayed_columns[c] for c in columns]
5354
+ disp_columns = columns
5355
+ event_data = self.delete_columns_displayed(
5356
+ disp_columns,
5357
+ event_data,
5125
5358
  )
5126
- if not try_binding(self.extra_begin_del_cols_rc_func, event_data, "begin_delete_columns"):
5127
- return
5128
- event_data = self.delete_columns_displayed(selected, event_data)
5129
- data_cols = selected if self.all_columns_displayed else [self.displayed_columns[c] for c in selected]
5130
- event_data = self.delete_columns_data(data_cols, event_data)
5131
- if self.undo_enabled:
5359
+ event_data = self.delete_columns_data(
5360
+ data_columns,
5361
+ event_data,
5362
+ )
5363
+ if undo and self.undo_enabled:
5132
5364
  self.undo_stack.append(stored_event_dict(event_data))
5365
+ if not ext:
5366
+ try_binding(self.extra_end_del_cols_rc_func, event_data, "end_delete_columns")
5367
+ if emit_event:
5368
+ self.sheet_modified(event_data)
5133
5369
  self.deselect("all")
5134
- try_binding(self.extra_end_del_cols_rc_func, event_data, "end_delete_columns")
5135
- self.sheet_modified(event_data)
5370
+ return event_data
5136
5371
 
5137
- def delete_rows_data(self, rows: list, event_data: dict) -> EventDataDict:
5372
+ def delete_rows_data(self, rows: list[int], event_data: EventDataDict | None = None) -> EventDataDict:
5138
5373
  self.mouseclick_outside_editor_or_dropdown_all_canvases()
5139
- event_data["deleted"]["displayed_rows"] = (
5140
- list(self.displayed_rows) if not isinstance(self.displayed_rows, int) else int(self.displayed_rows)
5141
- )
5374
+ if not event_data:
5375
+ event_data = self.new_event_dict("delete_rows", state=True)
5142
5376
  event_data["options"] = self.copy_options()
5143
5377
  event_data["named_spans"] = {k: span.pickle_self() for k, span in self.named_spans.items()}
5144
5378
  for datarn in reversed(rows):
@@ -5147,6 +5381,8 @@ class MainTable(tk.Canvas):
5147
5381
  event_data["deleted"]["index"][datarn] = self._row_index.pop(datarn)
5148
5382
  except Exception:
5149
5383
  continue
5384
+ if self.PAR.ops.treeview:
5385
+ event_data = self.RI.tree_del_rows(event_data=event_data)
5150
5386
  rows_set = set(rows)
5151
5387
  self.adjust_options_post_delete_rows(
5152
5388
  to_del=rows_set,
@@ -5160,35 +5396,78 @@ class MainTable(tk.Canvas):
5160
5396
  ]
5161
5397
  return event_data
5162
5398
 
5163
- def delete_rows_displayed(self, rows: list, event_data: dict) -> EventDataDict:
5399
+ def delete_rows_displayed(
5400
+ self,
5401
+ rows: list[int],
5402
+ event_data: EventDataDict | None = None,
5403
+ restored_state: bool = False,
5404
+ ) -> EventDataDict:
5405
+ if not event_data:
5406
+ event_data = self.new_event_dict("delete_rows", state=True)
5164
5407
  self.saved_row_heights = {}
5165
- rows_set = set(rows)
5166
- for r in reversed(rows):
5167
- event_data["deleted"]["row_heights"][r] = self.row_positions[r + 1] - self.row_positions[r]
5168
- self.set_row_positions(itr=(height for r, height in enumerate(self.gen_row_heights()) if r not in rows_set))
5408
+ if rows:
5409
+ for r in reversed(rows):
5410
+ if len(self.row_positions) > r + 1:
5411
+ event_data["deleted"]["row_heights"][r] = self.row_positions[r + 1] - self.row_positions[r]
5412
+ if not restored_state:
5413
+ rows_set = set(rows)
5414
+ self.set_row_positions(
5415
+ itr=(height for r, height in enumerate(self.gen_row_heights()) if r not in rows_set)
5416
+ )
5169
5417
  return event_data
5170
5418
 
5171
- def rc_delete_rows(self, event: object = None):
5172
- selected = sorted(self.get_selected_rows())
5173
- if not self.selected:
5419
+ def delete_rows(
5420
+ self,
5421
+ event: object = None,
5422
+ rows: list[int] | None = None,
5423
+ data_indexes: bool = False,
5424
+ undo: bool = True,
5425
+ emit_event: bool = True,
5426
+ ext: bool = False,
5427
+ ) -> EventDataDict:
5428
+ event_data = self.new_event_dict("delete_rows", state=True)
5429
+ if not rows:
5430
+ if not (rows := sorted(self.get_selected_rows())):
5431
+ return
5432
+ if not ext and not try_binding(self.extra_begin_del_rows_rc_func, event_data, "begin_delete_rows"):
5174
5433
  return
5175
- event_data = event_dict(
5176
- name="delete_rows",
5177
- sheet=self.PAR.name,
5178
- widget=self,
5179
- boxes=self.get_boxes(),
5180
- selected=self.selected,
5434
+ if self.all_rows_displayed:
5435
+ data_rows = rows
5436
+ disp_rows = rows
5437
+ else:
5438
+ if data_indexes:
5439
+ data_rows = rows
5440
+ disp_rows = data_to_displayed_idxs(data_rows, self.displayed_rows)
5441
+ else:
5442
+ data_rows = [self.displayed_rows[r] for r in rows]
5443
+ disp_rows = rows
5444
+ if self.PAR.ops.treeview:
5445
+ data_rows = sorted(
5446
+ chain(
5447
+ data_rows,
5448
+ (
5449
+ self.RI.tree_rns[did]
5450
+ for r in data_rows
5451
+ for did in self.RI.get_iid_descendants(self._row_index[r].iid)
5452
+ ),
5453
+ )
5454
+ )
5455
+ event_data = self.delete_rows_displayed(
5456
+ disp_rows,
5457
+ event_data,
5181
5458
  )
5182
- if not try_binding(self.extra_begin_del_rows_rc_func, event_data, "begin_delete_rows"):
5183
- return
5184
- event_data = self.delete_rows_displayed(selected, event_data)
5185
- data_rows = selected if self.all_rows_displayed else [self.displayed_rows[r] for r in selected]
5186
- event_data = self.delete_rows_data(data_rows, event_data)
5187
- if self.undo_enabled:
5459
+ event_data = self.delete_rows_data(
5460
+ data_rows,
5461
+ event_data,
5462
+ )
5463
+ if undo and self.undo_enabled:
5188
5464
  self.undo_stack.append(stored_event_dict(event_data))
5465
+ if not ext:
5466
+ try_binding(self.extra_end_del_rows_rc_func, event_data, "end_delete_rows")
5467
+ if emit_event:
5468
+ self.sheet_modified(event_data)
5189
5469
  self.deselect("all")
5190
- try_binding(self.extra_end_del_rows_rc_func, event_data, "end_delete_rows")
5191
- self.sheet_modified(event_data)
5470
+ return event_data
5192
5471
 
5193
5472
  def move_row_position(self, idx1: int, idx2: int):
5194
5473
  if not len(self.row_positions) <= 2:
@@ -5593,9 +5872,8 @@ class MainTable(tk.Canvas):
5593
5872
  self.itemconfig(iid, fill=fill, outline=outline)
5594
5873
  else:
5595
5874
  self.itemconfig(iid, fill=fill, outline=outline, state="normal")
5596
- self.tag_raise(iid)
5597
5875
  else:
5598
- iid = self.create_rectangle(coords, fill=fill, outline=outline)
5876
+ iid = self.create_rectangle(coords, fill=fill, outline=outline, tags="h")
5599
5877
  self.disp_high[iid] = True
5600
5878
  return True
5601
5879
 
@@ -5621,7 +5899,6 @@ class MainTable(tk.Canvas):
5621
5899
  joinstyle=tk.ROUND,
5622
5900
  state="normal",
5623
5901
  )
5624
- self.tag_raise(iid)
5625
5902
  else:
5626
5903
  iid = self.create_line(
5627
5904
  points,
@@ -5811,12 +6088,67 @@ class MainTable(tk.Canvas):
5811
6088
  self.PAR.yscroll.grid(row=0, column=2, rowspan=3, sticky="nswe")
5812
6089
  self.PAR.yscroll_showing = True
5813
6090
 
6091
+ def _overflow(
6092
+ self,
6093
+ cells: dict,
6094
+ rnge: Generator[int],
6095
+ datarn: int,
6096
+ ) -> Generator[float]:
6097
+ for c_ in rnge:
6098
+ if (
6099
+ cells[(datarn, cells["datacn"][c_])]
6100
+ or (datarn, cells["datacn"][c_]) in cells["dropdown"]
6101
+ or (datarn, cells["datacn"][c_]) in cells["checkbox"]
6102
+ ):
6103
+ return
6104
+ else:
6105
+ yield self.col_positions[c_ + 1] - self.col_positions[c_]
6106
+
6107
+ def _redraw_precache_cells(
6108
+ self,
6109
+ text_start_row: int,
6110
+ text_end_row: int,
6111
+ text_start_col: int,
6112
+ text_end_col: int,
6113
+ ) -> dict:
6114
+ cells = {"datarn": {}, "datacn": {}, "dropdown": {}, "checkbox": {}}
6115
+ for r in range(text_start_row, text_end_row):
6116
+ datarn = r if self.all_rows_displayed else self.displayed_rows[r]
6117
+ cells["datarn"][r] = datarn
6118
+ for c in range(text_start_col, text_end_col):
6119
+ if c in cells["datacn"]:
6120
+ datacn = cells["datacn"][c]
6121
+ else:
6122
+ datacn = c if self.all_columns_displayed else self.displayed_columns[c]
6123
+ cells["datacn"][c] = datacn
6124
+ if kwargs := self.get_cell_kwargs(datarn, datacn, key="dropdown"):
6125
+ cells["dropdown"][(datarn, datacn)] = kwargs
6126
+ elif kwargs := self.get_cell_kwargs(datarn, datacn, key="checkbox"):
6127
+ cells["checkbox"][(datarn, datacn)] = kwargs
6128
+ cells[(datarn, datacn)] = self.get_valid_cell_data_as_str(datarn, datacn, get_displayed=True)
6129
+ return cells
6130
+
6131
+ def wrap_get_char_w(self, c: str) -> int:
6132
+ self.txt_measure_canvas.itemconfig(
6133
+ self.txt_measure_canvas_text,
6134
+ text=_test_str + c,
6135
+ font=self.table_font,
6136
+ )
6137
+ b = self.txt_measure_canvas.bbox(self.txt_measure_canvas_text)
6138
+ if c in self.char_widths[self.table_font]:
6139
+ return self.char_widths[self.table_font][c]
6140
+ else:
6141
+ wd = b[2] - b[0] - self.table_test_str_w
6142
+ self.char_widths[self.table_font][c] = wd
6143
+ return wd
6144
+
5814
6145
  def main_table_redraw_grid_and_text(
5815
6146
  self,
5816
6147
  redraw_header: bool = False,
5817
6148
  redraw_row_index: bool = False,
5818
6149
  redraw_table: bool = True,
5819
6150
  setting_views: bool = False,
6151
+ set_scrollregion: bool = True,
5820
6152
  ) -> bool:
5821
6153
  try:
5822
6154
  can_width = self.winfo_width()
@@ -5838,29 +6170,33 @@ class MainTable(tk.Canvas):
5838
6170
  last_col_line_pos + self.PAR.ops.empty_horizontal + 2,
5839
6171
  last_row_line_pos + self.PAR.ops.empty_vertical + 2,
5840
6172
  )
5841
- if setting_views or scrollregion != self.scrollregion:
5842
- self.configure(scrollregion=scrollregion)
6173
+ if set_scrollregion and (setting_views or scrollregion != self.scrollregion):
6174
+ try:
6175
+ self.configure(scrollregion=scrollregion)
6176
+ except Exception:
6177
+ return False
5843
6178
  self.scrollregion = scrollregion
5844
- self.CH.configure_scrollregion(last_col_line_pos)
5845
- self.RI.configure_scrollregion(last_row_line_pos)
5846
- if setting_views:
6179
+ if (
6180
+ not self.CH.configure_scrollregion(last_col_line_pos)
6181
+ or not self.RI.configure_scrollregion(last_row_line_pos)
6182
+ or setting_views
6183
+ ):
5847
6184
  return False
5848
6185
 
5849
6186
  scrollpos_top = self.canvasy(0)
5850
6187
  scrollpos_bot = self.canvasy(can_height)
5851
6188
  scrollpos_left = self.canvasx(0)
5852
6189
  scrollpos_right = self.canvasx(can_width)
5853
-
5854
6190
  grid_start_row = bisect_left(self.row_positions, scrollpos_top)
5855
6191
  grid_end_row = bisect_right(self.row_positions, scrollpos_bot)
5856
6192
  grid_start_col = bisect_left(self.col_positions, scrollpos_left)
5857
6193
  grid_end_col = bisect_right(self.col_positions, scrollpos_right)
5858
-
5859
6194
  text_start_row = grid_start_row - 1 if grid_start_row else grid_start_row
5860
6195
  text_end_row = grid_end_row - 1 if grid_end_row == len(self.row_positions) else grid_end_row
5861
6196
  text_start_col = grid_start_col - 1 if grid_start_col else grid_start_col
5862
6197
  text_end_col = grid_end_col - 1 if grid_end_col == len(self.col_positions) else grid_end_col
5863
6198
 
6199
+ # check if auto resizing row index
5864
6200
  changed_w = False
5865
6201
  if self.PAR.ops.auto_resize_row_index and redraw_row_index and self.show_index:
5866
6202
  changed_w = self.RI.auto_set_index_width(
@@ -5873,7 +6209,10 @@ class MainTable(tk.Canvas):
5873
6209
  for widget in (self, self.RI, self.CH, self.TL):
5874
6210
  widget.update_idletasks()
5875
6211
  return False
5876
-
6212
+ # important vars
6213
+ x_stop = min(last_col_line_pos, scrollpos_right)
6214
+ y_stop = min(last_row_line_pos, scrollpos_bot)
6215
+ # manage find window
5877
6216
  if self.find_window.open:
5878
6217
  w, h, x, y = self.get_find_window_dimensions_coords(w_width=self.winfo_width())
5879
6218
  self.coords(self.find_window.canvas_id, x, y)
@@ -5883,19 +6222,71 @@ class MainTable(tk.Canvas):
5883
6222
  height=h,
5884
6223
  state="normal",
5885
6224
  )
5886
- self.hidd_text.update(self.disp_text)
5887
- self.disp_text = {}
5888
- self.hidd_high.update(self.disp_high)
5889
- self.disp_high = {}
5890
- self.hidd_grid.update(self.disp_grid)
5891
- self.disp_grid = {}
5892
- self.hidd_dropdown.update(self.disp_dropdown)
5893
- self.disp_dropdown = {}
5894
- self.hidd_checkbox.update(self.disp_checkbox)
5895
- self.disp_checkbox = {}
5896
- x_stop = min(last_col_line_pos, scrollpos_right)
5897
- y_stop = min(last_row_line_pos, scrollpos_bot)
6225
+ # redraw table
5898
6226
  if redraw_table:
6227
+ # reset canvas item storage
6228
+ self.hidd_text.update(self.disp_text)
6229
+ self.disp_text = {}
6230
+ self.hidd_high.update(self.disp_high)
6231
+ self.disp_high = {}
6232
+ self.hidd_grid.update(self.disp_grid)
6233
+ self.disp_grid = {}
6234
+ self.hidd_dropdown.update(self.disp_dropdown)
6235
+ self.disp_dropdown = {}
6236
+ self.hidd_checkbox.update(self.disp_checkbox)
6237
+ self.disp_checkbox = {}
6238
+ # manage horizontal grid lines
6239
+ if self.PAR.ops.show_horizontal_grid and row_pos_exists:
6240
+ if self.PAR.ops.horizontal_grid_to_end_of_window:
6241
+ x_grid_stop = scrollpos_right + can_width
6242
+ else:
6243
+ if last_col_line_pos > scrollpos_right:
6244
+ x_grid_stop = x_stop + 1
6245
+ else:
6246
+ x_grid_stop = x_stop - 1
6247
+ self.redraw_gridline(
6248
+ points=tuple(
6249
+ chain.from_iterable(
6250
+ (
6251
+ scrollpos_left - 1,
6252
+ self.row_positions[r],
6253
+ x_grid_stop,
6254
+ self.row_positions[r],
6255
+ scrollpos_left - 1,
6256
+ self.row_positions[r],
6257
+ scrollpos_left - 1,
6258
+ self.row_positions[r + 1] if len(self.row_positions) - 1 > r else self.row_positions[r],
6259
+ )
6260
+ for r in range(grid_start_row, grid_end_row)
6261
+ )
6262
+ )
6263
+ )
6264
+ # manage vertical grid lines
6265
+ if self.PAR.ops.show_vertical_grid and col_pos_exists:
6266
+ if self.PAR.ops.vertical_grid_to_end_of_window:
6267
+ y_grid_stop = scrollpos_bot + can_height
6268
+ else:
6269
+ if last_row_line_pos > scrollpos_bot:
6270
+ y_grid_stop = y_stop + 1
6271
+ else:
6272
+ y_grid_stop = y_stop - 1
6273
+ self.redraw_gridline(
6274
+ points=tuple(
6275
+ chain.from_iterable(
6276
+ (
6277
+ self.col_positions[c],
6278
+ scrollpos_top - 1,
6279
+ self.col_positions[c],
6280
+ y_grid_stop,
6281
+ self.col_positions[c],
6282
+ scrollpos_top - 1,
6283
+ self.col_positions[c + 1] if len(self.col_positions) - 1 > c else self.col_positions[c],
6284
+ scrollpos_top - 1,
6285
+ )
6286
+ for c in range(grid_start_col, grid_end_col)
6287
+ )
6288
+ ),
6289
+ )
5899
6290
  font = self.PAR.ops.table_font
5900
6291
  dd_coords = self.dropdown.get_coords()
5901
6292
  selections = self.get_redraw_selections(text_start_row, grid_end_row, text_start_col, grid_end_col)
@@ -5927,17 +6318,23 @@ class MainTable(tk.Canvas):
5927
6318
  )
5928
6319
  else:
5929
6320
  override = tuple()
6321
+ allow_overflow = self.PAR.ops.allow_cell_overflow
6322
+ wrap = self.PAR.ops.table_wrap
6323
+ cells = self._redraw_precache_cells(
6324
+ text_start_row=text_start_row,
6325
+ text_end_row=text_end_row,
6326
+ text_start_col=text_start_col,
6327
+ text_end_col=text_end_col,
6328
+ )
5930
6329
  for r in range(text_start_row, text_end_row):
5931
6330
  rtopgridln = self.row_positions[r]
5932
6331
  rbotgridln = self.row_positions[r + 1]
5933
- if rbotgridln - rtopgridln < self.table_txt_height:
5934
- continue
5935
- datarn = self.datarn(r)
6332
+ datarn = cells["datarn"][r]
5936
6333
 
5937
6334
  for c in range(text_start_col, text_end_col):
5938
6335
  cleftgridln = self.col_positions[c]
5939
6336
  crightgridln = self.col_positions[c + 1]
5940
- datacn = self.datacn(c)
6337
+ datacn = cells["datacn"][c]
5941
6338
  fill, dd_drawn = self.redraw_highlight_get_text_fg(
5942
6339
  r=r,
5943
6340
  c=c,
@@ -5957,13 +6354,14 @@ class MainTable(tk.Canvas):
5957
6354
  )
5958
6355
  if not (align := self.get_cell_kwargs(datarn, datacn, key="align")):
5959
6356
  align = self.align
5960
- if kwargs := self.get_cell_kwargs(datarn, datacn, key="dropdown"):
5961
- mw = crightgridln - cleftgridln - self.table_txt_height - 2
5962
- if align == "w":
5963
- draw_x = cleftgridln + 3
5964
- elif align == "e":
6357
+
6358
+ if kwargs := cells["dropdown"].get((datarn, datacn), None):
6359
+ max_width = crightgridln - cleftgridln - self.table_txt_height - 5
6360
+ if align.endswith("w"):
6361
+ draw_x = cleftgridln + 2
6362
+ elif align.endswith("e"):
5965
6363
  draw_x = crightgridln - 5 - self.table_txt_height
5966
- elif align == "center":
6364
+ elif align.endswith("n"):
5967
6365
  draw_x = cleftgridln + ceil((crightgridln - cleftgridln - self.table_txt_height) / 2)
5968
6366
  self.redraw_dropdown(
5969
6367
  cleftgridln,
@@ -5973,25 +6371,27 @@ class MainTable(tk.Canvas):
5973
6371
  fill=fill if kwargs["state"] != "disabled" else self.PAR.ops.table_grid_fg,
5974
6372
  outline=fill,
5975
6373
  draw_outline=not dd_drawn,
5976
- draw_arrow=mw >= 5,
6374
+ draw_arrow=max_width >= 5,
5977
6375
  open_=dd_coords == (r, c),
5978
6376
  )
5979
6377
  else:
5980
- mw = crightgridln - cleftgridln - 1
5981
- if align == "w":
5982
- draw_x = cleftgridln + 3
5983
- elif align == "e":
5984
- draw_x = crightgridln - 3
5985
- elif align == "center":
6378
+ max_width = crightgridln - cleftgridln - 2
6379
+ if align.endswith("w"):
6380
+ draw_x = cleftgridln + 2
6381
+ elif align.endswith("e"):
6382
+ draw_x = crightgridln - 2
6383
+ elif align.endswith("n"):
5986
6384
  draw_x = cleftgridln + floor((crightgridln - cleftgridln) / 2)
5987
- kwargs = self.get_cell_kwargs(datarn, datacn, key="checkbox")
5988
- if kwargs and mw > self.table_txt_height + 1:
6385
+
6386
+ if (
6387
+ kwargs := cells["checkbox"].get((datarn, datacn), None)
6388
+ ) and max_width > self.table_txt_height + 1:
5989
6389
  box_w = self.table_txt_height + 1
5990
- if align == "w":
6390
+ if align.endswith("w"):
5991
6391
  draw_x += box_w + 3
5992
- elif align == "center":
6392
+ elif align.endswith("n"):
5993
6393
  draw_x += ceil(box_w / 2) + 1
5994
- mw -= box_w + 3
6394
+ max_width -= box_w + 4
5995
6395
  try:
5996
6396
  draw_check = bool(self.data[datarn][datacn])
5997
6397
  except Exception:
@@ -6005,27 +6405,77 @@ class MainTable(tk.Canvas):
6005
6405
  outline="",
6006
6406
  draw_check=draw_check,
6007
6407
  )
6008
- lines = self.get_valid_cell_data_as_str(datarn, datacn, get_displayed=True)
6408
+ text = cells[(datarn, datacn)]
6009
6409
  if (
6010
- not lines
6011
- or mw < self.table_txt_width
6012
- or (align == "w" and draw_x > scrollpos_right)
6013
- or (align == "e" and cleftgridln + 5 > scrollpos_right)
6014
- or (align == "center" and cleftgridln + 5 > scrollpos_right)
6410
+ not text
6411
+ or (align.endswith("w") and draw_x > scrollpos_right)
6412
+ or (align.endswith("e") and cleftgridln + 5 > scrollpos_right)
6413
+ or (align.endswith("n") and cleftgridln + 5 > scrollpos_right)
6015
6414
  ):
6016
6415
  continue
6017
- lines = lines.split("\n")
6018
- start_ln = max(0, int((scrollpos_top - rtopgridln) / self.table_xtra_lines_increment))
6019
- draw_y = rtopgridln + self.table_first_ln_ins + start_ln * self.table_xtra_lines_increment
6020
- if draw_y + self.table_half_txt_height - 1 <= rbotgridln and len(lines) >= start_ln:
6021
- for txt in islice(lines, start_ln, None):
6416
+ if allow_overflow and not kwargs:
6417
+ if align.endswith("w"):
6418
+ max_width += sum(self._overflow(cells, range(c + 1, text_end_col), datarn))
6419
+ elif align.endswith("e"):
6420
+ max_width += sum(self._overflow(cells, reversed(range(text_start_col, c)), datarn))
6421
+ elif align.endswith("n"):
6422
+ ...
6423
+ if max_width <= 1:
6424
+ continue
6425
+ start_line = max(0, int((scrollpos_top - rtopgridln) / self.table_txt_height))
6426
+ draw_y = rtopgridln + 3 + (start_line * self.table_txt_height)
6427
+ gen_lines = wrap_text(
6428
+ text=text,
6429
+ max_width=max_width,
6430
+ max_lines=int((rbotgridln - rtopgridln - 2) / self.table_txt_height),
6431
+ char_width_fn=self.wrap_get_char_w,
6432
+ widths=self.char_widths[font],
6433
+ wrap=wrap,
6434
+ start_line=start_line,
6435
+ )
6436
+ if align.endswith(("w", "e")):
6437
+ if self.hidd_text:
6438
+ iid, showing = self.hidd_text.popitem()
6439
+ self.coords(iid, draw_x, draw_y)
6440
+ if showing:
6441
+ self.itemconfig(
6442
+ iid,
6443
+ text="\n".join(gen_lines),
6444
+ fill=fill,
6445
+ font=font,
6446
+ anchor=align,
6447
+ )
6448
+ else:
6449
+ self.itemconfig(
6450
+ iid,
6451
+ text="\n".join(gen_lines),
6452
+ fill=fill,
6453
+ font=font,
6454
+ anchor=align,
6455
+ state="normal",
6456
+ )
6457
+ self.tag_raise(iid)
6458
+ else:
6459
+ iid = self.create_text(
6460
+ draw_x,
6461
+ draw_y,
6462
+ text="\n".join(gen_lines),
6463
+ fill=fill,
6464
+ font=font,
6465
+ anchor=align,
6466
+ tags="t",
6467
+ )
6468
+ self.disp_text[iid] = True
6469
+
6470
+ elif align.endswith("n"):
6471
+ for text in gen_lines:
6022
6472
  if self.hidd_text:
6023
6473
  iid, showing = self.hidd_text.popitem()
6024
6474
  self.coords(iid, draw_x, draw_y)
6025
6475
  if showing:
6026
6476
  self.itemconfig(
6027
6477
  iid,
6028
- text=txt,
6478
+ text=text,
6029
6479
  fill=fill,
6030
6480
  font=font,
6031
6481
  anchor=align,
@@ -6033,7 +6483,7 @@ class MainTable(tk.Canvas):
6033
6483
  else:
6034
6484
  self.itemconfig(
6035
6485
  iid,
6036
- text=txt,
6486
+ text=text,
6037
6487
  fill=fill,
6038
6488
  font=font,
6039
6489
  anchor=align,
@@ -6044,96 +6494,14 @@ class MainTable(tk.Canvas):
6044
6494
  iid = self.create_text(
6045
6495
  draw_x,
6046
6496
  draw_y,
6047
- text=txt,
6497
+ text=text,
6048
6498
  fill=fill,
6049
6499
  font=font,
6050
6500
  anchor=align,
6051
- tag="t",
6501
+ tags="t",
6052
6502
  )
6053
6503
  self.disp_text[iid] = True
6054
- wd = self.bbox(iid)
6055
- if (wd := wd[2] - wd[0]) > mw:
6056
- if align == "w":
6057
- txt = txt[: int(len(txt) * (mw / wd))]
6058
- self.itemconfig(iid, text=txt)
6059
- wd = self.bbox(iid)
6060
- while wd[2] - wd[0] > mw:
6061
- txt = txt[:-1]
6062
- self.itemconfig(iid, text=txt)
6063
- wd = self.bbox(iid)
6064
- elif align == "e":
6065
- txt = txt[len(txt) - int(len(txt) * (mw / wd)) :]
6066
- self.itemconfig(iid, text=txt)
6067
- wd = self.bbox(iid)
6068
- while wd[2] - wd[0] > mw:
6069
- txt = txt[1:]
6070
- self.itemconfig(iid, text=txt)
6071
- wd = self.bbox(iid)
6072
- elif align == "center":
6073
- tmod = ceil((len(txt) - int(len(txt) * (mw / wd))) / 2)
6074
- txt = txt[tmod - 1 : -tmod]
6075
- self.itemconfig(iid, text=txt)
6076
- wd = self.bbox(iid)
6077
- while wd[2] - wd[0] > mw:
6078
- txt = txt[next(self.c_align_cyc)]
6079
- self.itemconfig(iid, text=txt)
6080
- wd = self.bbox(iid)
6081
- self.coords(iid, draw_x, draw_y)
6082
- draw_y += self.table_xtra_lines_increment
6083
- if draw_y + self.table_half_txt_height - 1 > rbotgridln:
6084
- break
6085
- # manage horizontal grid lines
6086
- if self.PAR.ops.show_horizontal_grid and row_pos_exists:
6087
- if self.PAR.ops.horizontal_grid_to_end_of_window:
6088
- x_grid_stop = scrollpos_right + can_width
6089
- else:
6090
- if last_col_line_pos > scrollpos_right:
6091
- x_grid_stop = x_stop + 1
6092
- else:
6093
- x_grid_stop = x_stop - 1
6094
- self.redraw_gridline(
6095
- points=tuple(
6096
- chain.from_iterable(
6097
- (
6098
- scrollpos_left - 1,
6099
- self.row_positions[r],
6100
- x_grid_stop,
6101
- self.row_positions[r],
6102
- scrollpos_left - 1,
6103
- self.row_positions[r],
6104
- scrollpos_left - 1,
6105
- self.row_positions[r + 1] if len(self.row_positions) - 1 > r else self.row_positions[r],
6106
- )
6107
- for r in range(grid_start_row, grid_end_row)
6108
- )
6109
- )
6110
- )
6111
- # manage vertical grid lines
6112
- if self.PAR.ops.show_vertical_grid and col_pos_exists:
6113
- if self.PAR.ops.vertical_grid_to_end_of_window:
6114
- y_grid_stop = scrollpos_bot + can_height
6115
- else:
6116
- if last_row_line_pos > scrollpos_bot:
6117
- y_grid_stop = y_stop + 1
6118
- else:
6119
- y_grid_stop = y_stop - 1
6120
- self.redraw_gridline(
6121
- points=tuple(
6122
- chain.from_iterable(
6123
- (
6124
- self.col_positions[c],
6125
- scrollpos_top - 1,
6126
- self.col_positions[c],
6127
- y_grid_stop,
6128
- self.col_positions[c],
6129
- scrollpos_top - 1,
6130
- self.col_positions[c + 1] if len(self.col_positions) - 1 > c else self.col_positions[c],
6131
- scrollpos_top - 1,
6132
- )
6133
- for c in range(grid_start_col, grid_end_col)
6134
- )
6135
- ),
6136
- )
6504
+ draw_y += self.table_txt_height
6137
6505
  for dct in (
6138
6506
  self.hidd_text,
6139
6507
  self.hidd_high,
@@ -6166,6 +6534,7 @@ class MainTable(tk.Canvas):
6166
6534
  text_end_col=text_end_col,
6167
6535
  scrollpos_right=scrollpos_right,
6168
6536
  col_pos_exists=col_pos_exists,
6537
+ set_scrollregion=set_scrollregion,
6169
6538
  )
6170
6539
  if redraw_row_index and self.show_index:
6171
6540
  self.RI.redraw_grid_and_text(
@@ -6178,6 +6547,7 @@ class MainTable(tk.Canvas):
6178
6547
  text_end_row=text_end_row,
6179
6548
  scrollpos_bot=scrollpos_bot,
6180
6549
  row_pos_exists=row_pos_exists,
6550
+ set_scrollregion=set_scrollregion,
6181
6551
  )
6182
6552
  event_data = {"sheetname": "", "header": redraw_header, "row_index": redraw_row_index, "table": redraw_table}
6183
6553
  self.PAR.emit_event("<<SheetRedrawn>>", data=event_data)
@@ -6902,7 +7272,7 @@ class MainTable(tk.Canvas):
6902
7272
  return False
6903
7273
  self.hide_text_editor()
6904
7274
  if not self.see(r=r, c=c, check_cell_visibility=True):
6905
- self.refresh()
7275
+ self.main_table_redraw_grid_and_text(True, True)
6906
7276
  x = self.col_positions[c]
6907
7277
  y = self.row_positions[r]
6908
7278
  w = self.col_positions[c + 1] - x + 1
@@ -6971,7 +7341,7 @@ class MainTable(tk.Canvas):
6971
7341
  > curr_height
6972
7342
  ):
6973
7343
  new_height = min(
6974
- curr_height + self.table_xtra_lines_increment,
7344
+ curr_height + self.table_txt_height,
6975
7345
  self.scrollregion[3] - self.scrollregion[1] - self.row_positions[r],
6976
7346
  )
6977
7347
  if new_height != curr_height:
@@ -7218,7 +7588,7 @@ class MainTable(tk.Canvas):
7218
7588
  for i, v in enumerate(self.get_cell_kwargs(datarn, datacn, key="dropdown")["values"]):
7219
7589
  v_numlines = len(v.split("\n") if isinstance(v, str) else f"{v}".split("\n"))
7220
7590
  if v_numlines > 1:
7221
- win_h += self.table_first_ln_ins + (v_numlines * self.table_xtra_lines_increment) + 5 # end of cell
7591
+ win_h += 8 + (v_numlines * self.table_txt_height) # end of cell
7222
7592
  else:
7223
7593
  win_h += self.min_row_height
7224
7594
  if i == 5:
@@ -7669,7 +8039,7 @@ class MainTable(tk.Canvas):
7669
8039
  else:
7670
8040
  # assumed given formatter class has get_data_with_valid_check()
7671
8041
  return f"{value.get_data_with_valid_check()}"
7672
- return "" if value is None else f"{value}"
8042
+ return "" if value is None else value if isinstance(value, str) else f"{value}"
7673
8043
 
7674
8044
  def get_cell_data(
7675
8045
  self,
@@ -7682,7 +8052,11 @@ class MainTable(tk.Canvas):
7682
8052
  ) -> object:
7683
8053
  if get_displayed:
7684
8054
  return self.get_valid_cell_data_as_str(datarn, datacn, get_displayed=True)
7685
- value = self.data[datarn][datacn] if len(self.data) > datarn and len(self.data[datarn]) > datacn else ""
8055
+ value = (
8056
+ self.data[datarn][datacn]
8057
+ if len(self.data) > datarn and len(self.data[datarn]) > datacn
8058
+ else self.get_value_for_empty_cell(datarn, datacn)
8059
+ )
7686
8060
  kwargs = self.get_cell_kwargs(datarn, datacn, key="format")
7687
8061
  if kwargs and kwargs["formatter"] is not None:
7688
8062
  value = value.value # assumed given formatter class has value attribute