tksheet 7.3.1__py3-none-any.whl → 7.3.2__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 +2 -2
- tksheet/column_headers.py +22 -36
- tksheet/find_window.py +251 -0
- tksheet/formatters.py +1 -1
- tksheet/functions.py +60 -41
- tksheet/main_table.py +436 -265
- tksheet/other_classes.py +6 -9
- tksheet/row_index.py +23 -35
- tksheet/sheet.py +75 -21
- tksheet/sheet_options.py +16 -1
- tksheet/text_editor.py +1 -1
- tksheet/top_left_rectangle.py +1 -1
- {tksheet-7.3.1.dist-info → tksheet-7.3.2.dist-info}/METADATA +10 -11
- tksheet-7.3.2.dist-info/RECORD +21 -0
- {tksheet-7.3.1.dist-info → tksheet-7.3.2.dist-info}/WHEEL +1 -1
- tksheet-7.3.1.dist-info/RECORD +0 -20
- /tksheet/{vars.py → constants.py} +0 -0
- {tksheet-7.3.1.dist-info → tksheet-7.3.2.dist-info}/LICENSE.txt +0 -0
- {tksheet-7.3.1.dist-info → tksheet-7.3.2.dist-info}/top_level.txt +0 -0
tksheet/__init__.py
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
tksheet - A Python tkinter table widget
|
5
5
|
"""
|
6
6
|
|
7
|
-
__version__ = "7.3.
|
7
|
+
__version__ = "7.3.2"
|
8
8
|
|
9
9
|
from .colors import (
|
10
10
|
color_map,
|
@@ -96,7 +96,7 @@ from .themes import (
|
|
96
96
|
theme_light_green,
|
97
97
|
)
|
98
98
|
from .top_left_rectangle import TopLeftRectangle
|
99
|
-
from .
|
99
|
+
from .constants import (
|
100
100
|
USER_OS,
|
101
101
|
ctrl_key,
|
102
102
|
emitted_events,
|
tksheet/column_headers.py
CHANGED
@@ -23,10 +23,19 @@ from typing import Literal
|
|
23
23
|
from .colors import (
|
24
24
|
color_map,
|
25
25
|
)
|
26
|
+
from .constants import (
|
27
|
+
USER_OS,
|
28
|
+
rc_binding,
|
29
|
+
text_editor_close_bindings,
|
30
|
+
text_editor_newline_bindings,
|
31
|
+
text_editor_to_unbind,
|
32
|
+
)
|
26
33
|
from .formatters import is_bool_like, try_to_bool
|
27
34
|
from .functions import (
|
28
35
|
consecutive_ranges,
|
29
36
|
event_dict,
|
37
|
+
event_has_char_key,
|
38
|
+
event_opens_dropdown_or_checkbox,
|
30
39
|
get_n2a,
|
31
40
|
int_x_tuple,
|
32
41
|
is_contiguous,
|
@@ -47,14 +56,6 @@ from .text_editor import (
|
|
47
56
|
from .types import (
|
48
57
|
AnyIter,
|
49
58
|
)
|
50
|
-
from .vars import (
|
51
|
-
USER_OS,
|
52
|
-
rc_binding,
|
53
|
-
symbols_set,
|
54
|
-
text_editor_close_bindings,
|
55
|
-
text_editor_newline_bindings,
|
56
|
-
text_editor_to_unbind,
|
57
|
-
)
|
58
59
|
|
59
60
|
|
60
61
|
class ColumnHeaders(tk.Canvas):
|
@@ -1672,8 +1673,8 @@ class ColumnHeaders(tk.Canvas):
|
|
1672
1673
|
|
1673
1674
|
def get_redraw_selections(self, startc: int, endc: int) -> dict[str, set[int]]:
|
1674
1675
|
d = defaultdict(set)
|
1675
|
-
for
|
1676
|
-
|
1676
|
+
for _, box in self.MT.get_selection_items():
|
1677
|
+
_, c1, _, c2 = box.coords
|
1677
1678
|
for c in range(startc, endc):
|
1678
1679
|
if c1 <= c and c2 > c:
|
1679
1680
|
d[box.type_ if box.type_ != "rows" else "cells"].add(c)
|
@@ -1689,7 +1690,7 @@ class ColumnHeaders(tk.Canvas):
|
|
1689
1690
|
if self.get_cell_kwargs(datacn, key="readonly"):
|
1690
1691
|
return
|
1691
1692
|
elif self.get_cell_kwargs(datacn, key="dropdown") or self.get_cell_kwargs(datacn, key="checkbox"):
|
1692
|
-
if
|
1693
|
+
if event_opens_dropdown_or_checkbox(event):
|
1693
1694
|
if self.get_cell_kwargs(datacn, key="dropdown"):
|
1694
1695
|
self.open_dropdown_window(c, event=event)
|
1695
1696
|
elif self.get_cell_kwargs(datacn, key="checkbox"):
|
@@ -1715,28 +1716,16 @@ class ColumnHeaders(tk.Canvas):
|
|
1715
1716
|
state: str = "normal",
|
1716
1717
|
dropdown: bool = False,
|
1717
1718
|
) -> bool:
|
1718
|
-
text =
|
1719
|
+
text = f"{self.get_cell_data(self.MT.datacn(c), none_to_empty_str=True, redirect_int=True)}"
|
1719
1720
|
extra_func_key = "??"
|
1720
|
-
if
|
1721
|
-
if event
|
1722
|
-
|
1723
|
-
|
1724
|
-
|
1725
|
-
|
1726
|
-
|
1727
|
-
elif event is not None
|
1728
|
-
(hasattr(event, "keysym") and event.keysym == "BackSpace") or event.keycode in (8, 855638143)
|
1729
|
-
):
|
1730
|
-
extra_func_key = "BackSpace"
|
1731
|
-
text = ""
|
1732
|
-
elif event is not None and (
|
1733
|
-
(hasattr(event, "char") and event.char.isalpha())
|
1734
|
-
or (hasattr(event, "char") and event.char.isdigit())
|
1735
|
-
or (hasattr(event, "char") and event.char in symbols_set)
|
1736
|
-
):
|
1737
|
-
extra_func_key = event.char
|
1738
|
-
text = event.char
|
1739
|
-
else:
|
1721
|
+
if event_opens_dropdown_or_checkbox(event):
|
1722
|
+
if hasattr(event, "keysym") and event.keysym in ("Return", "F2", "BackSpace"):
|
1723
|
+
extra_func_key = event.keysym
|
1724
|
+
if event.keysym == "BackSpace":
|
1725
|
+
text = ""
|
1726
|
+
elif event_has_char_key(event):
|
1727
|
+
extra_func_key = text = event.char
|
1728
|
+
elif event is not None:
|
1740
1729
|
return False
|
1741
1730
|
if self.extra_begin_edit_cell_func:
|
1742
1731
|
try:
|
@@ -1758,14 +1747,13 @@ class ColumnHeaders(tk.Canvas):
|
|
1758
1747
|
return False
|
1759
1748
|
else:
|
1760
1749
|
text = text if isinstance(text, str) else f"{text}"
|
1761
|
-
text = "" if text is None else text
|
1762
1750
|
if self.PAR.ops.cell_auto_resize_enabled:
|
1763
1751
|
if self.height_resizing_enabled:
|
1764
1752
|
self.set_height_of_header_to_text(text)
|
1765
1753
|
self.set_col_width_run_binding(c)
|
1766
1754
|
if self.text_editor.open and c == self.text_editor.column:
|
1767
1755
|
self.text_editor.set_text(self.text_editor.get() + "" if not isinstance(text, str) else text)
|
1768
|
-
return
|
1756
|
+
return False
|
1769
1757
|
self.hide_text_editor()
|
1770
1758
|
if not self.MT.see(r=0, c=c, keep_yscroll=True, check_cell_visibility=True):
|
1771
1759
|
self.MT.refresh()
|
@@ -1773,8 +1761,6 @@ class ColumnHeaders(tk.Canvas):
|
|
1773
1761
|
y = 0
|
1774
1762
|
w = self.MT.col_positions[c + 1] - x
|
1775
1763
|
h = self.current_height + 1
|
1776
|
-
if text is None:
|
1777
|
-
text = self.get_cell_data(self.MT.datacn(c), none_to_empty_str=True, redirect_int=True)
|
1778
1764
|
kwargs = {
|
1779
1765
|
"menu_kwargs": DotDict(
|
1780
1766
|
{
|
tksheet/find_window.py
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import tkinter as tk
|
4
|
+
from collections.abc import (
|
5
|
+
Callable,
|
6
|
+
)
|
7
|
+
from typing import Literal
|
8
|
+
|
9
|
+
from .other_classes import (
|
10
|
+
DotDict,
|
11
|
+
)
|
12
|
+
from .constants import (
|
13
|
+
ctrl_key,
|
14
|
+
rc_binding,
|
15
|
+
)
|
16
|
+
from .functions import (
|
17
|
+
recursive_bind,
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
class FindWindowTkText(tk.Text):
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
parent: tk.Misc,
|
25
|
+
) -> None:
|
26
|
+
super().__init__(
|
27
|
+
parent,
|
28
|
+
spacing1=0,
|
29
|
+
spacing2=1,
|
30
|
+
spacing3=0,
|
31
|
+
bd=0,
|
32
|
+
highlightthickness=0,
|
33
|
+
undo=True,
|
34
|
+
maxundo=30,
|
35
|
+
)
|
36
|
+
self.parent = parent
|
37
|
+
self.rc_popup_menu = tk.Menu(self, tearoff=0)
|
38
|
+
self.bind("<1>", lambda event: self.focus_set())
|
39
|
+
self.bind(rc_binding, self.rc)
|
40
|
+
self.bind(f"<{ctrl_key}-a>", self.select_all)
|
41
|
+
self.bind(f"<{ctrl_key}-A>", self.select_all)
|
42
|
+
self.bind("<Delete>", self.delete_key)
|
43
|
+
|
44
|
+
def reset(
|
45
|
+
self,
|
46
|
+
menu_kwargs: dict,
|
47
|
+
sheet_ops: dict,
|
48
|
+
font: tuple,
|
49
|
+
bg: str,
|
50
|
+
fg: str,
|
51
|
+
select_bg: str,
|
52
|
+
select_fg: str,
|
53
|
+
) -> None:
|
54
|
+
self.config(
|
55
|
+
font=font,
|
56
|
+
background=bg,
|
57
|
+
foreground=fg,
|
58
|
+
insertbackground=fg,
|
59
|
+
selectbackground=select_bg,
|
60
|
+
selectforeground=select_fg,
|
61
|
+
)
|
62
|
+
self.editor_del_key = sheet_ops.editor_del_key
|
63
|
+
self.rc_popup_menu.delete(0, "end")
|
64
|
+
self.rc_popup_menu.add_command(
|
65
|
+
label=sheet_ops.select_all_label,
|
66
|
+
accelerator=sheet_ops.select_all_accelerator,
|
67
|
+
command=self.select_all,
|
68
|
+
**menu_kwargs,
|
69
|
+
)
|
70
|
+
self.rc_popup_menu.add_command(
|
71
|
+
label=sheet_ops.cut_label,
|
72
|
+
accelerator=sheet_ops.cut_accelerator,
|
73
|
+
command=self.cut,
|
74
|
+
**menu_kwargs,
|
75
|
+
)
|
76
|
+
self.rc_popup_menu.add_command(
|
77
|
+
label=sheet_ops.copy_label,
|
78
|
+
accelerator=sheet_ops.copy_accelerator,
|
79
|
+
command=self.copy,
|
80
|
+
**menu_kwargs,
|
81
|
+
)
|
82
|
+
self.rc_popup_menu.add_command(
|
83
|
+
label=sheet_ops.paste_label,
|
84
|
+
accelerator=sheet_ops.paste_accelerator,
|
85
|
+
command=self.paste,
|
86
|
+
**menu_kwargs,
|
87
|
+
)
|
88
|
+
self.rc_popup_menu.add_command(
|
89
|
+
label=sheet_ops.undo_label,
|
90
|
+
accelerator=sheet_ops.undo_accelerator,
|
91
|
+
command=self.undo,
|
92
|
+
**menu_kwargs,
|
93
|
+
)
|
94
|
+
|
95
|
+
def rc(self, event: object) -> None:
|
96
|
+
self.focus_set()
|
97
|
+
self.rc_popup_menu.tk_popup(event.x_root, event.y_root)
|
98
|
+
|
99
|
+
def delete_key(self, event: object = None) -> None:
|
100
|
+
if self.editor_del_key == "forward":
|
101
|
+
return
|
102
|
+
elif not self.editor_del_key:
|
103
|
+
return "break"
|
104
|
+
elif self.editor_del_key == "backward":
|
105
|
+
if self.tag_ranges("sel"):
|
106
|
+
return
|
107
|
+
if self.index("insert") == "1.0":
|
108
|
+
return "break"
|
109
|
+
self.delete("insert-1c")
|
110
|
+
return "break"
|
111
|
+
|
112
|
+
def select_all(self, event: object = None) -> Literal["break"]:
|
113
|
+
self.tag_add(tk.SEL, "1.0", tk.END)
|
114
|
+
self.mark_set(tk.INSERT, tk.END)
|
115
|
+
# self.see(tk.INSERT)
|
116
|
+
return "break"
|
117
|
+
|
118
|
+
def cut(self, event: object = None) -> Literal["break"]:
|
119
|
+
self.event_generate(f"<{ctrl_key}-x>")
|
120
|
+
self.event_generate("<KeyRelease>")
|
121
|
+
return "break"
|
122
|
+
|
123
|
+
def copy(self, event: object = None) -> Literal["break"]:
|
124
|
+
self.event_generate(f"<{ctrl_key}-c>")
|
125
|
+
return "break"
|
126
|
+
|
127
|
+
def paste(self, event: object = None) -> Literal["break"]:
|
128
|
+
self.event_generate(f"<{ctrl_key}-v>")
|
129
|
+
self.event_generate("<KeyRelease>")
|
130
|
+
return "break"
|
131
|
+
|
132
|
+
def undo(self, event: object = None) -> Literal["break"]:
|
133
|
+
self.event_generate(f"<{ctrl_key}-z>")
|
134
|
+
self.event_generate("<KeyRelease>")
|
135
|
+
return "break"
|
136
|
+
|
137
|
+
|
138
|
+
class FindWindow(tk.Frame):
|
139
|
+
def __init__(
|
140
|
+
self,
|
141
|
+
parent: tk.Misc,
|
142
|
+
find_next_func: Callable,
|
143
|
+
find_prev_func: Callable,
|
144
|
+
close_func: Callable,
|
145
|
+
) -> None:
|
146
|
+
super().__init__(
|
147
|
+
parent,
|
148
|
+
width=0,
|
149
|
+
height=0,
|
150
|
+
bd=0,
|
151
|
+
)
|
152
|
+
self.grid_columnconfigure(0, weight=1)
|
153
|
+
self.grid_rowconfigure(0, weight=1)
|
154
|
+
self.grid_propagate(False)
|
155
|
+
self.parent = parent
|
156
|
+
self.tktext = FindWindowTkText(self)
|
157
|
+
self.tktext.grid(row=0, column=0, sticky="nswe")
|
158
|
+
self.bg = None
|
159
|
+
self.fg = None
|
160
|
+
|
161
|
+
self.find_previous_arrow = tk.Label(self, text="▲", cursor="hand2", highlightthickness=1)
|
162
|
+
self.find_previous_arrow.bind("<Button-1>", find_prev_func)
|
163
|
+
self.find_previous_arrow.grid(row=0, column=1)
|
164
|
+
|
165
|
+
self.find_next_arrow = tk.Label(self, text="▼", cursor="hand2", highlightthickness=1)
|
166
|
+
self.find_next_arrow.bind("<Button-1>", find_next_func)
|
167
|
+
self.find_next_arrow.grid(row=0, column=2)
|
168
|
+
|
169
|
+
self.find_in_selection = False
|
170
|
+
self.in_selection = tk.Label(self, text="🔎", cursor="hand2", highlightthickness=1)
|
171
|
+
self.in_selection.bind("<Button-1>", self.toggle_in_selection)
|
172
|
+
self.in_selection.grid(row=0, column=3)
|
173
|
+
|
174
|
+
self.close = tk.Label(self, text="✕", cursor="hand2", highlightthickness=1)
|
175
|
+
self.close.bind("<Button-1>", close_func)
|
176
|
+
self.close.grid(row=0, column=4)
|
177
|
+
|
178
|
+
for widget in (self.find_previous_arrow, self.find_next_arrow, self.in_selection, self.close):
|
179
|
+
widget.bind("<Enter>", lambda w, widget=widget: self.enter_label(widget=widget))
|
180
|
+
widget.bind("<Leave>", lambda w, widget=widget: self.leave_label(widget=widget))
|
181
|
+
|
182
|
+
for b in ("Option", "Alt"):
|
183
|
+
for c in ("l", "L"):
|
184
|
+
recursive_bind(self, f"<{b}-{c}>", self.toggle_in_selection)
|
185
|
+
|
186
|
+
def enter_label(self, widget: tk.Misc) -> None:
|
187
|
+
widget.config(
|
188
|
+
highlightbackground=self.fg,
|
189
|
+
highlightcolor=self.fg,
|
190
|
+
)
|
191
|
+
|
192
|
+
def leave_label(self, widget: tk.Misc) -> None:
|
193
|
+
if widget == self.in_selection and self.find_in_selection:
|
194
|
+
return
|
195
|
+
widget.config(
|
196
|
+
highlightbackground=self.bg,
|
197
|
+
highlightcolor=self.fg,
|
198
|
+
)
|
199
|
+
|
200
|
+
def toggle_in_selection(self, event: tk.Misc) -> None:
|
201
|
+
self.find_in_selection = not self.find_in_selection
|
202
|
+
self.enter_label(self.in_selection)
|
203
|
+
self.leave_label(self.in_selection)
|
204
|
+
|
205
|
+
def get(self) -> str:
|
206
|
+
return self.tktext.get("1.0", "end-1c")
|
207
|
+
|
208
|
+
def get_num_lines(self) -> int:
|
209
|
+
return int(self.tktext.index("end-1c").split(".")[0])
|
210
|
+
|
211
|
+
def set_text(self, text: str = "") -> None:
|
212
|
+
self.tktext.delete(1.0, "end")
|
213
|
+
self.tktext.insert(1.0, text)
|
214
|
+
|
215
|
+
def reset(
|
216
|
+
self,
|
217
|
+
border_color: str,
|
218
|
+
menu_kwargs: DotDict,
|
219
|
+
sheet_ops: DotDict,
|
220
|
+
bg: str,
|
221
|
+
fg: str,
|
222
|
+
select_bg: str,
|
223
|
+
select_fg: str,
|
224
|
+
) -> None:
|
225
|
+
self.bg = bg
|
226
|
+
self.fg = fg
|
227
|
+
self.tktext.reset(
|
228
|
+
menu_kwargs=menu_kwargs,
|
229
|
+
sheet_ops=sheet_ops,
|
230
|
+
font=menu_kwargs.font,
|
231
|
+
bg=bg,
|
232
|
+
fg=fg,
|
233
|
+
select_bg=select_bg,
|
234
|
+
select_fg=select_fg,
|
235
|
+
)
|
236
|
+
for widget in (self.find_previous_arrow, self.find_next_arrow, self.in_selection, self.close):
|
237
|
+
widget.config(
|
238
|
+
font=menu_kwargs.font,
|
239
|
+
bg=bg,
|
240
|
+
fg=fg,
|
241
|
+
highlightbackground=bg,
|
242
|
+
highlightcolor=fg,
|
243
|
+
)
|
244
|
+
if self.find_in_selection:
|
245
|
+
self.enter_label(self.in_selection)
|
246
|
+
self.config(
|
247
|
+
background=bg,
|
248
|
+
highlightbackground=border_color,
|
249
|
+
highlightcolor=border_color,
|
250
|
+
highlightthickness=1,
|
251
|
+
)
|
tksheet/formatters.py
CHANGED
tksheet/functions.py
CHANGED
@@ -35,6 +35,9 @@ from .other_classes import (
|
|
35
35
|
from .types import (
|
36
36
|
AnyIter,
|
37
37
|
)
|
38
|
+
from .constants import (
|
39
|
+
symbols_set,
|
40
|
+
)
|
38
41
|
|
39
42
|
unpickle_obj = pickle.loads
|
40
43
|
|
@@ -67,9 +70,15 @@ def get_data_from_clipboard(
|
|
67
70
|
return [[data]]
|
68
71
|
|
69
72
|
|
73
|
+
def recursive_bind(widget: tk.Misc, event: str, callback: Callable) -> None:
|
74
|
+
widget.bind(event, callback)
|
75
|
+
for child in widget.winfo_children():
|
76
|
+
recursive_bind(child, event, callback)
|
77
|
+
|
78
|
+
|
70
79
|
def tksheet_type_error(kwarg: str, valid_types: list[str], not_type: object) -> str:
|
71
80
|
valid_types = ", ".join(f"{type_}" for type_ in valid_types)
|
72
|
-
return f"Argument '{kwarg}' must be one of the following types: {valid_types},
|
81
|
+
return f"Argument '{kwarg}' must be one of the following types: {valid_types}, not {type(not_type)}."
|
73
82
|
|
74
83
|
|
75
84
|
def new_tk_event(keysym: str) -> tk.Event:
|
@@ -78,6 +87,25 @@ def new_tk_event(keysym: str) -> tk.Event:
|
|
78
87
|
return event
|
79
88
|
|
80
89
|
|
90
|
+
def event_has_char_key(event: object) -> bool:
|
91
|
+
return (
|
92
|
+
event and hasattr(event, "char") and (event.char.isalpha() or event.char.isdigit() or event.char in symbols_set)
|
93
|
+
)
|
94
|
+
|
95
|
+
|
96
|
+
def event_opens_dropdown_or_checkbox(event=None) -> bool:
|
97
|
+
if event is None:
|
98
|
+
return False
|
99
|
+
elif event == "rc":
|
100
|
+
return True
|
101
|
+
return (
|
102
|
+
(hasattr(event, "keysym") and event.keysym in {"Return", "F2", "BackSpace"})
|
103
|
+
or (
|
104
|
+
hasattr(event, "keycode") and event.keycode == "??" and hasattr(event, "num") and event.num == 1
|
105
|
+
) # mouseclick
|
106
|
+
)
|
107
|
+
|
108
|
+
|
81
109
|
def dropdown_search_function(
|
82
110
|
search_for: object,
|
83
111
|
data: Sequence[object],
|
@@ -230,6 +258,16 @@ def b_index(sorted_seq: Sequence[int], num_to_index: int) -> int:
|
|
230
258
|
return idx
|
231
259
|
|
232
260
|
|
261
|
+
def bisect_in(sorted_seq: Sequence[int], num: int) -> bool:
|
262
|
+
"""
|
263
|
+
Faster than 'num in sorted_seq'
|
264
|
+
"""
|
265
|
+
try:
|
266
|
+
return sorted_seq[bisect_left(sorted_seq, num)] == num
|
267
|
+
except Exception:
|
268
|
+
return False
|
269
|
+
|
270
|
+
|
233
271
|
def get_dropdown_kwargs(
|
234
272
|
values: list = [],
|
235
273
|
set_value: object = None,
|
@@ -546,35 +584,23 @@ def move_elements_by_mapping(
|
|
546
584
|
new_idxs: dict[int, int],
|
547
585
|
old_idxs: dict[int, int] | None = None,
|
548
586
|
) -> list[object]:
|
549
|
-
# move elements of a list around
|
550
|
-
# other elements based on mapping
|
551
|
-
#
|
587
|
+
# move elements of a list around
|
588
|
+
# displacing other elements based on mapping
|
589
|
+
# new_idxs = {old index: new index, ...}
|
590
|
+
# old_idxs = {new index: old index, ...}
|
552
591
|
if old_idxs is None:
|
553
592
|
old_idxs = dict(zip(new_idxs.values(), new_idxs))
|
554
593
|
|
555
|
-
# create dummy list
|
556
594
|
res = [0] * len(seq)
|
557
595
|
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
596
|
+
remaining_values = (e for i, e in enumerate(seq) if i not in new_idxs)
|
597
|
+
for i in range(len(res)):
|
598
|
+
if i in old_idxs:
|
599
|
+
res[i] = seq[old_idxs[i]]
|
600
|
+
else:
|
601
|
+
res[i] = next(remaining_values)
|
564
602
|
|
565
|
-
|
566
|
-
if len(new_idxs) > int(len(seq) / 2) - 1:
|
567
|
-
# if moving a lot of items better to do comprehension
|
568
|
-
return [
|
569
|
-
next(remaining) if i_ not in old_idxs else e_
|
570
|
-
for i_, e_ in enumerate(seq[old_idxs[i]] if i in old_idxs else e for i, e in enumerate(res))
|
571
|
-
]
|
572
|
-
else:
|
573
|
-
# if just moving a few items assignments are fine
|
574
|
-
for old, new in new_idxs.items():
|
575
|
-
res[new] = seq[old]
|
576
|
-
# fill remaining indexes
|
577
|
-
return [next(remaining) if i not in old_idxs else e for i, e in enumerate(res)]
|
603
|
+
return res
|
578
604
|
|
579
605
|
|
580
606
|
def move_elements_to(
|
@@ -609,23 +635,15 @@ def get_new_indexes(
|
|
609
635
|
|
610
636
|
|
611
637
|
def insert_items(
|
612
|
-
seq: list[object]
|
638
|
+
seq: list[object],
|
613
639
|
to_insert: dict[int, object],
|
614
640
|
seq_len_func: Callable | None = None,
|
615
|
-
) -> list:
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
# need to extend seq if it's not long enough
|
622
|
-
if seq_len_func and idx - i > len(seq):
|
623
|
-
seq_len_func(idx - i - 1)
|
624
|
-
res.extend(seq[extended : idx - i])
|
625
|
-
extended += idx - i - extended
|
626
|
-
res.append(v)
|
627
|
-
res.extend(seq[extended:])
|
628
|
-
seq = res
|
641
|
+
) -> list[object]:
|
642
|
+
if to_insert:
|
643
|
+
if seq_len_func and next(iter(to_insert)) >= len(seq) + len(to_insert):
|
644
|
+
seq_len_func(next(iter(to_insert)) - len(to_insert))
|
645
|
+
for idx, v in reversed(to_insert.items()):
|
646
|
+
seq[idx:idx] = [v]
|
629
647
|
return seq
|
630
648
|
|
631
649
|
|
@@ -633,8 +651,7 @@ def data_to_displayed_idxs(
|
|
633
651
|
to_convert: list[int],
|
634
652
|
displayed: list[int],
|
635
653
|
) -> list[int]:
|
636
|
-
|
637
|
-
return [i for i, e in enumerate(displayed) if e in data_indexes]
|
654
|
+
return [i for i, e in enumerate(displayed) if bisect_in(to_convert, e)]
|
638
655
|
|
639
656
|
|
640
657
|
def displayed_to_data_idxs(
|
@@ -651,6 +668,8 @@ def rounded_box_coords(
|
|
651
668
|
y2: float,
|
652
669
|
radius: int = 5,
|
653
670
|
) -> tuple[float]:
|
671
|
+
if y2 - y1 < 2 or x2 - x1 < 2:
|
672
|
+
return x1, y1, x2, y1, x2, y2, x1, y2
|
654
673
|
return (
|
655
674
|
x1 + radius,
|
656
675
|
y1,
|