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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tksheet/constants.py CHANGED
@@ -9,6 +9,7 @@ symbols_set: set[str] = set("""!#$%&'()*+,-./:;"@[]^_`{|}~>?= \\""")
9
9
  nonelike: set[object] = {None, "none", ""}
10
10
  truthy: set[object] = {True, "true", "t", "yes", "y", "on", "1", 1, 1.0}
11
11
  falsy: set[object] = {False, "false", "f", "no", "n", "off", "0", 0, 0.0}
12
+ _test_str: str = "aiW_-|"
12
13
 
13
14
  val_modifying_options: set[str] = {"checkbox", "format", "dropdown"}
14
15
 
@@ -34,6 +35,18 @@ emitted_events: set[str] = {
34
35
  "<<SelectAll>>",
35
36
  }
36
37
 
38
+ align_helper: dict[str, str] = {
39
+ "w": "nw",
40
+ "e": "ne",
41
+ "center": "n",
42
+ "n": "center",
43
+ "nw": "left",
44
+ "ne": "right",
45
+ }
46
+
47
+ align_value_error: str = "Align must be one of the following values: c, center, w, nw, west, left, e, ne, east, right"
48
+ font_value_error: str = "Argument must be font, size and 'normal', 'bold' or'italic' e.g. ('Carlito',12,'normal')"
49
+
37
50
  backwards_compatibility_keys: dict[str, str] = {
38
51
  "font": "table_font",
39
52
  }
tksheet/functions.py CHANGED
@@ -12,12 +12,159 @@ from itertools import islice, repeat
12
12
  from typing import Literal
13
13
 
14
14
  from .colors import color_map
15
- from .constants import symbols_set
15
+ from .constants import align_value_error, symbols_set
16
16
  from .formatters import to_bool
17
17
  from .other_classes import Box_nt, DotDict, EventDataDict, Highlight, Loc, Span
18
- from .types import AnyIter
18
+ from .tksheet_types import AnyIter
19
19
 
20
20
  unpickle_obj = pickle.loads
21
+ lines_re = re.compile(r"[^\n]+")
22
+
23
+
24
+ def wrap_text(
25
+ text: str,
26
+ max_width: int,
27
+ max_lines: int,
28
+ char_width_fn: Callable,
29
+ widths: dict[str, int],
30
+ wrap: Literal["", "c", "w"] = "",
31
+ start_line: int = 0,
32
+ ) -> Generator[str]:
33
+ lines = (match.group() for match in lines_re.finditer(text))
34
+ current_line = []
35
+ total_lines = 0
36
+ line_width = 0
37
+
38
+ if not wrap:
39
+ for line in lines:
40
+ line_width = 0
41
+ current_line = []
42
+ for char in line:
43
+ char_width = widths.get(char, char_width_fn(char))
44
+ line_width += char_width
45
+ if line_width >= max_width:
46
+ break
47
+ current_line.append(char)
48
+
49
+ if total_lines >= start_line:
50
+ yield "".join(current_line)
51
+
52
+ # Count the line whether it's empty or not
53
+ total_lines += 1
54
+ if total_lines >= max_lines:
55
+ return
56
+
57
+ elif wrap == "c":
58
+ for line in lines:
59
+ for char in line:
60
+ char_width = widths.get(char, char_width_fn(char))
61
+
62
+ # adding char to line would result in wrap
63
+ if line_width + char_width >= max_width:
64
+ if total_lines >= start_line:
65
+ yield "".join(current_line)
66
+
67
+ total_lines += 1
68
+ if total_lines >= max_lines:
69
+ return
70
+ current_line = []
71
+ line_width = 0
72
+
73
+ if char_width <= max_width:
74
+ current_line.append(char)
75
+ line_width = char_width
76
+ # adding char to line is okay
77
+ else:
78
+ current_line.append(char)
79
+ line_width += char_width
80
+
81
+ if total_lines >= start_line:
82
+ yield "".join(current_line)
83
+
84
+ total_lines += 1
85
+ if total_lines >= max_lines:
86
+ return
87
+ current_line = [] # Reset for next line
88
+ line_width = 0
89
+
90
+ elif wrap == "w":
91
+ space_width = widths.get(" ", char_width_fn(" "))
92
+
93
+ for line in lines:
94
+ words = line.split()
95
+ for i, word in enumerate(words):
96
+ # if we're going to next word and
97
+ # if a space fits on the end of the current line we add one
98
+ if i and line_width + space_width < max_width:
99
+ current_line.append(" ")
100
+ line_width += space_width
101
+
102
+ # check if word will fit
103
+ word_width = 0
104
+ word_char_widths = []
105
+ for char in word:
106
+ word_char_widths.append((w := widths.get(char, char_width_fn(char))))
107
+ word_width += w
108
+
109
+ # we only wrap by character if the whole word alone wont fit max width
110
+ # word won't fit at all we resort to char wrapping it
111
+ if word_width >= max_width:
112
+ # yield current line before char wrapping word
113
+ if current_line:
114
+ if total_lines >= start_line:
115
+ yield "".join(current_line)
116
+
117
+ total_lines += 1
118
+ if total_lines >= max_lines:
119
+ return
120
+ current_line = []
121
+ line_width = 0
122
+
123
+ for char, w in zip(word, word_char_widths):
124
+ # adding char to line would result in wrap
125
+ if line_width + w >= max_width:
126
+ if total_lines >= start_line:
127
+ yield "".join(current_line)
128
+
129
+ total_lines += 1
130
+ if total_lines >= max_lines:
131
+ return
132
+ current_line = []
133
+ line_width = 0
134
+
135
+ if w <= max_width:
136
+ current_line.append(char)
137
+ line_width = w
138
+ # adding char to line is okay
139
+ else:
140
+ current_line.append(char)
141
+ line_width += w
142
+
143
+ # word won't fit on current line but will fit on a newline
144
+ elif line_width + word_width >= max_width:
145
+ if total_lines >= start_line:
146
+ yield "".join(current_line)
147
+
148
+ total_lines += 1
149
+ if total_lines >= max_lines:
150
+ return
151
+ current_line = [word]
152
+ line_width = word_width
153
+
154
+ # word will fit we put it on the current line
155
+ else:
156
+ current_line.append(word)
157
+ line_width += word_width
158
+
159
+ if total_lines >= start_line:
160
+ yield "".join(current_line)
161
+
162
+ total_lines += 1
163
+ if total_lines >= max_lines:
164
+ return
165
+
166
+ current_line = [] # Reset for next line
167
+ line_width = 0
21
168
 
22
169
 
23
170
  def get_csv_str_dialect(s: str, delimiters: str) -> csv.Dialect:
@@ -161,6 +308,8 @@ def event_dict(
161
308
  # resized_header: None, dict] = None,
162
309
  being_selected: None | tuple = None,
163
310
  named_spans: None | dict = None,
311
+ sheet_state: None | dict = None,
312
+ treeview: None | dict = None,
164
313
  **kwargs,
165
314
  ) -> EventDataDict:
166
315
  return EventDataDict(
@@ -186,8 +335,6 @@ def event_dict(
186
335
  index=DotDict(),
187
336
  column_widths=DotDict(),
188
337
  row_heights=DotDict(),
189
- displayed_rows=None,
190
- displayed_columns=None,
191
338
  ),
192
339
  named_spans=DotDict() if named_spans is None else named_spans,
193
340
  options=DotDict(),
@@ -209,6 +356,12 @@ def event_dict(
209
356
  # "index": DotDict() if resized_index is None else resized_index,
210
357
  ),
211
358
  widget=widget,
359
+ sheet_state=DotDict() if sheet_state is None else sheet_state,
360
+ treeview=DotDict(
361
+ nodes={},
362
+ )
363
+ if treeview is None
364
+ else treeview,
212
365
  )
213
366
 
214
367
 
@@ -236,6 +389,12 @@ def b_index(sorted_seq: Sequence[int], num_to_index: int) -> int:
236
389
  return idx
237
390
 
238
391
 
392
+ def try_b_index(sorted_seq: Sequence[int], num_to_index: int) -> int | None:
393
+ if (idx := bisect_left(sorted_seq, num_to_index)) == len(sorted_seq) or sorted_seq[idx] != num_to_index:
394
+ return None
395
+ return idx
396
+
397
+
239
398
  def bisect_in(sorted_seq: Sequence[int], num: int) -> bool:
240
399
  """
241
400
  Faster than 'num in sorted_seq'
@@ -246,6 +405,18 @@ def bisect_in(sorted_seq: Sequence[int], num: int) -> bool:
246
405
  return False
247
406
 
248
407
 
408
+ def push_n(num: int, sorted_seq: Sequence[int]) -> int:
409
+ if num < sorted_seq[0]:
410
+ return num
411
+ else:
412
+ for e in sorted_seq:
413
+ if num >= e:
414
+ num += 1
415
+ else:
416
+ return num
417
+ return num
418
+
419
+
249
420
  def get_dropdown_kwargs(
250
421
  values: list = [],
251
422
  set_value: object = None,
@@ -616,6 +787,18 @@ def insert_items(
616
787
  return seq
617
788
 
618
789
 
790
+ def del_placeholder_dict_key(
791
+ d: dict[Hashable, object],
792
+ k: Hashable,
793
+ v: object,
794
+ p: tuple = tuple(),
795
+ ) -> dict[Hashable, object]:
796
+ if p in d:
797
+ del d[p]
798
+ d[k] = v
799
+ return d
800
+
801
+
619
802
  def data_to_displayed_idxs(
620
803
  to_convert: list[int],
621
804
  displayed: list[int],
@@ -1390,15 +1573,15 @@ def convert_align(align: str | None) -> str | None:
1390
1573
  a = align.lower()
1391
1574
  if a == "global":
1392
1575
  return None
1393
- elif a in ("c", "center", "centre"):
1394
- return "center"
1395
- elif a in ("w", "west", "left"):
1396
- return "w"
1397
- elif a in ("e", "east", "right"):
1398
- return "e"
1576
+ elif a in ("c", "center", "centre", "n"):
1577
+ return "n"
1578
+ elif a in ("w", "west", "left", "nw"):
1579
+ return "nw"
1580
+ elif a in ("e", "east", "right", "ne"):
1581
+ return "ne"
1399
1582
  elif align is None:
1400
1583
  return None
1401
- raise ValueError("Align must be one of the following values: c, center, w, west, left, e, east, right")
1584
+ raise ValueError(align_value_error)
1402
1585
 
1403
1586
 
1404
1587
  def set_align(