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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tksheet/column_headers.py CHANGED
@@ -2,29 +2,17 @@ from __future__ import annotations
2
2
 
3
3
  import tkinter as tk
4
4
  from collections import defaultdict
5
- from collections.abc import (
6
- Hashable,
7
- Sequence,
8
- )
9
- from functools import (
10
- partial,
11
- )
12
- from itertools import (
13
- cycle,
14
- islice,
15
- repeat,
16
- )
5
+ from collections.abc import Callable, Hashable, Sequence
6
+ from functools import partial
7
+ from itertools import cycle, islice, repeat
17
8
  from math import ceil, floor
18
- from operator import (
19
- itemgetter,
20
- )
9
+ from operator import itemgetter
21
10
  from typing import Literal
22
11
 
23
- from .colors import (
24
- color_map,
25
- )
12
+ from .colors import color_map
26
13
  from .constants import (
27
14
  USER_OS,
15
+ _test_str,
28
16
  rc_binding,
29
17
  text_editor_close_bindings,
30
18
  text_editor_newline_bindings,
@@ -39,36 +27,33 @@ from .functions import (
39
27
  get_n2a,
40
28
  int_x_tuple,
41
29
  is_contiguous,
30
+ mod_event_val,
42
31
  new_tk_event,
43
32
  rounded_box_coords,
44
33
  stored_event_dict,
34
+ try_b_index,
45
35
  try_binding,
36
+ wrap_text,
46
37
  )
47
- from .other_classes import (
48
- DotDict,
49
- DraggedRowColumn,
50
- DropdownStorage,
51
- TextEditorStorage,
52
- )
53
- from .text_editor import (
54
- TextEditor,
55
- )
56
- from .types import (
57
- AnyIter,
58
- )
38
+ from .other_classes import DotDict, DraggedRowColumn, DropdownStorage, EventDataDict, TextEditorStorage
39
+ from .row_index import RowIndex
40
+ from .sorting import sort_column, sort_rows_by_column, sort_tree_view
41
+ from .text_editor import TextEditor
42
+ from .tksheet_types import AnyIter
59
43
 
60
44
 
61
45
  class ColumnHeaders(tk.Canvas):
62
- def __init__(self, *args, **kwargs):
46
+ def __init__(self, parent, **kwargs):
63
47
  super().__init__(
64
- kwargs["parent"],
65
- background=kwargs["parent"].ops.header_bg,
48
+ parent,
49
+ background=parent.ops.header_bg,
66
50
  highlightthickness=0,
67
51
  )
68
- self.PAR = kwargs["parent"]
52
+ self.PAR = parent
53
+ self.ops = self.PAR.ops
69
54
  self.current_height = None
70
55
  self.MT = None # is set from within MainTable() __init__
71
- self.RI = None # is set from within MainTable() __init__
56
+ self.RI: RowIndex | None = None # is set from within MainTable() __init__
72
57
  self.TL = None # is set from within TopLeftRectangle() __init__
73
58
  self.popup_menu_loc = None
74
59
  self.extra_begin_edit_cell_func = None
@@ -85,6 +70,8 @@ class ColumnHeaders(tk.Canvas):
85
70
  self.extra_double_b1_func = None
86
71
  self.ch_extra_begin_drag_drop_func = None
87
72
  self.ch_extra_end_drag_drop_func = None
73
+ self.ch_extra_begin_sort_rows_func = None
74
+ self.ch_extra_end_sort_rows_func = None
88
75
  self.extra_rc_func = None
89
76
  self.selection_binding_func = None
90
77
  self.shift_selection_binding_func = None
@@ -164,36 +151,8 @@ class ColumnHeaders(tk.Canvas):
164
151
  self.unbind("<Button-4>")
165
152
  self.unbind("<Button-5>")
166
153
 
167
- def mousewheel(self, event: object) -> None:
168
- if isinstance(self.MT._headers, int):
169
- maxlines = max(
170
- (
171
- len(
172
- self.MT.get_valid_cell_data_as_str(self.MT._headers, datacn, get_displayed=True)
173
- .rstrip()
174
- .split("\n")
175
- )
176
- for datacn in range(len(self.MT.data[self.MT._headers]))
177
- ),
178
- default=0,
179
- )
180
- elif isinstance(self.MT._headers, (list, tuple)):
181
- maxlines = max(
182
- (
183
- len(e.rstrip().split("\n")) if isinstance(e, str) else len(f"{e}".rstrip().split("\n"))
184
- for e in self.MT._headers
185
- ),
186
- default=0,
187
- )
188
- if maxlines == 1:
189
- maxlines = 0
190
- if self.lines_start_at > maxlines:
191
- self.lines_start_at = maxlines
192
- if (event.delta < 0 or event.num == 5) and self.lines_start_at < maxlines:
193
- self.lines_start_at += 1
194
- elif (event.delta >= 0 or event.num == 4) and self.lines_start_at > 0:
195
- self.lines_start_at -= 1
196
- self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=False, redraw_table=False)
154
+ def mousewheel(self, event: tk.Event) -> None:
155
+ self.MT.mousewheel(event)
197
156
 
198
157
  def set_height(self, new_height: int, set_TL: bool = False) -> bool:
199
158
  try:
@@ -463,20 +422,22 @@ class ColumnHeaders(tk.Canvas):
463
422
  x,
464
423
  self.current_height,
465
424
  width=1,
466
- fill=self.PAR.ops.resizing_line_fg,
467
- tag="rwl",
425
+ fill=self.ops.resizing_line_fg,
426
+ tag=("rw", "rwl"),
468
427
  )
469
- self.MT.create_resize_line(x, y1, x, y2, width=1, fill=self.PAR.ops.resizing_line_fg, tag="rwl")
428
+ self.MT.create_resize_line(x, y1, x, y2, width=1, fill=self.ops.resizing_line_fg, tag=("rw", "rwl"))
470
429
  self.create_resize_line(
471
430
  line2x,
472
431
  0,
473
432
  line2x,
474
433
  self.current_height,
475
434
  width=1,
476
- fill=self.PAR.ops.resizing_line_fg,
477
- tag="rwl2",
435
+ fill=self.ops.resizing_line_fg,
436
+ tag=("rw", "rwl2"),
437
+ )
438
+ self.MT.create_resize_line(
439
+ line2x, y1, line2x, y2, width=1, fill=self.ops.resizing_line_fg, tag=("rw", "rwl2")
478
440
  )
479
- self.MT.create_resize_line(line2x, y1, line2x, y2, width=1, fill=self.PAR.ops.resizing_line_fg, tag="rwl2")
480
441
  elif self.height_resizing_enabled and self.rsz_w is None and self.rsz_h is not None:
481
442
  self.currently_resizing_height = True
482
443
  elif self.MT.identify_col(x=event.x, allow_end=False) is None:
@@ -506,7 +467,7 @@ class ColumnHeaders(tk.Canvas):
506
467
  if self.width_resizing_enabled and self.rsz_w is not None and self.currently_resizing_width:
507
468
  x = self.canvasx(event.x)
508
469
  size = x - self.MT.col_positions[self.rsz_w - 1]
509
- if size >= self.PAR.ops.min_column_width and size < self.PAR.ops.max_column_width:
470
+ if size >= self.ops.min_column_width and size < self.ops.max_column_width:
510
471
  self.hide_resize_and_ctrl_lines(ctrl_lines=False)
511
472
  line2x = self.MT.col_positions[self.rsz_w - 1]
512
473
  self.create_resize_line(
@@ -515,18 +476,18 @@ class ColumnHeaders(tk.Canvas):
515
476
  x,
516
477
  self.current_height,
517
478
  width=1,
518
- fill=self.PAR.ops.resizing_line_fg,
519
- tag="rwl",
479
+ fill=self.ops.resizing_line_fg,
480
+ tag=("rw", "rwl"),
520
481
  )
521
- self.MT.create_resize_line(x, y1, x, y2, width=1, fill=self.PAR.ops.resizing_line_fg, tag="rwl")
482
+ self.MT.create_resize_line(x, y1, x, y2, width=1, fill=self.ops.resizing_line_fg, tag=("rw", "rwl"))
522
483
  self.create_resize_line(
523
484
  line2x,
524
485
  0,
525
486
  line2x,
526
487
  self.current_height,
527
488
  width=1,
528
- fill=self.PAR.ops.resizing_line_fg,
529
- tag="rwl2",
489
+ fill=self.ops.resizing_line_fg,
490
+ tag=("rw", "rwl2"),
530
491
  )
531
492
  self.MT.create_resize_line(
532
493
  line2x,
@@ -534,15 +495,15 @@ class ColumnHeaders(tk.Canvas):
534
495
  line2x,
535
496
  y2,
536
497
  width=1,
537
- fill=self.PAR.ops.resizing_line_fg,
538
- tag="rwl2",
498
+ fill=self.ops.resizing_line_fg,
499
+ tag=("rw", "rwl2"),
539
500
  )
540
501
  self.drag_width_resize()
541
502
  elif self.height_resizing_enabled and self.rsz_h is not None and self.currently_resizing_height:
542
503
  evy = event.y
543
504
  if evy > self.current_height:
544
- if evy > self.PAR.ops.max_header_height:
545
- evy = int(self.PAR.ops.max_header_height)
505
+ if evy > self.ops.max_header_height:
506
+ evy = int(self.ops.max_header_height)
546
507
  self.drag_height_resize(evy)
547
508
  else:
548
509
  if evy < self.MT.min_header_height:
@@ -707,16 +668,16 @@ class ColumnHeaders(tk.Canvas):
707
668
  xpos,
708
669
  self.current_height,
709
670
  width=3,
710
- fill=self.PAR.ops.drag_and_drop_bg,
671
+ fill=self.ops.drag_and_drop_bg,
711
672
  tag="move_columns",
712
673
  )
713
- self.MT.create_resize_line(xpos, y1, xpos, y2, width=3, fill=self.PAR.ops.drag_and_drop_bg, tag="move_columns")
674
+ self.MT.create_resize_line(xpos, y1, xpos, y2, width=3, fill=self.ops.drag_and_drop_bg, tag="move_columns")
714
675
  for boxst, boxend in consecutive_ranges(cols):
715
676
  self.MT.show_ctrl_outline(
716
677
  start_cell=(boxst, 0),
717
678
  end_cell=(boxend, len(self.MT.row_positions) - 1),
718
679
  dash=(),
719
- outline=self.PAR.ops.drag_and_drop_bg,
680
+ outline=self.ops.drag_and_drop_bg,
720
681
  delete_on_timer=False,
721
682
  )
722
683
 
@@ -779,10 +740,10 @@ class ColumnHeaders(tk.Canvas):
779
740
  new_col_pos = int(self.coords("rwl")[0])
780
741
  old_width = self.MT.col_positions[self.rsz_w] - self.MT.col_positions[self.rsz_w - 1]
781
742
  size = new_col_pos - self.MT.col_positions[self.rsz_w - 1]
782
- if size < self.PAR.ops.min_column_width:
783
- new_col_pos = ceil(self.MT.col_positions[self.rsz_w - 1] + self.PAR.ops.min_column_width)
784
- elif size > self.PAR.ops.max_column_width:
785
- new_col_pos = floor(self.MT.col_positions[self.rsz_w - 1] + self.PAR.ops.max_column_width)
743
+ if size < self.ops.min_column_width:
744
+ new_col_pos = ceil(self.MT.col_positions[self.rsz_w - 1] + self.ops.min_column_width)
745
+ elif size > self.ops.max_column_width:
746
+ new_col_pos = floor(self.MT.col_positions[self.rsz_w - 1] + self.ops.max_column_width)
786
747
  increment = new_col_pos - self.MT.col_positions[self.rsz_w]
787
748
  self.MT.col_positions[self.rsz_w + 1 :] = [
788
749
  e + increment for e in islice(self.MT.col_positions, self.rsz_w + 1, None)
@@ -791,7 +752,7 @@ class ColumnHeaders(tk.Canvas):
791
752
  new_width = self.MT.col_positions[self.rsz_w] - self.MT.col_positions[self.rsz_w - 1]
792
753
  self.MT.allow_auto_resize_columns = False
793
754
  self.MT.recreate_all_selection_boxes()
794
- self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
755
+ self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True, set_scrollregion=False)
795
756
  if self.column_width_resize_func is not None and old_width != new_width:
796
757
  self.column_width_resize_func(
797
758
  event_dict(
@@ -816,8 +777,8 @@ class ColumnHeaders(tk.Canvas):
816
777
  self.MT.bind("<MouseWheel>", self.MT.mousewheel)
817
778
  if self.width_resizing_enabled and self.rsz_w is not None and self.currently_resizing_width:
818
779
  self.drag_width_resize()
819
- self.currently_resizing_width = False
820
780
  self.hide_resize_and_ctrl_lines(ctrl_lines=False)
781
+ self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
821
782
  elif (
822
783
  self.drag_and_drop_enabled
823
784
  and self.col_selection_enabled
@@ -843,22 +804,16 @@ class ColumnHeaders(tk.Canvas):
843
804
  c += 1
844
805
  if c > len(self.MT.col_positions) - 1:
845
806
  c = len(self.MT.col_positions) - 1
846
- event_data = event_dict(
847
- name="move_columns",
848
- sheet=self.PAR.name,
849
- widget=self,
850
- boxes=self.MT.get_boxes(),
851
- selected=self.MT.selected,
852
- value=c,
853
- )
807
+ event_data = self.MT.new_event_dict("move_columns", state=True)
808
+ event_data["value"] = c
854
809
  if try_binding(self.ch_extra_begin_drag_drop_func, event_data, "begin_move_columns"):
855
810
  data_new_idxs, disp_new_idxs, event_data = self.MT.move_columns_adjust_options_dict(
856
811
  *self.MT.get_args_for_move_columns(
857
812
  move_to=c,
858
813
  to_move=self.dragged_col.to_move,
859
814
  ),
860
- move_data=self.PAR.ops.column_drag_and_drop_perform,
861
- move_widths=self.PAR.ops.column_drag_and_drop_perform,
815
+ move_data=self.ops.column_drag_and_drop_perform,
816
+ move_widths=self.ops.column_drag_and_drop_perform,
862
817
  event_data=event_data,
863
818
  )
864
819
  event_data["moved"]["columns"] = {
@@ -904,6 +859,126 @@ class ColumnHeaders(tk.Canvas):
904
859
  self.mouse_motion(event)
905
860
  try_binding(self.extra_b1_release_func, event)
906
861
 
862
+ def _sort_columns(
863
+ self,
864
+ event: tk.Event | None = None,
865
+ columns: AnyIter[int] | None = None,
866
+ reverse: bool = False,
867
+ validation: bool = True,
868
+ key: Callable | None = None,
869
+ undo: bool = True,
870
+ ) -> EventDataDict:
871
+ if columns is None:
872
+ columns = self.MT.get_selected_cols()
873
+ if not columns:
874
+ columns = list(range(0, len(self.MT.col_positions) - 1))
875
+ event_data = self.MT.new_event_dict("edit_table")
876
+ try_binding(self.MT.extra_begin_sort_cells_func, event_data)
877
+ for c in columns:
878
+ datacn = self.MT.datacn(c)
879
+ for r, val in enumerate(
880
+ sort_column(
881
+ (self.MT.get_cell_data(row, datacn) for row in range(len(self.MT.data))),
882
+ reverse=reverse,
883
+ key=key,
884
+ )
885
+ ):
886
+ if (
887
+ not self.MT.edit_validation_func
888
+ or not validation
889
+ or (
890
+ self.MT.edit_validation_func
891
+ and (val := self.MT.edit_validation_func(mod_event_val(event_data, val, (r, datacn))))
892
+ is not None
893
+ )
894
+ ):
895
+ event_data = self.MT.event_data_set_cell(
896
+ datarn=r,
897
+ datacn=datacn,
898
+ value=val,
899
+ event_data=event_data,
900
+ )
901
+ if event_data["cells"]["table"]:
902
+ if undo and self.MT.undo_enabled:
903
+ self.MT.undo_stack.append(stored_event_dict(event_data))
904
+ try_binding(self.MT.extra_end_sort_cells_func, event_data, "end_edit_table")
905
+ self.MT.sheet_modified(event_data)
906
+ self.PAR.emit_event("<<SheetModified>>", event_data)
907
+ self.MT.refresh()
908
+ return event_data
909
+
910
+ def _sort_rows_by_column(
911
+ self,
912
+ event: tk.Event | None = None,
913
+ column: int | None = None,
914
+ reverse: bool = False,
915
+ key: Callable | None = None,
916
+ undo: bool = True,
917
+ ) -> EventDataDict:
918
+ event_data = self.MT.new_event_dict("sort_rows", state=True)
919
+ if not self.MT.data:
920
+ return event_data
921
+ if column is None:
922
+ if not self.MT.selected:
923
+ return event_data
924
+ column = self.MT.selected.column
925
+ if try_binding(self.ch_extra_begin_sort_rows_func, event_data, "begin_sort_rows"):
926
+ disp_new_idxs, disp_row_ctr = {}, 0
927
+ if self.ops.treeview:
928
+ new_nodes_order, data_new_idxs = sort_tree_view(
929
+ _row_index=self.MT._row_index,
930
+ tree_rns=self.RI.tree_rns,
931
+ tree=self.RI.tree,
932
+ key=key,
933
+ reverse=reverse,
934
+ )
935
+ for node in new_nodes_order:
936
+ if (idx := try_b_index(self.MT.displayed_rows, self.RI.tree_rns[node.iid])) is not None:
937
+ disp_new_idxs[idx] = disp_row_ctr
938
+ disp_row_ctr += 1
939
+ else:
940
+ new_rows_order, data_new_idxs = sort_rows_by_column(
941
+ self.MT.data, column=column, reverse=reverse, key=key
942
+ )
943
+ if self.MT.all_rows_displayed:
944
+ disp_new_idxs = data_new_idxs
945
+ else:
946
+ for old_idx, _ in new_rows_order:
947
+ if (idx := try_b_index(self.MT.displayed_rows, old_idx)) is not None:
948
+ disp_new_idxs[idx] = disp_row_ctr
949
+ disp_row_ctr += 1
950
+ if self.PAR.ops.treeview:
951
+ data_new_idxs, disp_new_idxs, event_data = self.MT.move_rows_adjust_options_dict(
952
+ data_new_idxs=data_new_idxs,
953
+ data_old_idxs=dict(zip(data_new_idxs.values(), data_new_idxs)),
954
+ totalrows=None,
955
+ disp_new_idxs=disp_new_idxs,
956
+ move_data=True,
957
+ create_selections=False,
958
+ event_data=event_data,
959
+ )
960
+ else:
961
+ data_new_idxs, disp_new_idxs, _ = self.PAR.mapping_move_rows(
962
+ data_new_idxs=data_new_idxs,
963
+ disp_new_idxs=disp_new_idxs,
964
+ move_data=True,
965
+ create_selections=False,
966
+ undo=False,
967
+ emit_event=False,
968
+ redraw=True,
969
+ )
970
+ event_data["moved"]["rows"] = {
971
+ "data": data_new_idxs,
972
+ "displayed": disp_new_idxs,
973
+ }
974
+ if undo and self.MT.undo_enabled:
975
+ self.MT.undo_stack.append(stored_event_dict(event_data))
976
+ try_binding(self.ch_extra_end_sort_rows_func, event_data, "end_sort_rows")
977
+ self.MT.sheet_modified(event_data)
978
+ self.PAR.emit_event("<<SheetModified>>", event_data)
979
+ self.MT.refresh()
980
+ return event_data
981
+
907
982
  def toggle_select_col(
908
983
  self,
909
984
  column: int,
@@ -992,7 +1067,7 @@ class ColumnHeaders(tk.Canvas):
992
1067
  y1,
993
1068
  x2,
994
1069
  y2,
995
- radius=5 if self.PAR.ops.rounded_boxes else 0,
1070
+ radius=5 if self.ops.rounded_boxes else 0,
996
1071
  )
997
1072
  if isinstance(iid, int):
998
1073
  self.coords(iid, coords)
@@ -1019,13 +1094,13 @@ class ColumnHeaders(tk.Canvas):
1019
1094
  self.MT.txt_measure_canvas.itemconfig(
1020
1095
  self.MT.txt_measure_canvas_text,
1021
1096
  text=txt,
1022
- font=self.PAR.ops.header_font,
1097
+ font=self.ops.header_font,
1023
1098
  )
1024
1099
  b = self.MT.txt_measure_canvas.bbox(self.MT.txt_measure_canvas_text)
1025
1100
  w = b[2] - b[0] + 7
1026
1101
  h = b[3] - b[1] + 5
1027
1102
  else:
1028
- w = self.PAR.ops.min_column_width
1103
+ w = self.ops.min_column_width
1029
1104
  h = self.MT.min_header_height
1030
1105
  if datacn in self.cell_options and (
1031
1106
  self.get_cell_kwargs(datacn, key="dropdown") or self.get_cell_kwargs(datacn, key="checkbox")
@@ -1047,7 +1122,7 @@ class ColumnHeaders(tk.Canvas):
1047
1122
  qconf = self.MT.txt_measure_canvas.itemconfig
1048
1123
  qbbox = self.MT.txt_measure_canvas.bbox
1049
1124
  qtxtm = self.MT.txt_measure_canvas_text
1050
- qfont = self.PAR.ops.header_font
1125
+ qfont = self.ops.header_font
1051
1126
  default_header_height = self.MT.get_default_header_height()
1052
1127
  if text is not None and text:
1053
1128
  qconf(qtxtm, text=text, font=qfont)
@@ -1083,8 +1158,8 @@ class ColumnHeaders(tk.Canvas):
1083
1158
  h = space_bot
1084
1159
  if h < self.MT.min_header_height:
1085
1160
  h = int(self.MT.min_header_height)
1086
- elif h > self.PAR.ops.max_header_height:
1087
- h = int(self.PAR.ops.max_header_height)
1161
+ elif h > self.ops.max_header_height:
1162
+ h = int(self.ops.max_header_height)
1088
1163
  if not only_if_too_small or (only_if_too_small and h > self.current_height):
1089
1164
  self.set_height(h, set_TL=True)
1090
1165
  self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
@@ -1097,7 +1172,7 @@ class ColumnHeaders(tk.Canvas):
1097
1172
  only_if_too_small: bool = False,
1098
1173
  ) -> int:
1099
1174
  self.fix_header()
1100
- w = self.PAR.ops.min_column_width
1175
+ w = self.ops.min_column_width
1101
1176
  datacn = col if self.MT.all_columns_displayed else self.MT.displayed_columns[col]
1102
1177
  # header
1103
1178
  hw, hh_ = self.get_cell_dimensions(datacn)
@@ -1118,7 +1193,7 @@ class ColumnHeaders(tk.Canvas):
1118
1193
  qbbox = self.MT.txt_measure_canvas.bbox
1119
1194
  qtxtm = self.MT.txt_measure_canvas_text
1120
1195
  qtxth = self.MT.table_txt_height
1121
- qfont = self.PAR.ops.table_font
1196
+ qfont = self.ops.table_font
1122
1197
  for datarn in iterable:
1123
1198
  if txt := self.MT.get_valid_cell_data_as_str(datarn, datacn, get_displayed=True):
1124
1199
  qconf(qtxtm, text=txt, font=qfont)
@@ -1134,10 +1209,10 @@ class ColumnHeaders(tk.Canvas):
1134
1209
  w = hw
1135
1210
  if only_if_too_small and w < self.MT.col_positions[col + 1] - self.MT.col_positions[col]:
1136
1211
  w = self.MT.col_positions[col + 1] - self.MT.col_positions[col]
1137
- if w <= self.PAR.ops.min_column_width:
1138
- w = self.PAR.ops.min_column_width
1139
- elif w > self.PAR.ops.max_column_width:
1140
- w = int(self.PAR.ops.max_column_width)
1212
+ if w <= self.ops.min_column_width:
1213
+ w = self.ops.min_column_width
1214
+ elif w > self.ops.max_column_width:
1215
+ w = int(self.ops.max_column_width)
1141
1216
  return w
1142
1217
 
1143
1218
  def set_col_width(
@@ -1150,10 +1225,10 @@ class ColumnHeaders(tk.Canvas):
1150
1225
  ) -> int:
1151
1226
  if width is None:
1152
1227
  width = self.get_col_text_width(col=col, visible_only=visible_only)
1153
- if width <= self.PAR.ops.min_column_width:
1154
- width = self.PAR.ops.min_column_width
1155
- elif width > self.PAR.ops.max_column_width:
1156
- width = int(self.PAR.ops.max_column_width)
1228
+ if width <= self.ops.min_column_width:
1229
+ width = self.ops.min_column_width
1230
+ elif width > self.ops.max_column_width:
1231
+ width = int(self.ops.max_column_width)
1157
1232
  if only_if_too_small and width <= self.MT.col_positions[col + 1] - self.MT.col_positions[col]:
1158
1233
  return self.MT.col_positions[col + 1] - self.MT.col_positions[col]
1159
1234
  new_col_pos = self.MT.col_positions[col] + width
@@ -1206,8 +1281,8 @@ class ColumnHeaders(tk.Canvas):
1206
1281
  fill = color_map[fill]
1207
1282
  if "columns" in selections and c in selections["columns"]:
1208
1283
  txtfg = (
1209
- self.PAR.ops.header_selected_columns_fg
1210
- if kwargs[1] is None or self.PAR.ops.display_selected_fg_over_highlights
1284
+ self.ops.header_selected_columns_fg
1285
+ if kwargs[1] is None or self.ops.display_selected_fg_over_highlights
1211
1286
  else kwargs[1]
1212
1287
  )
1213
1288
  if fill:
@@ -1218,8 +1293,8 @@ class ColumnHeaders(tk.Canvas):
1218
1293
  )
1219
1294
  elif "cells" in selections and c in selections["cells"]:
1220
1295
  txtfg = (
1221
- self.PAR.ops.header_selected_cells_fg
1222
- if kwargs[1] is None or self.PAR.ops.display_selected_fg_over_highlights
1296
+ self.ops.header_selected_cells_fg
1297
+ if kwargs[1] is None or self.ops.display_selected_fg_over_highlights
1223
1298
  else kwargs[1]
1224
1299
  )
1225
1300
  if fill:
@@ -1229,7 +1304,7 @@ class ColumnHeaders(tk.Canvas):
1229
1304
  + f"{int((int(fill[5:], 16) + int(sel_cells_bg[5:], 16)) / 2):02X}"
1230
1305
  )
1231
1306
  else:
1232
- txtfg = self.PAR.ops.header_fg if kwargs[1] is None else kwargs[1]
1307
+ txtfg = self.ops.header_fg if kwargs[1] is None else kwargs[1]
1233
1308
  if fill:
1234
1309
  redrawn = self.redraw_highlight(
1235
1310
  fc + 1,
@@ -1238,19 +1313,19 @@ class ColumnHeaders(tk.Canvas):
1238
1313
  self.current_height - 1,
1239
1314
  fill=fill,
1240
1315
  outline=(
1241
- self.PAR.ops.header_fg
1242
- if self.get_cell_kwargs(datacn, key="dropdown") and self.PAR.ops.show_dropdown_borders
1316
+ self.ops.header_fg
1317
+ if self.get_cell_kwargs(datacn, key="dropdown") and self.ops.show_dropdown_borders
1243
1318
  else ""
1244
1319
  ),
1245
1320
  tag="hi",
1246
1321
  )
1247
1322
  elif not kwargs:
1248
1323
  if "columns" in selections and c in selections["columns"]:
1249
- txtfg = self.PAR.ops.header_selected_columns_fg
1324
+ txtfg = self.ops.header_selected_columns_fg
1250
1325
  elif "cells" in selections and c in selections["cells"]:
1251
- txtfg = self.PAR.ops.header_selected_cells_fg
1326
+ txtfg = self.ops.header_selected_cells_fg
1252
1327
  else:
1253
- txtfg = self.PAR.ops.header_fg
1328
+ txtfg = self.ops.header_fg
1254
1329
  return txtfg, redrawn
1255
1330
 
1256
1331
  def redraw_highlight(
@@ -1271,6 +1346,7 @@ class ColumnHeaders(tk.Canvas):
1271
1346
  self.itemconfig(iid, fill=fill, outline=outline)
1272
1347
  else:
1273
1348
  self.itemconfig(iid, fill=fill, outline=outline, tag=tag, state="normal")
1349
+ self.tag_raise(iid)
1274
1350
  else:
1275
1351
  iid = self.create_rectangle(coords, fill=fill, outline=outline, tag=tag)
1276
1352
  self.disp_high[iid] = True
@@ -1291,6 +1367,7 @@ class ColumnHeaders(tk.Canvas):
1291
1367
  else:
1292
1368
  self.itemconfig(t, fill=fill, width=width, tag=tag, state="normal")
1293
1369
  self.disp_grid[t] = True
1370
+ self.tag_raise(t)
1294
1371
  else:
1295
1372
  self.disp_grid[self.create_line(points, fill=fill, width=width, tag=tag)] = True
1296
1373
 
@@ -1307,8 +1384,8 @@ class ColumnHeaders(tk.Canvas):
1307
1384
  draw_arrow: bool = True,
1308
1385
  open_: bool = False,
1309
1386
  ) -> None:
1310
- if draw_outline and self.PAR.ops.show_dropdown_borders:
1311
- self.redraw_highlight(x1 + 1, y1 + 1, x2, y2, fill="", outline=self.PAR.ops.header_fg, tag=tag)
1387
+ if draw_outline and self.ops.show_dropdown_borders:
1388
+ self.redraw_highlight(x1 + 1, y1 + 1, x2, y2, fill="", outline=self.ops.header_fg, tag=tag)
1312
1389
  if draw_arrow:
1313
1390
  mod = (self.MT.header_txt_height - 1) if self.MT.header_txt_height % 2 else self.MT.header_txt_height
1314
1391
  small_mod = int(mod / 5)
@@ -1394,15 +1471,33 @@ class ColumnHeaders(tk.Canvas):
1394
1471
  t = self.create_polygon(points, fill=fill, outline=outline, tag=tag, smooth=True)
1395
1472
  self.disp_checkbox[t] = True
1396
1473
 
1397
- def configure_scrollregion(self, last_col_line_pos: float) -> None:
1398
- self.configure(
1399
- scrollregion=(
1400
- 0,
1401
- 0,
1402
- last_col_line_pos + self.PAR.ops.empty_horizontal + 2,
1403
- self.current_height,
1474
+ def configure_scrollregion(self, last_col_line_pos: float) -> bool:
1475
+ try:
1476
+ self.configure(
1477
+ scrollregion=(
1478
+ 0,
1479
+ 0,
1480
+ last_col_line_pos + self.ops.empty_horizontal + 2,
1481
+ self.current_height,
1482
+ )
1404
1483
  )
1484
+ return True
1485
+ except Exception:
1486
+ return False
1487
+
1488
+ def wrap_get_char_w(self, c: str) -> int:
1489
+ self.MT.txt_measure_canvas.itemconfig(
1490
+ self.MT.txt_measure_canvas_text,
1491
+ text=_test_str + c,
1492
+ font=self.header_font,
1405
1493
  )
1494
+ b = self.MT.txt_measure_canvas.bbox(self.MT.txt_measure_canvas_text)
1495
+ if c in self.MT.char_widths[self.header_font]:
1496
+ return self.MT.char_widths[self.header_font][c]
1497
+ else:
1498
+ wd = b[2] - b[0] - self.header_test_str_w
1499
+ self.MT.char_widths[self.header_font][c] = wd
1500
+ return wd
1406
1501
 
1407
1502
  def redraw_grid_and_text(
1408
1503
  self,
@@ -1415,11 +1510,11 @@ class ColumnHeaders(tk.Canvas):
1415
1510
  text_end_col: int,
1416
1511
  scrollpos_right: float,
1417
1512
  col_pos_exists: bool,
1513
+ set_scrollregion: bool,
1418
1514
  ) -> bool:
1419
- try:
1420
- self.configure_scrollregion(last_col_line_pos=last_col_line_pos)
1421
- except Exception:
1422
- return False
1515
+ if set_scrollregion:
1516
+ if not self.configure_scrollregion(last_col_line_pos=last_col_line_pos):
1517
+ return False
1423
1518
  self.hidd_text.update(self.disp_text)
1424
1519
  self.disp_text = {}
1425
1520
  self.hidd_high.update(self.disp_high)
@@ -1437,49 +1532,24 @@ class ColumnHeaders(tk.Canvas):
1437
1532
  x_stop,
1438
1533
  self.current_height,
1439
1534
  )
1440
- yend = self.current_height - 5
1441
- if (self.PAR.ops.show_vertical_grid or self.width_resizing_enabled) and col_pos_exists:
1442
- points = [
1443
- x_stop - 1,
1444
- self.current_height - 1,
1445
- scrollpos_left - 1,
1446
- self.current_height - 1,
1447
- scrollpos_left - 1,
1448
- -1,
1449
- ]
1450
- for c in range(grid_start_col, grid_end_col):
1451
- draw_x = self.MT.col_positions[c]
1452
- if c and self.width_resizing_enabled:
1453
- self.visible_col_dividers[c] = (draw_x - 2, 1, draw_x + 2, yend)
1454
- points.extend(
1455
- (
1456
- draw_x,
1457
- -1,
1458
- draw_x,
1459
- self.current_height,
1460
- draw_x,
1461
- -1,
1462
- self.MT.col_positions[c + 1] if len(self.MT.col_positions) - 1 > c else draw_x,
1463
- -1,
1464
- )
1465
- )
1466
- self.redraw_gridline(points=points, fill=self.PAR.ops.header_grid_fg, width=1, tag="v")
1467
1535
  top = self.canvasy(0)
1468
1536
  sel_cols_bg = (
1469
- self.PAR.ops.header_selected_cells_bg
1470
- if self.PAR.ops.header_selected_cells_bg.startswith("#")
1471
- else color_map[self.PAR.ops.header_selected_cells_bg]
1537
+ self.ops.header_selected_cells_bg
1538
+ if self.ops.header_selected_cells_bg.startswith("#")
1539
+ else color_map[self.ops.header_selected_cells_bg]
1472
1540
  )
1473
1541
  sel_cells_bg = (
1474
- self.PAR.ops.header_selected_columns_bg
1475
- if self.PAR.ops.header_selected_columns_bg.startswith("#")
1476
- else color_map[self.PAR.ops.header_selected_columns_bg]
1542
+ self.ops.header_selected_columns_bg
1543
+ if self.ops.header_selected_columns_bg.startswith("#")
1544
+ else color_map[self.ops.header_selected_columns_bg]
1477
1545
  )
1478
- font = self.PAR.ops.header_font
1546
+ font = self.ops.header_font
1479
1547
  selections = self.get_redraw_selections(text_start_col, grid_end_col)
1480
1548
  dd_coords = self.dropdown.get_coords()
1549
+ wrap = self.ops.header_wrap
1550
+ txt_h = self.MT.header_txt_height
1481
1551
  for c in range(text_start_col, text_end_col):
1482
- draw_y = self.MT.header_first_ln_ins
1552
+ draw_y = 3
1483
1553
  cleftgridln = self.MT.col_positions[c]
1484
1554
  crightgridln = self.MT.col_positions[c + 1]
1485
1555
  datacn = c if self.MT.all_columns_displayed else self.MT.displayed_columns[c]
@@ -1492,80 +1562,45 @@ class ColumnHeaders(tk.Canvas):
1492
1562
  selections=selections,
1493
1563
  datacn=datacn,
1494
1564
  )
1495
-
1496
1565
  if datacn in self.cell_options and "align" in self.cell_options[datacn]:
1497
1566
  align = self.cell_options[datacn]["align"]
1498
1567
  else:
1499
1568
  align = self.align
1500
-
1501
- kwargs = self.get_cell_kwargs(datacn, key="dropdown")
1502
- if align == "w":
1503
- draw_x = cleftgridln + 3
1504
- if kwargs:
1505
- mw = crightgridln - cleftgridln - self.MT.header_txt_height - 2
1506
- self.redraw_dropdown(
1507
- cleftgridln,
1508
- 0,
1509
- crightgridln,
1510
- self.current_height - 1,
1511
- fill=fill if kwargs["state"] != "disabled" else self.PAR.ops.header_grid_fg,
1512
- outline=fill,
1513
- tag="dd",
1514
- draw_outline=not dd_drawn,
1515
- draw_arrow=mw >= 5,
1516
- open_=dd_coords == c,
1517
- )
1518
- else:
1519
- mw = crightgridln - cleftgridln - 1
1520
-
1521
- elif align == "e":
1522
- if kwargs:
1523
- mw = crightgridln - cleftgridln - self.MT.header_txt_height - 2
1524
- draw_x = crightgridln - 5 - self.MT.header_txt_height
1525
- self.redraw_dropdown(
1526
- cleftgridln,
1527
- 0,
1528
- crightgridln,
1529
- self.current_height - 1,
1530
- fill=fill if kwargs["state"] != "disabled" else self.PAR.ops.header_grid_fg,
1531
- outline=fill,
1532
- tag="dd",
1533
- draw_outline=not dd_drawn,
1534
- draw_arrow=mw >= 5,
1535
- open_=dd_coords == c,
1536
- )
1537
- else:
1538
- mw = crightgridln - cleftgridln - 1
1539
- draw_x = crightgridln - 3
1540
-
1541
- elif align == "center":
1542
- if kwargs:
1543
- mw = crightgridln - cleftgridln - self.MT.header_txt_height - 2
1544
- draw_x = cleftgridln + ceil((crightgridln - cleftgridln - self.MT.header_txt_height) / 2)
1545
- self.redraw_dropdown(
1546
- cleftgridln,
1547
- 0,
1548
- crightgridln,
1549
- self.current_height - 1,
1550
- fill=fill if kwargs["state"] != "disabled" else self.PAR.ops.header_grid_fg,
1551
- outline=fill,
1552
- tag="dd",
1553
- draw_outline=not dd_drawn,
1554
- draw_arrow=mw >= 5,
1555
- open_=dd_coords == c,
1556
- )
1557
- else:
1558
- mw = crightgridln - cleftgridln - 1
1569
+ if kwargs := self.get_cell_kwargs(datacn, key="dropdown"):
1570
+ max_width = crightgridln - cleftgridln - txt_h - 2
1571
+ if align.endswith("w"):
1572
+ draw_x = cleftgridln + 2
1573
+ elif align.endswith("e"):
1574
+ draw_x = crightgridln - 5 - txt_h
1575
+ elif align.endswith("n"):
1576
+ draw_x = cleftgridln + ceil((crightgridln - cleftgridln - txt_h) / 2)
1577
+ self.redraw_dropdown(
1578
+ cleftgridln,
1579
+ 0,
1580
+ crightgridln,
1581
+ self.current_height - 1,
1582
+ fill=fill if kwargs["state"] != "disabled" else self.ops.header_grid_fg,
1583
+ outline=fill,
1584
+ tag="dd",
1585
+ draw_outline=not dd_drawn,
1586
+ draw_arrow=max_width >= 5,
1587
+ open_=dd_coords == c,
1588
+ )
1589
+ else:
1590
+ max_width = crightgridln - cleftgridln - 2
1591
+ if align.endswith("w"):
1592
+ draw_x = cleftgridln + 2
1593
+ elif align.endswith("e"):
1594
+ draw_x = crightgridln - 2
1595
+ elif align.endswith("n"):
1559
1596
  draw_x = cleftgridln + floor((crightgridln - cleftgridln) / 2)
1560
- if not kwargs:
1561
- kwargs = self.get_cell_kwargs(datacn, key="checkbox")
1562
- if kwargs and mw > self.MT.header_txt_height + 1:
1563
- box_w = self.MT.header_txt_height + 1
1564
- if align == "w":
1597
+ if (kwargs := self.get_cell_kwargs(datacn, key="checkbox")) and max_width > txt_h + 1:
1598
+ box_w = txt_h + 1
1599
+ if align.endswith("w"):
1565
1600
  draw_x += box_w + 3
1566
- elif align == "center":
1601
+ elif align.endswith("n"):
1567
1602
  draw_x += ceil(box_w / 2) + 1
1568
- mw -= box_w + 3
1603
+ max_width -= box_w + 4
1569
1604
  try:
1570
1605
  draw_check = (
1571
1606
  self.MT._headers[datacn]
@@ -1577,98 +1612,99 @@ class ColumnHeaders(tk.Canvas):
1577
1612
  self.redraw_checkbox(
1578
1613
  cleftgridln + 2,
1579
1614
  2,
1580
- cleftgridln + self.MT.header_txt_height + 3,
1581
- self.MT.header_txt_height + 3,
1582
- fill=fill if kwargs["state"] == "normal" else self.PAR.ops.header_grid_fg,
1615
+ cleftgridln + txt_h + 3,
1616
+ txt_h + 3,
1617
+ fill=fill if kwargs["state"] == "normal" else self.ops.header_grid_fg,
1583
1618
  outline="",
1584
1619
  tag="cb",
1585
1620
  draw_check=draw_check,
1586
1621
  )
1587
- lns = self.get_valid_cell_data_as_str(datacn, fix=False)
1588
- if not lns:
1622
+ if (
1623
+ max_width < self.MT.header_txt_width
1624
+ or (align.endswith("w") and draw_x > scrollpos_right)
1625
+ or (align.endswith("e") and cleftgridln + 5 > scrollpos_right)
1626
+ or (align.endswith("n") and cleftgridln + 5 > scrollpos_right)
1627
+ ):
1628
+ continue
1629
+ text = self.get_valid_cell_data_as_str(datacn, fix=False)
1630
+ if not text:
1589
1631
  continue
1590
- lns = lns.split("\n")
1591
- if mw > self.MT.header_txt_width and not (
1592
- (align == "w" and draw_x > scrollpos_right)
1593
- or (align == "e" and cleftgridln + 5 > scrollpos_right)
1594
- or (align == "center" and cleftgridln + 5 > scrollpos_right)
1632
+ max_lines = int((self.current_height - top - 2) / txt_h)
1633
+ for text in wrap_text(
1634
+ text=text,
1635
+ max_width=max_width,
1636
+ max_lines=max_lines,
1637
+ char_width_fn=self.wrap_get_char_w,
1638
+ widths=self.MT.char_widths[font],
1639
+ wrap=wrap,
1595
1640
  ):
1596
- for txt in islice(
1597
- lns,
1598
- self.lines_start_at if self.lines_start_at < len(lns) else len(lns) - 1,
1599
- None,
1600
- ):
1601
- if draw_y > top:
1602
- if self.hidd_text:
1603
- iid, showing = self.hidd_text.popitem()
1604
- self.coords(iid, draw_x, draw_y)
1605
- if showing:
1606
- self.itemconfig(
1607
- iid,
1608
- text=txt,
1609
- fill=fill,
1610
- font=font,
1611
- anchor=align,
1612
- )
1613
- else:
1614
- self.itemconfig(
1615
- iid,
1616
- text=txt,
1617
- fill=fill,
1618
- font=font,
1619
- anchor=align,
1620
- state="normal",
1621
- )
1622
- self.tag_raise(iid)
1623
- else:
1624
- iid = self.create_text(
1625
- draw_x,
1626
- draw_y,
1627
- text=txt,
1628
- fill=fill,
1629
- font=font,
1630
- anchor=align,
1631
- tag="t",
1632
- )
1633
- self.disp_text[iid] = True
1634
- wd = self.bbox(iid)
1635
- wd = wd[2] - wd[0]
1636
- if wd > mw:
1637
- if align == "w":
1638
- txt = txt[: int(len(txt) * (mw / wd))]
1639
- self.itemconfig(iid, text=txt)
1640
- wd = self.bbox(iid)
1641
- while wd[2] - wd[0] > mw:
1642
- txt = txt[:-1]
1643
- self.itemconfig(iid, text=txt)
1644
- wd = self.bbox(iid)
1645
- elif align == "e":
1646
- txt = txt[len(txt) - int(len(txt) * (mw / wd)) :]
1647
- self.itemconfig(iid, text=txt)
1648
- wd = self.bbox(iid)
1649
- while wd[2] - wd[0] > mw:
1650
- txt = txt[1:]
1651
- self.itemconfig(iid, text=txt)
1652
- wd = self.bbox(iid)
1653
- elif align == "center":
1654
- self.c_align_cyc = cycle(self.centre_alignment_text_mod_indexes)
1655
- tmod = ceil((len(txt) - int(len(txt) * (mw / wd))) / 2)
1656
- txt = txt[tmod - 1 : -tmod]
1657
- self.itemconfig(iid, text=txt)
1658
- wd = self.bbox(iid)
1659
- while wd[2] - wd[0] > mw:
1660
- txt = txt[next(self.c_align_cyc)]
1661
- self.itemconfig(iid, text=txt)
1662
- wd = self.bbox(iid)
1663
- self.coords(iid, draw_x, draw_y)
1664
- draw_y += self.MT.header_xtra_lines_increment
1665
- if draw_y - 1 > self.current_height:
1666
- break
1641
+ if self.hidd_text:
1642
+ iid, showing = self.hidd_text.popitem()
1643
+ self.coords(iid, draw_x, draw_y)
1644
+ if showing:
1645
+ self.itemconfig(
1646
+ iid,
1647
+ text=text,
1648
+ fill=fill,
1649
+ font=font,
1650
+ anchor=align,
1651
+ )
1652
+ else:
1653
+ self.itemconfig(
1654
+ iid,
1655
+ text=text,
1656
+ fill=fill,
1657
+ font=font,
1658
+ anchor=align,
1659
+ state="normal",
1660
+ )
1661
+ self.tag_raise(iid)
1662
+ else:
1663
+ iid = self.create_text(
1664
+ draw_x,
1665
+ draw_y,
1666
+ text=text,
1667
+ fill=fill,
1668
+ font=font,
1669
+ anchor=align,
1670
+ tags="t",
1671
+ )
1672
+ self.disp_text[iid] = True
1673
+ draw_y += self.MT.header_txt_height
1674
+ yend = self.current_height - 5
1675
+ if (self.ops.show_vertical_grid or self.width_resizing_enabled) and col_pos_exists:
1676
+ points = [
1677
+ x_stop - 1,
1678
+ self.current_height - 1,
1679
+ scrollpos_left - 1,
1680
+ self.current_height - 1,
1681
+ scrollpos_left - 1,
1682
+ -1,
1683
+ ]
1684
+ for c in range(grid_start_col, grid_end_col):
1685
+ draw_x = self.MT.col_positions[c]
1686
+ if c and self.width_resizing_enabled:
1687
+ self.visible_col_dividers[c] = (draw_x - 2, 1, draw_x + 2, yend)
1688
+ points.extend(
1689
+ (
1690
+ draw_x,
1691
+ -1,
1692
+ draw_x,
1693
+ self.current_height,
1694
+ draw_x,
1695
+ -1,
1696
+ self.MT.col_positions[c + 1] if len(self.MT.col_positions) - 1 > c else draw_x,
1697
+ -1,
1698
+ )
1699
+ )
1700
+ self.redraw_gridline(points=points, fill=self.ops.header_grid_fg, width=1, tag="v")
1667
1701
  for dct in (self.hidd_text, self.hidd_high, self.hidd_grid, self.hidd_dropdown, self.hidd_checkbox):
1668
1702
  for iid, showing in dct.items():
1669
1703
  if showing:
1670
1704
  self.itemconfig(iid, state="hidden")
1671
1705
  dct[iid] = False
1706
+ if self.disp_resize_lines:
1707
+ self.tag_raise("rw")
1672
1708
  return True
1673
1709
 
1674
1710
  def get_redraw_selections(self, startc: int, endc: int) -> dict[str, set[int]]:
@@ -1747,7 +1783,7 @@ class ColumnHeaders(tk.Canvas):
1747
1783
  return False
1748
1784
  else:
1749
1785
  text = text if isinstance(text, str) else f"{text}"
1750
- if self.PAR.ops.cell_auto_resize_enabled:
1786
+ if self.ops.cell_auto_resize_enabled:
1751
1787
  if self.height_resizing_enabled:
1752
1788
  self.set_height_of_header_to_text(text)
1753
1789
  self.set_col_width_run_binding(c)
@@ -1756,7 +1792,7 @@ class ColumnHeaders(tk.Canvas):
1756
1792
  return False
1757
1793
  self.hide_text_editor()
1758
1794
  if not self.MT.see(r=0, c=c, keep_yscroll=True, check_cell_visibility=True):
1759
- self.MT.refresh()
1795
+ self.MT.main_table_redraw_grid_and_text(True, True)
1760
1796
  x = self.MT.col_positions[c] + 1
1761
1797
  y = 0
1762
1798
  w = self.MT.col_positions[c + 1] - x
@@ -1764,24 +1800,24 @@ class ColumnHeaders(tk.Canvas):
1764
1800
  kwargs = {
1765
1801
  "menu_kwargs": DotDict(
1766
1802
  {
1767
- "font": self.PAR.ops.table_font,
1768
- "foreground": self.PAR.ops.popup_menu_fg,
1769
- "background": self.PAR.ops.popup_menu_bg,
1770
- "activebackground": self.PAR.ops.popup_menu_highlight_bg,
1771
- "activeforeground": self.PAR.ops.popup_menu_highlight_fg,
1803
+ "font": self.ops.table_font,
1804
+ "foreground": self.ops.popup_menu_fg,
1805
+ "background": self.ops.popup_menu_bg,
1806
+ "activebackground": self.ops.popup_menu_highlight_bg,
1807
+ "activeforeground": self.ops.popup_menu_highlight_fg,
1772
1808
  }
1773
1809
  ),
1774
- "sheet_ops": self.PAR.ops,
1775
- "border_color": self.PAR.ops.header_selected_columns_bg,
1810
+ "sheet_ops": self.ops,
1811
+ "border_color": self.ops.header_selected_columns_bg,
1776
1812
  "text": text,
1777
1813
  "state": state,
1778
1814
  "width": w,
1779
1815
  "height": h,
1780
1816
  "show_border": True,
1781
- "bg": self.PAR.ops.header_editor_bg,
1782
- "fg": self.PAR.ops.header_editor_fg,
1783
- "select_bg": self.PAR.ops.header_editor_select_bg,
1784
- "select_fg": self.PAR.ops.header_editor_select_fg,
1817
+ "bg": self.ops.header_editor_bg,
1818
+ "fg": self.ops.header_editor_fg,
1819
+ "select_bg": self.ops.header_editor_select_bg,
1820
+ "select_fg": self.ops.header_editor_select_fg,
1785
1821
  "align": self.get_cell_align(c),
1786
1822
  "c": c,
1787
1823
  }
@@ -1841,7 +1877,7 @@ class ColumnHeaders(tk.Canvas):
1841
1877
  > curr_height
1842
1878
  ):
1843
1879
  c = self.text_editor.column
1844
- new_height = curr_height + self.MT.header_xtra_lines_increment
1880
+ new_height = curr_height + self.MT.header_txt_height
1845
1881
  space_bot = self.MT.get_space_bot(0)
1846
1882
  if new_height > space_bot:
1847
1883
  new_height = space_bot
@@ -1864,7 +1900,7 @@ class ColumnHeaders(tk.Canvas):
1864
1900
  height=self.current_height,
1865
1901
  width=self.MT.col_positions[c + 1] - self.MT.col_positions[c] + 1,
1866
1902
  )
1867
- self.text_editor.tktext.config(font=self.PAR.ops.header_font)
1903
+ self.text_editor.tktext.config(font=self.ops.header_font)
1868
1904
  self.coords(
1869
1905
  self.text_editor.canvas_id,
1870
1906
  self.MT.col_positions[c],
@@ -1972,15 +2008,12 @@ class ColumnHeaders(tk.Canvas):
1972
2008
  for i, v in enumerate(self.get_cell_kwargs(datacn, key="dropdown")["values"]):
1973
2009
  v_numlines = len(v.split("\n") if isinstance(v, str) else f"{v}".split("\n"))
1974
2010
  if v_numlines > 1:
1975
- win_h += (
1976
- self.MT.header_first_ln_ins + (v_numlines * self.MT.header_xtra_lines_increment) + 5
1977
- ) # end of cell
2011
+ win_h += self.MT.header_txt_height + (v_numlines * self.MT.header_txt_height) + 5 # end of cell
1978
2012
  else:
1979
2013
  win_h += self.MT.min_header_height
1980
2014
  if i == 5:
1981
2015
  break
1982
- if win_h > 500:
1983
- win_h = 500
2016
+ win_h = min(win_h, 500)
1984
2017
  space_bot = self.MT.get_space_bot(0, text_editor_h)
1985
2018
  win_h2 = int(win_h)
1986
2019
  if win_h > space_bot:
@@ -2029,15 +2062,15 @@ class ColumnHeaders(tk.Canvas):
2029
2062
  reset_kwargs = {
2030
2063
  "r": 0,
2031
2064
  "c": c,
2032
- "bg": self.PAR.ops.header_editor_bg,
2033
- "fg": self.PAR.ops.header_editor_fg,
2034
- "select_bg": self.PAR.ops.header_editor_select_bg,
2035
- "select_fg": self.PAR.ops.header_editor_select_fg,
2065
+ "bg": self.ops.header_editor_bg,
2066
+ "fg": self.ops.header_editor_fg,
2067
+ "select_bg": self.ops.header_editor_select_bg,
2068
+ "select_fg": self.ops.header_editor_select_fg,
2036
2069
  "width": win_w,
2037
2070
  "height": win_h,
2038
- "font": self.PAR.ops.header_font,
2039
- "ops": self.PAR.ops,
2040
- "outline_color": self.PAR.ops.header_selected_columns_bg,
2071
+ "font": self.ops.header_font,
2072
+ "ops": self.ops,
2073
+ "outline_color": self.ops.header_selected_columns_bg,
2041
2074
  "align": self.get_cell_align(c),
2042
2075
  "values": kwargs["values"],
2043
2076
  "search_function": kwargs["search_function"],
@@ -2049,7 +2082,7 @@ class ColumnHeaders(tk.Canvas):
2049
2082
  self.coords(self.dropdown.canvas_id, self.MT.col_positions[c], ypos)
2050
2083
  self.dropdown.window.tkraise()
2051
2084
  else:
2052
- self.dropdown.window = self.PAR.dropdown_class(
2085
+ self.dropdown.window = self.PAR._dropdown_cls(
2053
2086
  self.winfo_toplevel(),
2054
2087
  **reset_kwargs,
2055
2088
  single_index="c",
@@ -2092,13 +2125,11 @@ class ColumnHeaders(tk.Canvas):
2092
2125
  if c is not None and selection is not None:
2093
2126
  datacn = c if self.MT.all_columns_displayed else self.MT.displayed_columns[c]
2094
2127
  kwargs = self.get_cell_kwargs(datacn, key="dropdown")
2095
- pre_edit_value = self.get_cell_data(datacn)
2096
- edited = False
2097
2128
  event_data = event_dict(
2098
2129
  name="end_edit_header",
2099
2130
  sheet=self.PAR.name,
2100
2131
  widget=self,
2101
- cells_header={datacn: pre_edit_value},
2132
+ cells_header={datacn: self.get_cell_data(datacn)},
2102
2133
  key="??",
2103
2134
  value=selection,
2104
2135
  loc=c,
@@ -2106,16 +2137,12 @@ class ColumnHeaders(tk.Canvas):
2106
2137
  boxes=self.MT.get_boxes(),
2107
2138
  selected=self.MT.selected,
2108
2139
  )
2109
- if kwargs["select_function"] is not None:
2110
- kwargs["select_function"](event_data)
2111
- if self.MT.edit_validation_func:
2112
- selection = self.MT.edit_validation_func(event_data)
2113
- if selection is not None:
2114
- edited = self.set_cell_data_undo(c, datacn=datacn, value=selection, redraw=not redraw)
2115
- else:
2140
+ try_binding(kwargs["select_function"], event_data)
2141
+ selection = selection if not self.MT.edit_validation_func else self.MT.edit_validation_func(event_data)
2142
+ if selection is not None:
2116
2143
  edited = self.set_cell_data_undo(c, datacn=datacn, value=selection, redraw=not redraw)
2117
- if edited:
2118
- try_binding(self.extra_end_edit_cell_func, event_data)
2144
+ if edited:
2145
+ try_binding(self.extra_end_edit_cell_func, event_data)
2119
2146
  self.MT.recreate_all_selection_boxes()
2120
2147
  self.focus_set()
2121
2148
  self.hide_text_editor_and_dropdown(redraw=redraw)
@@ -2191,7 +2218,7 @@ class ColumnHeaders(tk.Canvas):
2191
2218
  self.MT.undo_stack.append(stored_event_dict(event_data))
2192
2219
  self.set_cell_data(datacn=datacn, value=value)
2193
2220
  edited = True
2194
- if edited and cell_resize and self.PAR.ops.cell_auto_resize_enabled:
2221
+ if edited and cell_resize and self.ops.cell_auto_resize_enabled:
2195
2222
  if self.height_resizing_enabled:
2196
2223
  self.set_height_of_header_to_text(self.get_valid_cell_data_as_str(datacn, fix=False))
2197
2224
  self.set_col_width_run_binding(c)
@@ -2264,11 +2291,15 @@ class ColumnHeaders(tk.Canvas):
2264
2291
  if fix:
2265
2292
  self.fix_header(datacn)
2266
2293
  try:
2267
- value = "" if self.MT._headers[datacn] is None else f"{self.MT._headers[datacn]}"
2294
+ value = self.MT._headers[datacn]
2295
+ if value is None:
2296
+ value = ""
2297
+ elif not isinstance(value, str):
2298
+ value = f"{value}"
2268
2299
  except Exception:
2269
2300
  value = ""
2270
- if not value and self.PAR.ops.show_default_header_for_empty:
2271
- value = get_n2a(datacn, self.PAR.ops.default_header)
2301
+ if not value and self.ops.show_default_header_for_empty:
2302
+ value = get_n2a(datacn, self.ops.default_header)
2272
2303
  return value
2273
2304
 
2274
2305
  def get_value_for_empty_cell(self, datacn: int, c_ops: bool = True) -> object: