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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tksheet/column_headers.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import tkinter as tk
4
4
  from collections import defaultdict
5
- from collections.abc import Hashable, Sequence
5
+ from collections.abc import Callable, Hashable, Sequence
6
6
  from functools import partial
7
7
  from itertools import cycle, islice, repeat
8
8
  from math import ceil, floor
@@ -12,6 +12,7 @@ from typing import Literal
12
12
  from .colors import color_map
13
13
  from .constants import (
14
14
  USER_OS,
15
+ _test_str,
15
16
  rc_binding,
16
17
  text_editor_close_bindings,
17
18
  text_editor_newline_bindings,
@@ -26,27 +27,33 @@ from .functions import (
26
27
  get_n2a,
27
28
  int_x_tuple,
28
29
  is_contiguous,
30
+ mod_event_val,
29
31
  new_tk_event,
30
32
  rounded_box_coords,
31
33
  stored_event_dict,
34
+ try_b_index,
32
35
  try_binding,
36
+ wrap_text,
33
37
  )
34
- from .other_classes import DotDict, DraggedRowColumn, DropdownStorage, TextEditorStorage
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
35
41
  from .text_editor import TextEditor
36
- from .types import AnyIter
42
+ from .tksheet_types import AnyIter
37
43
 
38
44
 
39
45
  class ColumnHeaders(tk.Canvas):
40
- def __init__(self, *args, **kwargs):
46
+ def __init__(self, parent, **kwargs):
41
47
  super().__init__(
42
- kwargs["parent"],
43
- background=kwargs["parent"].ops.header_bg,
48
+ parent,
49
+ background=parent.ops.header_bg,
44
50
  highlightthickness=0,
45
51
  )
46
- self.PAR = kwargs["parent"]
52
+ self.PAR = parent
53
+ self.ops = self.PAR.ops
47
54
  self.current_height = None
48
55
  self.MT = None # is set from within MainTable() __init__
49
- self.RI = None # is set from within MainTable() __init__
56
+ self.RI: RowIndex | None = None # is set from within MainTable() __init__
50
57
  self.TL = None # is set from within TopLeftRectangle() __init__
51
58
  self.popup_menu_loc = None
52
59
  self.extra_begin_edit_cell_func = None
@@ -63,6 +70,8 @@ class ColumnHeaders(tk.Canvas):
63
70
  self.extra_double_b1_func = None
64
71
  self.ch_extra_begin_drag_drop_func = None
65
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
66
75
  self.extra_rc_func = None
67
76
  self.selection_binding_func = None
68
77
  self.shift_selection_binding_func = None
@@ -142,36 +151,8 @@ class ColumnHeaders(tk.Canvas):
142
151
  self.unbind("<Button-4>")
143
152
  self.unbind("<Button-5>")
144
153
 
145
- def mousewheel(self, event: object) -> None:
146
- if isinstance(self.MT._headers, int):
147
- maxlines = max(
148
- (
149
- len(
150
- self.MT.get_valid_cell_data_as_str(self.MT._headers, datacn, get_displayed=True)
151
- .rstrip()
152
- .split("\n")
153
- )
154
- for datacn in range(len(self.MT.data[self.MT._headers]))
155
- ),
156
- default=0,
157
- )
158
- elif isinstance(self.MT._headers, (list, tuple)):
159
- maxlines = max(
160
- (
161
- len(e.rstrip().split("\n")) if isinstance(e, str) else len(f"{e}".rstrip().split("\n"))
162
- for e in self.MT._headers
163
- ),
164
- default=0,
165
- )
166
- if maxlines == 1:
167
- maxlines = 0
168
- if self.lines_start_at > maxlines:
169
- self.lines_start_at = maxlines
170
- if (event.delta < 0 or event.num == 5) and self.lines_start_at < maxlines:
171
- self.lines_start_at += 1
172
- elif (event.delta >= 0 or event.num == 4) and self.lines_start_at > 0:
173
- self.lines_start_at -= 1
174
- 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)
175
156
 
176
157
  def set_height(self, new_height: int, set_TL: bool = False) -> bool:
177
158
  try:
@@ -441,21 +422,21 @@ class ColumnHeaders(tk.Canvas):
441
422
  x,
442
423
  self.current_height,
443
424
  width=1,
444
- fill=self.PAR.ops.resizing_line_fg,
425
+ fill=self.ops.resizing_line_fg,
445
426
  tag=("rw", "rwl"),
446
427
  )
447
- self.MT.create_resize_line(x, y1, x, y2, width=1, fill=self.PAR.ops.resizing_line_fg, tag=("rw", "rwl"))
428
+ self.MT.create_resize_line(x, y1, x, y2, width=1, fill=self.ops.resizing_line_fg, tag=("rw", "rwl"))
448
429
  self.create_resize_line(
449
430
  line2x,
450
431
  0,
451
432
  line2x,
452
433
  self.current_height,
453
434
  width=1,
454
- fill=self.PAR.ops.resizing_line_fg,
435
+ fill=self.ops.resizing_line_fg,
455
436
  tag=("rw", "rwl2"),
456
437
  )
457
438
  self.MT.create_resize_line(
458
- line2x, y1, line2x, y2, width=1, fill=self.PAR.ops.resizing_line_fg, tag=("rw", "rwl2")
439
+ line2x, y1, line2x, y2, width=1, fill=self.ops.resizing_line_fg, tag=("rw", "rwl2")
459
440
  )
460
441
  elif self.height_resizing_enabled and self.rsz_w is None and self.rsz_h is not None:
461
442
  self.currently_resizing_height = True
@@ -486,7 +467,7 @@ class ColumnHeaders(tk.Canvas):
486
467
  if self.width_resizing_enabled and self.rsz_w is not None and self.currently_resizing_width:
487
468
  x = self.canvasx(event.x)
488
469
  size = x - self.MT.col_positions[self.rsz_w - 1]
489
- 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:
490
471
  self.hide_resize_and_ctrl_lines(ctrl_lines=False)
491
472
  line2x = self.MT.col_positions[self.rsz_w - 1]
492
473
  self.create_resize_line(
@@ -495,17 +476,17 @@ class ColumnHeaders(tk.Canvas):
495
476
  x,
496
477
  self.current_height,
497
478
  width=1,
498
- fill=self.PAR.ops.resizing_line_fg,
479
+ fill=self.ops.resizing_line_fg,
499
480
  tag=("rw", "rwl"),
500
481
  )
501
- self.MT.create_resize_line(x, y1, x, y2, width=1, fill=self.PAR.ops.resizing_line_fg, tag=("rw", "rwl"))
482
+ self.MT.create_resize_line(x, y1, x, y2, width=1, fill=self.ops.resizing_line_fg, tag=("rw", "rwl"))
502
483
  self.create_resize_line(
503
484
  line2x,
504
485
  0,
505
486
  line2x,
506
487
  self.current_height,
507
488
  width=1,
508
- fill=self.PAR.ops.resizing_line_fg,
489
+ fill=self.ops.resizing_line_fg,
509
490
  tag=("rw", "rwl2"),
510
491
  )
511
492
  self.MT.create_resize_line(
@@ -514,15 +495,15 @@ class ColumnHeaders(tk.Canvas):
514
495
  line2x,
515
496
  y2,
516
497
  width=1,
517
- fill=self.PAR.ops.resizing_line_fg,
498
+ fill=self.ops.resizing_line_fg,
518
499
  tag=("rw", "rwl2"),
519
500
  )
520
501
  self.drag_width_resize()
521
502
  elif self.height_resizing_enabled and self.rsz_h is not None and self.currently_resizing_height:
522
503
  evy = event.y
523
504
  if evy > self.current_height:
524
- if evy > self.PAR.ops.max_header_height:
525
- evy = int(self.PAR.ops.max_header_height)
505
+ if evy > self.ops.max_header_height:
506
+ evy = int(self.ops.max_header_height)
526
507
  self.drag_height_resize(evy)
527
508
  else:
528
509
  if evy < self.MT.min_header_height:
@@ -687,16 +668,16 @@ class ColumnHeaders(tk.Canvas):
687
668
  xpos,
688
669
  self.current_height,
689
670
  width=3,
690
- fill=self.PAR.ops.drag_and_drop_bg,
671
+ fill=self.ops.drag_and_drop_bg,
691
672
  tag="move_columns",
692
673
  )
693
- 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")
694
675
  for boxst, boxend in consecutive_ranges(cols):
695
676
  self.MT.show_ctrl_outline(
696
677
  start_cell=(boxst, 0),
697
678
  end_cell=(boxend, len(self.MT.row_positions) - 1),
698
679
  dash=(),
699
- outline=self.PAR.ops.drag_and_drop_bg,
680
+ outline=self.ops.drag_and_drop_bg,
700
681
  delete_on_timer=False,
701
682
  )
702
683
 
@@ -759,10 +740,10 @@ class ColumnHeaders(tk.Canvas):
759
740
  new_col_pos = int(self.coords("rwl")[0])
760
741
  old_width = self.MT.col_positions[self.rsz_w] - self.MT.col_positions[self.rsz_w - 1]
761
742
  size = new_col_pos - self.MT.col_positions[self.rsz_w - 1]
762
- if size < self.PAR.ops.min_column_width:
763
- new_col_pos = ceil(self.MT.col_positions[self.rsz_w - 1] + self.PAR.ops.min_column_width)
764
- elif size > self.PAR.ops.max_column_width:
765
- 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)
766
747
  increment = new_col_pos - self.MT.col_positions[self.rsz_w]
767
748
  self.MT.col_positions[self.rsz_w + 1 :] = [
768
749
  e + increment for e in islice(self.MT.col_positions, self.rsz_w + 1, None)
@@ -771,7 +752,7 @@ class ColumnHeaders(tk.Canvas):
771
752
  new_width = self.MT.col_positions[self.rsz_w] - self.MT.col_positions[self.rsz_w - 1]
772
753
  self.MT.allow_auto_resize_columns = False
773
754
  self.MT.recreate_all_selection_boxes()
774
- 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)
775
756
  if self.column_width_resize_func is not None and old_width != new_width:
776
757
  self.column_width_resize_func(
777
758
  event_dict(
@@ -796,8 +777,8 @@ class ColumnHeaders(tk.Canvas):
796
777
  self.MT.bind("<MouseWheel>", self.MT.mousewheel)
797
778
  if self.width_resizing_enabled and self.rsz_w is not None and self.currently_resizing_width:
798
779
  self.drag_width_resize()
799
- self.currently_resizing_width = False
800
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)
801
782
  elif (
802
783
  self.drag_and_drop_enabled
803
784
  and self.col_selection_enabled
@@ -823,22 +804,16 @@ class ColumnHeaders(tk.Canvas):
823
804
  c += 1
824
805
  if c > len(self.MT.col_positions) - 1:
825
806
  c = len(self.MT.col_positions) - 1
826
- event_data = event_dict(
827
- name="move_columns",
828
- sheet=self.PAR.name,
829
- widget=self,
830
- boxes=self.MT.get_boxes(),
831
- selected=self.MT.selected,
832
- value=c,
833
- )
807
+ event_data = self.MT.new_event_dict("move_columns", state=True)
808
+ event_data["value"] = c
834
809
  if try_binding(self.ch_extra_begin_drag_drop_func, event_data, "begin_move_columns"):
835
810
  data_new_idxs, disp_new_idxs, event_data = self.MT.move_columns_adjust_options_dict(
836
811
  *self.MT.get_args_for_move_columns(
837
812
  move_to=c,
838
813
  to_move=self.dragged_col.to_move,
839
814
  ),
840
- move_data=self.PAR.ops.column_drag_and_drop_perform,
841
- 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,
842
817
  event_data=event_data,
843
818
  )
844
819
  event_data["moved"]["columns"] = {
@@ -884,6 +859,126 @@ class ColumnHeaders(tk.Canvas):
884
859
  self.mouse_motion(event)
885
860
  try_binding(self.extra_b1_release_func, event)
886
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
+
887
982
  def toggle_select_col(
888
983
  self,
889
984
  column: int,
@@ -972,7 +1067,7 @@ class ColumnHeaders(tk.Canvas):
972
1067
  y1,
973
1068
  x2,
974
1069
  y2,
975
- radius=5 if self.PAR.ops.rounded_boxes else 0,
1070
+ radius=5 if self.ops.rounded_boxes else 0,
976
1071
  )
977
1072
  if isinstance(iid, int):
978
1073
  self.coords(iid, coords)
@@ -999,13 +1094,13 @@ class ColumnHeaders(tk.Canvas):
999
1094
  self.MT.txt_measure_canvas.itemconfig(
1000
1095
  self.MT.txt_measure_canvas_text,
1001
1096
  text=txt,
1002
- font=self.PAR.ops.header_font,
1097
+ font=self.ops.header_font,
1003
1098
  )
1004
1099
  b = self.MT.txt_measure_canvas.bbox(self.MT.txt_measure_canvas_text)
1005
1100
  w = b[2] - b[0] + 7
1006
1101
  h = b[3] - b[1] + 5
1007
1102
  else:
1008
- w = self.PAR.ops.min_column_width
1103
+ w = self.ops.min_column_width
1009
1104
  h = self.MT.min_header_height
1010
1105
  if datacn in self.cell_options and (
1011
1106
  self.get_cell_kwargs(datacn, key="dropdown") or self.get_cell_kwargs(datacn, key="checkbox")
@@ -1027,7 +1122,7 @@ class ColumnHeaders(tk.Canvas):
1027
1122
  qconf = self.MT.txt_measure_canvas.itemconfig
1028
1123
  qbbox = self.MT.txt_measure_canvas.bbox
1029
1124
  qtxtm = self.MT.txt_measure_canvas_text
1030
- qfont = self.PAR.ops.header_font
1125
+ qfont = self.ops.header_font
1031
1126
  default_header_height = self.MT.get_default_header_height()
1032
1127
  if text is not None and text:
1033
1128
  qconf(qtxtm, text=text, font=qfont)
@@ -1063,8 +1158,8 @@ class ColumnHeaders(tk.Canvas):
1063
1158
  h = space_bot
1064
1159
  if h < self.MT.min_header_height:
1065
1160
  h = int(self.MT.min_header_height)
1066
- elif h > self.PAR.ops.max_header_height:
1067
- h = int(self.PAR.ops.max_header_height)
1161
+ elif h > self.ops.max_header_height:
1162
+ h = int(self.ops.max_header_height)
1068
1163
  if not only_if_too_small or (only_if_too_small and h > self.current_height):
1069
1164
  self.set_height(h, set_TL=True)
1070
1165
  self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
@@ -1077,7 +1172,7 @@ class ColumnHeaders(tk.Canvas):
1077
1172
  only_if_too_small: bool = False,
1078
1173
  ) -> int:
1079
1174
  self.fix_header()
1080
- w = self.PAR.ops.min_column_width
1175
+ w = self.ops.min_column_width
1081
1176
  datacn = col if self.MT.all_columns_displayed else self.MT.displayed_columns[col]
1082
1177
  # header
1083
1178
  hw, hh_ = self.get_cell_dimensions(datacn)
@@ -1098,7 +1193,7 @@ class ColumnHeaders(tk.Canvas):
1098
1193
  qbbox = self.MT.txt_measure_canvas.bbox
1099
1194
  qtxtm = self.MT.txt_measure_canvas_text
1100
1195
  qtxth = self.MT.table_txt_height
1101
- qfont = self.PAR.ops.table_font
1196
+ qfont = self.ops.table_font
1102
1197
  for datarn in iterable:
1103
1198
  if txt := self.MT.get_valid_cell_data_as_str(datarn, datacn, get_displayed=True):
1104
1199
  qconf(qtxtm, text=txt, font=qfont)
@@ -1114,10 +1209,10 @@ class ColumnHeaders(tk.Canvas):
1114
1209
  w = hw
1115
1210
  if only_if_too_small and w < self.MT.col_positions[col + 1] - self.MT.col_positions[col]:
1116
1211
  w = self.MT.col_positions[col + 1] - self.MT.col_positions[col]
1117
- if w <= self.PAR.ops.min_column_width:
1118
- w = self.PAR.ops.min_column_width
1119
- elif w > self.PAR.ops.max_column_width:
1120
- 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)
1121
1216
  return w
1122
1217
 
1123
1218
  def set_col_width(
@@ -1130,10 +1225,10 @@ class ColumnHeaders(tk.Canvas):
1130
1225
  ) -> int:
1131
1226
  if width is None:
1132
1227
  width = self.get_col_text_width(col=col, visible_only=visible_only)
1133
- if width <= self.PAR.ops.min_column_width:
1134
- width = self.PAR.ops.min_column_width
1135
- elif width > self.PAR.ops.max_column_width:
1136
- 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)
1137
1232
  if only_if_too_small and width <= self.MT.col_positions[col + 1] - self.MT.col_positions[col]:
1138
1233
  return self.MT.col_positions[col + 1] - self.MT.col_positions[col]
1139
1234
  new_col_pos = self.MT.col_positions[col] + width
@@ -1186,8 +1281,8 @@ class ColumnHeaders(tk.Canvas):
1186
1281
  fill = color_map[fill]
1187
1282
  if "columns" in selections and c in selections["columns"]:
1188
1283
  txtfg = (
1189
- self.PAR.ops.header_selected_columns_fg
1190
- 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
1191
1286
  else kwargs[1]
1192
1287
  )
1193
1288
  if fill:
@@ -1198,8 +1293,8 @@ class ColumnHeaders(tk.Canvas):
1198
1293
  )
1199
1294
  elif "cells" in selections and c in selections["cells"]:
1200
1295
  txtfg = (
1201
- self.PAR.ops.header_selected_cells_fg
1202
- 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
1203
1298
  else kwargs[1]
1204
1299
  )
1205
1300
  if fill:
@@ -1209,7 +1304,7 @@ class ColumnHeaders(tk.Canvas):
1209
1304
  + f"{int((int(fill[5:], 16) + int(sel_cells_bg[5:], 16)) / 2):02X}"
1210
1305
  )
1211
1306
  else:
1212
- 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]
1213
1308
  if fill:
1214
1309
  redrawn = self.redraw_highlight(
1215
1310
  fc + 1,
@@ -1218,19 +1313,19 @@ class ColumnHeaders(tk.Canvas):
1218
1313
  self.current_height - 1,
1219
1314
  fill=fill,
1220
1315
  outline=(
1221
- self.PAR.ops.header_fg
1222
- 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
1223
1318
  else ""
1224
1319
  ),
1225
1320
  tag="hi",
1226
1321
  )
1227
1322
  elif not kwargs:
1228
1323
  if "columns" in selections and c in selections["columns"]:
1229
- txtfg = self.PAR.ops.header_selected_columns_fg
1324
+ txtfg = self.ops.header_selected_columns_fg
1230
1325
  elif "cells" in selections and c in selections["cells"]:
1231
- txtfg = self.PAR.ops.header_selected_cells_fg
1326
+ txtfg = self.ops.header_selected_cells_fg
1232
1327
  else:
1233
- txtfg = self.PAR.ops.header_fg
1328
+ txtfg = self.ops.header_fg
1234
1329
  return txtfg, redrawn
1235
1330
 
1236
1331
  def redraw_highlight(
@@ -1289,8 +1384,8 @@ class ColumnHeaders(tk.Canvas):
1289
1384
  draw_arrow: bool = True,
1290
1385
  open_: bool = False,
1291
1386
  ) -> None:
1292
- if draw_outline and self.PAR.ops.show_dropdown_borders:
1293
- 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)
1294
1389
  if draw_arrow:
1295
1390
  mod = (self.MT.header_txt_height - 1) if self.MT.header_txt_height % 2 else self.MT.header_txt_height
1296
1391
  small_mod = int(mod / 5)
@@ -1376,15 +1471,33 @@ class ColumnHeaders(tk.Canvas):
1376
1471
  t = self.create_polygon(points, fill=fill, outline=outline, tag=tag, smooth=True)
1377
1472
  self.disp_checkbox[t] = True
1378
1473
 
1379
- def configure_scrollregion(self, last_col_line_pos: float) -> None:
1380
- self.configure(
1381
- scrollregion=(
1382
- 0,
1383
- 0,
1384
- last_col_line_pos + self.PAR.ops.empty_horizontal + 2,
1385
- 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
+ )
1386
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,
1387
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
1388
1501
 
1389
1502
  def redraw_grid_and_text(
1390
1503
  self,
@@ -1397,11 +1510,11 @@ class ColumnHeaders(tk.Canvas):
1397
1510
  text_end_col: int,
1398
1511
  scrollpos_right: float,
1399
1512
  col_pos_exists: bool,
1513
+ set_scrollregion: bool,
1400
1514
  ) -> bool:
1401
- try:
1402
- self.configure_scrollregion(last_col_line_pos=last_col_line_pos)
1403
- except Exception:
1404
- return False
1515
+ if set_scrollregion:
1516
+ if not self.configure_scrollregion(last_col_line_pos=last_col_line_pos):
1517
+ return False
1405
1518
  self.hidd_text.update(self.disp_text)
1406
1519
  self.disp_text = {}
1407
1520
  self.hidd_high.update(self.disp_high)
@@ -1421,20 +1534,22 @@ class ColumnHeaders(tk.Canvas):
1421
1534
  )
1422
1535
  top = self.canvasy(0)
1423
1536
  sel_cols_bg = (
1424
- self.PAR.ops.header_selected_cells_bg
1425
- if self.PAR.ops.header_selected_cells_bg.startswith("#")
1426
- 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]
1427
1540
  )
1428
1541
  sel_cells_bg = (
1429
- self.PAR.ops.header_selected_columns_bg
1430
- if self.PAR.ops.header_selected_columns_bg.startswith("#")
1431
- 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]
1432
1545
  )
1433
- font = self.PAR.ops.header_font
1546
+ font = self.ops.header_font
1434
1547
  selections = self.get_redraw_selections(text_start_col, grid_end_col)
1435
1548
  dd_coords = self.dropdown.get_coords()
1549
+ wrap = self.ops.header_wrap
1550
+ txt_h = self.MT.header_txt_height
1436
1551
  for c in range(text_start_col, text_end_col):
1437
- draw_y = self.MT.header_first_ln_ins
1552
+ draw_y = 3
1438
1553
  cleftgridln = self.MT.col_positions[c]
1439
1554
  crightgridln = self.MT.col_positions[c + 1]
1440
1555
  datacn = c if self.MT.all_columns_displayed else self.MT.displayed_columns[c]
@@ -1452,40 +1567,40 @@ class ColumnHeaders(tk.Canvas):
1452
1567
  else:
1453
1568
  align = self.align
1454
1569
  if kwargs := self.get_cell_kwargs(datacn, key="dropdown"):
1455
- mw = crightgridln - cleftgridln - self.MT.header_txt_height - 2
1456
- if align == "w":
1457
- draw_x = cleftgridln + 3
1458
- elif align == "e":
1459
- draw_x = crightgridln - 5 - self.MT.header_txt_height
1460
- elif align == "center":
1461
- draw_x = cleftgridln + ceil((crightgridln - cleftgridln - self.MT.header_txt_height) / 2)
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)
1462
1577
  self.redraw_dropdown(
1463
1578
  cleftgridln,
1464
1579
  0,
1465
1580
  crightgridln,
1466
1581
  self.current_height - 1,
1467
- fill=fill if kwargs["state"] != "disabled" else self.PAR.ops.header_grid_fg,
1582
+ fill=fill if kwargs["state"] != "disabled" else self.ops.header_grid_fg,
1468
1583
  outline=fill,
1469
1584
  tag="dd",
1470
1585
  draw_outline=not dd_drawn,
1471
- draw_arrow=mw >= 5,
1586
+ draw_arrow=max_width >= 5,
1472
1587
  open_=dd_coords == c,
1473
1588
  )
1474
1589
  else:
1475
- mw = crightgridln - cleftgridln - 1
1476
- if align == "w":
1477
- draw_x = cleftgridln + 3
1478
- elif align == "e":
1479
- draw_x = crightgridln - 3
1480
- elif align == "center":
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"):
1481
1596
  draw_x = cleftgridln + floor((crightgridln - cleftgridln) / 2)
1482
- if (kwargs := self.get_cell_kwargs(datacn, key="checkbox")) and mw > self.MT.header_txt_height + 1:
1483
- box_w = self.MT.header_txt_height + 1
1484
- 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"):
1485
1600
  draw_x += box_w + 3
1486
- elif align == "center":
1601
+ elif align.endswith("n"):
1487
1602
  draw_x += ceil(box_w / 2) + 1
1488
- mw -= box_w + 3
1603
+ max_width -= box_w + 4
1489
1604
  try:
1490
1605
  draw_check = (
1491
1606
  self.MT._headers[datacn]
@@ -1497,95 +1612,67 @@ class ColumnHeaders(tk.Canvas):
1497
1612
  self.redraw_checkbox(
1498
1613
  cleftgridln + 2,
1499
1614
  2,
1500
- cleftgridln + self.MT.header_txt_height + 3,
1501
- self.MT.header_txt_height + 3,
1502
- 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,
1503
1618
  outline="",
1504
1619
  tag="cb",
1505
1620
  draw_check=draw_check,
1506
1621
  )
1507
1622
  if (
1508
- mw < self.MT.header_txt_width
1509
- or (align == "w" and draw_x > scrollpos_right)
1510
- or (align == "e" and cleftgridln + 5 > scrollpos_right)
1511
- or (align == "center" and cleftgridln + 5 > scrollpos_right)
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)
1512
1627
  ):
1513
1628
  continue
1514
- if not (lines := self.get_valid_cell_data_as_str(datacn, fix=False)):
1629
+ text = self.get_valid_cell_data_as_str(datacn, fix=False)
1630
+ if not text:
1515
1631
  continue
1516
- lines = lines.split("\n")
1517
- for txt in islice(
1518
- lines,
1519
- self.lines_start_at if self.lines_start_at < len(lines) else len(lines) - 1,
1520
- None,
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,
1521
1640
  ):
1522
- if draw_y > top:
1523
- if self.hidd_text:
1524
- iid, showing = self.hidd_text.popitem()
1525
- self.coords(iid, draw_x, draw_y)
1526
- if showing:
1527
- self.itemconfig(
1528
- iid,
1529
- text=txt,
1530
- fill=fill,
1531
- font=font,
1532
- anchor=align,
1533
- )
1534
- else:
1535
- self.itemconfig(
1536
- iid,
1537
- text=txt,
1538
- fill=fill,
1539
- font=font,
1540
- anchor=align,
1541
- state="normal",
1542
- )
1543
- self.tag_raise(iid)
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
+ )
1544
1652
  else:
1545
- iid = self.create_text(
1546
- draw_x,
1547
- draw_y,
1548
- text=txt,
1653
+ self.itemconfig(
1654
+ iid,
1655
+ text=text,
1549
1656
  fill=fill,
1550
1657
  font=font,
1551
1658
  anchor=align,
1552
- tag="t",
1659
+ state="normal",
1553
1660
  )
1554
- self.disp_text[iid] = True
1555
- wd = self.bbox(iid)
1556
- if (wd := wd[2] - wd[0]) > mw:
1557
- if align == "w":
1558
- txt = txt[: int(len(txt) * (mw / wd))]
1559
- self.itemconfig(iid, text=txt)
1560
- wd = self.bbox(iid)
1561
- while wd[2] - wd[0] > mw:
1562
- txt = txt[:-1]
1563
- self.itemconfig(iid, text=txt)
1564
- wd = self.bbox(iid)
1565
- elif align == "e":
1566
- txt = txt[len(txt) - int(len(txt) * (mw / wd)) :]
1567
- self.itemconfig(iid, text=txt)
1568
- wd = self.bbox(iid)
1569
- while wd[2] - wd[0] > mw:
1570
- txt = txt[1:]
1571
- self.itemconfig(iid, text=txt)
1572
- wd = self.bbox(iid)
1573
- elif align == "center":
1574
- self.c_align_cyc = cycle(self.centre_alignment_text_mod_indexes)
1575
- tmod = ceil((len(txt) - int(len(txt) * (mw / wd))) / 2)
1576
- txt = txt[tmod - 1 : -tmod]
1577
- self.itemconfig(iid, text=txt)
1578
- wd = self.bbox(iid)
1579
- while wd[2] - wd[0] > mw:
1580
- txt = txt[next(self.c_align_cyc)]
1581
- self.itemconfig(iid, text=txt)
1582
- wd = self.bbox(iid)
1583
- self.coords(iid, draw_x, draw_y)
1584
- draw_y += self.MT.header_xtra_lines_increment
1585
- if draw_y - 1 > self.current_height:
1586
- break
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
1587
1674
  yend = self.current_height - 5
1588
- if (self.PAR.ops.show_vertical_grid or self.width_resizing_enabled) and col_pos_exists:
1675
+ if (self.ops.show_vertical_grid or self.width_resizing_enabled) and col_pos_exists:
1589
1676
  points = [
1590
1677
  x_stop - 1,
1591
1678
  self.current_height - 1,
@@ -1610,7 +1697,7 @@ class ColumnHeaders(tk.Canvas):
1610
1697
  -1,
1611
1698
  )
1612
1699
  )
1613
- self.redraw_gridline(points=points, fill=self.PAR.ops.header_grid_fg, width=1, tag="v")
1700
+ self.redraw_gridline(points=points, fill=self.ops.header_grid_fg, width=1, tag="v")
1614
1701
  for dct in (self.hidd_text, self.hidd_high, self.hidd_grid, self.hidd_dropdown, self.hidd_checkbox):
1615
1702
  for iid, showing in dct.items():
1616
1703
  if showing:
@@ -1696,7 +1783,7 @@ class ColumnHeaders(tk.Canvas):
1696
1783
  return False
1697
1784
  else:
1698
1785
  text = text if isinstance(text, str) else f"{text}"
1699
- if self.PAR.ops.cell_auto_resize_enabled:
1786
+ if self.ops.cell_auto_resize_enabled:
1700
1787
  if self.height_resizing_enabled:
1701
1788
  self.set_height_of_header_to_text(text)
1702
1789
  self.set_col_width_run_binding(c)
@@ -1705,7 +1792,7 @@ class ColumnHeaders(tk.Canvas):
1705
1792
  return False
1706
1793
  self.hide_text_editor()
1707
1794
  if not self.MT.see(r=0, c=c, keep_yscroll=True, check_cell_visibility=True):
1708
- self.MT.refresh()
1795
+ self.MT.main_table_redraw_grid_and_text(True, True)
1709
1796
  x = self.MT.col_positions[c] + 1
1710
1797
  y = 0
1711
1798
  w = self.MT.col_positions[c + 1] - x
@@ -1713,24 +1800,24 @@ class ColumnHeaders(tk.Canvas):
1713
1800
  kwargs = {
1714
1801
  "menu_kwargs": DotDict(
1715
1802
  {
1716
- "font": self.PAR.ops.table_font,
1717
- "foreground": self.PAR.ops.popup_menu_fg,
1718
- "background": self.PAR.ops.popup_menu_bg,
1719
- "activebackground": self.PAR.ops.popup_menu_highlight_bg,
1720
- "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,
1721
1808
  }
1722
1809
  ),
1723
- "sheet_ops": self.PAR.ops,
1724
- "border_color": self.PAR.ops.header_selected_columns_bg,
1810
+ "sheet_ops": self.ops,
1811
+ "border_color": self.ops.header_selected_columns_bg,
1725
1812
  "text": text,
1726
1813
  "state": state,
1727
1814
  "width": w,
1728
1815
  "height": h,
1729
1816
  "show_border": True,
1730
- "bg": self.PAR.ops.header_editor_bg,
1731
- "fg": self.PAR.ops.header_editor_fg,
1732
- "select_bg": self.PAR.ops.header_editor_select_bg,
1733
- "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,
1734
1821
  "align": self.get_cell_align(c),
1735
1822
  "c": c,
1736
1823
  }
@@ -1790,7 +1877,7 @@ class ColumnHeaders(tk.Canvas):
1790
1877
  > curr_height
1791
1878
  ):
1792
1879
  c = self.text_editor.column
1793
- new_height = curr_height + self.MT.header_xtra_lines_increment
1880
+ new_height = curr_height + self.MT.header_txt_height
1794
1881
  space_bot = self.MT.get_space_bot(0)
1795
1882
  if new_height > space_bot:
1796
1883
  new_height = space_bot
@@ -1813,7 +1900,7 @@ class ColumnHeaders(tk.Canvas):
1813
1900
  height=self.current_height,
1814
1901
  width=self.MT.col_positions[c + 1] - self.MT.col_positions[c] + 1,
1815
1902
  )
1816
- self.text_editor.tktext.config(font=self.PAR.ops.header_font)
1903
+ self.text_editor.tktext.config(font=self.ops.header_font)
1817
1904
  self.coords(
1818
1905
  self.text_editor.canvas_id,
1819
1906
  self.MT.col_positions[c],
@@ -1921,9 +2008,7 @@ class ColumnHeaders(tk.Canvas):
1921
2008
  for i, v in enumerate(self.get_cell_kwargs(datacn, key="dropdown")["values"]):
1922
2009
  v_numlines = len(v.split("\n") if isinstance(v, str) else f"{v}".split("\n"))
1923
2010
  if v_numlines > 1:
1924
- win_h += (
1925
- self.MT.header_first_ln_ins + (v_numlines * self.MT.header_xtra_lines_increment) + 5
1926
- ) # end of cell
2011
+ win_h += self.MT.header_txt_height + (v_numlines * self.MT.header_txt_height) + 5 # end of cell
1927
2012
  else:
1928
2013
  win_h += self.MT.min_header_height
1929
2014
  if i == 5:
@@ -1977,15 +2062,15 @@ class ColumnHeaders(tk.Canvas):
1977
2062
  reset_kwargs = {
1978
2063
  "r": 0,
1979
2064
  "c": c,
1980
- "bg": self.PAR.ops.header_editor_bg,
1981
- "fg": self.PAR.ops.header_editor_fg,
1982
- "select_bg": self.PAR.ops.header_editor_select_bg,
1983
- "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,
1984
2069
  "width": win_w,
1985
2070
  "height": win_h,
1986
- "font": self.PAR.ops.header_font,
1987
- "ops": self.PAR.ops,
1988
- "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,
1989
2074
  "align": self.get_cell_align(c),
1990
2075
  "values": kwargs["values"],
1991
2076
  "search_function": kwargs["search_function"],
@@ -2133,7 +2218,7 @@ class ColumnHeaders(tk.Canvas):
2133
2218
  self.MT.undo_stack.append(stored_event_dict(event_data))
2134
2219
  self.set_cell_data(datacn=datacn, value=value)
2135
2220
  edited = True
2136
- 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:
2137
2222
  if self.height_resizing_enabled:
2138
2223
  self.set_height_of_header_to_text(self.get_valid_cell_data_as_str(datacn, fix=False))
2139
2224
  self.set_col_width_run_binding(c)
@@ -2206,11 +2291,15 @@ class ColumnHeaders(tk.Canvas):
2206
2291
  if fix:
2207
2292
  self.fix_header(datacn)
2208
2293
  try:
2209
- 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}"
2210
2299
  except Exception:
2211
2300
  value = ""
2212
- if not value and self.PAR.ops.show_default_header_for_empty:
2213
- 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)
2214
2303
  return value
2215
2304
 
2216
2305
  def get_value_for_empty_cell(self, datacn: int, c_ops: bool = True) -> object: