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/__init__.py +11 -11
- tksheet/column_headers.py +328 -239
- tksheet/constants.py +13 -0
- tksheet/functions.py +194 -11
- tksheet/main_table.py +957 -583
- tksheet/other_classes.py +12 -8
- tksheet/row_index.py +830 -259
- tksheet/sheet.py +465 -589
- tksheet/sheet_options.py +44 -1
- tksheet/sorting.py +287 -0
- tksheet/text_editor.py +2 -6
- tksheet/{types.py → tksheet_types.py} +10 -1
- {tksheet-7.3.4.dist-info → tksheet-7.4.1.dist-info}/METADATA +13 -16
- tksheet-7.4.1.dist-info/RECORD +22 -0
- tksheet-7.3.4.dist-info/RECORD +0 -21
- {tksheet-7.3.4.dist-info → tksheet-7.4.1.dist-info}/LICENSE.txt +0 -0
- {tksheet-7.3.4.dist-info → tksheet-7.4.1.dist-info}/WHEEL +0 -0
- {tksheet-7.3.4.dist-info → tksheet-7.4.1.dist-info}/top_level.txt +0 -0
tksheet/row_index.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 Generator, Hashable, Sequence
|
5
|
+
from collections.abc import Callable, Generator, Hashable, Sequence
|
6
6
|
from functools import partial
|
7
7
|
from itertools import chain, cycle, islice, repeat
|
8
8
|
from math import ceil, floor
|
@@ -10,39 +10,53 @@ from operator import itemgetter
|
|
10
10
|
from typing import Literal
|
11
11
|
|
12
12
|
from .colors import color_map
|
13
|
-
from .constants import
|
13
|
+
from .constants import (
|
14
|
+
_test_str,
|
15
|
+
rc_binding,
|
16
|
+
text_editor_close_bindings,
|
17
|
+
text_editor_newline_bindings,
|
18
|
+
text_editor_to_unbind,
|
19
|
+
)
|
14
20
|
from .formatters import is_bool_like, try_to_bool
|
15
21
|
from .functions import (
|
16
22
|
consecutive_chunks,
|
17
23
|
consecutive_ranges,
|
24
|
+
del_placeholder_dict_key,
|
18
25
|
event_dict,
|
19
26
|
event_has_char_key,
|
20
27
|
event_opens_dropdown_or_checkbox,
|
21
28
|
get_n2a,
|
29
|
+
get_new_indexes,
|
22
30
|
int_x_tuple,
|
23
31
|
is_contiguous,
|
32
|
+
mod_event_val,
|
24
33
|
new_tk_event,
|
25
34
|
num2alpha,
|
26
35
|
rounded_box_coords,
|
27
36
|
stored_event_dict,
|
37
|
+
try_b_index,
|
28
38
|
try_binding,
|
39
|
+
wrap_text,
|
29
40
|
)
|
30
|
-
from .other_classes import DotDict, DraggedRowColumn, DropdownStorage, Node, TextEditorStorage
|
41
|
+
from .other_classes import DotDict, DraggedRowColumn, DropdownStorage, EventDataDict, Node, TextEditorStorage
|
42
|
+
from .sorting import sort_columns_by_row, sort_row
|
31
43
|
from .text_editor import TextEditor
|
32
|
-
from .
|
44
|
+
from .tksheet_types import AnyIter
|
33
45
|
|
34
46
|
|
35
47
|
class RowIndex(tk.Canvas):
|
36
|
-
def __init__(self,
|
48
|
+
def __init__(self, parent, **kwargs):
|
37
49
|
super().__init__(
|
38
|
-
|
39
|
-
background=
|
50
|
+
parent,
|
51
|
+
background=parent.ops.index_bg,
|
40
52
|
highlightthickness=0,
|
41
53
|
)
|
42
|
-
self.PAR =
|
54
|
+
self.PAR = parent
|
55
|
+
self.ops = self.PAR.ops
|
43
56
|
self.MT = None # is set from within MainTable() __init__
|
44
57
|
self.CH = None # is set from within MainTable() __init__
|
45
58
|
self.TL = None # is set from within TopLeftRectangle() __init__
|
59
|
+
self.new_iid_ctr = -1
|
46
60
|
self.current_width = None
|
47
61
|
self.popup_menu_loc = None
|
48
62
|
self.extra_begin_edit_cell_func = None
|
@@ -63,6 +77,8 @@ class RowIndex(tk.Canvas):
|
|
63
77
|
self.drag_selection_binding_func = None
|
64
78
|
self.ri_extra_begin_drag_drop_func = None
|
65
79
|
self.ri_extra_end_drag_drop_func = None
|
80
|
+
self.ri_extra_begin_sort_cols_func = None
|
81
|
+
self.ri_extra_end_sort_cols_func = None
|
66
82
|
self.extra_double_b1_func = None
|
67
83
|
self.row_height_resize_func = None
|
68
84
|
self.cell_options = {}
|
@@ -134,18 +150,6 @@ class RowIndex(tk.Canvas):
|
|
134
150
|
self.unbind("<Double-Button-1>")
|
135
151
|
self.unbind(rc_binding)
|
136
152
|
|
137
|
-
def tree_reset(self) -> None:
|
138
|
-
# treeview mode
|
139
|
-
self.tree = {}
|
140
|
-
self.tree_open_ids = set()
|
141
|
-
self.tree_rns = {}
|
142
|
-
if self.MT:
|
143
|
-
self.MT.displayed_rows = []
|
144
|
-
self.MT._row_index = []
|
145
|
-
self.MT.data = []
|
146
|
-
self.MT.row_positions = [0]
|
147
|
-
self.MT.saved_row_heights = {}
|
148
|
-
|
149
153
|
def set_width(self, new_width: int, set_TL: bool = False, recreate_selection_boxes: bool = True) -> None:
|
150
154
|
try:
|
151
155
|
self.config(width=new_width)
|
@@ -328,9 +332,9 @@ class RowIndex(tk.Canvas):
|
|
328
332
|
if (
|
329
333
|
self.width_resizing_enabled
|
330
334
|
and not mouse_over_resize
|
331
|
-
and self.
|
335
|
+
and self.ops.auto_resize_row_index is not True
|
332
336
|
and not (
|
333
|
-
self.
|
337
|
+
self.ops.auto_resize_row_index == "empty"
|
334
338
|
and not isinstance(self.MT._row_index, int)
|
335
339
|
and not self.MT._row_index
|
336
340
|
)
|
@@ -385,7 +389,7 @@ class RowIndex(tk.Canvas):
|
|
385
389
|
)
|
386
390
|
elif self.width_resizing_enabled and self.rsz_h is None and self.rsz_w is True:
|
387
391
|
self.set_width_of_index_to_text()
|
388
|
-
elif (self.row_selection_enabled or self.
|
392
|
+
elif (self.row_selection_enabled or self.ops.treeview) and self.rsz_h is None and self.rsz_w is None:
|
389
393
|
r = self.MT.identify_row(y=event.y)
|
390
394
|
if r < len(self.MT.row_positions) - 1:
|
391
395
|
iid = self.event_over_tree_arrow(r, self.canvasy(event.y), event.x)
|
@@ -435,21 +439,21 @@ class RowIndex(tk.Canvas):
|
|
435
439
|
self.current_width,
|
436
440
|
y,
|
437
441
|
width=1,
|
438
|
-
fill=self.
|
442
|
+
fill=self.ops.resizing_line_fg,
|
439
443
|
tag=("rh", "rhl"),
|
440
444
|
)
|
441
|
-
self.MT.create_resize_line(x1, y, x2, y, width=1, fill=self.
|
445
|
+
self.MT.create_resize_line(x1, y, x2, y, width=1, fill=self.ops.resizing_line_fg, tag=("rh", "rhl"))
|
442
446
|
self.create_resize_line(
|
443
447
|
0,
|
444
448
|
line2y,
|
445
449
|
self.current_width,
|
446
450
|
line2y,
|
447
451
|
width=1,
|
448
|
-
fill=self.
|
452
|
+
fill=self.ops.resizing_line_fg,
|
449
453
|
tag=("rh", "rhl2"),
|
450
454
|
)
|
451
455
|
self.MT.create_resize_line(
|
452
|
-
x1, line2y, x2, line2y, width=1, fill=self.
|
456
|
+
x1, line2y, x2, line2y, width=1, fill=self.ops.resizing_line_fg, tag=("rh", "rhl2")
|
453
457
|
)
|
454
458
|
elif self.width_resizing_enabled and self.rsz_h is None and self.rsz_w is True:
|
455
459
|
self.currently_resizing_width = True
|
@@ -481,7 +485,7 @@ class RowIndex(tk.Canvas):
|
|
481
485
|
if self.height_resizing_enabled and self.rsz_h is not None and self.currently_resizing_height:
|
482
486
|
y = self.canvasy(event.y)
|
483
487
|
size = y - self.MT.row_positions[self.rsz_h - 1]
|
484
|
-
if size >= self.MT.min_row_height and size < self.
|
488
|
+
if size >= self.MT.min_row_height and size < self.ops.max_row_height:
|
485
489
|
self.hide_resize_and_ctrl_lines(ctrl_lines=False)
|
486
490
|
line2y = self.MT.row_positions[self.rsz_h - 1]
|
487
491
|
self.create_resize_line(
|
@@ -490,17 +494,17 @@ class RowIndex(tk.Canvas):
|
|
490
494
|
self.current_width,
|
491
495
|
y,
|
492
496
|
width=1,
|
493
|
-
fill=self.
|
497
|
+
fill=self.ops.resizing_line_fg,
|
494
498
|
tag=("rh", "rhl"),
|
495
499
|
)
|
496
|
-
self.MT.create_resize_line(x1, y, x2, y, width=1, fill=self.
|
500
|
+
self.MT.create_resize_line(x1, y, x2, y, width=1, fill=self.ops.resizing_line_fg, tag=("rh", "rhl"))
|
497
501
|
self.create_resize_line(
|
498
502
|
0,
|
499
503
|
line2y,
|
500
504
|
self.current_width,
|
501
505
|
line2y,
|
502
506
|
width=1,
|
503
|
-
fill=self.
|
507
|
+
fill=self.ops.resizing_line_fg,
|
504
508
|
tag=("rh", "rhl2"),
|
505
509
|
)
|
506
510
|
self.MT.create_resize_line(
|
@@ -509,19 +513,19 @@ class RowIndex(tk.Canvas):
|
|
509
513
|
x2,
|
510
514
|
line2y,
|
511
515
|
width=1,
|
512
|
-
fill=self.
|
516
|
+
fill=self.ops.resizing_line_fg,
|
513
517
|
tag=("rh", "rhl2"),
|
514
518
|
)
|
515
519
|
self.drag_height_resize()
|
516
520
|
elif self.width_resizing_enabled and self.rsz_w is not None and self.currently_resizing_width:
|
517
521
|
evx = event.x
|
518
522
|
if evx > self.current_width:
|
519
|
-
if evx > self.
|
520
|
-
evx = int(self.
|
523
|
+
if evx > self.ops.max_index_width:
|
524
|
+
evx = int(self.ops.max_index_width)
|
521
525
|
self.drag_width_resize(evx)
|
522
526
|
else:
|
523
|
-
if evx < self.
|
524
|
-
evx = self.
|
527
|
+
if evx < self.ops.min_column_width:
|
528
|
+
evx = self.ops.min_column_width
|
525
529
|
self.drag_width_resize(evx)
|
526
530
|
elif (
|
527
531
|
self.drag_and_drop_enabled
|
@@ -678,16 +682,16 @@ class RowIndex(tk.Canvas):
|
|
678
682
|
self.current_width,
|
679
683
|
ypos,
|
680
684
|
width=3,
|
681
|
-
fill=self.
|
685
|
+
fill=self.ops.drag_and_drop_bg,
|
682
686
|
tag="move_rows",
|
683
687
|
)
|
684
|
-
self.MT.create_resize_line(x1, ypos, x2, ypos, width=3, fill=self.
|
688
|
+
self.MT.create_resize_line(x1, ypos, x2, ypos, width=3, fill=self.ops.drag_and_drop_bg, tag="move_rows")
|
685
689
|
for chunk in consecutive_chunks(rows):
|
686
690
|
self.MT.show_ctrl_outline(
|
687
691
|
start_cell=(0, chunk[0]),
|
688
692
|
end_cell=(len(self.MT.col_positions) - 1, chunk[-1] + 1),
|
689
693
|
dash=(),
|
690
|
-
outline=self.
|
694
|
+
outline=self.ops.drag_and_drop_bg,
|
691
695
|
delete_on_timer=False,
|
692
696
|
)
|
693
697
|
|
@@ -751,8 +755,8 @@ class RowIndex(tk.Canvas):
|
|
751
755
|
size = new_row_pos - self.MT.row_positions[self.rsz_h - 1]
|
752
756
|
if size < self.MT.min_row_height:
|
753
757
|
new_row_pos = ceil(self.MT.row_positions[self.rsz_h - 1] + self.MT.min_row_height)
|
754
|
-
elif size > self.
|
755
|
-
new_row_pos = floor(self.MT.row_positions[self.rsz_h - 1] + self.
|
758
|
+
elif size > self.ops.max_row_height:
|
759
|
+
new_row_pos = floor(self.MT.row_positions[self.rsz_h - 1] + self.ops.max_row_height)
|
756
760
|
increment = new_row_pos - self.MT.row_positions[self.rsz_h]
|
757
761
|
self.MT.row_positions[self.rsz_h + 1 :] = [
|
758
762
|
e + increment for e in islice(self.MT.row_positions, self.rsz_h + 1, None)
|
@@ -761,7 +765,7 @@ class RowIndex(tk.Canvas):
|
|
761
765
|
new_height = self.MT.row_positions[self.rsz_h] - self.MT.row_positions[self.rsz_h - 1]
|
762
766
|
self.MT.allow_auto_resize_rows = False
|
763
767
|
self.MT.recreate_all_selection_boxes()
|
764
|
-
self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
|
768
|
+
self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True, set_scrollregion=False)
|
765
769
|
if self.row_height_resize_func is not None and old_height != new_height:
|
766
770
|
self.row_height_resize_func(
|
767
771
|
event_dict(
|
@@ -787,6 +791,7 @@ class RowIndex(tk.Canvas):
|
|
787
791
|
if self.height_resizing_enabled and self.rsz_h is not None and self.currently_resizing_height:
|
788
792
|
self.drag_height_resize()
|
789
793
|
self.hide_resize_and_ctrl_lines(ctrl_lines=False)
|
794
|
+
self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
|
790
795
|
elif (
|
791
796
|
self.drag_and_drop_enabled
|
792
797
|
and self.MT.anything_selected(exclude_cells=True, exclude_columns=True)
|
@@ -812,33 +817,28 @@ class RowIndex(tk.Canvas):
|
|
812
817
|
r += 1
|
813
818
|
if r > len(self.MT.row_positions) - 1:
|
814
819
|
r = len(self.MT.row_positions) - 1
|
815
|
-
event_data =
|
816
|
-
|
817
|
-
sheet=self.PAR.name,
|
818
|
-
widget=self,
|
819
|
-
boxes=self.MT.get_boxes(),
|
820
|
-
selected=self.MT.selected,
|
821
|
-
value=r,
|
822
|
-
)
|
820
|
+
event_data = self.MT.new_event_dict("move_rows", state=True)
|
821
|
+
event_data["value"] = r
|
823
822
|
if try_binding(self.ri_extra_begin_drag_drop_func, event_data, "begin_move_rows"):
|
824
823
|
data_new_idxs, disp_new_idxs, event_data = self.MT.move_rows_adjust_options_dict(
|
825
824
|
*self.MT.get_args_for_move_rows(
|
826
825
|
move_to=r,
|
827
826
|
to_move=self.dragged_row.to_move,
|
828
827
|
),
|
829
|
-
move_data=self.
|
830
|
-
move_heights=self.
|
828
|
+
move_data=self.ops.row_drag_and_drop_perform,
|
829
|
+
move_heights=self.ops.row_drag_and_drop_perform,
|
831
830
|
event_data=event_data,
|
832
831
|
)
|
833
|
-
|
834
|
-
"
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
self.MT.
|
839
|
-
|
840
|
-
|
841
|
-
|
832
|
+
if data_new_idxs and disp_new_idxs:
|
833
|
+
event_data["moved"]["rows"] = {
|
834
|
+
"data": data_new_idxs,
|
835
|
+
"displayed": disp_new_idxs,
|
836
|
+
}
|
837
|
+
if self.MT.undo_enabled:
|
838
|
+
self.MT.undo_stack.append(stored_event_dict(event_data))
|
839
|
+
self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
|
840
|
+
try_binding(self.ri_extra_end_drag_drop_func, event_data, "end_move_rows")
|
841
|
+
self.MT.sheet_modified(event_data)
|
842
842
|
elif self.b1_pressed_loc is not None and self.rsz_w is None and self.rsz_h is None:
|
843
843
|
r = self.MT.identify_row(y=event.y)
|
844
844
|
if (
|
@@ -875,7 +875,7 @@ class RowIndex(tk.Canvas):
|
|
875
875
|
canvasy: float,
|
876
876
|
eventx: int,
|
877
877
|
) -> bool:
|
878
|
-
if self.
|
878
|
+
if self.ops.treeview and (
|
879
879
|
canvasy < self.MT.row_positions[r] + self.MT.index_txt_height + 5
|
880
880
|
and isinstance(self.MT._row_index, list)
|
881
881
|
and (datarn := self.MT.datarn(r)) < len(self.MT._row_index)
|
@@ -886,6 +886,96 @@ class RowIndex(tk.Canvas):
|
|
886
886
|
return iid
|
887
887
|
return None
|
888
888
|
|
889
|
+
def _sort_rows(
|
890
|
+
self,
|
891
|
+
event: tk.Event | None = None,
|
892
|
+
rows: AnyIter[int] | None = None,
|
893
|
+
reverse: bool = False,
|
894
|
+
validation: bool = True,
|
895
|
+
key: Callable | None = None,
|
896
|
+
undo: bool = True,
|
897
|
+
) -> EventDataDict:
|
898
|
+
if rows is None:
|
899
|
+
rows = self.MT.get_selected_rows()
|
900
|
+
if not rows:
|
901
|
+
rows = list(range(0, len(self.MT.row_positions) - 1))
|
902
|
+
event_data = self.MT.new_event_dict("edit_table")
|
903
|
+
try_binding(self.MT.extra_begin_sort_cells_func, event_data)
|
904
|
+
for r in rows:
|
905
|
+
datarn = self.MT.datarn(r)
|
906
|
+
for c, val in enumerate(sort_row(self.MT.data[datarn], reverse=reverse, key=key)):
|
907
|
+
if (
|
908
|
+
not self.MT.edit_validation_func
|
909
|
+
or not validation
|
910
|
+
or (
|
911
|
+
self.MT.edit_validation_func
|
912
|
+
and (val := self.MT.edit_validation_func(mod_event_val(event_data, val, (datarn, c))))
|
913
|
+
is not None
|
914
|
+
)
|
915
|
+
):
|
916
|
+
event_data = self.MT.event_data_set_cell(
|
917
|
+
datarn=datarn,
|
918
|
+
datacn=c,
|
919
|
+
value=val,
|
920
|
+
event_data=event_data,
|
921
|
+
)
|
922
|
+
if event_data["cells"]["table"]:
|
923
|
+
if undo and self.MT.undo_enabled:
|
924
|
+
self.MT.undo_stack.append(stored_event_dict(event_data))
|
925
|
+
try_binding(self.MT.extra_end_sort_cells_func, event_data, "end_edit_table")
|
926
|
+
self.MT.sheet_modified(event_data)
|
927
|
+
self.PAR.emit_event("<<SheetModified>>", event_data)
|
928
|
+
self.MT.refresh()
|
929
|
+
return event_data
|
930
|
+
|
931
|
+
def _sort_columns_by_row(
|
932
|
+
self,
|
933
|
+
event: tk.Event | None = None,
|
934
|
+
row: int | None = None,
|
935
|
+
reverse: bool = False,
|
936
|
+
key: Callable | None = None,
|
937
|
+
undo: bool = True,
|
938
|
+
) -> EventDataDict:
|
939
|
+
event_data = self.MT.new_event_dict("sort_columns", state=True)
|
940
|
+
if not self.MT.data:
|
941
|
+
return event_data
|
942
|
+
if row is None:
|
943
|
+
if not self.MT.selected:
|
944
|
+
return event_data
|
945
|
+
row = self.MT.selected.row
|
946
|
+
if try_binding(self.ri_extra_begin_sort_cols_func, event_data, "begin_sort_columns"):
|
947
|
+
sorted_indices, data_new_idxs = sort_columns_by_row(self.MT.data, row=row, reverse=reverse, key=key)
|
948
|
+
disp_new_idxs = {}
|
949
|
+
if self.MT.all_columns_displayed:
|
950
|
+
disp_new_idxs = data_new_idxs
|
951
|
+
else:
|
952
|
+
col_ctr = 0
|
953
|
+
# idx is the displayed index, can just do range
|
954
|
+
for old_idx in sorted_indices:
|
955
|
+
if (idx := try_b_index(self.MT.displayed_columns, old_idx)) is not None:
|
956
|
+
disp_new_idxs[idx] = col_ctr
|
957
|
+
col_ctr += 1
|
958
|
+
data_new_idxs, disp_new_idxs, _ = self.PAR.mapping_move_columns(
|
959
|
+
data_new_idxs=data_new_idxs,
|
960
|
+
disp_new_idxs=disp_new_idxs,
|
961
|
+
move_data=True,
|
962
|
+
create_selections=False,
|
963
|
+
undo=False,
|
964
|
+
emit_event=False,
|
965
|
+
redraw=True,
|
966
|
+
)
|
967
|
+
event_data["moved"]["columns"] = {
|
968
|
+
"data": data_new_idxs,
|
969
|
+
"displayed": disp_new_idxs,
|
970
|
+
}
|
971
|
+
if undo and self.MT.undo_enabled:
|
972
|
+
self.MT.undo_stack.append(stored_event_dict(event_data))
|
973
|
+
try_binding(self.ri_extra_end_sort_cols_func, event_data, "end_sort_columns")
|
974
|
+
self.MT.sheet_modified(event_data)
|
975
|
+
self.PAR.emit_event("<<SheetModified>>", event_data)
|
976
|
+
self.MT.refresh()
|
977
|
+
return event_data
|
978
|
+
|
889
979
|
def toggle_select_row(
|
890
980
|
self,
|
891
981
|
row: int,
|
@@ -974,7 +1064,7 @@ class RowIndex(tk.Canvas):
|
|
974
1064
|
y1,
|
975
1065
|
x2,
|
976
1066
|
y2,
|
977
|
-
radius=5 if self.
|
1067
|
+
radius=5 if self.ops.rounded_boxes else 0,
|
978
1068
|
)
|
979
1069
|
if isinstance(iid, int):
|
980
1070
|
self.coords(iid, coords)
|
@@ -1005,27 +1095,42 @@ class RowIndex(tk.Canvas):
|
|
1005
1095
|
def get_cell_dimensions(self, datarn: int) -> tuple[int, int]:
|
1006
1096
|
txt = self.get_valid_cell_data_as_str(datarn, fix=False)
|
1007
1097
|
if txt:
|
1008
|
-
self.MT.txt_measure_canvas.itemconfig(
|
1009
|
-
self.MT.txt_measure_canvas_text, text=txt, font=self.PAR.ops.index_font
|
1010
|
-
)
|
1098
|
+
self.MT.txt_measure_canvas.itemconfig(self.MT.txt_measure_canvas_text, text=txt, font=self.ops.index_font)
|
1011
1099
|
b = self.MT.txt_measure_canvas.bbox(self.MT.txt_measure_canvas_text)
|
1012
1100
|
w = b[2] - b[0] + 7
|
1013
1101
|
h = b[3] - b[1] + 5
|
1014
1102
|
else:
|
1015
|
-
w = self.
|
1103
|
+
w = self.ops.default_row_index_width
|
1016
1104
|
h = self.MT.min_row_height
|
1017
1105
|
if self.get_cell_kwargs(datarn, key="dropdown") or self.get_cell_kwargs(datarn, key="checkbox"):
|
1018
1106
|
w += self.MT.index_txt_height + 2
|
1019
|
-
if self.
|
1107
|
+
if self.ops.treeview:
|
1020
1108
|
if datarn in self.cell_options and "align" in self.cell_options[datarn]:
|
1021
1109
|
align = self.cell_options[datarn]["align"]
|
1022
1110
|
else:
|
1023
1111
|
align = self.align
|
1024
|
-
if align
|
1112
|
+
if align.endswith("w"):
|
1025
1113
|
w += self.MT.index_txt_height
|
1026
1114
|
w += self.get_iid_indent(self.MT._row_index[datarn].iid) + 10
|
1027
1115
|
return w, h
|
1028
1116
|
|
1117
|
+
def get_wrapped_cell_height(self, datarn: int) -> int:
|
1118
|
+
n_lines = max(
|
1119
|
+
1,
|
1120
|
+
sum(
|
1121
|
+
1
|
1122
|
+
for _ in wrap_text(
|
1123
|
+
text=self.get_valid_cell_data_as_str(datarn, fix=False),
|
1124
|
+
max_width=self.current_width,
|
1125
|
+
max_lines=float("inf"),
|
1126
|
+
char_width_fn=self.wrap_get_char_w,
|
1127
|
+
widths=self.MT.char_widths[self.index_font],
|
1128
|
+
wrap=self.ops.index_wrap,
|
1129
|
+
)
|
1130
|
+
),
|
1131
|
+
)
|
1132
|
+
return 3 + (n_lines * self.MT.index_txt_height)
|
1133
|
+
|
1029
1134
|
def get_row_text_height(
|
1030
1135
|
self,
|
1031
1136
|
row: int,
|
@@ -1035,7 +1140,7 @@ class RowIndex(tk.Canvas):
|
|
1035
1140
|
h = self.MT.min_row_height
|
1036
1141
|
datarn = row if self.MT.all_rows_displayed else self.MT.displayed_rows[row]
|
1037
1142
|
# index
|
1038
|
-
|
1143
|
+
ih = self.get_wrapped_cell_height(datarn)
|
1039
1144
|
# table
|
1040
1145
|
if self.MT.data:
|
1041
1146
|
if self.MT.all_columns_displayed:
|
@@ -1052,20 +1157,22 @@ class RowIndex(tk.Canvas):
|
|
1052
1157
|
else:
|
1053
1158
|
start_col, end_col = 0, len(self.MT.displayed_columns)
|
1054
1159
|
iterable = self.MT.displayed_columns[start_col:end_col]
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1160
|
+
h = max(
|
1161
|
+
h,
|
1162
|
+
max(
|
1163
|
+
self.MT.get_wrapped_cell_height(
|
1164
|
+
datarn,
|
1165
|
+
datacn,
|
1166
|
+
)
|
1167
|
+
for datacn in iterable
|
1168
|
+
),
|
1169
|
+
)
|
1170
|
+
self.MT.cells_cache = None
|
1171
|
+
h = max(h, ih)
|
1062
1172
|
if only_if_too_small and h < self.MT.row_positions[row + 1] - self.MT.row_positions[row]:
|
1063
1173
|
return self.MT.row_positions[row + 1] - self.MT.row_positions[row]
|
1064
|
-
|
1065
|
-
|
1066
|
-
elif h > self.PAR.ops.max_row_height:
|
1067
|
-
h = int(self.PAR.ops.max_row_height)
|
1068
|
-
return h
|
1174
|
+
else:
|
1175
|
+
return max(int(min(h, self.ops.max_row_height)), self.MT.min_row_height)
|
1069
1176
|
|
1070
1177
|
def set_row_height(
|
1071
1178
|
self,
|
@@ -1079,8 +1186,8 @@ class RowIndex(tk.Canvas):
|
|
1079
1186
|
height = self.get_row_text_height(row=row, visible_only=visible_only)
|
1080
1187
|
if height < self.MT.min_row_height:
|
1081
1188
|
height = int(self.MT.min_row_height)
|
1082
|
-
elif height > self.
|
1083
|
-
height = int(self.
|
1189
|
+
elif height > self.ops.max_row_height:
|
1190
|
+
height = int(self.ops.max_row_height)
|
1084
1191
|
if only_if_too_small and height <= self.MT.row_positions[row + 1] - self.MT.row_positions[row]:
|
1085
1192
|
return self.MT.row_positions[row + 1] - self.MT.row_positions[row]
|
1086
1193
|
new_row_pos = self.MT.row_positions[row] + height
|
@@ -1098,7 +1205,7 @@ class RowIndex(tk.Canvas):
|
|
1098
1205
|
only_rows: AnyIter[int] | None = None,
|
1099
1206
|
) -> int:
|
1100
1207
|
self.fix_index()
|
1101
|
-
w = self.
|
1208
|
+
w = self.ops.default_row_index_width
|
1102
1209
|
if (not self.MT._row_index and isinstance(self.MT._row_index, list)) or (
|
1103
1210
|
isinstance(self.MT._row_index, int) and self.MT._row_index >= len(self.MT.data)
|
1104
1211
|
):
|
@@ -1114,8 +1221,8 @@ class RowIndex(tk.Canvas):
|
|
1114
1221
|
iterable = self.MT.displayed_rows
|
1115
1222
|
if (new_w := max(map(itemgetter(0), map(self.get_cell_dimensions, iterable)), default=w)) > w:
|
1116
1223
|
w = new_w
|
1117
|
-
if w > self.
|
1118
|
-
w = int(self.
|
1224
|
+
if w > self.ops.max_index_width:
|
1225
|
+
w = int(self.ops.max_index_width)
|
1119
1226
|
return w
|
1120
1227
|
|
1121
1228
|
def set_width_of_index_to_text(
|
@@ -1124,7 +1231,7 @@ class RowIndex(tk.Canvas):
|
|
1124
1231
|
only_rows: list = [],
|
1125
1232
|
) -> int:
|
1126
1233
|
self.fix_index()
|
1127
|
-
w = self.
|
1234
|
+
w = self.ops.default_row_index_width
|
1128
1235
|
if (text is None and isinstance(self.MT._row_index, list) and not self.MT._row_index) or (
|
1129
1236
|
isinstance(self.MT._row_index, int) and self.MT._row_index >= len(self.MT.data)
|
1130
1237
|
):
|
@@ -1136,8 +1243,8 @@ class RowIndex(tk.Canvas):
|
|
1136
1243
|
w = tw
|
1137
1244
|
elif text is None:
|
1138
1245
|
w = self.get_index_text_width(only_rows=only_rows)
|
1139
|
-
if w > self.
|
1140
|
-
w = int(self.
|
1246
|
+
if w > self.ops.max_index_width:
|
1247
|
+
w = int(self.ops.max_index_width)
|
1141
1248
|
self.set_width(w, set_TL=True)
|
1142
1249
|
self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
|
1143
1250
|
return w
|
@@ -1166,15 +1273,15 @@ class RowIndex(tk.Canvas):
|
|
1166
1273
|
|
1167
1274
|
def auto_set_index_width(self, end_row: int, only_rows: list) -> bool:
|
1168
1275
|
if not isinstance(self.MT._row_index, int) and not self.MT._row_index:
|
1169
|
-
if self.
|
1170
|
-
new_w = self.MT.get_txt_w(f"{num2alpha(end_row)}") + 20
|
1171
|
-
elif self.
|
1172
|
-
new_w = self.MT.get_txt_w(f"{end_row}") + 20
|
1173
|
-
elif self.
|
1174
|
-
new_w = self.MT.get_txt_w(f"{end_row + 1} {num2alpha(end_row)}") + 20
|
1175
|
-
elif self.
|
1276
|
+
if self.ops.default_row_index == "letters":
|
1277
|
+
new_w = self.MT.get_txt_w(f"{num2alpha(end_row)}", self.index_font) + 20
|
1278
|
+
elif self.ops.default_row_index == "numbers":
|
1279
|
+
new_w = self.MT.get_txt_w(f"{end_row}", self.index_font) + 20
|
1280
|
+
elif self.ops.default_row_index == "both":
|
1281
|
+
new_w = self.MT.get_txt_w(f"{end_row + 1} {num2alpha(end_row)}", self.index_font) + 20
|
1282
|
+
elif self.ops.default_row_index is None:
|
1176
1283
|
new_w = 20
|
1177
|
-
elif self.
|
1284
|
+
elif self.ops.auto_resize_row_index is True:
|
1178
1285
|
new_w = self.get_index_text_width(only_rows=only_rows)
|
1179
1286
|
else:
|
1180
1287
|
new_w = None
|
@@ -1205,8 +1312,8 @@ class RowIndex(tk.Canvas):
|
|
1205
1312
|
fill = color_map[fill]
|
1206
1313
|
if "rows" in selections and r in selections["rows"]:
|
1207
1314
|
txtfg = (
|
1208
|
-
self.
|
1209
|
-
if kwargs[1] is None or self.
|
1315
|
+
self.ops.index_selected_rows_fg
|
1316
|
+
if kwargs[1] is None or self.ops.display_selected_fg_over_highlights
|
1210
1317
|
else kwargs[1]
|
1211
1318
|
)
|
1212
1319
|
if fill:
|
@@ -1217,8 +1324,8 @@ class RowIndex(tk.Canvas):
|
|
1217
1324
|
)
|
1218
1325
|
elif "cells" in selections and r in selections["cells"]:
|
1219
1326
|
txtfg = (
|
1220
|
-
self.
|
1221
|
-
if kwargs[1] is None or self.
|
1327
|
+
self.ops.index_selected_cells_fg
|
1328
|
+
if kwargs[1] is None or self.ops.display_selected_fg_over_highlights
|
1222
1329
|
else kwargs[1]
|
1223
1330
|
)
|
1224
1331
|
if fill:
|
@@ -1228,7 +1335,7 @@ class RowIndex(tk.Canvas):
|
|
1228
1335
|
+ f"{int((int(fill[5:], 16) + int(sel_cells_bg[5:], 16)) / 2):02X}"
|
1229
1336
|
)
|
1230
1337
|
else:
|
1231
|
-
txtfg = self.
|
1338
|
+
txtfg = self.ops.index_fg if kwargs[1] is None else kwargs[1]
|
1232
1339
|
if fill:
|
1233
1340
|
redrawn = self.redraw_highlight(
|
1234
1341
|
0,
|
@@ -1237,8 +1344,8 @@ class RowIndex(tk.Canvas):
|
|
1237
1344
|
sr,
|
1238
1345
|
fill=fill,
|
1239
1346
|
outline=(
|
1240
|
-
self.
|
1241
|
-
if self.get_cell_kwargs(datarn, key="dropdown") and self.
|
1347
|
+
self.ops.index_fg
|
1348
|
+
if self.get_cell_kwargs(datarn, key="dropdown") and self.ops.show_dropdown_borders
|
1242
1349
|
else ""
|
1243
1350
|
),
|
1244
1351
|
tag="s",
|
@@ -1246,14 +1353,14 @@ class RowIndex(tk.Canvas):
|
|
1246
1353
|
tree_arrow_fg = txtfg
|
1247
1354
|
elif not kwargs:
|
1248
1355
|
if "rows" in selections and r in selections["rows"]:
|
1249
|
-
txtfg = self.
|
1250
|
-
tree_arrow_fg = self.
|
1356
|
+
txtfg = self.ops.index_selected_rows_fg
|
1357
|
+
tree_arrow_fg = self.ops.selected_rows_tree_arrow_fg
|
1251
1358
|
elif "cells" in selections and r in selections["cells"]:
|
1252
|
-
txtfg = self.
|
1253
|
-
tree_arrow_fg = self.
|
1359
|
+
txtfg = self.ops.index_selected_cells_fg
|
1360
|
+
tree_arrow_fg = self.ops.selected_cells_tree_arrow_fg
|
1254
1361
|
else:
|
1255
|
-
txtfg = self.
|
1256
|
-
tree_arrow_fg = self.
|
1362
|
+
txtfg = self.ops.index_fg
|
1363
|
+
tree_arrow_fg = self.ops.tree_arrow_fg
|
1257
1364
|
return txtfg, tree_arrow_fg, redrawn
|
1258
1365
|
|
1259
1366
|
def redraw_highlight(
|
@@ -1375,14 +1482,14 @@ class RowIndex(tk.Canvas):
|
|
1375
1482
|
t, sh = self.hidd_tree_arrow.popitem()
|
1376
1483
|
self.coords(t, points)
|
1377
1484
|
if sh:
|
1378
|
-
self.itemconfig(t, fill=fill if has_children else self.
|
1485
|
+
self.itemconfig(t, fill=fill if has_children else self.ops.index_grid_fg)
|
1379
1486
|
else:
|
1380
|
-
self.itemconfig(t, fill=fill if has_children else self.
|
1487
|
+
self.itemconfig(t, fill=fill if has_children else self.ops.index_grid_fg, tag=tag, state="normal")
|
1381
1488
|
self.lift(t)
|
1382
1489
|
else:
|
1383
1490
|
t = self.create_line(
|
1384
1491
|
points,
|
1385
|
-
fill=fill if has_children else self.
|
1492
|
+
fill=fill if has_children else self.ops.index_grid_fg,
|
1386
1493
|
tag=tag,
|
1387
1494
|
width=2,
|
1388
1495
|
capstyle=tk.ROUND,
|
@@ -1403,8 +1510,8 @@ class RowIndex(tk.Canvas):
|
|
1403
1510
|
draw_arrow: bool = True,
|
1404
1511
|
open_: bool = False,
|
1405
1512
|
) -> None:
|
1406
|
-
if draw_outline and self.
|
1407
|
-
self.redraw_highlight(x1 + 1, y1 + 1, x2, y2, fill="", outline=self.
|
1513
|
+
if draw_outline and self.ops.show_dropdown_borders:
|
1514
|
+
self.redraw_highlight(x1 + 1, y1 + 1, x2, y2, fill="", outline=self.ops.index_fg, tag=tag)
|
1408
1515
|
if draw_arrow:
|
1409
1516
|
mod = (self.MT.index_txt_height - 1) if self.MT.index_txt_height % 2 else self.MT.index_txt_height
|
1410
1517
|
small_mod = int(mod / 5)
|
@@ -1490,15 +1597,33 @@ class RowIndex(tk.Canvas):
|
|
1490
1597
|
t = self.create_polygon(points, fill=fill, outline=outline, tag=tag, smooth=True)
|
1491
1598
|
self.disp_checkbox[t] = True
|
1492
1599
|
|
1493
|
-
def configure_scrollregion(self, last_row_line_pos: float) ->
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1600
|
+
def configure_scrollregion(self, last_row_line_pos: float) -> bool:
|
1601
|
+
try:
|
1602
|
+
self.configure(
|
1603
|
+
scrollregion=(
|
1604
|
+
0,
|
1605
|
+
0,
|
1606
|
+
self.current_width,
|
1607
|
+
last_row_line_pos + self.ops.empty_vertical + 2,
|
1608
|
+
)
|
1500
1609
|
)
|
1610
|
+
return True
|
1611
|
+
except Exception:
|
1612
|
+
return False
|
1613
|
+
|
1614
|
+
def wrap_get_char_w(self, c: str) -> int:
|
1615
|
+
self.MT.txt_measure_canvas.itemconfig(
|
1616
|
+
self.MT.txt_measure_canvas_text,
|
1617
|
+
text=_test_str + c,
|
1618
|
+
font=self.index_font,
|
1501
1619
|
)
|
1620
|
+
b = self.MT.txt_measure_canvas.bbox(self.MT.txt_measure_canvas_text)
|
1621
|
+
if c in self.MT.char_widths[self.index_font]:
|
1622
|
+
return self.MT.char_widths[self.index_font][c]
|
1623
|
+
else:
|
1624
|
+
wd = b[2] - b[0] - self.index_test_str_w
|
1625
|
+
self.MT.char_widths[self.index_font][c] = wd
|
1626
|
+
return wd
|
1502
1627
|
|
1503
1628
|
def redraw_grid_and_text(
|
1504
1629
|
self,
|
@@ -1511,11 +1636,11 @@ class RowIndex(tk.Canvas):
|
|
1511
1636
|
text_end_row: int,
|
1512
1637
|
scrollpos_bot: int,
|
1513
1638
|
row_pos_exists: bool,
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1639
|
+
set_scrollregion: bool,
|
1640
|
+
) -> bool:
|
1641
|
+
if set_scrollregion:
|
1642
|
+
if not self.configure_scrollregion(last_row_line_pos=last_row_line_pos):
|
1643
|
+
return False
|
1519
1644
|
self.hidd_text.update(self.disp_text)
|
1520
1645
|
self.disp_text = {}
|
1521
1646
|
self.hidd_high.update(self.disp_high)
|
@@ -1536,20 +1661,20 @@ class RowIndex(tk.Canvas):
|
|
1536
1661
|
scrollpos_bot,
|
1537
1662
|
)
|
1538
1663
|
sel_cells_bg = (
|
1539
|
-
self.
|
1540
|
-
if self.
|
1541
|
-
else color_map[self.
|
1664
|
+
self.ops.index_selected_cells_bg
|
1665
|
+
if self.ops.index_selected_cells_bg.startswith("#")
|
1666
|
+
else color_map[self.ops.index_selected_cells_bg]
|
1542
1667
|
)
|
1543
1668
|
sel_rows_bg = (
|
1544
|
-
self.
|
1545
|
-
if self.
|
1546
|
-
else color_map[self.
|
1669
|
+
self.ops.index_selected_rows_bg
|
1670
|
+
if self.ops.index_selected_rows_bg.startswith("#")
|
1671
|
+
else color_map[self.ops.index_selected_rows_bg]
|
1547
1672
|
)
|
1548
|
-
font = self.
|
1673
|
+
font = self.ops.index_font
|
1549
1674
|
selections = self.get_redraw_selections(text_start_row, grid_end_row)
|
1550
1675
|
dd_coords = self.dropdown.get_coords()
|
1551
|
-
treeview = self.
|
1552
|
-
|
1676
|
+
treeview = self.ops.treeview
|
1677
|
+
wrap = self.ops.index_wrap
|
1553
1678
|
for r in range(text_start_row, text_end_row):
|
1554
1679
|
rtopgridln = self.MT.row_positions[r]
|
1555
1680
|
rbotgridln = self.MT.row_positions[r + 1]
|
@@ -1571,19 +1696,19 @@ class RowIndex(tk.Canvas):
|
|
1571
1696
|
else:
|
1572
1697
|
align = self.align
|
1573
1698
|
if dropdown_kwargs := self.get_cell_kwargs(datarn, key="dropdown"):
|
1574
|
-
|
1575
|
-
if align
|
1699
|
+
max_width = self.current_width - self.MT.index_txt_height - 2
|
1700
|
+
if align.endswith("w"):
|
1576
1701
|
draw_x = 3
|
1577
|
-
elif align
|
1702
|
+
elif align.endswith("e"):
|
1578
1703
|
draw_x = self.current_width - 5 - self.MT.index_txt_height
|
1579
|
-
elif align
|
1704
|
+
elif align.endswith("n"):
|
1580
1705
|
draw_x = ceil((self.current_width - self.MT.index_txt_height) / 2)
|
1581
1706
|
self.redraw_dropdown(
|
1582
1707
|
0,
|
1583
1708
|
rtopgridln,
|
1584
1709
|
self.current_width - 1,
|
1585
1710
|
rbotgridln - 1,
|
1586
|
-
fill=fill if dropdown_kwargs["state"] != "disabled" else self.
|
1711
|
+
fill=fill if dropdown_kwargs["state"] != "disabled" else self.ops.index_grid_fg,
|
1587
1712
|
outline=fill,
|
1588
1713
|
tag="dd",
|
1589
1714
|
draw_outline=not dd_drawn,
|
@@ -1591,27 +1716,24 @@ class RowIndex(tk.Canvas):
|
|
1591
1716
|
open_=dd_coords == r,
|
1592
1717
|
)
|
1593
1718
|
else:
|
1594
|
-
|
1595
|
-
if align
|
1719
|
+
max_width = self.current_width - 2
|
1720
|
+
if align.endswith("w"):
|
1596
1721
|
draw_x = 3
|
1597
|
-
elif align
|
1722
|
+
elif align.endswith("e"):
|
1598
1723
|
draw_x = self.current_width - 3
|
1599
|
-
elif align
|
1724
|
+
elif align.endswith("n"):
|
1600
1725
|
draw_x = floor(self.current_width / 2)
|
1601
1726
|
if (
|
1602
1727
|
(checkbox_kwargs := self.get_cell_kwargs(datarn, key="checkbox"))
|
1603
1728
|
and not dropdown_kwargs
|
1604
|
-
and
|
1729
|
+
and max_width > self.MT.index_txt_height + 1
|
1605
1730
|
):
|
1606
1731
|
box_w = self.MT.index_txt_height + 1
|
1607
|
-
if align
|
1732
|
+
if align.endswith("w"):
|
1608
1733
|
draw_x += box_w + 3
|
1609
|
-
|
1610
|
-
elif align == "center":
|
1734
|
+
elif align.endswith("n"):
|
1611
1735
|
draw_x += ceil(box_w / 2) + 1
|
1612
|
-
|
1613
|
-
else:
|
1614
|
-
mw -= box_w + 1
|
1736
|
+
max_width -= box_w + 4
|
1615
1737
|
try:
|
1616
1738
|
draw_check = (
|
1617
1739
|
self.MT._row_index[datarn]
|
@@ -1625,15 +1747,15 @@ class RowIndex(tk.Canvas):
|
|
1625
1747
|
rtopgridln + 2,
|
1626
1748
|
self.MT.index_txt_height + 3,
|
1627
1749
|
rtopgridln + self.MT.index_txt_height + 3,
|
1628
|
-
fill=fill if checkbox_kwargs["state"] == "normal" else self.
|
1750
|
+
fill=fill if checkbox_kwargs["state"] == "normal" else self.ops.index_grid_fg,
|
1629
1751
|
outline="",
|
1630
1752
|
tag="cb",
|
1631
1753
|
draw_check=draw_check,
|
1632
1754
|
)
|
1633
1755
|
if treeview and isinstance(self.MT._row_index, list) and len(self.MT._row_index) > datarn:
|
1634
1756
|
iid = self.MT._row_index[datarn].iid
|
1635
|
-
|
1636
|
-
if align
|
1757
|
+
max_width -= self.MT.index_txt_height
|
1758
|
+
if align.endswith("w"):
|
1637
1759
|
draw_x += self.MT.index_txt_height + 3
|
1638
1760
|
level, indent = self.get_iid_level_indent(iid)
|
1639
1761
|
draw_x += indent + 5
|
@@ -1648,24 +1770,30 @@ class RowIndex(tk.Canvas):
|
|
1648
1770
|
open_=self.MT._row_index[datarn].iid in self.tree_open_ids,
|
1649
1771
|
level=level,
|
1650
1772
|
)
|
1651
|
-
if
|
1652
|
-
continue
|
1653
|
-
lines = self.get_valid_cell_data_as_str(datarn, fix=False)
|
1654
|
-
if not lines:
|
1773
|
+
if max_width <= 1:
|
1655
1774
|
continue
|
1656
|
-
|
1657
|
-
|
1658
|
-
lines = lines.split("\n")
|
1659
|
-
if len(lines) <= start_ln or draw_y + self.MT.index_half_txt_height - 1 > rbotgridln:
|
1775
|
+
text = self.get_valid_cell_data_as_str(datarn, fix=False)
|
1776
|
+
if not text:
|
1660
1777
|
continue
|
1661
|
-
|
1778
|
+
start_line = max(0, int((scrollpos_top - rtopgridln) / self.MT.index_txt_height))
|
1779
|
+
draw_y = rtopgridln + 3 + (start_line * self.MT.index_txt_height)
|
1780
|
+
gen_lines = wrap_text(
|
1781
|
+
text=text,
|
1782
|
+
max_width=max_width,
|
1783
|
+
max_lines=int((rbotgridln - rtopgridln - 2) / self.MT.index_txt_height),
|
1784
|
+
char_width_fn=self.wrap_get_char_w,
|
1785
|
+
widths=self.MT.char_widths[font],
|
1786
|
+
wrap=wrap,
|
1787
|
+
start_line=start_line,
|
1788
|
+
)
|
1789
|
+
if align.endswith(("w", "e")):
|
1662
1790
|
if self.hidd_text:
|
1663
1791
|
iid, showing = self.hidd_text.popitem()
|
1664
1792
|
self.coords(iid, draw_x, draw_y)
|
1665
1793
|
if showing:
|
1666
1794
|
self.itemconfig(
|
1667
1795
|
iid,
|
1668
|
-
text=
|
1796
|
+
text="\n".join(gen_lines),
|
1669
1797
|
fill=fill,
|
1670
1798
|
font=font,
|
1671
1799
|
anchor=align,
|
@@ -1673,7 +1801,7 @@ class RowIndex(tk.Canvas):
|
|
1673
1801
|
else:
|
1674
1802
|
self.itemconfig(
|
1675
1803
|
iid,
|
1676
|
-
text=
|
1804
|
+
text="\n".join(gen_lines),
|
1677
1805
|
fill=fill,
|
1678
1806
|
font=font,
|
1679
1807
|
anchor=align,
|
@@ -1684,47 +1812,51 @@ class RowIndex(tk.Canvas):
|
|
1684
1812
|
iid = self.create_text(
|
1685
1813
|
draw_x,
|
1686
1814
|
draw_y,
|
1687
|
-
text=
|
1815
|
+
text="\n".join(gen_lines),
|
1688
1816
|
fill=fill,
|
1689
1817
|
font=font,
|
1690
1818
|
anchor=align,
|
1691
|
-
|
1819
|
+
tags="t",
|
1692
1820
|
)
|
1693
1821
|
self.disp_text[iid] = True
|
1694
|
-
|
1695
|
-
|
1696
|
-
if
|
1697
|
-
|
1698
|
-
self.itemconfig(iid, text=txt)
|
1699
|
-
wd = self.bbox(iid)
|
1700
|
-
while wd[2] - wd[0] > mw:
|
1701
|
-
txt = txt[:-1]
|
1702
|
-
self.itemconfig(iid, text=txt)
|
1703
|
-
wd = self.bbox(iid)
|
1704
|
-
elif align == "e" and checkbox_kwargs:
|
1705
|
-
txt = txt[len(txt) - int(len(txt) * (mw / wd)) :]
|
1706
|
-
self.itemconfig(iid, text=txt)
|
1707
|
-
wd = self.bbox(iid)
|
1708
|
-
while wd[2] - wd[0] > mw:
|
1709
|
-
txt = txt[1:]
|
1710
|
-
self.itemconfig(iid, text=txt)
|
1711
|
-
wd = self.bbox(iid)
|
1712
|
-
elif align == "center" and (dropdown_kwargs or checkbox_kwargs):
|
1713
|
-
tmod = ceil((len(txt) - int(len(txt) * (mw / wd))) / 2)
|
1714
|
-
txt = txt[tmod - 1 : -tmod]
|
1715
|
-
self.itemconfig(iid, text=txt)
|
1716
|
-
wd = self.bbox(iid)
|
1717
|
-
self.c_align_cyc = cycle(self.centre_alignment_text_mod_indexes)
|
1718
|
-
while wd[2] - wd[0] > mw:
|
1719
|
-
txt = txt[next(self.c_align_cyc)]
|
1720
|
-
self.itemconfig(iid, text=txt)
|
1721
|
-
wd = self.bbox(iid)
|
1822
|
+
else:
|
1823
|
+
for text in gen_lines:
|
1824
|
+
if self.hidd_text:
|
1825
|
+
iid, showing = self.hidd_text.popitem()
|
1722
1826
|
self.coords(iid, draw_x, draw_y)
|
1723
|
-
|
1724
|
-
|
1725
|
-
|
1827
|
+
if showing:
|
1828
|
+
self.itemconfig(
|
1829
|
+
iid,
|
1830
|
+
text=text,
|
1831
|
+
fill=fill,
|
1832
|
+
font=font,
|
1833
|
+
anchor=align,
|
1834
|
+
)
|
1835
|
+
else:
|
1836
|
+
self.itemconfig(
|
1837
|
+
iid,
|
1838
|
+
text=text,
|
1839
|
+
fill=fill,
|
1840
|
+
font=font,
|
1841
|
+
anchor=align,
|
1842
|
+
state="normal",
|
1843
|
+
)
|
1844
|
+
self.tag_raise(iid)
|
1845
|
+
else:
|
1846
|
+
iid = self.create_text(
|
1847
|
+
draw_x,
|
1848
|
+
draw_y,
|
1849
|
+
text=text,
|
1850
|
+
fill=fill,
|
1851
|
+
font=font,
|
1852
|
+
anchor=align,
|
1853
|
+
tags="t",
|
1854
|
+
)
|
1855
|
+
self.disp_text[iid] = True
|
1856
|
+
draw_y += self.MT.header_txt_height
|
1857
|
+
|
1726
1858
|
xend = self.current_width - 6
|
1727
|
-
if (self.
|
1859
|
+
if (self.ops.show_horizontal_grid or self.height_resizing_enabled) and row_pos_exists:
|
1728
1860
|
points = [
|
1729
1861
|
self.current_width - 1,
|
1730
1862
|
y_stop - 1,
|
@@ -1749,7 +1881,7 @@ class RowIndex(tk.Canvas):
|
|
1749
1881
|
self.MT.row_positions[r + 1] if len(self.MT.row_positions) - 1 > r else draw_y,
|
1750
1882
|
)
|
1751
1883
|
)
|
1752
|
-
self.redraw_gridline(points=points, fill=self.
|
1884
|
+
self.redraw_gridline(points=points, fill=self.ops.index_grid_fg, width=1, tag="h")
|
1753
1885
|
for dct in (
|
1754
1886
|
self.hidd_text,
|
1755
1887
|
self.hidd_high,
|
@@ -1842,14 +1974,14 @@ class RowIndex(tk.Canvas):
|
|
1842
1974
|
return False
|
1843
1975
|
else:
|
1844
1976
|
text = text if isinstance(text, str) else f"{text}"
|
1845
|
-
if self.
|
1977
|
+
if self.ops.cell_auto_resize_enabled:
|
1846
1978
|
self.set_row_height_run_binding(r)
|
1847
1979
|
if self.text_editor.open and r == self.text_editor.row:
|
1848
1980
|
self.text_editor.set_text(self.text_editor.get() + "" if not isinstance(text, str) else text)
|
1849
1981
|
return False
|
1850
1982
|
self.hide_text_editor()
|
1851
1983
|
if not self.MT.see(r=r, c=0, keep_yscroll=True, check_cell_visibility=True):
|
1852
|
-
self.MT.
|
1984
|
+
self.MT.main_table_redraw_grid_and_text(True, True)
|
1853
1985
|
x = 0
|
1854
1986
|
y = self.MT.row_positions[r]
|
1855
1987
|
w = self.current_width + 1
|
@@ -1857,24 +1989,24 @@ class RowIndex(tk.Canvas):
|
|
1857
1989
|
kwargs = {
|
1858
1990
|
"menu_kwargs": DotDict(
|
1859
1991
|
{
|
1860
|
-
"font": self.
|
1861
|
-
"foreground": self.
|
1862
|
-
"background": self.
|
1863
|
-
"activebackground": self.
|
1864
|
-
"activeforeground": self.
|
1992
|
+
"font": self.ops.index_font,
|
1993
|
+
"foreground": self.ops.popup_menu_fg,
|
1994
|
+
"background": self.ops.popup_menu_bg,
|
1995
|
+
"activebackground": self.ops.popup_menu_highlight_bg,
|
1996
|
+
"activeforeground": self.ops.popup_menu_highlight_fg,
|
1865
1997
|
}
|
1866
1998
|
),
|
1867
|
-
"sheet_ops": self.
|
1868
|
-
"border_color": self.
|
1999
|
+
"sheet_ops": self.ops,
|
2000
|
+
"border_color": self.ops.index_selected_rows_bg,
|
1869
2001
|
"text": text,
|
1870
2002
|
"state": state,
|
1871
2003
|
"width": w,
|
1872
2004
|
"height": h,
|
1873
2005
|
"show_border": True,
|
1874
|
-
"bg": self.
|
1875
|
-
"fg": self.
|
1876
|
-
"select_bg": self.
|
1877
|
-
"select_fg": self.
|
2006
|
+
"bg": self.ops.index_editor_bg,
|
2007
|
+
"fg": self.ops.index_editor_fg,
|
2008
|
+
"select_bg": self.ops.index_editor_select_bg,
|
2009
|
+
"select_fg": self.ops.index_editor_select_fg,
|
1878
2010
|
"align": self.get_cell_align(r),
|
1879
2011
|
"r": r,
|
1880
2012
|
}
|
@@ -1913,13 +2045,13 @@ class RowIndex(tk.Canvas):
|
|
1913
2045
|
> curr_height
|
1914
2046
|
):
|
1915
2047
|
r = self.text_editor.row
|
1916
|
-
new_height = curr_height + self.MT.
|
2048
|
+
new_height = curr_height + self.MT.index_txt_height
|
1917
2049
|
space_bot = self.MT.get_space_bot(r)
|
1918
2050
|
if new_height > space_bot:
|
1919
2051
|
new_height = space_bot
|
1920
2052
|
if new_height != curr_height:
|
1921
2053
|
self.set_row_height(r, new_height)
|
1922
|
-
self.MT.
|
2054
|
+
self.MT.main_table_redraw_grid_and_text(True, True)
|
1923
2055
|
self.text_editor.window.config(height=new_height)
|
1924
2056
|
self.coords(self.text_editor.canvas_id, 0, self.MT.row_positions[r] + 1)
|
1925
2057
|
if self.dropdown.open and self.dropdown.get_coords() == r:
|
@@ -1944,7 +2076,7 @@ class RowIndex(tk.Canvas):
|
|
1944
2076
|
if self.text_editor.open:
|
1945
2077
|
r = self.text_editor.row
|
1946
2078
|
self.text_editor.window.config(height=self.MT.row_positions[r + 1] - self.MT.row_positions[r])
|
1947
|
-
self.text_editor.tktext.config(font=self.
|
2079
|
+
self.text_editor.tktext.config(font=self.ops.index_font)
|
1948
2080
|
self.coords(
|
1949
2081
|
self.text_editor.canvas_id,
|
1950
2082
|
0,
|
@@ -2047,9 +2179,7 @@ class RowIndex(tk.Canvas):
|
|
2047
2179
|
for i, v in enumerate(self.get_cell_kwargs(datarn, key="dropdown")["values"]):
|
2048
2180
|
v_numlines = len(v.split("\n") if isinstance(v, str) else f"{v}".split("\n"))
|
2049
2181
|
if v_numlines > 1:
|
2050
|
-
win_h += (
|
2051
|
-
self.MT.index_first_ln_ins + (v_numlines * self.MT.index_xtra_lines_increment) + 5
|
2052
|
-
) # end of cell
|
2182
|
+
win_h += self.MT.index_txt_height + (v_numlines * self.MT.index_txt_height) + 5 # end of cell
|
2053
2183
|
else:
|
2054
2184
|
win_h += self.MT.min_row_height
|
2055
2185
|
if i == 5:
|
@@ -2117,15 +2247,15 @@ class RowIndex(tk.Canvas):
|
|
2117
2247
|
reset_kwargs = {
|
2118
2248
|
"r": r,
|
2119
2249
|
"c": 0,
|
2120
|
-
"bg": self.
|
2121
|
-
"fg": self.
|
2122
|
-
"select_bg": self.
|
2123
|
-
"select_fg": self.
|
2250
|
+
"bg": self.ops.index_editor_bg,
|
2251
|
+
"fg": self.ops.index_editor_fg,
|
2252
|
+
"select_bg": self.ops.index_editor_select_bg,
|
2253
|
+
"select_fg": self.ops.index_editor_select_fg,
|
2124
2254
|
"width": win_w,
|
2125
2255
|
"height": win_h,
|
2126
|
-
"font": self.
|
2127
|
-
"ops": self.
|
2128
|
-
"outline_color": self.
|
2256
|
+
"font": self.ops.index_font,
|
2257
|
+
"ops": self.ops,
|
2258
|
+
"outline_color": self.ops.index_selected_rows_bg,
|
2129
2259
|
"align": self.get_cell_align(r),
|
2130
2260
|
"values": kwargs["values"],
|
2131
2261
|
"search_function": kwargs["search_function"],
|
@@ -2274,7 +2404,7 @@ class RowIndex(tk.Canvas):
|
|
2274
2404
|
self.MT.undo_stack.append(stored_event_dict(event_data))
|
2275
2405
|
self.set_cell_data(datarn=datarn, value=value)
|
2276
2406
|
edited = True
|
2277
|
-
if edited and cell_resize and self.
|
2407
|
+
if edited and cell_resize and self.ops.cell_auto_resize_enabled:
|
2278
2408
|
self.set_row_height_run_binding(r, only_if_too_small=False)
|
2279
2409
|
if redraw:
|
2280
2410
|
self.MT.refresh()
|
@@ -2289,7 +2419,7 @@ class RowIndex(tk.Canvas):
|
|
2289
2419
|
self.fix_index(datarn)
|
2290
2420
|
if self.get_cell_kwargs(datarn, key="checkbox"):
|
2291
2421
|
self.MT._row_index[datarn] = try_to_bool(value)
|
2292
|
-
elif self.
|
2422
|
+
elif self.ops.treeview:
|
2293
2423
|
self.MT._row_index[datarn].text = value
|
2294
2424
|
else:
|
2295
2425
|
self.MT._row_index[datarn] = value
|
@@ -2331,7 +2461,7 @@ class RowIndex(tk.Canvas):
|
|
2331
2461
|
or (self.MT._row_index[datarn] is None and none_to_empty_str)
|
2332
2462
|
):
|
2333
2463
|
return ""
|
2334
|
-
if self.
|
2464
|
+
if self.ops.treeview:
|
2335
2465
|
return self.MT._row_index[datarn].text
|
2336
2466
|
return self.MT._row_index[datarn]
|
2337
2467
|
|
@@ -2349,14 +2479,23 @@ class RowIndex(tk.Canvas):
|
|
2349
2479
|
if fix:
|
2350
2480
|
self.fix_index(datarn)
|
2351
2481
|
try:
|
2352
|
-
value =
|
2482
|
+
value = self.MT._row_index[datarn]
|
2483
|
+
if value is None:
|
2484
|
+
value = ""
|
2485
|
+
elif isinstance(value, Node):
|
2486
|
+
value = value.text
|
2487
|
+
elif not isinstance(value, str):
|
2488
|
+
value = f"{value}"
|
2353
2489
|
except Exception:
|
2354
2490
|
value = ""
|
2355
|
-
if not value and self.
|
2356
|
-
value = get_n2a(datarn, self.
|
2491
|
+
if not value and self.ops.show_default_index_for_empty:
|
2492
|
+
value = get_n2a(datarn, self.ops.default_row_index)
|
2357
2493
|
return value
|
2358
2494
|
|
2359
2495
|
def get_value_for_empty_cell(self, datarn: int, r_ops: bool = True) -> object:
|
2496
|
+
if self.ops.treeview:
|
2497
|
+
iid = self.new_iid()
|
2498
|
+
return Node(text=iid, iid=iid, parent=self.get_row_parent(datarn))
|
2360
2499
|
if self.get_cell_kwargs(datarn, key="checkbox", cell=r_ops):
|
2361
2500
|
return False
|
2362
2501
|
kwargs = self.get_cell_kwargs(datarn, key="dropdown", cell=r_ops)
|
@@ -2436,63 +2575,378 @@ class RowIndex(tk.Canvas):
|
|
2436
2575
|
|
2437
2576
|
# Treeview Mode
|
2438
2577
|
|
2578
|
+
def tree_reset(self) -> None:
|
2579
|
+
self.tree: dict[str, Node] = {}
|
2580
|
+
self.tree_open_ids = set()
|
2581
|
+
self.tree_rns = {}
|
2582
|
+
if self.MT:
|
2583
|
+
self.MT.displayed_rows = []
|
2584
|
+
self.MT._row_index = []
|
2585
|
+
self.MT.data = []
|
2586
|
+
self.MT.row_positions = [0]
|
2587
|
+
self.MT.saved_row_heights = {}
|
2588
|
+
|
2589
|
+
def new_iid(self) -> str:
|
2590
|
+
self.new_iid_ctr += 1
|
2591
|
+
while (iid := f"{num2alpha(self.new_iid_ctr)}") in self.tree:
|
2592
|
+
self.new_iid_ctr += 1
|
2593
|
+
return iid
|
2594
|
+
|
2595
|
+
def get_row_parent(self, r: int) -> str:
|
2596
|
+
if r >= len(self.MT._row_index):
|
2597
|
+
return ""
|
2598
|
+
else:
|
2599
|
+
return self.MT._row_index[r].parent
|
2600
|
+
|
2601
|
+
def tree_del_rows(self, event_data: EventDataDict) -> EventDataDict:
|
2602
|
+
event_data["treeview"]["nodes"] = {}
|
2603
|
+
for node in reversed(event_data["deleted"]["index"].values()):
|
2604
|
+
iid = node.iid
|
2605
|
+
if parent_node := self.parent_node(iid):
|
2606
|
+
if parent_node.iid not in event_data["treeview"]["nodes"]:
|
2607
|
+
event_data["treeview"]["nodes"][parent_node.iid] = Node(
|
2608
|
+
text=parent_node.text,
|
2609
|
+
iid=parent_node.iid,
|
2610
|
+
parent=parent_node.parent,
|
2611
|
+
children=parent_node.children.copy(),
|
2612
|
+
)
|
2613
|
+
self.remove_iid_from_parents_children(iid)
|
2614
|
+
for node in reversed(event_data["deleted"]["index"].values()):
|
2615
|
+
iid = node.iid
|
2616
|
+
for did in self.get_iid_descendants(iid):
|
2617
|
+
self.tree_open_ids.discard(did)
|
2618
|
+
del self.tree[did]
|
2619
|
+
self.tree_open_ids.discard(iid)
|
2620
|
+
del self.tree[iid]
|
2621
|
+
return event_data
|
2622
|
+
|
2623
|
+
def tree_add_rows(self, event_data: EventDataDict) -> EventDataDict:
|
2624
|
+
for rn, node in event_data["added"]["rows"]["index"].items():
|
2625
|
+
self.tree[node.iid] = node
|
2626
|
+
self.tree_rns[node.iid] = rn
|
2627
|
+
if event_data["treeview"]["nodes"]:
|
2628
|
+
self.restore_nodes(event_data=event_data)
|
2629
|
+
else:
|
2630
|
+
row, a_node = next(reversed(event_data["added"]["rows"]["index"].items()))
|
2631
|
+
if parent := a_node.parent:
|
2632
|
+
if self.tree[parent].children:
|
2633
|
+
index = next(
|
2634
|
+
(i for i, cid in enumerate(self.tree[parent].children) if self.tree_rns[cid] >= row),
|
2635
|
+
len(self.tree[parent].children),
|
2636
|
+
)
|
2637
|
+
self.tree[parent].children[index:index] = [
|
2638
|
+
n.iid for n in reversed(event_data["added"]["rows"]["index"].values())
|
2639
|
+
]
|
2640
|
+
else:
|
2641
|
+
self.tree[parent].children.extend(
|
2642
|
+
n.iid for n in reversed(event_data["added"]["rows"]["index"].values())
|
2643
|
+
)
|
2644
|
+
if not self.PAR.item_displayed(parent) or parent not in self.tree_open_ids:
|
2645
|
+
self.PAR.hide_rows(event_data["added"]["rows"]["index"], data_indexes=True)
|
2646
|
+
return event_data
|
2647
|
+
|
2648
|
+
def move_rows_mod_nodes(
|
2649
|
+
self,
|
2650
|
+
data_new_idxs: dict[int, int],
|
2651
|
+
data_old_idxs: dict[int, int],
|
2652
|
+
disp_new_idxs: dict[int, int],
|
2653
|
+
maxidx: int,
|
2654
|
+
event_data: EventDataDict,
|
2655
|
+
undo_modification: EventDataDict | None = None,
|
2656
|
+
node_change: tuple[str, str, int] | None = None,
|
2657
|
+
) -> Generator[tuple[dict[int, int], dict[int, int], dict[str, int], EventDataDict]] | None:
|
2658
|
+
# data_new_idxs is {old: new, old: new}
|
2659
|
+
# data_old_idxs is {new: old, new: old}
|
2660
|
+
if not event_data["treeview"]["nodes"]:
|
2661
|
+
if undo_modification:
|
2662
|
+
"""
|
2663
|
+
Used by undo/redo
|
2664
|
+
"""
|
2665
|
+
event_data = self.copy_nodes(undo_modification["treeview"]["nodes"], event_data)
|
2666
|
+
self.restore_nodes(undo_modification)
|
2667
|
+
|
2668
|
+
elif event_data["moved"]["rows"]:
|
2669
|
+
if node_change:
|
2670
|
+
item = node_change[0]
|
2671
|
+
moved_rows = [self.tree_rns[item]]
|
2672
|
+
new_parent = node_change[1]
|
2673
|
+
move_to_index = node_change[2]
|
2674
|
+
if new_parent:
|
2675
|
+
move_to_index = (
|
2676
|
+
move_to_index if isinstance(move_to_index, int) else len(self.tree[new_parent].children)
|
2677
|
+
)
|
2678
|
+
move_to_row = self.tree_rns[new_parent] + max(
|
2679
|
+
0, min(move_to_index, len(self.tree[new_parent].children))
|
2680
|
+
)
|
2681
|
+
else:
|
2682
|
+
num_top_nodes = sum(1 for _ in self.gen_top_nodes())
|
2683
|
+
if move_to_index is None:
|
2684
|
+
move_to_row = self.PAR.top_index_row(num_top_nodes - 1)
|
2685
|
+
move_to_index = num_top_nodes
|
2686
|
+
else:
|
2687
|
+
move_to_row = self.PAR.top_index_row(move_to_index)
|
2688
|
+
if move_to_row is None:
|
2689
|
+
move_to_row = self.PAR.top_index_row(num_top_nodes - 1)
|
2690
|
+
move_to_index = num_top_nodes
|
2691
|
+
|
2692
|
+
move_to_iid = self.MT._row_index[move_to_row].iid
|
2693
|
+
insert_row = move_to_row + 1
|
2694
|
+
disp_insert_row = None
|
2695
|
+
|
2696
|
+
else:
|
2697
|
+
iids = set(self.MT._row_index[r].iid for r in event_data["moved"]["rows"]["data"])
|
2698
|
+
iids_descendants = {iid: set(self.get_iid_descendants(iid)) for iid in iids}
|
2699
|
+
|
2700
|
+
# remove descendants in iids to move
|
2701
|
+
iids -= set.union(*iids_descendants.values()) & iids
|
2702
|
+
moved_rows = sorted(map(self.tree_rns.__getitem__, iids))
|
2703
|
+
item = self.MT._row_index[moved_rows[0]].iid
|
2704
|
+
|
2705
|
+
if isinstance(event_data.value, int):
|
2706
|
+
disp_insert_row = event_data.value
|
2707
|
+
if disp_insert_row >= len(self.MT.displayed_rows):
|
2708
|
+
insert_row = len(self.MT._row_index)
|
2709
|
+
else:
|
2710
|
+
insert_row = self.MT.datarn(disp_insert_row)
|
2711
|
+
move_to_iid = self.MT._row_index[min(insert_row, len(self.MT._row_index) - 1)].iid
|
2712
|
+
|
2713
|
+
else:
|
2714
|
+
disp_insert_row = None
|
2715
|
+
min_from = min(event_data["moved"]["rows"]["data"])
|
2716
|
+
# max_from = max(event_data.moved.rows)
|
2717
|
+
min_to = min(event_data["moved"]["rows"]["data"].values())
|
2718
|
+
max_to = max(event_data["moved"]["rows"]["data"].values())
|
2719
|
+
if min_from <= min_to:
|
2720
|
+
insert_row = max_to
|
2721
|
+
else:
|
2722
|
+
insert_row = min_to
|
2723
|
+
move_to_iid = self.MT._row_index[insert_row].iid
|
2724
|
+
|
2725
|
+
move_to_index = self.PAR.index(move_to_iid)
|
2726
|
+
new_parent = self.items_parent(move_to_iid)
|
2727
|
+
|
2728
|
+
event_data["moved"]["rows"]["data"] = {moved_rows[0]: insert_row}
|
2729
|
+
|
2730
|
+
new_loc_is_displayed = not new_parent or (
|
2731
|
+
new_parent and new_parent in self.tree_open_ids and self.PAR.item_displayed(new_parent)
|
2732
|
+
)
|
2733
|
+
# deal with displayed mapping
|
2734
|
+
event_data["moved"]["rows"]["displayed"] = {}
|
2735
|
+
if new_loc_is_displayed:
|
2736
|
+
if disp_insert_row is None:
|
2737
|
+
if new_parent == self.tree[item].parent:
|
2738
|
+
disp_insert_row = self.MT.disprn(self.tree_rns[move_to_iid]) + 1
|
2739
|
+
else:
|
2740
|
+
disp_insert_row = self.MT.disprn(self.tree_rns[move_to_iid]) + 1
|
2741
|
+
if (disp_from_row := self.MT.try_disprn(self.tree_rns[item])) is not None:
|
2742
|
+
event_data["moved"]["rows"]["displayed"] = {disp_from_row: disp_insert_row}
|
2743
|
+
else:
|
2744
|
+
event_data["moved"]["rows"]["displayed"] = {tuple(): disp_insert_row}
|
2745
|
+
|
2746
|
+
if any(self.move_pid_causes_recursive_loop(self.MT._row_index[r].iid, new_parent) for r in moved_rows):
|
2747
|
+
event_data["moved"]["rows"] = {}
|
2748
|
+
data_new_idxs, data_old_idxs, disp_new_idxs = {}, {}, {}
|
2749
|
+
|
2750
|
+
else:
|
2751
|
+
for r in moved_rows:
|
2752
|
+
iid = self.MT._row_index[r].iid
|
2753
|
+
event_data = self.move_node(
|
2754
|
+
event_data=event_data,
|
2755
|
+
item=iid,
|
2756
|
+
parent=new_parent,
|
2757
|
+
index=move_to_index,
|
2758
|
+
)
|
2759
|
+
move_to_index += 1
|
2760
|
+
|
2761
|
+
event_data["moved"]["rows"]["data"] = get_new_indexes(
|
2762
|
+
insert_row,
|
2763
|
+
event_data["moved"]["rows"]["data"],
|
2764
|
+
)
|
2765
|
+
data_new_idxs = event_data["moved"]["rows"]["data"]
|
2766
|
+
data_old_idxs = dict(zip(data_new_idxs.values(), data_new_idxs))
|
2767
|
+
|
2768
|
+
if tuple() in event_data["moved"]["rows"]["displayed"]:
|
2769
|
+
del event_data["moved"]["rows"]["displayed"][tuple()]
|
2770
|
+
|
2771
|
+
if event_data["moved"]["rows"]["displayed"]:
|
2772
|
+
event_data["moved"]["rows"]["displayed"] = get_new_indexes(
|
2773
|
+
disp_insert_row,
|
2774
|
+
event_data["moved"]["rows"]["displayed"],
|
2775
|
+
)
|
2776
|
+
disp_new_idxs = event_data["moved"]["rows"]["displayed"]
|
2777
|
+
|
2778
|
+
if data_new_idxs:
|
2779
|
+
self.MT.move_rows_data(data_new_idxs, data_old_idxs, maxidx)
|
2780
|
+
|
2781
|
+
yield data_new_idxs, data_old_idxs, disp_new_idxs, event_data
|
2782
|
+
|
2783
|
+
if not undo_modification and data_new_idxs:
|
2784
|
+
if new_parent and (not self.PAR.item_displayed(new_parent) or new_parent not in self.tree_open_ids):
|
2785
|
+
self.PAR.hide_rows(set(data_new_idxs.values()), data_indexes=True)
|
2786
|
+
|
2787
|
+
if new_loc_is_displayed:
|
2788
|
+
self.PAR.show_rows(
|
2789
|
+
(r for r in data_new_idxs.values() if self.ancestors_all_open(self.MT._row_index[r].iid))
|
2790
|
+
)
|
2791
|
+
|
2792
|
+
yield None
|
2793
|
+
|
2794
|
+
def move_node(
|
2795
|
+
self,
|
2796
|
+
event_data: EventDataDict,
|
2797
|
+
item: str,
|
2798
|
+
parent: str | None = None,
|
2799
|
+
index: int = 0,
|
2800
|
+
) -> EventDataDict:
|
2801
|
+
# also backs up nodes
|
2802
|
+
if parent is None:
|
2803
|
+
parent = self.items_parent(item)
|
2804
|
+
|
2805
|
+
item_node = self.tree[item]
|
2806
|
+
|
2807
|
+
# new parent is an item
|
2808
|
+
if parent:
|
2809
|
+
parent_node = self.tree[parent]
|
2810
|
+
# its the same parent, we're just moving index
|
2811
|
+
if parent == item_node.parent:
|
2812
|
+
event_data = self.copy_nodes((item, parent), event_data)
|
2813
|
+
pop_index = parent_node.children.index(item)
|
2814
|
+
parent_node.children.insert(index, parent_node.children.pop(pop_index))
|
2815
|
+
|
2816
|
+
else:
|
2817
|
+
if item_node.parent:
|
2818
|
+
event_data = self.copy_nodes((item, item_node.parent, parent), event_data)
|
2819
|
+
else:
|
2820
|
+
event_data = self.copy_nodes((item, parent), event_data)
|
2821
|
+
self.remove_iid_from_parents_children(item)
|
2822
|
+
item_node.parent = parent_node.iid
|
2823
|
+
parent_node.children.insert(index, item)
|
2824
|
+
|
2825
|
+
# no new parent
|
2826
|
+
else:
|
2827
|
+
if item_node.parent:
|
2828
|
+
event_data = self.copy_nodes((item, item_node.parent), event_data)
|
2829
|
+
else:
|
2830
|
+
event_data = self.copy_nodes((item,), event_data)
|
2831
|
+
self.remove_iid_from_parents_children(item)
|
2832
|
+
self.tree[item].parent = ""
|
2833
|
+
|
2834
|
+
# last row in mapping is where to start from +1
|
2835
|
+
mapping = event_data["moved"]["rows"]["data"]
|
2836
|
+
row_ctr = next(reversed(mapping.values())) + 1
|
2837
|
+
|
2838
|
+
if disp_mapping := event_data["moved"]["rows"]["displayed"]:
|
2839
|
+
if tuple() in disp_mapping:
|
2840
|
+
disp_row_ctr = next(reversed(disp_mapping.values()))
|
2841
|
+
else:
|
2842
|
+
disp_row_ctr = next(reversed(disp_mapping.values())) + 1
|
2843
|
+
|
2844
|
+
rn = self.tree_rns[item]
|
2845
|
+
if rn not in mapping:
|
2846
|
+
mapping[rn] = row_ctr
|
2847
|
+
row_ctr += 1
|
2848
|
+
if disp_mapping and (disp_from := self.MT.try_disprn(rn)) is not None:
|
2849
|
+
disp_mapping = del_placeholder_dict_key(disp_mapping, disp_from, disp_row_ctr)
|
2850
|
+
disp_row_ctr += 1
|
2851
|
+
|
2852
|
+
for did in self.get_iid_descendants(item):
|
2853
|
+
mapping[self.tree_rns[did]] = row_ctr
|
2854
|
+
row_ctr += 1
|
2855
|
+
if disp_mapping and (disp_from := self.MT.try_disprn(self.tree_rns[did])) is not None:
|
2856
|
+
disp_mapping = del_placeholder_dict_key(disp_mapping, disp_from, disp_row_ctr)
|
2857
|
+
disp_row_ctr += 1
|
2858
|
+
|
2859
|
+
event_data["moved"]["rows"]["data"] = mapping
|
2860
|
+
event_data["moved"]["rows"]["displayed"] = disp_mapping
|
2861
|
+
|
2862
|
+
return event_data
|
2863
|
+
|
2864
|
+
def restore_nodes(self, event_data: EventDataDict) -> None:
|
2865
|
+
for iid, node in event_data["treeview"]["nodes"].items():
|
2866
|
+
self.MT._row_index[self.tree_rns[iid]] = node
|
2867
|
+
self.tree[iid] = node
|
2868
|
+
|
2869
|
+
def copy_node(self, item: str) -> Node:
|
2870
|
+
n = self.tree[item]
|
2871
|
+
return Node(
|
2872
|
+
text=n.text,
|
2873
|
+
iid=n.iid,
|
2874
|
+
parent=n.parent,
|
2875
|
+
children=n.children.copy(),
|
2876
|
+
)
|
2877
|
+
|
2878
|
+
def copy_nodes(self, items: AnyIter[str], event_data: EventDataDict) -> EventDataDict:
|
2879
|
+
nodes = event_data["treeview"]["nodes"]
|
2880
|
+
for iid in items:
|
2881
|
+
if iid not in nodes:
|
2882
|
+
n = self.tree[iid]
|
2883
|
+
nodes[iid] = Node(
|
2884
|
+
text=n.text,
|
2885
|
+
iid=n.iid,
|
2886
|
+
parent=n.parent,
|
2887
|
+
children=n.children.copy(),
|
2888
|
+
)
|
2889
|
+
return event_data
|
2890
|
+
|
2439
2891
|
def get_node_level(self, node: Node, level: int = 0) -> Generator[int]:
|
2440
2892
|
yield level
|
2441
2893
|
if node.parent:
|
2442
|
-
yield from self.get_node_level(node.parent, level + 1)
|
2894
|
+
yield from self.get_node_level(self.tree[node.parent], level + 1)
|
2443
2895
|
|
2444
|
-
def ancestors_all_open(self, iid: str, stop_at: str
|
2896
|
+
def ancestors_all_open(self, iid: str, stop_at: str = "") -> bool:
|
2445
2897
|
if stop_at:
|
2446
|
-
stop_at = stop_at.iid
|
2447
2898
|
for iid in self.get_iid_ancestors(iid):
|
2448
2899
|
if iid == stop_at:
|
2449
2900
|
return True
|
2450
|
-
|
2901
|
+
elif iid not in self.tree_open_ids:
|
2451
2902
|
return False
|
2452
2903
|
return True
|
2453
2904
|
return all(map(self.tree_open_ids.__contains__, self.get_iid_ancestors(iid)))
|
2454
2905
|
|
2455
2906
|
def get_iid_ancestors(self, iid: str) -> Generator[str]:
|
2456
2907
|
if self.tree[iid].parent:
|
2457
|
-
yield self.tree[iid].parent
|
2458
|
-
yield from self.get_iid_ancestors(self.tree[iid].parent
|
2908
|
+
yield self.tree[iid].parent
|
2909
|
+
yield from self.get_iid_ancestors(self.tree[iid].parent)
|
2459
2910
|
|
2460
2911
|
def get_iid_descendants(self, iid: str, check_open: bool = False) -> Generator[str]:
|
2461
|
-
for
|
2462
|
-
yield
|
2463
|
-
if
|
2464
|
-
|
2465
|
-
):
|
2466
|
-
yield from self.get_iid_descendants(cnode.iid, check_open)
|
2912
|
+
for ciid in self.tree[iid].children:
|
2913
|
+
yield ciid
|
2914
|
+
if self.tree[ciid].children and (not check_open or ciid in self.tree_open_ids):
|
2915
|
+
yield from self.get_iid_descendants(ciid, check_open)
|
2467
2916
|
|
2468
2917
|
def items_parent(self, iid: str) -> str:
|
2469
2918
|
if self.tree[iid].parent:
|
2470
|
-
return self.tree[iid].parent
|
2919
|
+
return self.tree[iid].parent
|
2920
|
+
return ""
|
2921
|
+
|
2922
|
+
def parent_node(self, iid: str) -> Node:
|
2923
|
+
if self.tree[iid].parent:
|
2924
|
+
return self.tree[self.tree[iid].parent]
|
2471
2925
|
return ""
|
2472
2926
|
|
2473
2927
|
def gen_top_nodes(self) -> Generator[Node]:
|
2474
2928
|
yield from (node for node in self.MT._row_index if node.parent == "")
|
2475
2929
|
|
2476
2930
|
def get_iid_indent(self, iid: str) -> int:
|
2477
|
-
if isinstance(self.
|
2478
|
-
indent = self.MT.index_txt_width * int(self.
|
2931
|
+
if isinstance(self.ops.treeview_indent, str):
|
2932
|
+
indent = self.MT.index_txt_width * int(self.ops.treeview_indent)
|
2479
2933
|
else:
|
2480
|
-
indent = self.
|
2934
|
+
indent = self.ops.treeview_indent
|
2481
2935
|
return indent * max(self.get_node_level(self.tree[iid]))
|
2482
2936
|
|
2483
2937
|
def get_iid_level_indent(self, iid: str) -> tuple[int, int]:
|
2484
|
-
if isinstance(self.
|
2485
|
-
indent = self.MT.index_txt_width * int(self.
|
2938
|
+
if isinstance(self.ops.treeview_indent, str):
|
2939
|
+
indent = self.MT.index_txt_width * int(self.ops.treeview_indent)
|
2486
2940
|
else:
|
2487
|
-
indent = self.
|
2941
|
+
indent = self.ops.treeview_indent
|
2488
2942
|
level = max(self.get_node_level(self.tree[iid]))
|
2489
2943
|
return level, indent * level
|
2490
2944
|
|
2491
|
-
def
|
2492
|
-
if
|
2493
|
-
|
2494
|
-
if not
|
2495
|
-
self.tree_open_ids.discard(
|
2945
|
+
def remove_iid_from_parents_children(self, iid: str) -> None:
|
2946
|
+
if parent_node := self.parent_node(iid):
|
2947
|
+
parent_node.children.remove(iid)
|
2948
|
+
if not parent_node.children:
|
2949
|
+
self.tree_open_ids.discard(parent_node.iid)
|
2496
2950
|
|
2497
2951
|
def build_pid_causes_recursive_loop(self, iid: str, pid: str) -> bool:
|
2498
2952
|
return any(
|
@@ -2506,4 +2960,121 @@ class RowIndex(tk.Canvas):
|
|
2506
2960
|
def move_pid_causes_recursive_loop(self, to_move_iid: str, move_to_parent: str) -> bool:
|
2507
2961
|
# if the parent the item is being moved under is one of the item's descendants
|
2508
2962
|
# then it is a recursive loop
|
2509
|
-
return
|
2963
|
+
return to_move_iid == move_to_parent or any(
|
2964
|
+
move_to_parent == diid for diid in self.get_iid_descendants(to_move_iid)
|
2965
|
+
)
|
2966
|
+
|
2967
|
+
def tree_build(
|
2968
|
+
self,
|
2969
|
+
data: list[list[object]],
|
2970
|
+
iid_column: int,
|
2971
|
+
parent_column: int,
|
2972
|
+
text_column: None | int | list[str] = None,
|
2973
|
+
push_ops: bool = False,
|
2974
|
+
row_heights: Sequence[int] | None | False = None,
|
2975
|
+
open_ids: AnyIter[str] | None = None,
|
2976
|
+
safety: bool = True,
|
2977
|
+
ncols: int | None = None,
|
2978
|
+
lower: bool = False,
|
2979
|
+
include_iid_column: bool = True,
|
2980
|
+
include_parent_column: bool = True,
|
2981
|
+
include_text_column: bool = True,
|
2982
|
+
) -> None:
|
2983
|
+
self.PAR.reset(cell_options=False, column_widths=False, header=False, redraw=False)
|
2984
|
+
if text_column is None:
|
2985
|
+
text_column = iid_column
|
2986
|
+
tally_of_ids = defaultdict(lambda: -1)
|
2987
|
+
if not isinstance(ncols, int):
|
2988
|
+
ncols = max(map(len, data), default=0)
|
2989
|
+
for rn, row in enumerate(data):
|
2990
|
+
if safety and ncols > (lnr := len(row)):
|
2991
|
+
row += self.MT.get_empty_row_seq(rn, end=ncols, start=lnr)
|
2992
|
+
if lower:
|
2993
|
+
iid = row[iid_column].lower()
|
2994
|
+
pid = row[parent_column].lower()
|
2995
|
+
else:
|
2996
|
+
iid = row[iid_column]
|
2997
|
+
pid = row[parent_column]
|
2998
|
+
if safety:
|
2999
|
+
if not iid:
|
3000
|
+
continue
|
3001
|
+
tally_of_ids[iid] += 1
|
3002
|
+
if tally_of_ids[iid]:
|
3003
|
+
x = 1
|
3004
|
+
while iid in tally_of_ids:
|
3005
|
+
new = f"{row[iid_column]}_DUPLICATED_{x}"
|
3006
|
+
iid = new.lower() if lower else new
|
3007
|
+
x += 1
|
3008
|
+
tally_of_ids[iid] += 1
|
3009
|
+
row[iid_column] = new
|
3010
|
+
if iid in self.tree:
|
3011
|
+
self.tree[iid].text = row[text_column] if isinstance(text_column, int) else text_column[rn]
|
3012
|
+
else:
|
3013
|
+
self.tree[iid] = Node(row[text_column] if isinstance(text_column, int) else text_column[rn], iid, "")
|
3014
|
+
if safety and (iid == pid or self.build_pid_causes_recursive_loop(iid, pid)):
|
3015
|
+
row[parent_column] = ""
|
3016
|
+
pid = ""
|
3017
|
+
if pid:
|
3018
|
+
if pid in self.tree:
|
3019
|
+
self.tree[pid].children.append(iid)
|
3020
|
+
else:
|
3021
|
+
self.tree[pid] = Node(
|
3022
|
+
text=row[text_column] if isinstance(text_column, int) else text_column[rn],
|
3023
|
+
iid=pid,
|
3024
|
+
children=[iid],
|
3025
|
+
)
|
3026
|
+
self.tree[iid].parent = pid
|
3027
|
+
else:
|
3028
|
+
self.tree[iid].parent = ""
|
3029
|
+
self.tree_rns[iid] = rn
|
3030
|
+
if safety:
|
3031
|
+
for n in self.tree.values():
|
3032
|
+
if n.parent is None:
|
3033
|
+
n.parent = ""
|
3034
|
+
newrow = self.MT.get_empty_row_seq(len(data), ncols)
|
3035
|
+
newrow[iid_column] = n.iid
|
3036
|
+
self.tree_rns[n.iid] = len(data)
|
3037
|
+
data.append(newrow)
|
3038
|
+
insert_rows = partial(
|
3039
|
+
self.PAR.insert_rows,
|
3040
|
+
idx=0,
|
3041
|
+
heights={} if row_heights is False else row_heights,
|
3042
|
+
row_index=True,
|
3043
|
+
create_selections=False,
|
3044
|
+
fill=False,
|
3045
|
+
undo=False,
|
3046
|
+
push_ops=push_ops,
|
3047
|
+
redraw=False,
|
3048
|
+
)
|
3049
|
+
exclude = set()
|
3050
|
+
if not include_iid_column:
|
3051
|
+
exclude.add(iid_column)
|
3052
|
+
if not include_parent_column:
|
3053
|
+
exclude.add(parent_column)
|
3054
|
+
if isinstance(text_column, int) and not include_text_column:
|
3055
|
+
exclude.add(text_column)
|
3056
|
+
if exclude:
|
3057
|
+
insert_rows(
|
3058
|
+
rows=[
|
3059
|
+
[self.tree[iid]] + [e for i, e in enumerate(data[self.tree_rns[iid]]) if i not in exclude]
|
3060
|
+
for iid in self.PAR.get_iids()
|
3061
|
+
],
|
3062
|
+
tree=False,
|
3063
|
+
)
|
3064
|
+
else:
|
3065
|
+
insert_rows(
|
3066
|
+
rows=[[self.tree[iid]] + data[self.tree_rns[iid]] for iid in self.PAR.get_iids()],
|
3067
|
+
tree=False,
|
3068
|
+
)
|
3069
|
+
self.MT.all_rows_displayed = False
|
3070
|
+
self.MT.displayed_rows = list(range(len(self.MT._row_index)))
|
3071
|
+
self.tree_rns = {n.iid: i for i, n in enumerate(self.MT._row_index)}
|
3072
|
+
if open_ids:
|
3073
|
+
self.PAR.tree_set_open(open_ids=open_ids)
|
3074
|
+
else:
|
3075
|
+
self.PAR.hide_rows(
|
3076
|
+
{self.tree_rns[iid] for iid in self.PAR.get_children() if self.tree[iid].parent},
|
3077
|
+
deselect_all=False,
|
3078
|
+
data_indexes=True,
|
3079
|
+
row_heights=False if row_heights is False else True,
|
3080
|
+
)
|