tksheet 7.3.4__py3-none-any.whl → 7.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tksheet/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,14 +258,19 @@ 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
@@ -280,9 +304,12 @@ class MainTable(tk.Canvas):
280
304
  self.txt_measure_canvas_text = self.txt_measure_canvas.create_text(0, 0, text="", font=self.PAR.ops.table_font)
281
305
 
282
306
  self.RI.set_width(self.PAR.ops.default_row_index_width)
307
+
308
+ self.char_widths = {}
283
309
  self.set_table_font_help()
284
310
  self.set_header_font_help()
285
311
  self.set_index_font_help()
312
+
286
313
  self.data = kwargs["data_reference"]
287
314
  if isinstance(self.data, (list, tuple)):
288
315
  self.data = kwargs["data_reference"]
@@ -344,7 +371,7 @@ class MainTable(tk.Canvas):
344
371
  super().event_generate(*args, **kwargs)
345
372
 
346
373
  def refresh(self, event: object = None) -> None:
347
- self.main_table_redraw_grid_and_text(True, True)
374
+ self.PAR.set_refresh_timer()
348
375
 
349
376
  def window_configured(self, event: object) -> None:
350
377
  w = self.PAR.winfo_width()
@@ -450,7 +477,7 @@ class MainTable(tk.Canvas):
450
477
  dash=dash,
451
478
  width=3,
452
479
  outline=self.PAR.ops.resizing_line_fg if outline is None else outline,
453
- tag="ctrl",
480
+ tags="ctrl",
454
481
  )
455
482
  if delete_on_timer:
456
483
  self.after(1500, self.delete_ctrl_outlines)
@@ -711,13 +738,13 @@ class MainTable(tk.Canvas):
711
738
  dash: tuple[int, int],
712
739
  width: int,
713
740
  outline: str,
714
- tag: str | tuple[str, ...],
741
+ tags: str | tuple[str, ...],
715
742
  ) -> None:
716
743
  if self.hidd_ctrl_outline:
717
744
  t, sh = self.hidd_ctrl_outline.popitem()
718
745
  self.coords(t, x1, y1, x2, y2)
719
746
  if sh:
720
- self.itemconfig(t, fill=fill, dash=dash, width=width, outline=outline, tag=tag)
747
+ self.itemconfig(t, fill=fill, dash=dash, width=width, outline=outline, tags=tags)
721
748
  else:
722
749
  self.itemconfig(
723
750
  t,
@@ -725,7 +752,7 @@ class MainTable(tk.Canvas):
725
752
  dash=dash,
726
753
  width=width,
727
754
  outline=outline,
728
- tag=tag,
755
+ tags=tags,
729
756
  state="normal",
730
757
  )
731
758
  self.lift(t)
@@ -739,7 +766,7 @@ class MainTable(tk.Canvas):
739
766
  dash=dash,
740
767
  width=width,
741
768
  outline=outline,
742
- tag=tag,
769
+ tags=tags,
743
770
  )
744
771
  self.disp_ctrl_outline[t] = True
745
772
 
@@ -778,15 +805,20 @@ class MainTable(tk.Canvas):
778
805
  )
779
806
  return s, writer
780
807
 
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",
808
+ def new_event_dict(self, name: str, boxes: dict | None = None, state: bool = False) -> EventDataDict:
809
+ return event_dict(
810
+ name=name,
786
811
  sheet=self.PAR.name,
787
812
  widget=self,
813
+ boxes=self.get_boxes() if boxes is None else boxes,
788
814
  selected=self.selected,
815
+ sheet_state=self.copy_sheet_state() if state else None,
789
816
  )
817
+
818
+ def ctrl_c(self, event=None) -> None | EventDataDict:
819
+ if not self.selected:
820
+ return
821
+ event_data = self.new_event_dict("begin_ctrl_c")
790
822
  boxes, maxrows = self.get_ctrl_x_c_boxes()
791
823
  event_data["selection_boxes"] = boxes
792
824
  s, writer = self.io_csv_writer()
@@ -831,12 +863,7 @@ class MainTable(tk.Canvas):
831
863
  def ctrl_x(self, event=None, validation: bool = True) -> None | EventDataDict:
832
864
  if not self.selected:
833
865
  return
834
- event_data = event_dict(
835
- name="edit_table",
836
- sheet=self.PAR.name,
837
- widget=self,
838
- selected=self.selected,
839
- )
866
+ event_data = self.new_event_dict("edit_table")
840
867
  boxes, maxrows = self.get_ctrl_x_c_boxes()
841
868
  event_data["selection_boxes"] = boxes
842
869
  s, writer = self.io_csv_writer()
@@ -912,17 +939,64 @@ class MainTable(tk.Canvas):
912
939
  self.PAR.emit_event("<<Cut>>", event_data)
913
940
  return event_data
914
941
 
942
+ def sort_boxes(
943
+ self,
944
+ event: tk.Event | None = None,
945
+ boxes: AnyIter[int, int, int, int] | None = None,
946
+ reverse: bool = False,
947
+ row_wise: bool = False,
948
+ validation: bool = True,
949
+ key: Callable | None = None,
950
+ undo: bool = True,
951
+ ) -> EventDataDict:
952
+ if boxes is None:
953
+ boxes = self.get_boxes()
954
+ if not boxes:
955
+ boxes = [(0, 0, len(self.row_positions) - 1, len(self.col_positions) - 1)]
956
+ event_data = self.new_event_dict("edit_table", boxes=boxes)
957
+ try_binding(self.extra_begin_sort_cells_func, event_data)
958
+ for r1, c1, r2, c2 in boxes:
959
+ data = sort_selection(
960
+ [[self.get_cell_data(self.datarn(r), self.datacn(c)) for c in range(c1, c2)] for r in range(r1, r2)],
961
+ reverse=reverse,
962
+ key=key,
963
+ row_wise=row_wise,
964
+ )
965
+ for ir, r in enumerate(range(r1, r2)):
966
+ data_r = self.datarn(r)
967
+ for ic, c in enumerate(range(c1, c2)):
968
+ data_c = self.datacn(c)
969
+ val = data[ir][ic]
970
+ if (
971
+ not self.edit_validation_func
972
+ or not validation
973
+ or (
974
+ self.edit_validation_func
975
+ and (val := self.edit_validation_func(mod_event_val(event_data, val, (data_r, data_c))))
976
+ is not None
977
+ )
978
+ ):
979
+ event_data = self.event_data_set_cell(
980
+ datarn=data_r,
981
+ datacn=data_c,
982
+ value=val,
983
+ event_data=event_data,
984
+ )
985
+ if event_data["cells"]["table"]:
986
+ if undo and self.undo_enabled:
987
+ self.undo_stack.append(stored_event_dict(event_data))
988
+ try_binding(self.extra_end_sort_cells_func, event_data, "end_edit_table")
989
+ self.sheet_modified(event_data)
990
+ self.PAR.emit_event("<<SheetModified>>", event_data)
991
+ self.refresh()
992
+ return event_data
993
+
915
994
  def ctrl_v(self, event: object = None, validation: bool = True) -> None | EventDataDict:
916
995
  if not self.PAR.ops.paste_can_expand_x and len(self.col_positions) == 1:
917
996
  return
918
997
  if not self.PAR.ops.paste_can_expand_y and len(self.row_positions) == 1:
919
998
  return
920
- event_data = event_dict(
921
- name="edit_table",
922
- sheet=self.PAR.name,
923
- widget=self,
924
- selected=self.selected,
925
- )
999
+ event_data = self.new_event_dict("edit_table", state=True)
926
1000
  if self.selected:
927
1001
  selected_r = self.selected.box.from_r
928
1002
  selected_c = self.selected.box.from_c
@@ -1179,12 +1253,7 @@ class MainTable(tk.Canvas):
1179
1253
  def delete_key(self, event: object = None, validation: bool = True) -> None | EventDataDict:
1180
1254
  if not self.selected:
1181
1255
  return
1182
- event_data = event_dict(
1183
- name="edit_table",
1184
- sheet=self.PAR.name,
1185
- widget=self,
1186
- selected=self.selected,
1187
- )
1256
+ event_data = self.new_event_dict("edit_table")
1188
1257
  boxes = self.get_boxes()
1189
1258
  event_data["selection_boxes"] = boxes
1190
1259
  if not try_binding(self.extra_begin_delete_key_func, event_data, "begin_delete"):
@@ -1259,7 +1328,6 @@ class MainTable(tk.Canvas):
1259
1328
  move_data: bool = True,
1260
1329
  move_widths: bool = True,
1261
1330
  create_selections: bool = True,
1262
- data_indexes: bool = False,
1263
1331
  event_data: EventDataDict | None = None,
1264
1332
  ) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
1265
1333
  self.saved_column_widths = {}
@@ -1268,22 +1336,17 @@ class MainTable(tk.Canvas):
1268
1336
  if totalcols:
1269
1337
  totalcols += 1
1270
1338
  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
- )
1339
+ if not event_data:
1340
+ event_data = self.new_event_dict("move_columns", state=True)
1341
+ if not event_data["moved"]["columns"]:
1279
1342
  event_data["moved"]["columns"] = {
1280
1343
  "data": data_new_idxs,
1281
1344
  "displayed": {} if disp_new_idxs is None else disp_new_idxs,
1282
1345
  }
1283
1346
  event_data["options"] = self.copy_options()
1284
1347
  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)
1348
+
1349
+ if move_widths and disp_new_idxs:
1287
1350
  self.set_col_positions(
1288
1351
  itr=move_elements_by_mapping(
1289
1352
  self.get_column_widths(),
@@ -1297,6 +1360,7 @@ class MainTable(tk.Canvas):
1297
1360
  )
1298
1361
  )
1299
1362
  if create_selections:
1363
+ self.deselect("all", run_binding=False, redraw=False)
1300
1364
  for boxst, boxend in consecutive_ranges(sorted(disp_new_idxs.values())):
1301
1365
  self.create_selection_box(
1302
1366
  0,
@@ -1306,6 +1370,8 @@ class MainTable(tk.Canvas):
1306
1370
  "columns",
1307
1371
  run_binding=True,
1308
1372
  )
1373
+ else:
1374
+ self.recreate_all_selection_boxes()
1309
1375
  if move_data:
1310
1376
  self.data = list(
1311
1377
  map(
@@ -1459,29 +1525,54 @@ class MainTable(tk.Canvas):
1459
1525
  data_indexes: bool = False,
1460
1526
  ) -> tuple[dict[int, int], dict[int, int], int, dict[int, int]]:
1461
1527
  if not data_indexes or self.all_rows_displayed:
1462
- disp_new_idxs = get_new_indexes(move_to=move_to, to_move=to_move)
1528
+ disp_new_idxs = get_new_indexes(
1529
+ move_to=move_to,
1530
+ to_move=to_move,
1531
+ )
1532
+ data_new_idxs = dict(disp_new_idxs)
1463
1533
  else:
1464
1534
  disp_new_idxs = {}
1535
+ data_new_idxs = get_new_indexes(
1536
+ move_to=move_to,
1537
+ to_move=to_move,
1538
+ )
1465
1539
  # move_to can be len and fix_data_len() takes index so - 1
1466
1540
  fix_len = (move_to - 1) if move_to else move_to
1467
1541
  if not self.all_rows_displayed and not data_indexes:
1468
1542
  fix_len = self.datarn(fix_len)
1469
1543
  self.fix_data_len(fix_len)
1470
1544
  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
1545
  if not self.all_rows_displayed and not data_indexes:
1473
- data_new_idxs = dict(
1474
- zip(
1546
+ keep = set(map(self.datarn, to_move))
1547
+ data_new_idxs = {
1548
+ k: v
1549
+ for k, v in zip(
1475
1550
  move_elements_by_mapping(
1476
1551
  self.displayed_rows,
1477
1552
  data_new_idxs,
1478
1553
  dict(zip(data_new_idxs.values(), data_new_idxs)),
1479
1554
  ),
1480
1555
  self.displayed_rows,
1481
- ),
1482
- )
1556
+ )
1557
+ if k in keep
1558
+ }
1483
1559
  return data_new_idxs, dict(zip(data_new_idxs.values(), data_new_idxs)), totalrows, disp_new_idxs
1484
1560
 
1561
+ def move_rows_data(
1562
+ self,
1563
+ data_new_idxs: dict[int, int],
1564
+ data_old_idxs: dict[int, int],
1565
+ maxidx: int,
1566
+ ) -> None:
1567
+ self.data = move_elements_by_mapping(
1568
+ self.data,
1569
+ data_new_idxs,
1570
+ data_old_idxs,
1571
+ )
1572
+ self.RI.fix_index(maxidx)
1573
+ if isinstance(self._row_index, list) and self._row_index:
1574
+ self._row_index = move_elements_by_mapping(self._row_index, data_new_idxs, data_old_idxs)
1575
+
1485
1576
  def move_rows_adjust_options_dict(
1486
1577
  self,
1487
1578
  data_new_idxs: dict[int, int],
@@ -1491,8 +1582,9 @@ class MainTable(tk.Canvas):
1491
1582
  move_data: bool = True,
1492
1583
  move_heights: bool = True,
1493
1584
  create_selections: bool = True,
1494
- data_indexes: bool = False,
1495
1585
  event_data: EventDataDict | None = None,
1586
+ undo_modification: EventDataDict | None = None,
1587
+ node_change: None | tuple[str, str, int] = None,
1496
1588
  ) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
1497
1589
  self.saved_row_heights = {}
1498
1590
  if not isinstance(totalrows, int):
@@ -1502,54 +1594,33 @@ class MainTable(tk.Canvas):
1502
1594
  max(data_new_idxs.values(), default=0),
1503
1595
  )
1504
1596
  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
- )
1597
+ if not event_data:
1598
+ event_data = self.new_event_dict("move_rows", state=True)
1599
+ if not event_data["moved"]["rows"]:
1513
1600
  event_data["moved"]["rows"] = {
1514
1601
  "data": data_new_idxs,
1515
1602
  "displayed": {} if disp_new_idxs is None else disp_new_idxs,
1516
1603
  }
1517
1604
  event_data["options"] = self.copy_options()
1518
1605
  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
- )
1606
+
1543
1607
  if move_data:
1544
- self.data = move_elements_by_mapping(
1545
- self.data,
1546
- data_new_idxs,
1547
- data_old_idxs,
1548
- )
1549
1608
  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)
1609
+ if self.PAR.ops.treeview:
1610
+ two_step_move = self.RI.move_rows_mod_nodes(
1611
+ data_new_idxs=data_new_idxs,
1612
+ data_old_idxs=data_old_idxs,
1613
+ disp_new_idxs=disp_new_idxs,
1614
+ maxidx=maxidx,
1615
+ event_data=event_data,
1616
+ undo_modification=undo_modification,
1617
+ node_change=node_change,
1618
+ )
1619
+ data_new_idxs, data_old_idxs, disp_new_idxs, event_data = next(two_step_move)
1620
+ if not data_new_idxs and not disp_new_idxs:
1621
+ return data_new_idxs, disp_new_idxs, event_data
1622
+ else:
1623
+ self.move_rows_data(data_new_idxs, data_old_idxs, maxidx)
1553
1624
  maxidx = self.get_max_row_idx(maxidx)
1554
1625
  full_new_idxs = self.get_full_new_idxs(
1555
1626
  max_idx=maxidx,
@@ -1567,6 +1638,9 @@ class MainTable(tk.Canvas):
1567
1638
  self.RI.cell_options = {full_new_idxs[k]: v for k, v in self.RI.cell_options.items()}
1568
1639
  self.RI.tree_rns = {v: full_new_idxs[k] for v, k in self.RI.tree_rns.items()}
1569
1640
  self.displayed_rows = sorted(full_new_idxs[k] for k in self.displayed_rows)
1641
+ if self.PAR.ops.treeview:
1642
+ next(two_step_move)
1643
+
1570
1644
  if self.named_spans:
1571
1645
  totalcols = self.total_data_cols()
1572
1646
  new_ops = self.PAR.create_options_from_span
@@ -1665,6 +1739,33 @@ class MainTable(tk.Canvas):
1665
1739
  del self.cell_options[(full_new_idxs[k], c)][span["type_"]]
1666
1740
  # finally, change the span coords
1667
1741
  span["from_r"], span["upto_r"] = newfrom, newupto
1742
+
1743
+ if move_heights and disp_new_idxs:
1744
+ self.set_row_positions(
1745
+ itr=move_elements_by_mapping(
1746
+ self.get_row_heights(),
1747
+ disp_new_idxs,
1748
+ dict(
1749
+ zip(
1750
+ disp_new_idxs.values(),
1751
+ disp_new_idxs,
1752
+ )
1753
+ ),
1754
+ )
1755
+ )
1756
+ if create_selections:
1757
+ self.deselect("all", run_binding=False, redraw=False)
1758
+ for boxst, boxend in consecutive_ranges(sorted(disp_new_idxs.values())):
1759
+ self.create_selection_box(
1760
+ boxst,
1761
+ 0,
1762
+ boxend,
1763
+ len(self.col_positions) - 1,
1764
+ "rows",
1765
+ run_binding=True,
1766
+ )
1767
+ else:
1768
+ self.recreate_all_selection_boxes()
1668
1769
  return data_new_idxs, disp_new_idxs, event_data
1669
1770
 
1670
1771
  def get_max_row_idx(self, maxidx: int | None = None) -> int:
@@ -1781,7 +1882,7 @@ class MainTable(tk.Canvas):
1781
1882
  event_data["cells"]["table"][k] = self.get_cell_data(k[0], k[1])
1782
1883
  return event_data
1783
1884
 
1784
- def restore_options_named_spans(self, modification: EventDataDict) -> None:
1885
+ def restore_sheet_state(self, modification: EventDataDict) -> None:
1785
1886
  if "cell_options" in modification["options"]:
1786
1887
  self.cell_options = modification["options"]["cell_options"]
1787
1888
  if "column_options" in modification["options"]:
@@ -1798,17 +1899,25 @@ class MainTable(tk.Canvas):
1798
1899
  self.tagged_rows = modification["options"]["tagged_rows"]
1799
1900
  if "tagged_columns" in modification["options"]:
1800
1901
  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
- }
1902
+ if modification["named_spans"]:
1903
+ self.named_spans = {
1904
+ k: mod_span_widget(unpickle_obj(v), self.PAR) for k, v in modification["named_spans"].items()
1905
+ }
1906
+ if modification["sheet_state"]:
1907
+ self.RI.tree_open_ids = modification["sheet_state"]["tree_open_ids"]
1908
+ self.row_positions = modification["sheet_state"]["row_positions"]
1909
+ self.col_positions = modification["sheet_state"]["col_positions"]
1910
+ self.displayed_rows = modification["sheet_state"]["displayed_rows"]
1911
+ self.displayed_columns = modification["sheet_state"]["displayed_columns"]
1912
+ self.all_rows_displayed = modification["sheet_state"]["all_rows_displayed"]
1913
+ self.all_columns_displayed = modification["sheet_state"]["all_columns_displayed"]
1914
+ self.saved_row_heights = modification["sheet_state"]["saved_row_heights"]
1915
+ self.saved_column_widths = modification["sheet_state"]["saved_column_widths"]
1916
+ self.recreate_all_selection_boxes()
1804
1917
 
1805
1918
  def undo_modification_invert_event(self, modification: EventDataDict, name: str = "undo") -> EventDataDict:
1806
1919
  self.deselect("all", redraw=False)
1807
- event_data = event_dict(
1808
- name=modification["eventname"],
1809
- sheet=self.PAR.name,
1810
- widget=self,
1811
- )
1920
+ event_data = self.new_event_dict(modification["eventname"], state=True)
1812
1921
  event_data["selection_boxes"] = modification["selection_boxes"]
1813
1922
  event_data["selected"] = modification["selected"]
1814
1923
  saved_cells = False
@@ -1839,7 +1948,6 @@ class MainTable(tk.Canvas):
1839
1948
  "data": data_new_idxs,
1840
1949
  "displayed": disp_new_idxs,
1841
1950
  }
1842
- self.restore_options_named_spans(modification)
1843
1951
 
1844
1952
  if modification["moved"]["rows"]:
1845
1953
  totalrows = max(self.total_data_rows(), max(modification["moved"]["rows"]["data"].values()))
@@ -1859,12 +1967,12 @@ class MainTable(tk.Canvas):
1859
1967
  )
1860
1968
  ),
1861
1969
  event_data=event_data,
1970
+ undo_modification=modification,
1862
1971
  )
1863
1972
  event_data["moved"]["rows"] = {
1864
1973
  "data": data_new_idxs,
1865
1974
  "displayed": disp_new_idxs,
1866
1975
  }
1867
- self.restore_options_named_spans(modification)
1868
1976
 
1869
1977
  if modification["added"]["rows"]:
1870
1978
  self.deselect("all", run_binding=False, redraw=False)
@@ -1875,8 +1983,8 @@ class MainTable(tk.Canvas):
1875
1983
  event_data = self.delete_rows_displayed(
1876
1984
  rows=tuple(reversed(modification["added"]["rows"]["row_heights"])),
1877
1985
  event_data=event_data,
1986
+ restored_state=True,
1878
1987
  )
1879
- self.displayed_rows = modification["added"]["rows"]["displayed_rows"]
1880
1988
 
1881
1989
  if modification["added"]["columns"]:
1882
1990
  self.deselect("all", run_binding=False, redraw=False)
@@ -1887,21 +1995,21 @@ class MainTable(tk.Canvas):
1887
1995
  event_data = self.delete_columns_displayed(
1888
1996
  cols=tuple(reversed(modification["added"]["columns"]["column_widths"])),
1889
1997
  event_data=event_data,
1998
+ restored_state=True,
1890
1999
  )
1891
- self.displayed_columns = modification["added"]["columns"]["displayed_columns"]
1892
2000
 
1893
2001
  if modification["deleted"]["rows"] or modification["deleted"]["row_heights"]:
2002
+ event_data["treeview"] = modification["treeview"]
1894
2003
  self.add_rows(
1895
2004
  rows=modification["deleted"]["rows"],
1896
2005
  index=modification["deleted"]["index"],
1897
2006
  row_heights=modification["deleted"]["row_heights"],
1898
2007
  event_data=event_data,
1899
- displayed_rows=modification["deleted"]["displayed_rows"],
1900
2008
  create_ops=False,
1901
- create_selections=not modification["eventname"].startswith("edit"),
2009
+ create_selections=False,
1902
2010
  add_col_positions=False,
2011
+ restored_state=True,
1903
2012
  )
1904
- self.restore_options_named_spans(modification)
1905
2013
 
1906
2014
  if modification["deleted"]["columns"] or modification["deleted"]["column_widths"]:
1907
2015
  self.add_columns(
@@ -1909,12 +2017,11 @@ class MainTable(tk.Canvas):
1909
2017
  header=modification["deleted"]["header"],
1910
2018
  column_widths=modification["deleted"]["column_widths"],
1911
2019
  event_data=event_data,
1912
- displayed_columns=modification["deleted"]["displayed_columns"],
1913
2020
  create_ops=False,
1914
- create_selections=not modification["eventname"].startswith("edit"),
2021
+ create_selections=False,
1915
2022
  add_row_positions=False,
2023
+ restored_state=True,
1916
2024
  )
1917
- self.restore_options_named_spans(modification)
1918
2025
 
1919
2026
  if modification["eventname"].startswith(("edit", "move")):
1920
2027
  if not saved_cells:
@@ -1927,6 +2034,8 @@ class MainTable(tk.Canvas):
1927
2034
  elif modification["eventname"].startswith("delete"):
1928
2035
  event_data["eventname"] = modification["eventname"].replace("delete", "add")
1929
2036
 
2037
+ self.restore_sheet_state(modification)
2038
+
1930
2039
  if not modification["eventname"].startswith("move") and (
1931
2040
  len(self.row_positions) > 1 or len(self.col_positions) > 1
1932
2041
  ):
@@ -2819,7 +2928,7 @@ class MainTable(tk.Canvas):
2819
2928
  self.menu_add_command(
2820
2929
  self.CH.ch_rc_popup_menu,
2821
2930
  label=self.PAR.ops.delete_columns_label,
2822
- command=self.rc_delete_columns,
2931
+ command=self.delete_columns,
2823
2932
  **mnkwgs,
2824
2933
  )
2825
2934
  if self.rc_insert_column_enabled:
@@ -2845,7 +2954,7 @@ class MainTable(tk.Canvas):
2845
2954
  self.menu_add_command(
2846
2955
  self.RI.ri_rc_popup_menu,
2847
2956
  label=self.PAR.ops.delete_rows_label,
2848
- command=self.rc_delete_rows,
2957
+ command=self.delete_rows,
2849
2958
  **mnkwgs,
2850
2959
  )
2851
2960
  if self.rc_insert_row_enabled:
@@ -2867,6 +2976,99 @@ class MainTable(tk.Canvas):
2867
2976
  command=lambda: self.rc_add_rows("below"),
2868
2977
  **mnkwgs,
2869
2978
  )
2979
+ if self.rc_sort_cells_enabled:
2980
+ self.menu_add_command(
2981
+ self.rc_popup_menu,
2982
+ label=self.PAR.ops.sort_cells_label,
2983
+ accelerator=self.PAR.ops.sort_cells_accelerator,
2984
+ command=self.sort_boxes,
2985
+ **mnkwgs,
2986
+ )
2987
+ self.menu_add_command(
2988
+ self.rc_popup_menu,
2989
+ label=self.PAR.ops.sort_cells_reverse_label,
2990
+ accelerator=self.PAR.ops.sort_cells_reverse_accelerator,
2991
+ command=lambda: self.sort_boxes(reverse=True),
2992
+ **mnkwgs,
2993
+ )
2994
+ self.menu_add_command(
2995
+ self.rc_popup_menu,
2996
+ label=self.PAR.ops.sort_cells_x_label,
2997
+ accelerator=self.PAR.ops.sort_cells_x_accelerator,
2998
+ command=lambda: self.sort_boxes(row_wise=True),
2999
+ **mnkwgs,
3000
+ )
3001
+ self.menu_add_command(
3002
+ self.rc_popup_menu,
3003
+ label=self.PAR.ops.sort_cells_x_reverse_label,
3004
+ accelerator=self.PAR.ops.sort_cells_x_reverse_accelerator,
3005
+ command=lambda: self.sort_boxes(reverse=True, row_wise=True),
3006
+ **mnkwgs,
3007
+ )
3008
+ # row index sort rows cells
3009
+ if self.rc_sort_row_enabled:
3010
+ self.menu_add_command(
3011
+ self.RI.ri_rc_popup_menu,
3012
+ label=self.PAR.ops.sort_row_label,
3013
+ accelerator=self.PAR.ops.sort_row_accelerator,
3014
+ command=self.RI._sort_rows,
3015
+ **mnkwgs,
3016
+ )
3017
+ self.menu_add_command(
3018
+ self.RI.ri_rc_popup_menu,
3019
+ label=self.PAR.ops.sort_row_reverse_label,
3020
+ accelerator=self.PAR.ops.sort_row_reverse_accelerator,
3021
+ command=lambda: self.RI._sort_rows(reverse=True),
3022
+ **mnkwgs,
3023
+ )
3024
+ # header sort columns cells
3025
+ if self.rc_sort_column_enabled:
3026
+ self.menu_add_command(
3027
+ self.CH.ch_rc_popup_menu,
3028
+ label=self.PAR.ops.sort_column_label,
3029
+ accelerator=self.PAR.ops.sort_column_accelerator,
3030
+ command=self.CH._sort_columns,
3031
+ **mnkwgs,
3032
+ )
3033
+ self.menu_add_command(
3034
+ self.CH.ch_rc_popup_menu,
3035
+ label=self.PAR.ops.sort_column_reverse_label,
3036
+ accelerator=self.PAR.ops.sort_column_reverse_accelerator,
3037
+ command=lambda: self.CH._sort_columns(reverse=True),
3038
+ **mnkwgs,
3039
+ )
3040
+ # row index sort columns by row
3041
+ if self.rc_sort_columns_enabled:
3042
+ self.menu_add_command(
3043
+ self.RI.ri_rc_popup_menu,
3044
+ label=self.PAR.ops.sort_columns_label,
3045
+ accelerator=self.PAR.ops.sort_columns_accelerator,
3046
+ command=self.RI._sort_columns_by_row,
3047
+ **mnkwgs,
3048
+ )
3049
+ self.menu_add_command(
3050
+ self.RI.ri_rc_popup_menu,
3051
+ label=self.PAR.ops.sort_columns_reverse_label,
3052
+ accelerator=self.PAR.ops.sort_columns_reverse_accelerator,
3053
+ command=lambda: self.RI._sort_columns_by_row(reverse=True),
3054
+ **mnkwgs,
3055
+ )
3056
+ # header sort rows by column
3057
+ if self.rc_sort_rows_enabled:
3058
+ self.menu_add_command(
3059
+ self.CH.ch_rc_popup_menu,
3060
+ label=self.PAR.ops.sort_rows_label,
3061
+ accelerator=self.PAR.ops.sort_rows_accelerator,
3062
+ command=self.CH._sort_rows_by_column,
3063
+ **mnkwgs,
3064
+ )
3065
+ self.menu_add_command(
3066
+ self.CH.ch_rc_popup_menu,
3067
+ label=self.PAR.ops.sort_rows_reverse_label,
3068
+ accelerator=self.PAR.ops.sort_rows_reverse_accelerator,
3069
+ command=lambda: self.CH._sort_rows_by_column(reverse=True),
3070
+ **mnkwgs,
3071
+ )
2870
3072
  for label, func in self.extra_table_rc_menu_funcs.items():
2871
3073
  self.menu_add_command(
2872
3074
  self.rc_popup_menu,
@@ -3037,6 +3239,16 @@ class MainTable(tk.Canvas):
3037
3239
  self.rc_insert_row_enabled = True
3038
3240
  self.rc_popup_menus_enabled = True
3039
3241
  self.rc_select_enabled = True
3242
+ if binding in ("all", "sort_cells"):
3243
+ self.rc_sort_cells_enabled = True
3244
+ if binding in ("all", "sort_row"):
3245
+ self.rc_sort_row_enabled = True
3246
+ if binding in ("all", "sort_column", "sort_col"):
3247
+ self.rc_sort_column_enabled = True
3248
+ if binding in ("all", "sort_columns", "sort_cols"):
3249
+ self.rc_sort_columns_enabled = True
3250
+ if binding in ("all", "sort_rows"):
3251
+ self.rc_sort_rows_enabled = True
3040
3252
  if binding in ("all", "right_click_popup_menu", "rc_popup_menu", "rc_menu"):
3041
3253
  self.rc_popup_menus_enabled = True
3042
3254
  self.rc_select_enabled = True
@@ -3110,6 +3322,16 @@ class MainTable(tk.Canvas):
3110
3322
  self.rc_insert_column_enabled = False
3111
3323
  if binding in bind_add_rows:
3112
3324
  self.rc_insert_row_enabled = False
3325
+ if binding in ("all", "sort_cells"):
3326
+ self.rc_sort_cells_enabled = False
3327
+ if binding in ("all", "sort_row"):
3328
+ self.rc_sort_row_enabled = False
3329
+ if binding in ("all", "sort_column", "sort_col"):
3330
+ self.rc_sort_column_enabled = False
3331
+ if binding in ("all", "sort_columns", "sort_cols"):
3332
+ self.rc_sort_columns_enabled = False
3333
+ if binding in ("all", "sort_rows"):
3334
+ self.rc_sort_rows_enabled = False
3113
3335
  if binding in ("all", "right_click_popup_menu", "rc_popup_menu", "rc_menu"):
3114
3336
  self.rc_popup_menus_enabled = False
3115
3337
  if binding in ("all", "right_click_select", "rc_select"):
@@ -3255,12 +3477,12 @@ class MainTable(tk.Canvas):
3255
3477
  t, sh = self.hidd_resize_lines.popitem()
3256
3478
  self.coords(t, x1, y1, x2, y2)
3257
3479
  if sh:
3258
- self.itemconfig(t, width=width, fill=fill, tag=tag)
3480
+ self.itemconfig(t, width=width, fill=fill, tags=tag)
3259
3481
  else:
3260
- self.itemconfig(t, width=width, fill=fill, tag=tag, state="normal")
3482
+ self.itemconfig(t, width=width, fill=fill, tags=tag, state="normal")
3261
3483
  self.lift(t)
3262
3484
  else:
3263
- t = self.create_line(x1, y1, x2, y2, width=width, fill=fill, tag=tag)
3485
+ t = self.create_line(x1, y1, x2, y2, width=width, fill=fill, tags=tag)
3264
3486
  self.disp_resize_lines[t] = True
3265
3487
 
3266
3488
  def delete_resize_lines(self):
@@ -3768,38 +3990,14 @@ class MainTable(tk.Canvas):
3768
3990
  c_pc = 0.0
3769
3991
  old_min_row_height = int(self.min_row_height)
3770
3992
  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)
3993
+ self.set_table_font(table_font, row_heights=False)
3994
+ self.set_index_font(index_font, row_heights=False)
3776
3995
  self.set_header_font(header_font)
3777
3996
  if self.PAR.ops.set_cell_sizes_on_zoom:
3778
3997
  self.set_all_cell_sizes_to_text()
3779
3998
  self.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
3780
3999
  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
- )
4000
+ self.change_font_manage_row_heights(old_min_row_height, old_default_row_height)
3803
4001
  self.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
3804
4002
  self.recreate_all_selection_boxes()
3805
4003
  self.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
@@ -3843,13 +4041,20 @@ class MainTable(tk.Canvas):
3843
4041
  return b[2] - b[0], b[3] - b[1]
3844
4042
 
3845
4043
  def get_lines_cell_height(self, n: int, font: None | FontTuple = None) -> int:
3846
- return (
3847
- self.get_txt_h(
4044
+ if font == self.PAR.ops.table_font:
4045
+ return 3 + (n * self.table_txt_height)
4046
+
4047
+ elif font == self.PAR.ops.index_font:
4048
+ return 3 + (n * self.index_txt_height)
4049
+
4050
+ elif font == self.PAR.ops.header_font:
4051
+ return 3 + (n * self.header_txt_height)
4052
+
4053
+ else:
4054
+ return 3 + self.get_txt_h(
3848
4055
  txt="\n".join("|" for _ in range(n)) if n > 1 else "|",
3849
- font=self.PAR.ops.table_font if font is None else font,
4056
+ font=font,
3850
4057
  )
3851
- + 5
3852
- )
3853
4058
 
3854
4059
  def set_min_column_width(self, width: int) -> None:
3855
4060
  if width:
@@ -3880,62 +4085,95 @@ class MainTable(tk.Canvas):
3880
4085
  )
3881
4086
  return self.PAR.ops.default_header_height
3882
4087
 
3883
- def set_table_font(self, newfont: tuple | None = None, reset_row_positions: bool = False) -> tuple[str, int, str]:
4088
+ def check_font(self, newfont: tuple) -> None:
4089
+ if (
4090
+ not isinstance(newfont, tuple)
4091
+ or len(newfont) != 3
4092
+ or not isinstance(newfont[0], str)
4093
+ or not isinstance(newfont[1], int)
4094
+ or not isinstance(newfont[2], str)
4095
+ ):
4096
+ raise ValueError(font_value_error)
4097
+
4098
+ def set_table_font(self, newfont: tuple | None = None, row_heights: bool = True) -> tuple[str, int, str]:
3884
4099
  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
- )
4100
+ self.check_font(newfont)
3893
4101
  self.PAR.ops.table_font = FontTuple(*newfont)
4102
+ old_min_row_height = int(self.min_row_height)
4103
+ old_default_row_height = int(self.get_default_row_height())
3894
4104
  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)
4105
+ if row_heights:
4106
+ self.change_font_manage_row_heights(old_min_row_height, old_default_row_height)
3900
4107
  self.recreate_all_selection_boxes()
3901
4108
  return self.PAR.ops.table_font
3902
4109
 
3903
4110
  def set_table_font_help(self):
4111
+ self.table_font = self.PAR.ops.table_font
4112
+ if self.PAR.ops.table_font not in self.char_widths:
4113
+ self.char_widths[self.PAR.ops.table_font] = {}
4114
+ self.table_test_str_w = self.get_txt_w(_test_str)
3904
4115
  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
4116
+ self.min_row_height = max(6, self.table_txt_height, self.index_txt_height) + 6
3914
4117
 
3915
- def set_header_font(self, newfont: tuple | None = None) -> tuple[str, int, str]:
4118
+ def set_index_font(self, newfont: tuple | None = None, row_heights: bool = True) -> tuple[str, int, str]:
3916
4119
  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')"
4120
+ self.check_font(newfont)
4121
+ self.PAR.ops.index_font = FontTuple(*newfont)
4122
+ old_min_row_height = int(self.min_row_height)
4123
+ old_default_row_height = int(self.get_default_row_height())
4124
+ self.set_index_font_help()
4125
+ if row_heights:
4126
+ self.change_font_manage_row_heights(old_min_row_height, old_default_row_height)
4127
+ self.recreate_all_selection_boxes()
4128
+ return self.PAR.ops.index_font
4129
+
4130
+ def set_index_font_help(self):
4131
+ self.RI.index_font = self.PAR.ops.index_font
4132
+ if self.PAR.ops.index_font not in self.char_widths:
4133
+ self.char_widths[self.PAR.ops.index_font] = {}
4134
+ self.RI.index_test_str_w = self.get_txt_w(_test_str, self.PAR.ops.index_font)
4135
+ self.index_txt_width, self.index_txt_height = self.get_txt_dimensions("|", self.PAR.ops.index_font)
4136
+ self.min_row_height = max(6, self.table_txt_height, self.index_txt_height) + 6
4137
+
4138
+ def change_font_manage_row_heights(self, old_min_row_height: int, old_default_row_height: int) -> None:
4139
+ default_row_height = self.get_default_row_height()
4140
+ self.row_positions = list(
4141
+ accumulate(
4142
+ chain(
4143
+ [0],
4144
+ (
4145
+ (
4146
+ self.min_row_height
4147
+ if h == old_min_row_height
4148
+ else (
4149
+ default_row_height
4150
+ if h == old_default_row_height
4151
+ else self.min_row_height
4152
+ if h < self.min_row_height
4153
+ else h
4154
+ )
4155
+ )
4156
+ for h in self.gen_row_heights()
4157
+ ),
3924
4158
  )
4159
+ )
4160
+ )
4161
+
4162
+ def set_header_font(self, newfont: tuple | None = None) -> tuple[str, int, str]:
4163
+ if newfont:
4164
+ self.check_font(newfont)
3925
4165
  self.PAR.ops.header_font = FontTuple(*newfont)
3926
4166
  self.set_header_font_help()
3927
4167
  self.recreate_all_selection_boxes()
3928
4168
  return self.PAR.ops.header_font
3929
4169
 
3930
4170
  def set_header_font_help(self):
4171
+ self.CH.header_font = self.PAR.ops.header_font
4172
+ if self.PAR.ops.header_font not in self.char_widths:
4173
+ self.char_widths[self.PAR.ops.header_font] = {}
4174
+ self.CH.header_test_str_w = self.get_txt_w(_test_str, self.PAR.ops.header_font)
3931
4175
  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)
4176
+ self.min_header_height = self.header_txt_height + 6
3939
4177
  if (
3940
4178
  isinstance(self.PAR.ops.default_header_height, int)
3941
4179
  and self.PAR.ops.default_header_height < self.min_header_height
@@ -3943,30 +4181,6 @@ class MainTable(tk.Canvas):
3943
4181
  self.PAR.ops.default_header_height = int(self.min_header_height)
3944
4182
  self.CH.set_height(self.get_default_header_height(), set_TL=True)
3945
4183
 
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
4184
  def purge_undo_and_redo_stack(self):
3971
4185
  self.undo_stack = deque(maxlen=self.PAR.ops.max_undos)
3972
4186
  self.redo_stack = deque(maxlen=self.PAR.ops.max_undos)
@@ -4088,10 +4302,62 @@ class MainTable(tk.Canvas):
4088
4302
  else:
4089
4303
  return False
4090
4304
 
4091
- def set_all_cell_sizes_to_text(
4092
- self,
4093
- width: int | None = None,
4094
- slim: bool = False,
4305
+ def get_cell_max_width(self, datarn: int, dispcn: int) -> int:
4306
+ datacn = self.datacn(dispcn)
4307
+ col_width = self.col_positions[dispcn + 1] - self.col_positions[dispcn]
4308
+ if kwargs := self.get_cell_kwargs(datarn, datacn, "dropdown"):
4309
+ max_width = col_width - self.table_txt_height - 5
4310
+ else:
4311
+ max_width = col_width - 2
4312
+ if (kwargs := self.get_cell_kwargs(datarn, datacn, "dropdown")) and max_width > self.table_txt_height + 1:
4313
+ box_w = self.table_txt_height + 1
4314
+ max_width -= box_w + 4
4315
+ if self.PAR.ops.allow_cell_overflow and not kwargs:
4316
+ if self.cells_cache is None:
4317
+ disprn = self.disprn(datarn)
4318
+ self.cells_cache = self._redraw_precache_cells(disprn, disprn + 1, 0, len(self.col_positions) - 1)
4319
+ if not (align := self.get_cell_kwargs(datarn, datacn, key="align")):
4320
+ align = self.align
4321
+ if align.endswith("w"):
4322
+ max_width += sum(
4323
+ self._overflow(
4324
+ self.cells_cache,
4325
+ range(dispcn + 1, len(self.col_positions) - 1),
4326
+ datarn,
4327
+ )
4328
+ )
4329
+ elif align.endswith("e"):
4330
+ max_width += sum(
4331
+ self._overflow(
4332
+ self.cells_cache,
4333
+ reversed(range(0, dispcn)),
4334
+ datarn,
4335
+ )
4336
+ )
4337
+ return max_width
4338
+
4339
+ def get_wrapped_cell_height(self, datarn: int, datacn: int) -> int:
4340
+ dispcn = self.dispcn(datacn)
4341
+ n_lines = max(
4342
+ 1,
4343
+ sum(
4344
+ 1
4345
+ for _ in wrap_text(
4346
+ text=self.get_valid_cell_data_as_str(datarn, datacn, get_displayed=True),
4347
+ max_width=self.get_cell_max_width(datarn, dispcn),
4348
+ max_lines=float("inf"),
4349
+ char_width_fn=self.wrap_get_char_w,
4350
+ widths=self.char_widths[self.table_font],
4351
+ wrap=self.PAR.ops.table_wrap,
4352
+ )
4353
+ ),
4354
+ )
4355
+ return 3 + (n_lines * self.table_txt_height)
4356
+
4357
+ def set_all_cell_sizes_to_text(
4358
+ self,
4359
+ width: int | None = None,
4360
+ slim: bool = False,
4095
4361
  ) -> tuple[list[float], list[float]]:
4096
4362
  min_column_width = self.PAR.ops.min_column_width
4097
4363
  max_column_width = float_to_int(self.PAR.ops.max_column_width)
@@ -4171,26 +4437,28 @@ class MainTable(tk.Canvas):
4171
4437
  def set_col_positions(self, itr: AnyIter[float]) -> None:
4172
4438
  self.col_positions = list(accumulate(chain([0], itr)))
4173
4439
 
4174
- def reset_col_positions(self, ncols: int | None = None):
4175
- colpos = self.PAR.ops.default_column_width
4440
+ def reset_col_positions(self, ncols: int | None = None, width: int | None = None) -> None:
4441
+ if width is None:
4442
+ width = self.PAR.ops.default_column_width
4176
4443
  if isinstance(ncols, int):
4177
- self.set_col_positions(itr=repeat(colpos, ncols))
4444
+ self.set_col_positions(itr=repeat(width, ncols))
4178
4445
  elif self.all_columns_displayed:
4179
- self.set_col_positions(itr=repeat(colpos, self.total_data_cols()))
4446
+ self.set_col_positions(itr=repeat(width, self.total_data_cols()))
4180
4447
  else:
4181
- self.set_col_positions(itr=repeat(colpos, len(self.displayed_columns)))
4448
+ self.set_col_positions(itr=repeat(width, len(self.displayed_columns)))
4182
4449
 
4183
4450
  def set_row_positions(self, itr: AnyIter[float]) -> None:
4184
4451
  self.row_positions = list(accumulate(chain([0], itr)))
4185
4452
 
4186
- def reset_row_positions(self, nrows: int | None = None):
4187
- rowpos = self.get_default_row_height()
4453
+ def reset_row_positions(self, nrows: int | None = None, height: int | None = None) -> None:
4454
+ if height is None:
4455
+ height = self.get_default_row_height()
4188
4456
  if isinstance(nrows, int):
4189
- self.set_row_positions(itr=repeat(rowpos, nrows))
4457
+ self.set_row_positions(itr=repeat(height, nrows))
4190
4458
  elif self.all_rows_displayed:
4191
- self.set_row_positions(itr=repeat(rowpos, self.total_data_rows()))
4459
+ self.set_row_positions(itr=repeat(height, self.total_data_rows()))
4192
4460
  else:
4193
- self.set_row_positions(itr=repeat(rowpos, len(self.displayed_rows)))
4461
+ self.set_row_positions(itr=repeat(height, len(self.displayed_rows)))
4194
4462
 
4195
4463
  def del_col_position(self, idx: int, deselect_all: bool = False):
4196
4464
  if deselect_all:
@@ -4242,46 +4510,6 @@ class MainTable(tk.Canvas):
4242
4510
  def gen_row_heights(self) -> Generator[int]:
4243
4511
  return diff_gen(self.row_positions)
4244
4512
 
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
4513
  def insert_col_positions(
4286
4514
  self,
4287
4515
  idx: Literal["end"] | int = "end",
@@ -4368,25 +4596,13 @@ class MainTable(tk.Canvas):
4368
4596
  create_ops: bool = True,
4369
4597
  ) -> None:
4370
4598
  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()
4599
+ tags: {(r, push_n(c, cols)) for (r, c) in tagged} for tags, tagged in self.tagged_cells.items()
4389
4600
  }
4601
+ self.cell_options = {(r, push_n(c, cols)): v for (r, c), v in self.cell_options.items()}
4602
+ self.progress_bars = {(r, push_n(c, cols)): v for (r, c), v in self.progress_bars.items()}
4603
+ self.tagged_columns = {tags: {push_n(c, cols) for c in tagged} for tags, tagged in self.tagged_columns.items()}
4604
+ self.col_options = {push_n(c, cols): v for c, v in self.col_options.items()}
4605
+ self.CH.cell_options = {push_n(c, cols): v for c, v in self.CH.cell_options.items()}
4390
4606
  # if there are named spans where columns were added
4391
4607
  # add options to gap which was created by adding columns
4392
4608
  totalrows = None
@@ -4436,32 +4652,18 @@ class MainTable(tk.Canvas):
4436
4652
 
4437
4653
  def adjust_options_post_add_rows(
4438
4654
  self,
4439
- rows: list | tuple,
4655
+ rows: list[int] | tuple[int],
4440
4656
  create_ops: bool = True,
4441
4657
  ) -> None:
4442
4658
  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()
4659
+ tags: {(push_n(r, rows), c) for (r, c) in tagged} for tags, tagged in self.tagged_cells.items()
4464
4660
  }
4661
+ self.cell_options = {(push_n(r, rows), c): v for (r, c), v in self.cell_options.items()}
4662
+ self.progress_bars = {(push_n(r, rows), c): v for (r, c), v in self.progress_bars.items()}
4663
+ self.tagged_rows = {tags: {push_n(r, rows) for r in tagged} for tags, tagged in self.tagged_rows.items()}
4664
+ self.row_options = {push_n(r, rows): v for r, v in self.row_options.items()}
4665
+ self.RI.cell_options = {push_n(r, rows): v for r, v in self.RI.cell_options.items()}
4666
+ self.RI.tree_rns = {k: push_n(r, rows) for k, r in self.RI.tree_rns.items()}
4465
4667
  # if there are named spans where rows were added
4466
4668
  # add options to gap which was created by adding rows
4467
4669
  totalcols = None
@@ -4696,29 +4898,27 @@ class MainTable(tk.Canvas):
4696
4898
  header: dict,
4697
4899
  column_widths: dict,
4698
4900
  event_data: dict,
4699
- displayed_columns: None | list[int] = None,
4700
4901
  create_ops: bool = True,
4701
4902
  create_selections: bool = True,
4702
4903
  add_row_positions: bool = True,
4703
4904
  push_ops: bool = True,
4704
4905
  mod_event_boxes: bool = True,
4906
+ restored_state: bool = False,
4705
4907
  ) -> EventDataDict:
4706
4908
  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:
4909
+ if not restored_state and not self.all_columns_displayed:
4711
4910
  self.displayed_columns = add_to_displayed(self.displayed_columns, columns)
4712
4911
  cws = self.get_column_widths()
4713
4912
  if column_widths and next(reversed(column_widths)) > len(cws):
4714
4913
  for i in reversed(range(len(cws), len(cws) + next(reversed(column_widths)) - len(cws))):
4715
4914
  column_widths[i] = self.PAR.ops.default_column_width
4716
- self.set_col_positions(
4717
- itr=insert_items(
4718
- cws,
4719
- column_widths,
4915
+ if not restored_state:
4916
+ self.set_col_positions(
4917
+ itr=insert_items(
4918
+ cws,
4919
+ column_widths,
4920
+ )
4720
4921
  )
4721
- )
4722
4922
  # rn needed for indexing but cn insert
4723
4923
  maxrn = 0
4724
4924
  for cn, rowdict in reversed(columns.items()):
@@ -4737,14 +4937,14 @@ class MainTable(tk.Canvas):
4737
4937
  "table": {},
4738
4938
  "index": {},
4739
4939
  "row_heights": {rn: default_height for rn in range(len(self.row_positions) - 1, maxrn + 1)},
4740
- "displayed_rows": self.displayed_rows,
4741
4940
  }
4742
- self.set_row_positions(
4743
- itr=chain(
4744
- self.gen_row_heights(),
4745
- repeat(default_height, maxrn + 1 - (len(self.row_positions) - 1)),
4941
+ if not restored_state:
4942
+ self.set_row_positions(
4943
+ itr=chain(
4944
+ self.gen_row_heights(),
4945
+ repeat(default_height, maxrn + 1 - (len(self.row_positions) - 1)),
4946
+ )
4746
4947
  )
4747
- )
4748
4948
  if isinstance(self._headers, list) and header:
4749
4949
  self._headers = insert_items(self._headers, header, self.CH.fix_header)
4750
4950
  if push_ops:
@@ -4770,7 +4970,6 @@ class MainTable(tk.Canvas):
4770
4970
  "table": columns,
4771
4971
  "header": header,
4772
4972
  "column_widths": column_widths,
4773
- "displayed_columns": saved_displayed_columns,
4774
4973
  }
4775
4974
  return event_data
4776
4975
 
@@ -4807,13 +5006,7 @@ class MainTable(tk.Canvas):
4807
5006
  numcols = self.PAR.ops.paste_insert_column_limit - len(self.col_positions) - 1
4808
5007
  if numcols < 1:
4809
5008
  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
- )
5009
+ event_data = self.new_event_dict("add_columns", state=True)
4817
5010
  if not try_binding(self.extra_begin_insert_cols_rc_func, event_data, "begin_add_columns"):
4818
5011
  return
4819
5012
  event_data = self.add_columns(
@@ -4828,34 +5021,33 @@ class MainTable(tk.Canvas):
4828
5021
 
4829
5022
  def add_rows(
4830
5023
  self,
4831
- rows: dict,
4832
- index: dict,
4833
- row_heights: dict,
4834
- event_data: dict,
4835
- displayed_rows: None | list[int] = None,
5024
+ rows: dict[int, list[object]],
5025
+ index: dict[int, object],
5026
+ row_heights: dict[int, float | int],
5027
+ event_data: EventDataDict,
4836
5028
  create_ops: bool = True,
4837
5029
  create_selections: bool = True,
4838
5030
  add_col_positions: bool = True,
4839
5031
  push_ops: bool = True,
5032
+ tree: bool = True,
4840
5033
  mod_event_boxes: bool = True,
5034
+ restored_state: bool = False,
4841
5035
  ) -> EventDataDict:
4842
5036
  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:
5037
+ if not restored_state and not self.all_rows_displayed:
4847
5038
  self.displayed_rows = add_to_displayed(self.displayed_rows, rows)
4848
5039
  rhs = self.get_row_heights()
4849
5040
  if row_heights and next(reversed(row_heights)) > len(rhs):
4850
5041
  default_row_height = self.get_default_row_height()
4851
5042
  for i in reversed(range(len(rhs), len(rhs) + next(reversed(row_heights)) - len(rhs))):
4852
5043
  row_heights[i] = default_row_height
4853
- self.set_row_positions(
4854
- itr=insert_items(
4855
- rhs,
4856
- row_heights,
5044
+ if not restored_state:
5045
+ self.set_row_positions(
5046
+ itr=insert_items(
5047
+ rhs,
5048
+ row_heights,
5049
+ )
4857
5050
  )
4858
- )
4859
5051
  maxcn = 0
4860
5052
  # rn needed for insert but cn indexing
4861
5053
  for rn, row in reversed(rows.items()):
@@ -4874,14 +5066,14 @@ class MainTable(tk.Canvas):
4874
5066
  "table": {},
4875
5067
  "header": {},
4876
5068
  "column_widths": {cn: default_width for cn in range(len(self.col_positions) - 1, maxcn + 1)},
4877
- "displayed_columns": self.displayed_columns,
4878
5069
  }
4879
- self.set_col_positions(
4880
- itr=chain(
4881
- self.gen_column_widths(),
4882
- repeat(default_width, maxcn + 1 - (len(self.col_positions) - 1)),
5070
+ if not restored_state:
5071
+ self.set_col_positions(
5072
+ itr=chain(
5073
+ self.gen_column_widths(),
5074
+ repeat(default_width, maxcn + 1 - (len(self.col_positions) - 1)),
5075
+ )
4883
5076
  )
4884
- )
4885
5077
  if push_ops:
4886
5078
  self.adjust_options_post_add_rows(
4887
5079
  rows=tuple(reversed(rows)),
@@ -4905,8 +5097,9 @@ class MainTable(tk.Canvas):
4905
5097
  "table": rows,
4906
5098
  "index": index,
4907
5099
  "row_heights": row_heights,
4908
- "displayed_rows": saved_displayed_rows,
4909
5100
  }
5101
+ if tree and self.PAR.ops.treeview:
5102
+ event_data = self.RI.tree_add_rows(event_data=event_data)
4910
5103
  return event_data
4911
5104
 
4912
5105
  def rc_add_rows(self, event: object = None):
@@ -4942,13 +5135,7 @@ class MainTable(tk.Canvas):
4942
5135
  numrows = self.PAR.ops.paste_insert_row_limit - len(self.row_positions) - 1
4943
5136
  if numrows < 1:
4944
5137
  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
- )
5138
+ event_data = self.new_event_dict("add_rows", state=True)
4952
5139
  if not try_binding(self.extra_begin_insert_rows_rc_func, event_data, "begin_add_rows"):
4953
5140
  return
4954
5141
  event_data = self.add_rows(
@@ -5029,10 +5216,14 @@ class MainTable(tk.Canvas):
5029
5216
  for datarn, v in zip(reversed(range(data_ins_row, data_ins_row + numrows)), reversed(rows))
5030
5217
  }
5031
5218
  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
- }
5219
+ if self.PAR.ops.treeview:
5220
+ nodes = [self.RI.get_value_for_empty_cell(data_ins_row, r_ops=False) for _ in range(numrows)]
5221
+ index_data = dict(zip(reversed(range(data_ins_row, data_ins_row + numrows)), reversed(nodes)))
5222
+ else:
5223
+ index_data = {
5224
+ datarn: self.RI.get_value_for_empty_cell(datarn, r_ops=False)
5225
+ for datarn in reversed(range(data_ins_row, data_ins_row + numrows))
5226
+ }
5036
5227
  if rows is None:
5037
5228
  if total_data_cols is None:
5038
5229
  total_data_cols = self.total_data_cols()
@@ -5072,11 +5263,27 @@ class MainTable(tk.Canvas):
5072
5263
  "tagged_columns": {f"{tag}": set(s) for tag, s in self.tagged_columns.items()},
5073
5264
  }
5074
5265
 
5075
- def delete_columns_data(self, cols: list, event_data: dict) -> EventDataDict:
5266
+ def copy_sheet_state(self) -> dict:
5267
+ return {
5268
+ "row_positions": list(self.row_positions),
5269
+ "col_positions": list(self.col_positions),
5270
+ "displayed_rows": int(self.displayed_rows)
5271
+ if isinstance(self.displayed_rows, int)
5272
+ else list(self.displayed_rows),
5273
+ "displayed_columns": int(self.displayed_columns)
5274
+ if isinstance(self.displayed_columns, int)
5275
+ else list(self.displayed_columns),
5276
+ "all_rows_displayed": bool(self.all_rows_displayed),
5277
+ "saved_row_heights": dict(self.saved_row_heights),
5278
+ "saved_column_widths": dict(self.saved_column_widths),
5279
+ "all_columns_displayed": bool(self.all_columns_displayed),
5280
+ "tree_open_ids": set(self.RI.tree_open_ids),
5281
+ }
5282
+
5283
+ def delete_columns_data(self, cols: list[int], event_data: EventDataDict | None = None) -> EventDataDict:
5076
5284
  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
- )
5285
+ if not event_data:
5286
+ event_data = self.new_event_dict("delete_columns", state=True)
5080
5287
  event_data["options"] = self.copy_options()
5081
5288
  event_data["named_spans"] = {k: span.pickle_self() for k, span in self.named_spans.items()}
5082
5289
  for datacn in reversed(cols):
@@ -5104,41 +5311,62 @@ class MainTable(tk.Canvas):
5104
5311
  ]
5105
5312
  return event_data
5106
5313
 
5107
- def delete_columns_displayed(self, cols: list, event_data: dict) -> EventDataDict:
5314
+ def delete_columns_displayed(
5315
+ self,
5316
+ cols: list[int],
5317
+ event_data: EventDataDict | None = None,
5318
+ restored_state: bool = False,
5319
+ ) -> EventDataDict:
5320
+ if not event_data:
5321
+ event_data = self.new_event_dict("delete_columns", state=True)
5108
5322
  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))
5323
+ if cols:
5324
+ for c in reversed(cols):
5325
+ if len(self.col_positions) > c + 1:
5326
+ event_data["deleted"]["column_widths"][c] = self.col_positions[c + 1] - self.col_positions[c]
5327
+ if not restored_state:
5328
+ cols_set = set(cols)
5329
+ self.set_col_positions(
5330
+ itr=(width for c, width in enumerate(self.gen_column_widths()) if c not in cols_set)
5331
+ )
5113
5332
  return event_data
5114
5333
 
5115
- def rc_delete_columns(self, event: object = None):
5116
- selected = sorted(self.get_selected_cols())
5117
- if not self.selected:
5334
+ def delete_columns(
5335
+ self,
5336
+ event: object = None,
5337
+ columns: list[int] | None = None,
5338
+ data_indexes: bool = False,
5339
+ undo: bool = True,
5340
+ emit_event: bool = True,
5341
+ ext: bool = False,
5342
+ ) -> EventDataDict:
5343
+ event_data = self.new_event_dict("delete_columns", state=True)
5344
+ if not columns:
5345
+ if not (columns := sorted(self.get_selected_cols())):
5346
+ return event_data
5347
+ if not ext and not try_binding(self.extra_begin_del_cols_rc_func, event_data, "begin_delete_columns"):
5118
5348
  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,
5349
+ event_data = self.delete_columns_displayed(
5350
+ data_to_displayed_idxs(columns, self.MT.displayed_columns) if data_indexes else columns,
5351
+ event_data,
5125
5352
  )
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:
5353
+ event_data = self.delete_columns_data(
5354
+ columns if data_indexes or self.all_columns_displayed else [self.displayed_columns[c] for c in columns],
5355
+ event_data,
5356
+ )
5357
+ if undo and self.undo_enabled:
5132
5358
  self.undo_stack.append(stored_event_dict(event_data))
5359
+ if not ext:
5360
+ try_binding(self.extra_end_del_cols_rc_func, event_data, "end_delete_columns")
5361
+ if emit_event:
5362
+ self.sheet_modified(event_data)
5133
5363
  self.deselect("all")
5134
- try_binding(self.extra_end_del_cols_rc_func, event_data, "end_delete_columns")
5135
- self.sheet_modified(event_data)
5364
+ return event_data
5136
5365
 
5137
- def delete_rows_data(self, rows: list, event_data: dict) -> EventDataDict:
5366
+ def delete_rows_data(self, rows: list[int], event_data: EventDataDict | None = None) -> EventDataDict:
5138
5367
  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
- )
5368
+ if not event_data:
5369
+ event_data = self.new_event_dict("delete_rows", state=True)
5142
5370
  event_data["options"] = self.copy_options()
5143
5371
  event_data["named_spans"] = {k: span.pickle_self() for k, span in self.named_spans.items()}
5144
5372
  for datarn in reversed(rows):
@@ -5147,6 +5375,8 @@ class MainTable(tk.Canvas):
5147
5375
  event_data["deleted"]["index"][datarn] = self._row_index.pop(datarn)
5148
5376
  except Exception:
5149
5377
  continue
5378
+ if self.PAR.ops.treeview:
5379
+ event_data = self.RI.tree_del_rows(event_data=event_data)
5150
5380
  rows_set = set(rows)
5151
5381
  self.adjust_options_post_delete_rows(
5152
5382
  to_del=rows_set,
@@ -5160,35 +5390,72 @@ class MainTable(tk.Canvas):
5160
5390
  ]
5161
5391
  return event_data
5162
5392
 
5163
- def delete_rows_displayed(self, rows: list, event_data: dict) -> EventDataDict:
5393
+ def delete_rows_displayed(
5394
+ self,
5395
+ rows: list[int],
5396
+ event_data: EventDataDict | None = None,
5397
+ restored_state: bool = False,
5398
+ ) -> EventDataDict:
5399
+ if not event_data:
5400
+ event_data = self.new_event_dict("delete_rows", state=True)
5164
5401
  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))
5402
+ if rows:
5403
+ for r in reversed(rows):
5404
+ if len(self.row_positions) > r + 1:
5405
+ event_data["deleted"]["row_heights"][r] = self.row_positions[r + 1] - self.row_positions[r]
5406
+ if not restored_state:
5407
+ rows_set = set(rows)
5408
+ self.set_row_positions(
5409
+ itr=(height for r, height in enumerate(self.gen_row_heights()) if r not in rows_set)
5410
+ )
5169
5411
  return event_data
5170
5412
 
5171
- def rc_delete_rows(self, event: object = None):
5172
- selected = sorted(self.get_selected_rows())
5173
- if not self.selected:
5413
+ def delete_rows(
5414
+ self,
5415
+ event: object = None,
5416
+ rows: list[int] | None = None,
5417
+ data_indexes: bool = False,
5418
+ undo: bool = True,
5419
+ emit_event: bool = True,
5420
+ ext: bool = False,
5421
+ ) -> EventDataDict:
5422
+ event_data = self.new_event_dict("delete_rows", state=True)
5423
+ if not rows:
5424
+ if not (rows := sorted(self.get_selected_rows())):
5425
+ return
5426
+ if not ext and not try_binding(self.extra_begin_del_rows_rc_func, event_data, "begin_delete_rows"):
5174
5427
  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,
5428
+ if data_indexes or self.all_rows_displayed:
5429
+ data_rows = rows
5430
+ else:
5431
+ data_rows = [self.displayed_rows[r] for r in rows]
5432
+ if self.PAR.ops.treeview:
5433
+ data_rows = sorted(
5434
+ chain(
5435
+ data_rows,
5436
+ (
5437
+ self.RI.tree_rns[did]
5438
+ for r in data_rows
5439
+ for did in self.RI.get_iid_descendants(self._row_index[r].iid)
5440
+ ),
5441
+ )
5442
+ )
5443
+ event_data = self.delete_rows_displayed(
5444
+ data_to_displayed_idxs(data_rows, self.displayed_rows) if self.PAR.ops.treeview or data_indexes else rows,
5445
+ event_data,
5181
5446
  )
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:
5447
+ event_data = self.delete_rows_data(
5448
+ data_rows,
5449
+ event_data,
5450
+ )
5451
+ if undo and self.undo_enabled:
5188
5452
  self.undo_stack.append(stored_event_dict(event_data))
5453
+ if not ext:
5454
+ try_binding(self.extra_end_del_rows_rc_func, event_data, "end_delete_rows")
5455
+ if emit_event:
5456
+ self.sheet_modified(event_data)
5189
5457
  self.deselect("all")
5190
- try_binding(self.extra_end_del_rows_rc_func, event_data, "end_delete_rows")
5191
- self.sheet_modified(event_data)
5458
+ return event_data
5192
5459
 
5193
5460
  def move_row_position(self, idx1: int, idx2: int):
5194
5461
  if not len(self.row_positions) <= 2:
@@ -5593,9 +5860,8 @@ class MainTable(tk.Canvas):
5593
5860
  self.itemconfig(iid, fill=fill, outline=outline)
5594
5861
  else:
5595
5862
  self.itemconfig(iid, fill=fill, outline=outline, state="normal")
5596
- self.tag_raise(iid)
5597
5863
  else:
5598
- iid = self.create_rectangle(coords, fill=fill, outline=outline)
5864
+ iid = self.create_rectangle(coords, fill=fill, outline=outline, tags="h")
5599
5865
  self.disp_high[iid] = True
5600
5866
  return True
5601
5867
 
@@ -5621,7 +5887,6 @@ class MainTable(tk.Canvas):
5621
5887
  joinstyle=tk.ROUND,
5622
5888
  state="normal",
5623
5889
  )
5624
- self.tag_raise(iid)
5625
5890
  else:
5626
5891
  iid = self.create_line(
5627
5892
  points,
@@ -5811,12 +6076,67 @@ class MainTable(tk.Canvas):
5811
6076
  self.PAR.yscroll.grid(row=0, column=2, rowspan=3, sticky="nswe")
5812
6077
  self.PAR.yscroll_showing = True
5813
6078
 
6079
+ def _overflow(
6080
+ self,
6081
+ cells: dict,
6082
+ rnge: Generator[int],
6083
+ datarn: int,
6084
+ ) -> Generator[float]:
6085
+ for c_ in rnge:
6086
+ if (
6087
+ cells[(datarn, cells["datacn"][c_])]
6088
+ or (datarn, cells["datacn"][c_]) in cells["dropdown"]
6089
+ or (datarn, cells["datacn"][c_]) in cells["checkbox"]
6090
+ ):
6091
+ return
6092
+ else:
6093
+ yield self.col_positions[c_ + 1] - self.col_positions[c_]
6094
+
6095
+ def _redraw_precache_cells(
6096
+ self,
6097
+ text_start_row: int,
6098
+ text_end_row: int,
6099
+ text_start_col: int,
6100
+ text_end_col: int,
6101
+ ) -> dict:
6102
+ cells = {"datarn": {}, "datacn": {}, "dropdown": {}, "checkbox": {}}
6103
+ for r in range(text_start_row, text_end_row):
6104
+ datarn = r if self.all_rows_displayed else self.displayed_rows[r]
6105
+ cells["datarn"][r] = datarn
6106
+ for c in range(text_start_col, text_end_col):
6107
+ if c in cells["datacn"]:
6108
+ datacn = cells["datacn"][c]
6109
+ else:
6110
+ datacn = c if self.all_columns_displayed else self.displayed_columns[c]
6111
+ cells["datacn"][c] = datacn
6112
+ if kwargs := self.get_cell_kwargs(datarn, datacn, key="dropdown"):
6113
+ cells["dropdown"][(datarn, datacn)] = kwargs
6114
+ elif kwargs := self.get_cell_kwargs(datarn, datacn, key="checkbox"):
6115
+ cells["checkbox"][(datarn, datacn)] = kwargs
6116
+ cells[(datarn, datacn)] = self.get_valid_cell_data_as_str(datarn, datacn, get_displayed=True)
6117
+ return cells
6118
+
6119
+ def wrap_get_char_w(self, c: str) -> int:
6120
+ self.txt_measure_canvas.itemconfig(
6121
+ self.txt_measure_canvas_text,
6122
+ text=_test_str + c,
6123
+ font=self.table_font,
6124
+ )
6125
+ b = self.txt_measure_canvas.bbox(self.txt_measure_canvas_text)
6126
+ if c in self.char_widths[self.table_font]:
6127
+ return self.char_widths[self.table_font][c]
6128
+ else:
6129
+ wd = b[2] - b[0] - self.table_test_str_w
6130
+ self.char_widths[self.table_font][c] = wd
6131
+ return wd
6132
+
5814
6133
  def main_table_redraw_grid_and_text(
5815
6134
  self,
5816
6135
  redraw_header: bool = False,
5817
6136
  redraw_row_index: bool = False,
5818
6137
  redraw_table: bool = True,
5819
6138
  setting_views: bool = False,
6139
+ set_scrollregion: bool = True,
5820
6140
  ) -> bool:
5821
6141
  try:
5822
6142
  can_width = self.winfo_width()
@@ -5838,29 +6158,33 @@ class MainTable(tk.Canvas):
5838
6158
  last_col_line_pos + self.PAR.ops.empty_horizontal + 2,
5839
6159
  last_row_line_pos + self.PAR.ops.empty_vertical + 2,
5840
6160
  )
5841
- if setting_views or scrollregion != self.scrollregion:
5842
- self.configure(scrollregion=scrollregion)
6161
+ if set_scrollregion and (setting_views or scrollregion != self.scrollregion):
6162
+ try:
6163
+ self.configure(scrollregion=scrollregion)
6164
+ except Exception:
6165
+ return False
5843
6166
  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:
6167
+ if (
6168
+ not self.CH.configure_scrollregion(last_col_line_pos)
6169
+ or not self.RI.configure_scrollregion(last_row_line_pos)
6170
+ or setting_views
6171
+ ):
5847
6172
  return False
5848
6173
 
5849
6174
  scrollpos_top = self.canvasy(0)
5850
6175
  scrollpos_bot = self.canvasy(can_height)
5851
6176
  scrollpos_left = self.canvasx(0)
5852
6177
  scrollpos_right = self.canvasx(can_width)
5853
-
5854
6178
  grid_start_row = bisect_left(self.row_positions, scrollpos_top)
5855
6179
  grid_end_row = bisect_right(self.row_positions, scrollpos_bot)
5856
6180
  grid_start_col = bisect_left(self.col_positions, scrollpos_left)
5857
6181
  grid_end_col = bisect_right(self.col_positions, scrollpos_right)
5858
-
5859
6182
  text_start_row = grid_start_row - 1 if grid_start_row else grid_start_row
5860
6183
  text_end_row = grid_end_row - 1 if grid_end_row == len(self.row_positions) else grid_end_row
5861
6184
  text_start_col = grid_start_col - 1 if grid_start_col else grid_start_col
5862
6185
  text_end_col = grid_end_col - 1 if grid_end_col == len(self.col_positions) else grid_end_col
5863
6186
 
6187
+ # check if auto resizing row index
5864
6188
  changed_w = False
5865
6189
  if self.PAR.ops.auto_resize_row_index and redraw_row_index and self.show_index:
5866
6190
  changed_w = self.RI.auto_set_index_width(
@@ -5873,7 +6197,10 @@ class MainTable(tk.Canvas):
5873
6197
  for widget in (self, self.RI, self.CH, self.TL):
5874
6198
  widget.update_idletasks()
5875
6199
  return False
5876
-
6200
+ # important vars
6201
+ x_stop = min(last_col_line_pos, scrollpos_right)
6202
+ y_stop = min(last_row_line_pos, scrollpos_bot)
6203
+ # manage find window
5877
6204
  if self.find_window.open:
5878
6205
  w, h, x, y = self.get_find_window_dimensions_coords(w_width=self.winfo_width())
5879
6206
  self.coords(self.find_window.canvas_id, x, y)
@@ -5883,19 +6210,71 @@ class MainTable(tk.Canvas):
5883
6210
  height=h,
5884
6211
  state="normal",
5885
6212
  )
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)
6213
+ # redraw table
5898
6214
  if redraw_table:
6215
+ # reset canvas item storage
6216
+ self.hidd_text.update(self.disp_text)
6217
+ self.disp_text = {}
6218
+ self.hidd_high.update(self.disp_high)
6219
+ self.disp_high = {}
6220
+ self.hidd_grid.update(self.disp_grid)
6221
+ self.disp_grid = {}
6222
+ self.hidd_dropdown.update(self.disp_dropdown)
6223
+ self.disp_dropdown = {}
6224
+ self.hidd_checkbox.update(self.disp_checkbox)
6225
+ self.disp_checkbox = {}
6226
+ # manage horizontal grid lines
6227
+ if self.PAR.ops.show_horizontal_grid and row_pos_exists:
6228
+ if self.PAR.ops.horizontal_grid_to_end_of_window:
6229
+ x_grid_stop = scrollpos_right + can_width
6230
+ else:
6231
+ if last_col_line_pos > scrollpos_right:
6232
+ x_grid_stop = x_stop + 1
6233
+ else:
6234
+ x_grid_stop = x_stop - 1
6235
+ self.redraw_gridline(
6236
+ points=tuple(
6237
+ chain.from_iterable(
6238
+ (
6239
+ scrollpos_left - 1,
6240
+ self.row_positions[r],
6241
+ x_grid_stop,
6242
+ self.row_positions[r],
6243
+ scrollpos_left - 1,
6244
+ self.row_positions[r],
6245
+ scrollpos_left - 1,
6246
+ self.row_positions[r + 1] if len(self.row_positions) - 1 > r else self.row_positions[r],
6247
+ )
6248
+ for r in range(grid_start_row, grid_end_row)
6249
+ )
6250
+ )
6251
+ )
6252
+ # manage vertical grid lines
6253
+ if self.PAR.ops.show_vertical_grid and col_pos_exists:
6254
+ if self.PAR.ops.vertical_grid_to_end_of_window:
6255
+ y_grid_stop = scrollpos_bot + can_height
6256
+ else:
6257
+ if last_row_line_pos > scrollpos_bot:
6258
+ y_grid_stop = y_stop + 1
6259
+ else:
6260
+ y_grid_stop = y_stop - 1
6261
+ self.redraw_gridline(
6262
+ points=tuple(
6263
+ chain.from_iterable(
6264
+ (
6265
+ self.col_positions[c],
6266
+ scrollpos_top - 1,
6267
+ self.col_positions[c],
6268
+ y_grid_stop,
6269
+ self.col_positions[c],
6270
+ scrollpos_top - 1,
6271
+ self.col_positions[c + 1] if len(self.col_positions) - 1 > c else self.col_positions[c],
6272
+ scrollpos_top - 1,
6273
+ )
6274
+ for c in range(grid_start_col, grid_end_col)
6275
+ )
6276
+ ),
6277
+ )
5899
6278
  font = self.PAR.ops.table_font
5900
6279
  dd_coords = self.dropdown.get_coords()
5901
6280
  selections = self.get_redraw_selections(text_start_row, grid_end_row, text_start_col, grid_end_col)
@@ -5927,17 +6306,23 @@ class MainTable(tk.Canvas):
5927
6306
  )
5928
6307
  else:
5929
6308
  override = tuple()
6309
+ allow_overflow = self.PAR.ops.allow_cell_overflow
6310
+ wrap = self.PAR.ops.table_wrap
6311
+ cells = self._redraw_precache_cells(
6312
+ text_start_row=text_start_row,
6313
+ text_end_row=text_end_row,
6314
+ text_start_col=text_start_col,
6315
+ text_end_col=text_end_col,
6316
+ )
5930
6317
  for r in range(text_start_row, text_end_row):
5931
6318
  rtopgridln = self.row_positions[r]
5932
6319
  rbotgridln = self.row_positions[r + 1]
5933
- if rbotgridln - rtopgridln < self.table_txt_height:
5934
- continue
5935
- datarn = self.datarn(r)
6320
+ datarn = cells["datarn"][r]
5936
6321
 
5937
6322
  for c in range(text_start_col, text_end_col):
5938
6323
  cleftgridln = self.col_positions[c]
5939
6324
  crightgridln = self.col_positions[c + 1]
5940
- datacn = self.datacn(c)
6325
+ datacn = cells["datacn"][c]
5941
6326
  fill, dd_drawn = self.redraw_highlight_get_text_fg(
5942
6327
  r=r,
5943
6328
  c=c,
@@ -5957,13 +6342,14 @@ class MainTable(tk.Canvas):
5957
6342
  )
5958
6343
  if not (align := self.get_cell_kwargs(datarn, datacn, key="align")):
5959
6344
  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":
6345
+
6346
+ if kwargs := cells["dropdown"].get((datarn, datacn), None):
6347
+ max_width = crightgridln - cleftgridln - self.table_txt_height - 5
6348
+ if align.endswith("w"):
6349
+ draw_x = cleftgridln + 2
6350
+ elif align.endswith("e"):
5965
6351
  draw_x = crightgridln - 5 - self.table_txt_height
5966
- elif align == "center":
6352
+ elif align.endswith("n"):
5967
6353
  draw_x = cleftgridln + ceil((crightgridln - cleftgridln - self.table_txt_height) / 2)
5968
6354
  self.redraw_dropdown(
5969
6355
  cleftgridln,
@@ -5973,25 +6359,27 @@ class MainTable(tk.Canvas):
5973
6359
  fill=fill if kwargs["state"] != "disabled" else self.PAR.ops.table_grid_fg,
5974
6360
  outline=fill,
5975
6361
  draw_outline=not dd_drawn,
5976
- draw_arrow=mw >= 5,
6362
+ draw_arrow=max_width >= 5,
5977
6363
  open_=dd_coords == (r, c),
5978
6364
  )
5979
6365
  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":
6366
+ max_width = crightgridln - cleftgridln - 2
6367
+ if align.endswith("w"):
6368
+ draw_x = cleftgridln + 2
6369
+ elif align.endswith("e"):
6370
+ draw_x = crightgridln - 2
6371
+ elif align.endswith("n"):
5986
6372
  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:
6373
+
6374
+ if (
6375
+ kwargs := cells["checkbox"].get((datarn, datacn), None)
6376
+ ) and max_width > self.table_txt_height + 1:
5989
6377
  box_w = self.table_txt_height + 1
5990
- if align == "w":
6378
+ if align.endswith("w"):
5991
6379
  draw_x += box_w + 3
5992
- elif align == "center":
6380
+ elif align.endswith("n"):
5993
6381
  draw_x += ceil(box_w / 2) + 1
5994
- mw -= box_w + 3
6382
+ max_width -= box_w + 4
5995
6383
  try:
5996
6384
  draw_check = bool(self.data[datarn][datacn])
5997
6385
  except Exception:
@@ -6005,27 +6393,77 @@ class MainTable(tk.Canvas):
6005
6393
  outline="",
6006
6394
  draw_check=draw_check,
6007
6395
  )
6008
- lines = self.get_valid_cell_data_as_str(datarn, datacn, get_displayed=True)
6396
+ text = cells[(datarn, datacn)]
6009
6397
  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)
6398
+ not text
6399
+ or (align.endswith("w") and draw_x > scrollpos_right)
6400
+ or (align.endswith("e") and cleftgridln + 5 > scrollpos_right)
6401
+ or (align.endswith("n") and cleftgridln + 5 > scrollpos_right)
6015
6402
  ):
6016
6403
  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):
6404
+ if allow_overflow and not kwargs:
6405
+ if align.endswith("w"):
6406
+ max_width += sum(self._overflow(cells, range(c + 1, text_end_col), datarn))
6407
+ elif align.endswith("e"):
6408
+ max_width += sum(self._overflow(cells, reversed(range(text_start_col, c)), datarn))
6409
+ elif align.endswith("n"):
6410
+ ...
6411
+ if max_width <= 1:
6412
+ continue
6413
+ start_line = max(0, int((scrollpos_top - rtopgridln) / self.table_txt_height))
6414
+ draw_y = rtopgridln + 3 + (start_line * self.table_txt_height)
6415
+ gen_lines = wrap_text(
6416
+ text=text,
6417
+ max_width=max_width,
6418
+ max_lines=int((rbotgridln - rtopgridln - 2) / self.table_txt_height),
6419
+ char_width_fn=self.wrap_get_char_w,
6420
+ widths=self.char_widths[font],
6421
+ wrap=wrap,
6422
+ start_line=start_line,
6423
+ )
6424
+ if align.endswith(("w", "e")):
6425
+ if self.hidd_text:
6426
+ iid, showing = self.hidd_text.popitem()
6427
+ self.coords(iid, draw_x, draw_y)
6428
+ if showing:
6429
+ self.itemconfig(
6430
+ iid,
6431
+ text="\n".join(gen_lines),
6432
+ fill=fill,
6433
+ font=font,
6434
+ anchor=align,
6435
+ )
6436
+ else:
6437
+ self.itemconfig(
6438
+ iid,
6439
+ text="\n".join(gen_lines),
6440
+ fill=fill,
6441
+ font=font,
6442
+ anchor=align,
6443
+ state="normal",
6444
+ )
6445
+ self.tag_raise(iid)
6446
+ else:
6447
+ iid = self.create_text(
6448
+ draw_x,
6449
+ draw_y,
6450
+ text="\n".join(gen_lines),
6451
+ fill=fill,
6452
+ font=font,
6453
+ anchor=align,
6454
+ tags="t",
6455
+ )
6456
+ self.disp_text[iid] = True
6457
+
6458
+ elif align.endswith("n"):
6459
+ for text in gen_lines:
6022
6460
  if self.hidd_text:
6023
6461
  iid, showing = self.hidd_text.popitem()
6024
6462
  self.coords(iid, draw_x, draw_y)
6025
6463
  if showing:
6026
6464
  self.itemconfig(
6027
6465
  iid,
6028
- text=txt,
6466
+ text=text,
6029
6467
  fill=fill,
6030
6468
  font=font,
6031
6469
  anchor=align,
@@ -6033,7 +6471,7 @@ class MainTable(tk.Canvas):
6033
6471
  else:
6034
6472
  self.itemconfig(
6035
6473
  iid,
6036
- text=txt,
6474
+ text=text,
6037
6475
  fill=fill,
6038
6476
  font=font,
6039
6477
  anchor=align,
@@ -6044,96 +6482,14 @@ class MainTable(tk.Canvas):
6044
6482
  iid = self.create_text(
6045
6483
  draw_x,
6046
6484
  draw_y,
6047
- text=txt,
6485
+ text=text,
6048
6486
  fill=fill,
6049
6487
  font=font,
6050
6488
  anchor=align,
6051
- tag="t",
6489
+ tags="t",
6052
6490
  )
6053
6491
  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
- )
6492
+ draw_y += self.table_txt_height
6137
6493
  for dct in (
6138
6494
  self.hidd_text,
6139
6495
  self.hidd_high,
@@ -6166,6 +6522,7 @@ class MainTable(tk.Canvas):
6166
6522
  text_end_col=text_end_col,
6167
6523
  scrollpos_right=scrollpos_right,
6168
6524
  col_pos_exists=col_pos_exists,
6525
+ set_scrollregion=set_scrollregion,
6169
6526
  )
6170
6527
  if redraw_row_index and self.show_index:
6171
6528
  self.RI.redraw_grid_and_text(
@@ -6178,6 +6535,7 @@ class MainTable(tk.Canvas):
6178
6535
  text_end_row=text_end_row,
6179
6536
  scrollpos_bot=scrollpos_bot,
6180
6537
  row_pos_exists=row_pos_exists,
6538
+ set_scrollregion=set_scrollregion,
6181
6539
  )
6182
6540
  event_data = {"sheetname": "", "header": redraw_header, "row_index": redraw_row_index, "table": redraw_table}
6183
6541
  self.PAR.emit_event("<<SheetRedrawn>>", data=event_data)
@@ -6902,7 +7260,7 @@ class MainTable(tk.Canvas):
6902
7260
  return False
6903
7261
  self.hide_text_editor()
6904
7262
  if not self.see(r=r, c=c, check_cell_visibility=True):
6905
- self.refresh()
7263
+ self.main_table_redraw_grid_and_text(True, True)
6906
7264
  x = self.col_positions[c]
6907
7265
  y = self.row_positions[r]
6908
7266
  w = self.col_positions[c + 1] - x + 1
@@ -6971,7 +7329,7 @@ class MainTable(tk.Canvas):
6971
7329
  > curr_height
6972
7330
  ):
6973
7331
  new_height = min(
6974
- curr_height + self.table_xtra_lines_increment,
7332
+ curr_height + self.table_txt_height,
6975
7333
  self.scrollregion[3] - self.scrollregion[1] - self.row_positions[r],
6976
7334
  )
6977
7335
  if new_height != curr_height:
@@ -7218,7 +7576,7 @@ class MainTable(tk.Canvas):
7218
7576
  for i, v in enumerate(self.get_cell_kwargs(datarn, datacn, key="dropdown")["values"]):
7219
7577
  v_numlines = len(v.split("\n") if isinstance(v, str) else f"{v}".split("\n"))
7220
7578
  if v_numlines > 1:
7221
- win_h += self.table_first_ln_ins + (v_numlines * self.table_xtra_lines_increment) + 5 # end of cell
7579
+ win_h += 8 + (v_numlines * self.table_txt_height) # end of cell
7222
7580
  else:
7223
7581
  win_h += self.min_row_height
7224
7582
  if i == 5:
@@ -7669,7 +8027,7 @@ class MainTable(tk.Canvas):
7669
8027
  else:
7670
8028
  # assumed given formatter class has get_data_with_valid_check()
7671
8029
  return f"{value.get_data_with_valid_check()}"
7672
- return "" if value is None else f"{value}"
8030
+ return "" if value is None else value if isinstance(value, str) else f"{value}"
7673
8031
 
7674
8032
  def get_cell_data(
7675
8033
  self,
@@ -7682,7 +8040,11 @@ class MainTable(tk.Canvas):
7682
8040
  ) -> object:
7683
8041
  if get_displayed:
7684
8042
  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 ""
8043
+ value = (
8044
+ self.data[datarn][datacn]
8045
+ if len(self.data) > datarn and len(self.data[datarn]) > datacn
8046
+ else self.get_value_for_empty_cell(datarn, datacn)
8047
+ )
7686
8048
  kwargs = self.get_cell_kwargs(datarn, datacn, key="format")
7687
8049
  if kwargs and kwargs["formatter"] is not None:
7688
8050
  value = value.value # assumed given formatter class has value attribute