tksheet 7.5.5__py3-none-any.whl → 7.5.7__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 +1 -1
- tksheet/column_headers.py +230 -87
- tksheet/constants.py +1 -0
- tksheet/find_window.py +4 -4
- tksheet/formatters.py +4 -2
- tksheet/functions.py +91 -35
- tksheet/main_table.py +360 -631
- tksheet/menus.py +494 -0
- tksheet/other_classes.py +3 -0
- tksheet/row_index.py +229 -91
- tksheet/sheet.py +99 -39
- tksheet/sheet_options.py +5 -1
- tksheet/tksheet_types.py +1 -0
- tksheet/tooltip.py +335 -0
- {tksheet-7.5.5.dist-info → tksheet-7.5.7.dist-info}/METADATA +1 -1
- tksheet-7.5.7.dist-info/RECORD +24 -0
- {tksheet-7.5.5.dist-info → tksheet-7.5.7.dist-info}/WHEEL +1 -1
- tksheet-7.5.5.dist-info/RECORD +0 -22
- {tksheet-7.5.5.dist-info → tksheet-7.5.7.dist-info}/licenses/LICENSE.txt +0 -0
- {tksheet-7.5.5.dist-info → tksheet-7.5.7.dist-info}/top_level.txt +0 -0
tksheet/__init__.py
CHANGED
tksheet/column_headers.py
CHANGED
@@ -24,22 +24,28 @@ from .functions import (
|
|
24
24
|
event_dict,
|
25
25
|
event_has_char_key,
|
26
26
|
event_opens_dropdown_or_checkbox,
|
27
|
+
get_bg_fg,
|
27
28
|
get_menu_kwargs,
|
28
29
|
get_n2a,
|
29
30
|
int_x_tuple,
|
30
31
|
is_contiguous,
|
31
32
|
mod_event_val,
|
32
33
|
new_tk_event,
|
34
|
+
recursive_bind,
|
33
35
|
rounded_box_coords,
|
36
|
+
safe_copy,
|
34
37
|
stored_event_dict,
|
35
38
|
try_b_index,
|
36
39
|
try_binding,
|
40
|
+
widget_descendants,
|
37
41
|
wrap_text,
|
38
42
|
)
|
43
|
+
from .menus import build_empty_rc_menu, build_header_rc_menu
|
39
44
|
from .other_classes import DraggedRowColumn, DropdownStorage, EventDataDict, TextEditorStorage
|
40
45
|
from .row_index import RowIndex
|
41
46
|
from .sorting import sort_column, sort_rows_by_column, sort_tree_rows_by_column
|
42
47
|
from .text_editor import TextEditor
|
48
|
+
from .tooltip import Tooltip
|
43
49
|
|
44
50
|
|
45
51
|
class ColumnHeaders(tk.Canvas):
|
@@ -55,6 +61,18 @@ class ColumnHeaders(tk.Canvas):
|
|
55
61
|
self.MT = None # is set from within MainTable() __init__
|
56
62
|
self.RI: RowIndex | None = None # is set from within MainTable() __init__
|
57
63
|
self.TL = None # is set from within TopLeftRectangle() __init__
|
64
|
+
self.tooltip = Tooltip(
|
65
|
+
**{
|
66
|
+
"parent": self,
|
67
|
+
"sheet_ops": self.ops,
|
68
|
+
"menu_kwargs": get_menu_kwargs(self.ops),
|
69
|
+
**get_bg_fg(self.ops),
|
70
|
+
"scrollbar_style": f"Sheet{self.PAR.unique_id}.Vertical.TScrollbar",
|
71
|
+
}
|
72
|
+
)
|
73
|
+
self.tooltip_widgets = widget_descendants(self.tooltip)
|
74
|
+
self.tooltip_coords, self.tooltip_after_id, self.tooltip_showing = None, None, False
|
75
|
+
recursive_bind(self.tooltip, "<Leave>", self.close_tooltip_save)
|
58
76
|
self.current_cursor = ""
|
59
77
|
self.popup_menu_loc = None
|
60
78
|
self.extra_begin_edit_cell_func = None
|
@@ -106,7 +124,7 @@ class ColumnHeaders(tk.Canvas):
|
|
106
124
|
self.disp_resize_lines = {}
|
107
125
|
self.disp_dropdown = {}
|
108
126
|
self.disp_checkbox = {}
|
109
|
-
self.
|
127
|
+
self.disp_corners = set()
|
110
128
|
self.hidd_text = {}
|
111
129
|
self.hidd_high = {}
|
112
130
|
self.hidd_grid = {}
|
@@ -114,7 +132,7 @@ class ColumnHeaders(tk.Canvas):
|
|
114
132
|
self.hidd_resize_lines = {}
|
115
133
|
self.hidd_dropdown = {}
|
116
134
|
self.hidd_checkbox = {}
|
117
|
-
self.
|
135
|
+
self.hidd_corners = set()
|
118
136
|
|
119
137
|
self.align = kwargs["header_align"]
|
120
138
|
self.basic_bindings()
|
@@ -177,12 +195,14 @@ class ColumnHeaders(tk.Canvas):
|
|
177
195
|
c = len(self.MT.col_positions) - 1
|
178
196
|
if self.MT.rc_popup_menus_enabled:
|
179
197
|
popup_menu = self.MT.empty_rc_popup_menu
|
198
|
+
build_empty_rc_menu(self.MT, popup_menu)
|
180
199
|
elif self.col_selection_enabled and not self.currently_resizing_width and not self.currently_resizing_height:
|
181
200
|
c = self.MT.identify_col(x=event.x)
|
182
201
|
if c < len(self.MT.col_positions) - 1:
|
183
202
|
if self.MT.col_selected(c):
|
184
203
|
if self.MT.rc_popup_menus_enabled:
|
185
204
|
popup_menu = self.ch_rc_popup_menu
|
205
|
+
build_header_rc_menu(self.MT, popup_menu, c)
|
186
206
|
else:
|
187
207
|
if self.MT.single_selection_enabled and self.MT.rc_select_enabled:
|
188
208
|
self.select_col(c, redraw=True)
|
@@ -190,11 +210,10 @@ class ColumnHeaders(tk.Canvas):
|
|
190
210
|
self.toggle_select_col(c, redraw=True)
|
191
211
|
if self.MT.rc_popup_menus_enabled:
|
192
212
|
popup_menu = self.ch_rc_popup_menu
|
213
|
+
build_header_rc_menu(self.MT, popup_menu, c)
|
193
214
|
try_binding(self.extra_rc_func, event)
|
194
215
|
if popup_menu is not None:
|
195
216
|
self.popup_menu_loc = c
|
196
|
-
self.MT.popup_menu_disable_edit_if_readonly(popup_menu)
|
197
|
-
self.MT.popup_menu_disable_undo_redo(popup_menu)
|
198
217
|
popup_menu.tk_popup(event.x_root, event.y_root)
|
199
218
|
|
200
219
|
def ctrl_b1_press(self, event: Any) -> None:
|
@@ -880,7 +899,7 @@ class ColumnHeaders(tk.Canvas):
|
|
880
899
|
event_data = self.MT.new_event_dict("edit_table")
|
881
900
|
try_binding(self.MT.extra_begin_sort_cells_func, event_data)
|
882
901
|
if key is None:
|
883
|
-
key = self.
|
902
|
+
key = self.ops.sort_key
|
884
903
|
for c in columns:
|
885
904
|
datacn = self.MT.datacn(c)
|
886
905
|
for r, val in enumerate(
|
@@ -932,7 +951,7 @@ class ColumnHeaders(tk.Canvas):
|
|
932
951
|
column = self.MT.datacn(self.MT.selected.column)
|
933
952
|
if try_binding(self.ch_extra_begin_sort_rows_func, event_data, "begin_move_rows"):
|
934
953
|
if key is None:
|
935
|
-
key = self.
|
954
|
+
key = self.ops.sort_key
|
936
955
|
disp_new_idxs, disp_row_ctr = {}, 0
|
937
956
|
if self.ops.treeview:
|
938
957
|
new_nodes_order, data_new_idxs = sort_tree_rows_by_column(
|
@@ -961,7 +980,7 @@ class ColumnHeaders(tk.Canvas):
|
|
961
980
|
if (idx := try_b_index(self.MT.displayed_rows, old_idx)) is not None:
|
962
981
|
disp_new_idxs[idx] = disp_row_ctr
|
963
982
|
disp_row_ctr += 1
|
964
|
-
if self.
|
983
|
+
if self.ops.treeview:
|
965
984
|
data_new_idxs, disp_new_idxs, event_data = self.MT.move_rows_adjust_options_dict(
|
966
985
|
data_new_idxs=data_new_idxs,
|
967
986
|
data_old_idxs=dict(zip(data_new_idxs.values(), data_new_idxs)),
|
@@ -1253,44 +1272,60 @@ class ColumnHeaders(tk.Canvas):
|
|
1253
1272
|
redrawn = False
|
1254
1273
|
kwargs = self.get_cell_kwargs(datacn, key="highlight")
|
1255
1274
|
if kwargs:
|
1256
|
-
|
1257
|
-
if
|
1258
|
-
|
1275
|
+
high_bg = kwargs[0]
|
1276
|
+
if high_bg and not high_bg.startswith("#"):
|
1277
|
+
high_bg = color_map[high_bg]
|
1259
1278
|
if "columns" in selections and c in selections["columns"]:
|
1260
1279
|
txtfg = (
|
1261
1280
|
self.ops.header_selected_columns_fg
|
1262
1281
|
if kwargs[1] is None or self.ops.display_selected_fg_over_highlights
|
1263
1282
|
else kwargs[1]
|
1264
1283
|
)
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1284
|
+
redrawn = self.redraw_highlight(
|
1285
|
+
fc + 1,
|
1286
|
+
0,
|
1287
|
+
sc,
|
1288
|
+
self.current_height - 1,
|
1289
|
+
fill=self.ops.header_selected_columns_bg
|
1290
|
+
if high_bg is None
|
1291
|
+
else (
|
1292
|
+
f"#{int((int(high_bg[1:3], 16) + int(sel_cols_bg[1:3], 16)) / 2):02X}"
|
1293
|
+
+ f"{int((int(high_bg[3:5], 16) + int(sel_cols_bg[3:5], 16)) / 2):02X}"
|
1294
|
+
+ f"{int((int(high_bg[5:], 16) + int(sel_cols_bg[5:], 16)) / 2):02X}"
|
1295
|
+
),
|
1296
|
+
outline=self.ops.header_fg if has_dd and self.ops.show_dropdown_borders else "",
|
1297
|
+
)
|
1271
1298
|
elif "cells" in selections and c in selections["cells"]:
|
1272
1299
|
txtfg = (
|
1273
1300
|
self.ops.header_selected_cells_fg
|
1274
1301
|
if kwargs[1] is None or self.ops.display_selected_fg_over_highlights
|
1275
1302
|
else kwargs[1]
|
1276
1303
|
)
|
1277
|
-
if fill:
|
1278
|
-
fill = (
|
1279
|
-
f"#{int((int(fill[1:3], 16) + int(sel_cells_bg[1:3], 16)) / 2):02X}"
|
1280
|
-
+ f"{int((int(fill[3:5], 16) + int(sel_cells_bg[3:5], 16)) / 2):02X}"
|
1281
|
-
+ f"{int((int(fill[5:], 16) + int(sel_cells_bg[5:], 16)) / 2):02X}"
|
1282
|
-
)
|
1283
|
-
else:
|
1284
|
-
txtfg = self.ops.header_fg if kwargs[1] is None else kwargs[1]
|
1285
|
-
if fill:
|
1286
1304
|
redrawn = self.redraw_highlight(
|
1287
1305
|
fc + 1,
|
1288
1306
|
0,
|
1289
1307
|
sc,
|
1290
1308
|
self.current_height - 1,
|
1291
|
-
fill=
|
1309
|
+
fill=self.ops.header_selected_cells_bg
|
1310
|
+
if high_bg is None
|
1311
|
+
else (
|
1312
|
+
f"#{int((int(high_bg[1:3], 16) + int(sel_cells_bg[1:3], 16)) / 2):02X}"
|
1313
|
+
+ f"{int((int(high_bg[3:5], 16) + int(sel_cells_bg[3:5], 16)) / 2):02X}"
|
1314
|
+
+ f"{int((int(high_bg[5:], 16) + int(sel_cells_bg[5:], 16)) / 2):02X}"
|
1315
|
+
),
|
1292
1316
|
outline=self.ops.header_fg if has_dd and self.ops.show_dropdown_borders else "",
|
1293
1317
|
)
|
1318
|
+
else:
|
1319
|
+
txtfg = self.ops.header_fg if kwargs[1] is None else kwargs[1]
|
1320
|
+
if high_bg:
|
1321
|
+
redrawn = self.redraw_highlight(
|
1322
|
+
fc + 1,
|
1323
|
+
0,
|
1324
|
+
sc,
|
1325
|
+
self.current_height - 1,
|
1326
|
+
fill=high_bg,
|
1327
|
+
outline=self.ops.header_fg if has_dd and self.ops.show_dropdown_borders else "",
|
1328
|
+
)
|
1294
1329
|
elif not kwargs:
|
1295
1330
|
if "columns" in selections and c in selections["columns"]:
|
1296
1331
|
txtfg = self.ops.header_selected_columns_fg
|
@@ -1471,6 +1506,17 @@ class ColumnHeaders(tk.Canvas):
|
|
1471
1506
|
self.MT.char_widths[self.header_font][c] = wd
|
1472
1507
|
return wd
|
1473
1508
|
|
1509
|
+
def redraw_corner(self, x: float, y: float) -> None:
|
1510
|
+
if self.hidd_corners:
|
1511
|
+
iid = self.hidd_corners.pop()
|
1512
|
+
self.coords(iid, x - 10, y, x, y, x, y + 10)
|
1513
|
+
self.itemconfig(iid, fill=self.ops.header_grid_fg, state="normal")
|
1514
|
+
self.disp_corners.add(iid)
|
1515
|
+
else:
|
1516
|
+
self.disp_corners.add(
|
1517
|
+
self.create_polygon(x - 10, y, x, y, x, y + 10, fill=self.ops.header_grid_fg, tags="lift")
|
1518
|
+
)
|
1519
|
+
|
1474
1520
|
def redraw_grid_and_text(
|
1475
1521
|
self,
|
1476
1522
|
last_col_line_pos: float,
|
@@ -1496,6 +1542,8 @@ class ColumnHeaders(tk.Canvas):
|
|
1496
1542
|
self.disp_dropdown = {}
|
1497
1543
|
self.hidd_checkbox.update(self.disp_checkbox)
|
1498
1544
|
self.disp_checkbox = {}
|
1545
|
+
self.hidd_corners.update(self.disp_corners)
|
1546
|
+
self.disp_corners = set()
|
1499
1547
|
self.visible_col_dividers = {}
|
1500
1548
|
self.col_height_resize_bbox = (
|
1501
1549
|
scrollpos_left,
|
@@ -1533,12 +1581,12 @@ class ColumnHeaders(tk.Canvas):
|
|
1533
1581
|
)
|
1534
1582
|
self.redraw_gridline(points=points, fill=self.ops.header_grid_fg, width=1)
|
1535
1583
|
|
1536
|
-
|
1584
|
+
sel_cells_bg = (
|
1537
1585
|
self.ops.header_selected_cells_bg
|
1538
1586
|
if self.ops.header_selected_cells_bg.startswith("#")
|
1539
1587
|
else color_map[self.ops.header_selected_cells_bg]
|
1540
1588
|
)
|
1541
|
-
|
1589
|
+
sel_cols_bg = (
|
1542
1590
|
self.ops.header_selected_columns_bg
|
1543
1591
|
if self.ops.header_selected_columns_bg.startswith("#")
|
1544
1592
|
else color_map[self.ops.header_selected_columns_bg]
|
@@ -1548,6 +1596,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1548
1596
|
dd_coords = self.dropdown.get_coords()
|
1549
1597
|
wrap = self.ops.header_wrap
|
1550
1598
|
txt_h = self.MT.header_txt_height
|
1599
|
+
note_corners = self.ops.note_corners
|
1551
1600
|
for c in range(text_start_col, text_end_col):
|
1552
1601
|
draw_y = 3
|
1553
1602
|
cleftgridln = self.MT.col_positions[c]
|
@@ -1626,6 +1675,10 @@ class ColumnHeaders(tk.Canvas):
|
|
1626
1675
|
or (align[-1] == "n" and cleftgridln + 5 > scrollpos_right)
|
1627
1676
|
):
|
1628
1677
|
continue
|
1678
|
+
|
1679
|
+
if note_corners and max_width > 5 and datacn in self.cell_options and "note" in self.cell_options[datacn]:
|
1680
|
+
self.redraw_corner(crightgridln, 0)
|
1681
|
+
|
1629
1682
|
text = self.cell_str(datacn, fix=False)
|
1630
1683
|
if not text:
|
1631
1684
|
continue
|
@@ -1648,6 +1701,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1648
1701
|
fill=fill,
|
1649
1702
|
font=font,
|
1650
1703
|
anchor=align,
|
1704
|
+
tags=("lift", "t", f"{c}"),
|
1651
1705
|
)
|
1652
1706
|
else:
|
1653
1707
|
self.itemconfig(
|
@@ -1657,6 +1711,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1657
1711
|
font=font,
|
1658
1712
|
anchor=align,
|
1659
1713
|
state="normal",
|
1714
|
+
tags=("lift", "t", f"{c}"),
|
1660
1715
|
)
|
1661
1716
|
else:
|
1662
1717
|
iid = self.create_text(
|
@@ -1666,7 +1721,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1666
1721
|
fill=fill,
|
1667
1722
|
font=font,
|
1668
1723
|
anchor=align,
|
1669
|
-
|
1724
|
+
tags=("lift", "t", f"{c}"),
|
1670
1725
|
)
|
1671
1726
|
self.disp_text[iid] = True
|
1672
1727
|
else:
|
@@ -1681,6 +1736,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1681
1736
|
fill=fill,
|
1682
1737
|
font=font,
|
1683
1738
|
anchor=align,
|
1739
|
+
tags=("lift", "t", f"{c}"),
|
1684
1740
|
)
|
1685
1741
|
else:
|
1686
1742
|
self.itemconfig(
|
@@ -1690,6 +1746,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1690
1746
|
font=font,
|
1691
1747
|
anchor=align,
|
1692
1748
|
state="normal",
|
1749
|
+
tags=("lift", "t", f"{c}"),
|
1693
1750
|
)
|
1694
1751
|
else:
|
1695
1752
|
iid = self.create_text(
|
@@ -1699,7 +1756,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1699
1756
|
fill=fill,
|
1700
1757
|
font=font,
|
1701
1758
|
anchor=align,
|
1702
|
-
|
1759
|
+
tags=("lift", "t", f"{c}"),
|
1703
1760
|
)
|
1704
1761
|
self.disp_text[iid] = True
|
1705
1762
|
draw_y += self.MT.header_txt_height
|
@@ -1709,11 +1766,115 @@ class ColumnHeaders(tk.Canvas):
|
|
1709
1766
|
if showing:
|
1710
1767
|
self.itemconfig(iid, state="hidden")
|
1711
1768
|
dct[iid] = False
|
1769
|
+
for iid in self.hidd_corners:
|
1770
|
+
self.itemconfig(iid, state="hidden")
|
1712
1771
|
self.tag_raise("lift")
|
1713
1772
|
if self.disp_resize_lines:
|
1714
1773
|
self.tag_raise("rw")
|
1774
|
+
self.tag_bind("t", "<Enter>", self.enter_text)
|
1775
|
+
self.tag_bind("t", "<Leave>", self.leave_text)
|
1715
1776
|
return True
|
1716
1777
|
|
1778
|
+
def enter_text(self, event: tk.Event | None = None) -> None:
|
1779
|
+
if self.text_editor.open or self.dropdown.open:
|
1780
|
+
return
|
1781
|
+
can_x, can_y = self.canvasx(event.x), self.canvasy(event.y)
|
1782
|
+
for i in self.find_overlapping(can_x - 1, can_y - 1, can_x + 1, can_y + 1):
|
1783
|
+
try:
|
1784
|
+
if (coords := self.gettags(i)[2]) == self.tooltip_coords:
|
1785
|
+
return
|
1786
|
+
self.tooltip_coords = coords
|
1787
|
+
self.tooltip_last_x, self.tooltip_last_y = self.winfo_pointerx(), self.winfo_pointery()
|
1788
|
+
self.start_tooltip_timer()
|
1789
|
+
return
|
1790
|
+
except Exception:
|
1791
|
+
continue
|
1792
|
+
|
1793
|
+
def leave_text(self, event: tk.Event | None = None) -> None:
|
1794
|
+
if self.tooltip_after_id is not None:
|
1795
|
+
self.after_cancel(self.tooltip_after_id)
|
1796
|
+
self.tooltip_after_id = None
|
1797
|
+
if self.tooltip_showing:
|
1798
|
+
if self.winfo_containing(self.winfo_pointerx(), self.winfo_pointery()) not in self.tooltip_widgets:
|
1799
|
+
self.close_tooltip_save()
|
1800
|
+
else:
|
1801
|
+
self.tooltip_coords = None
|
1802
|
+
|
1803
|
+
def start_tooltip_timer(self) -> None:
|
1804
|
+
self.tooltip_after_id = self.after(1000, self.check_and_show_tooltip)
|
1805
|
+
|
1806
|
+
def check_and_show_tooltip(self, event: tk.Event | None = None) -> None:
|
1807
|
+
current_x, current_y = self.winfo_pointerx(), self.winfo_pointery()
|
1808
|
+
if current_x < 0 or current_y < 0:
|
1809
|
+
return
|
1810
|
+
if abs(current_x - self.tooltip_last_x) <= 1 and abs(current_y - self.tooltip_last_y) <= 1:
|
1811
|
+
self.show_tooltip()
|
1812
|
+
else:
|
1813
|
+
self.tooltip_last_x, self.tooltip_last_y = current_x, current_y
|
1814
|
+
self.tooltip_after_id = self.after(400, self.check_and_show_tooltip)
|
1815
|
+
|
1816
|
+
def hide_tooltip(self):
|
1817
|
+
self.tooltip.withdraw()
|
1818
|
+
self.tooltip_showing, self.tooltip_coords = False, None
|
1819
|
+
|
1820
|
+
def show_tooltip(self) -> None:
|
1821
|
+
c = int(self.tooltip_coords)
|
1822
|
+
datacn = self.MT.datacn(c)
|
1823
|
+
kws = self.get_cell_kwargs(datacn, key="note")
|
1824
|
+
if not self.ops.tooltips and not kws and not self.ops.user_can_create_notes:
|
1825
|
+
return
|
1826
|
+
cell_readonly = self.get_cell_kwargs(datacn, "readonly") or not self.MT.index_edit_cell_enabled()
|
1827
|
+
if kws:
|
1828
|
+
note = kws["note"]
|
1829
|
+
note_readonly = kws["readonly"]
|
1830
|
+
elif self.ops.user_can_create_notes:
|
1831
|
+
note = ""
|
1832
|
+
note_readonly = bool(cell_readonly)
|
1833
|
+
else:
|
1834
|
+
note = None
|
1835
|
+
note_readonly = True
|
1836
|
+
self.tooltip.reset(
|
1837
|
+
**{
|
1838
|
+
"text": f"{self.get_cell_data(datacn, none_to_empty_str=True)}",
|
1839
|
+
"cell_readonly": cell_readonly,
|
1840
|
+
"note": note,
|
1841
|
+
"note_readonly": note_readonly,
|
1842
|
+
"row": 0,
|
1843
|
+
"col": c,
|
1844
|
+
"menu_kwargs": get_menu_kwargs(self.ops),
|
1845
|
+
**get_bg_fg(self.ops),
|
1846
|
+
"user_can_create_notes": self.ops.user_can_create_notes,
|
1847
|
+
"note_only": not self.ops.tooltips and isinstance(note, str),
|
1848
|
+
"width": self.ops.tooltip_width,
|
1849
|
+
"height": self.ops.tooltip_height,
|
1850
|
+
}
|
1851
|
+
)
|
1852
|
+
self.tooltip.set_position(self.tooltip_last_x - 4, self.tooltip_last_y - 4)
|
1853
|
+
self.tooltip_showing = True
|
1854
|
+
|
1855
|
+
def close_tooltip_save(self, event: tk.Event | None = None) -> None:
|
1856
|
+
widget = self.winfo_containing(self.winfo_pointerx(), self.winfo_pointery())
|
1857
|
+
if any(widget == tw for tw in self.tooltip_widgets):
|
1858
|
+
try:
|
1859
|
+
if self.tooltip.notebook.index("current") == 0:
|
1860
|
+
self.tooltip.content_text.focus_set()
|
1861
|
+
else:
|
1862
|
+
self.tooltip.note_text.focus_set()
|
1863
|
+
except Exception:
|
1864
|
+
self.tooltip.content_text.focus_set()
|
1865
|
+
return
|
1866
|
+
if not self.tooltip.cell_readonly:
|
1867
|
+
_, c, cell, note = self.tooltip.get()
|
1868
|
+
datacn = self.MT.datacn(c)
|
1869
|
+
event_data = self.new_single_edit_event(c, datacn, "??", self.get_cell_data(datacn), cell)
|
1870
|
+
self.do_single_edit(c, datacn, event_data, cell)
|
1871
|
+
if not self.tooltip.note_readonly:
|
1872
|
+
span = self.PAR.span(None, datacn, None, datacn + 1).options(table=False, header=True)
|
1873
|
+
self.PAR.note(span, note=note if note else None, readonly=False)
|
1874
|
+
self.hide_tooltip()
|
1875
|
+
self.MT.refresh()
|
1876
|
+
self.focus_set()
|
1877
|
+
|
1717
1878
|
def get_redraw_selections(self, startc: int, endc: int) -> dict[str, set[int]]:
|
1718
1879
|
d = defaultdict(set)
|
1719
1880
|
for _, box in self.MT.get_selection_items():
|
@@ -1942,6 +2103,18 @@ class ColumnHeaders(tk.Canvas):
|
|
1942
2103
|
self.itemconfig(self.text_editor.canvas_id, state="hidden")
|
1943
2104
|
self.text_editor.open = False
|
1944
2105
|
|
2106
|
+
def do_single_edit(self, c: int, datacn: int, event_data: EventDataDict, val):
|
2107
|
+
edited = False
|
2108
|
+
set_data = partial(self.set_cell_data_undo, c=c, datacn=datacn, check_input_valid=False)
|
2109
|
+
if self.MT.edit_validation_func:
|
2110
|
+
val = self.MT.edit_validation_func(event_data)
|
2111
|
+
if val is not None and self.input_valid_for_cell(datacn, val):
|
2112
|
+
edited = set_data(value=val)
|
2113
|
+
elif self.input_valid_for_cell(datacn, val):
|
2114
|
+
edited = set_data(value=val)
|
2115
|
+
if edited:
|
2116
|
+
try_binding(self.extra_end_edit_cell_func, event_data)
|
2117
|
+
|
1945
2118
|
# c is displayed col
|
1946
2119
|
def close_text_editor(self, event: tk.Event) -> Literal["break"] | None:
|
1947
2120
|
# checking if text editor should be closed or not
|
@@ -1965,33 +2138,8 @@ class ColumnHeaders(tk.Canvas):
|
|
1965
2138
|
text_editor_value = self.text_editor.get()
|
1966
2139
|
c = self.text_editor.column
|
1967
2140
|
datacn = c if self.MT.all_columns_displayed else self.MT.displayed_columns[c]
|
1968
|
-
event_data =
|
1969
|
-
|
1970
|
-
sheet=self.PAR.name,
|
1971
|
-
widget=self,
|
1972
|
-
cells_header={datacn: self.get_cell_data(datacn)},
|
1973
|
-
key=event.keysym,
|
1974
|
-
value=text_editor_value,
|
1975
|
-
loc=c,
|
1976
|
-
column=c,
|
1977
|
-
boxes=self.MT.get_boxes(),
|
1978
|
-
selected=self.MT.selected,
|
1979
|
-
)
|
1980
|
-
edited = False
|
1981
|
-
set_data = partial(
|
1982
|
-
self.set_cell_data_undo,
|
1983
|
-
c=c,
|
1984
|
-
datacn=datacn,
|
1985
|
-
check_input_valid=False,
|
1986
|
-
)
|
1987
|
-
if self.MT.edit_validation_func:
|
1988
|
-
text_editor_value = self.MT.edit_validation_func(event_data)
|
1989
|
-
if text_editor_value is not None and self.input_valid_for_cell(datacn, text_editor_value):
|
1990
|
-
edited = set_data(value=text_editor_value)
|
1991
|
-
elif self.input_valid_for_cell(datacn, text_editor_value):
|
1992
|
-
edited = set_data(value=text_editor_value)
|
1993
|
-
if edited:
|
1994
|
-
try_binding(self.extra_end_edit_cell_func, event_data)
|
2141
|
+
event_data = self.new_single_edit_event(c, datacn, event.keysym, self.get_cell_data(datacn), text_editor_value)
|
2142
|
+
self.do_single_edit(c, datacn, event_data, text_editor_value)
|
1995
2143
|
self.MT.recreate_all_selection_boxes()
|
1996
2144
|
self.hide_text_editor_and_dropdown()
|
1997
2145
|
if event.keysym != "FocusOut":
|
@@ -2125,18 +2273,7 @@ class ColumnHeaders(tk.Canvas):
|
|
2125
2273
|
if c is not None and selection is not None:
|
2126
2274
|
datacn = c if self.MT.all_columns_displayed else self.MT.displayed_columns[c]
|
2127
2275
|
kwargs = self.get_cell_kwargs(datacn, key="dropdown")
|
2128
|
-
event_data =
|
2129
|
-
name="end_edit_header",
|
2130
|
-
sheet=self.PAR.name,
|
2131
|
-
widget=self,
|
2132
|
-
cells_header={datacn: self.get_cell_data(datacn)},
|
2133
|
-
key="??",
|
2134
|
-
value=selection,
|
2135
|
-
loc=c,
|
2136
|
-
column=c,
|
2137
|
-
boxes=self.MT.get_boxes(),
|
2138
|
-
selected=self.MT.selected,
|
2139
|
-
)
|
2276
|
+
event_data = self.new_single_edit_event(c, datacn, "??", self.get_cell_data(datacn), selection)
|
2140
2277
|
try_binding(kwargs["select_function"], event_data)
|
2141
2278
|
selection = selection if not self.MT.edit_validation_func else self.MT.edit_validation_func(event_data)
|
2142
2279
|
if selection is not None:
|
@@ -2308,12 +2445,14 @@ class ColumnHeaders(tk.Canvas):
|
|
2308
2445
|
def get_value_for_empty_cell(self, datacn: int, c_ops: bool = True) -> Any:
|
2309
2446
|
if self.get_cell_kwargs(datacn, key="checkbox", cell=c_ops):
|
2310
2447
|
return False
|
2311
|
-
elif (
|
2312
|
-
|
2313
|
-
|
2314
|
-
|
2315
|
-
|
2316
|
-
|
2448
|
+
elif (kwargs := self.get_cell_kwargs(datacn, key="dropdown", cell=c_ops)) and kwargs["validate_input"]:
|
2449
|
+
if kwargs["default_value"] is None:
|
2450
|
+
if kwargs["values"]:
|
2451
|
+
return safe_copy(kwargs["values"][0])
|
2452
|
+
else:
|
2453
|
+
return ""
|
2454
|
+
else:
|
2455
|
+
return safe_copy(kwargs["default_value"])
|
2317
2456
|
else:
|
2318
2457
|
return ""
|
2319
2458
|
|
@@ -2365,18 +2504,7 @@ class ColumnHeaders(tk.Canvas):
|
|
2365
2504
|
else:
|
2366
2505
|
value = False
|
2367
2506
|
self.set_cell_data_undo(c, datacn=datacn, value=value, cell_resize=False)
|
2368
|
-
event_data =
|
2369
|
-
name="end_edit_header",
|
2370
|
-
sheet=self.PAR.name,
|
2371
|
-
widget=self,
|
2372
|
-
cells_header={datacn: pre_edit_value},
|
2373
|
-
key="??",
|
2374
|
-
value=value,
|
2375
|
-
loc=c,
|
2376
|
-
column=c,
|
2377
|
-
boxes=self.MT.get_boxes(),
|
2378
|
-
selected=self.MT.selected,
|
2379
|
-
)
|
2507
|
+
event_data = self.new_single_edit_event(c, datacn, "??", pre_edit_value, value)
|
2380
2508
|
if kwargs["check_function"] is not None:
|
2381
2509
|
kwargs["check_function"](event_data)
|
2382
2510
|
try_binding(self.extra_end_edit_cell_func, event_data)
|
@@ -2388,3 +2516,18 @@ class ColumnHeaders(tk.Canvas):
|
|
2388
2516
|
return self.cell_options[datacn] if key is None else self.cell_options[datacn].get(key, {})
|
2389
2517
|
else:
|
2390
2518
|
return {}
|
2519
|
+
|
2520
|
+
def new_single_edit_event(self, c: int, datacn: int, k: str, before_val: Any, after_val: Any) -> EventDataDict:
|
2521
|
+
return event_dict(
|
2522
|
+
name="end_edit_header",
|
2523
|
+
sheet=self.PAR.name,
|
2524
|
+
widget=self,
|
2525
|
+
cells_header={datacn: before_val},
|
2526
|
+
key=k,
|
2527
|
+
value=after_val,
|
2528
|
+
loc=c,
|
2529
|
+
column=c,
|
2530
|
+
boxes=self.MT.get_boxes(),
|
2531
|
+
selected=self.MT.selected,
|
2532
|
+
data={datacn: after_val},
|
2533
|
+
)
|
tksheet/constants.py
CHANGED
tksheet/find_window.py
CHANGED
@@ -7,7 +7,7 @@ from typing import Any, Literal
|
|
7
7
|
|
8
8
|
from .constants import ctrl_key, rc_binding
|
9
9
|
from .functions import recursive_bind
|
10
|
-
from .other_classes import DotDict
|
10
|
+
from .other_classes import DotDict, FontTuple
|
11
11
|
|
12
12
|
|
13
13
|
class FindWindowTkText(tk.Text):
|
@@ -39,7 +39,7 @@ class FindWindowTkText(tk.Text):
|
|
39
39
|
self,
|
40
40
|
menu_kwargs: dict,
|
41
41
|
sheet_ops: dict,
|
42
|
-
font:
|
42
|
+
font: FontTuple,
|
43
43
|
bg: str,
|
44
44
|
fg: str,
|
45
45
|
select_bg: str,
|
@@ -159,7 +159,7 @@ class FindWindowTkText(tk.Text):
|
|
159
159
|
return "break"
|
160
160
|
|
161
161
|
|
162
|
-
class
|
162
|
+
class LabelTooltip(tk.Toplevel):
|
163
163
|
def __init__(self, parent: tk.Misc, text: str, bg: str, fg: str) -> None:
|
164
164
|
super().__init__(parent)
|
165
165
|
self.withdraw()
|
@@ -510,7 +510,7 @@ class FindWindow(tk.Frame):
|
|
510
510
|
"""Show the tooltip at the specified position."""
|
511
511
|
bg = self.bg if self.bg is not None else "white"
|
512
512
|
fg = self.fg if self.fg is not None else "black"
|
513
|
-
self.tooltip =
|
513
|
+
self.tooltip = LabelTooltip(self, widget.tooltip_text, bg, fg)
|
514
514
|
# Use current mouse position instead of recorded position
|
515
515
|
self.tooltip.deiconify()
|
516
516
|
show_x = max(0, self.winfo_toplevel().winfo_pointerx() - self.tooltip.winfo_width() - 5)
|
tksheet/formatters.py
CHANGED
@@ -227,11 +227,13 @@ def data_to_str(
|
|
227
227
|
) -> str:
|
228
228
|
if not isinstance(value, datatypes):
|
229
229
|
return invalid_value
|
230
|
-
|
230
|
+
elif value is None and nullable:
|
231
231
|
return ""
|
232
|
-
|
232
|
+
else:
|
233
|
+
return to_str_function(value, **kwargs)
|
233
234
|
|
234
235
|
|
236
|
+
# Only used if MainTable.cell_str is called with get_displayed=False
|
235
237
|
def get_data_with_valid_check(value="", datatypes: tuple[()] | tuple[Any] | Any = (), invalid_value="NA"):
|
236
238
|
if isinstance(value, datatypes):
|
237
239
|
return value
|