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/sheet_options.py CHANGED
@@ -29,12 +29,14 @@ def new_sheet_options() -> DotDict:
29
29
  13 if USER_OS == "darwin" else 11,
30
30
  "normal",
31
31
  ),
32
+ # cell editing
32
33
  "edit_header_label": "Edit header",
33
34
  "edit_header_accelerator": "",
34
35
  "edit_index_label": "Edit index",
35
36
  "edit_index_accelerator": "",
36
37
  "edit_cell_label": "Edit cell",
37
38
  "edit_cell_accelerator": "",
39
+ # cut/copy/paste
38
40
  "cut_label": "Cut",
39
41
  "cut_accelerator": "Ctrl+X",
40
42
  "cut_contents_label": "Cut contents",
@@ -45,10 +47,12 @@ def new_sheet_options() -> DotDict:
45
47
  "copy_contents_accelerator": "Ctrl+C",
46
48
  "paste_label": "Paste",
47
49
  "paste_accelerator": "Ctrl+V",
50
+ # clear/del
48
51
  "delete_label": "Delete",
49
52
  "delete_accelerator": "Del",
50
53
  "clear_contents_label": "Clear contents",
51
54
  "clear_contents_accelerator": "Del",
55
+ # del/insert columns
52
56
  "delete_columns_label": "Delete columns",
53
57
  "delete_columns_accelerator": "",
54
58
  "insert_columns_left_label": "Insert columns left",
@@ -57,6 +61,7 @@ def new_sheet_options() -> DotDict:
57
61
  "insert_column_accelerator": "",
58
62
  "insert_columns_right_label": "Insert columns right",
59
63
  "insert_columns_right_accelerator": "",
64
+ # del/insert rows
60
65
  "delete_rows_label": "Delete rows",
61
66
  "delete_rows_accelerator": "",
62
67
  "insert_rows_above_label": "Insert rows above",
@@ -65,10 +70,42 @@ def new_sheet_options() -> DotDict:
65
70
  "insert_rows_below_accelerator": "",
66
71
  "insert_row_label": "Insert row",
67
72
  "insert_row_accelerator": "",
73
+ # sorting
74
+ # labels
75
+ "sort_cells_label": "Sort ↓",
76
+ "sort_cells_x_label": "Sort →",
77
+ "sort_row_label": "Sort values →",
78
+ "sort_column_label": "Sort values ↓",
79
+ "sort_rows_label": "Sort rows ↓",
80
+ "sort_columns_label": "Sort columns →",
81
+ # reverse labels
82
+ "sort_cells_reverse_label": "Sort ↑",
83
+ "sort_cells_x_reverse_label": "Sort ←",
84
+ "sort_row_reverse_label": "Sort values ←",
85
+ "sort_column_reverse_label": "Sort values ↑",
86
+ "sort_rows_reverse_label": "Sort rows ↑",
87
+ "sort_columns_reverse_label": "Sort columns ←",
88
+ # accelerators
89
+ "sort_cells_accelerator": "",
90
+ "sort_cells_x_accelerator": "",
91
+ "sort_row_accelerator": "",
92
+ "sort_column_accelerator": "",
93
+ "sort_rows_accelerator": "",
94
+ "sort_columns_accelerator": "",
95
+ # reverse accelerators
96
+ "sort_cells_reverse_accelerator": "",
97
+ "sort_cells_x_reverse_accelerator": "",
98
+ "sort_row_reverse_accelerator": "",
99
+ "sort_column_reverse_accelerator": "",
100
+ "sort_rows_reverse_accelerator": "",
101
+ "sort_columns_reverse_accelerator": "",
102
+ # select all
68
103
  "select_all_label": "Select all",
69
104
  "select_all_accelerator": "Ctrl+A",
105
+ # undo
70
106
  "undo_label": "Undo",
71
107
  "undo_accelerator": "Ctrl+Z",
108
+ # bindings
72
109
  "copy_bindings": [
73
110
  f"<{ctrl_key}-c>",
74
111
  f"<{ctrl_key}-C>",
@@ -158,6 +195,7 @@ def new_sheet_options() -> DotDict:
158
195
  "escape_bindings": [
159
196
  "<Escape>",
160
197
  ],
198
+ # other
161
199
  "vertical_scroll_borderwidth": 1,
162
200
  "horizontal_scroll_borderwidth": 1,
163
201
  "vertical_scroll_gripcount": 0,
@@ -204,13 +242,18 @@ def new_sheet_options() -> DotDict:
204
242
  "edit_cell_return": "down",
205
243
  "editor_del_key": "forward",
206
244
  "treeview": False,
207
- "treeview_indent": "5",
245
+ "treeview_indent": "2",
208
246
  "rounded_boxes": True,
209
247
  "alternate_color": "",
248
+ "allow_cell_overflow": False,
249
+ "table_wrap": "c",
250
+ "header_wrap": "c",
251
+ "index_wrap": "c",
210
252
  "min_column_width": 1,
211
253
  "max_column_width": float("inf"),
212
254
  "max_header_height": float("inf"),
213
255
  "max_row_height": float("inf"),
214
256
  "max_index_width": float("inf"),
257
+ "show_top_left": False,
215
258
  }
216
259
  )
tksheet/sorting.py ADDED
@@ -0,0 +1,369 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ import unittest
5
+ from collections.abc import Callable, Generator
6
+ from datetime import datetime
7
+
8
+ from .other_classes import Node
9
+ from .tksheet_types import AnyIter
10
+
11
+ # Possible date formats to try for the entire string
12
+ date_formats = [
13
+ # Common formats
14
+ "%d/%m/%Y", # Day/Month/Year
15
+ "%m/%d/%Y", # Month/Day/Year (US format)
16
+ "%Y/%m/%d", # Year/Month/Day
17
+ "%d.%m.%Y", # Day.Month.Year (European format)
18
+ "%d-%m-%Y", # Day-Month-Year
19
+ "%m-%d-%Y", # Month-Day-Year
20
+ "%Y-%m-%d", # Year-Month-Day (ISO format without time)
21
+ "%d/%m/%y", # Day/Month/2-digit year
22
+ "%m/%d/%y", # Month/Day/2-digit year
23
+ "%y/%m/%d", # 2-digit year/Month/Day
24
+ "%d,%m,%Y", # Day,Month,Year
25
+ "%m,%d,%Y", # Month,Day,Year
26
+ "%Y,%m,%d", # Year,Month,Day
27
+ "%d %m %Y", # Day Month Year (with space)
28
+ "%m %d %Y", # Month Day Year
29
+ # With month names
30
+ "%d %b %Y", # Day Abbreviated Month Year
31
+ "%b %d, %Y", # Abbreviated Month Day, Year
32
+ "%d %B %Y", # Day Full Month Name Year
33
+ "%B %d, %Y", # Full Month Name Day, Year
34
+ # ISO 8601 with/without time
35
+ "%Y-%m-%dT%H:%M:%S", # With time
36
+ "%Y-%m-%d", # Without time
37
+ # Regional or less common formats
38
+ # "%Y年%m月%d日", # Japanese-style date
39
+ "%Y%m%d", # YYYYMMDD format, often used in logs or filenames
40
+ "%y%m%d", # YYMMDD
41
+ "%d%m%Y", # DDMMYYYY, sometimes used in Europe
42
+ # Additional formats
43
+ "%d/%m/%y %H:%M", # Day/Month/Year Hour:Minute
44
+ "%m/%d/%y %H:%M", # Month/Day/Year Hour:Minute
45
+ "%Y-%m-%d %H:%M:%S", # Year-Month-Day Hour:Minute:Second
46
+ ]
47
+
48
+
49
+ def natural_sort_key(item: object) -> tuple[int, object]:
50
+ """
51
+ A key function for natural sorting that handles various Python types, including
52
+ date-like strings in multiple formats.
53
+
54
+ This function aims to sort elements in a human-readable order:
55
+ - None values first
56
+ - Booleans (False before True)
57
+ - Numbers (integers, floats combined)
58
+ - Datetime objects
59
+ - Strings with natural sorting for embedded numbers and dates
60
+ - Unknown types treated as strings or left at the end
61
+
62
+ With love from Grok ❤️
63
+
64
+ Args:
65
+ item: Any Python object to be sorted.
66
+
67
+ Returns:
68
+ A tuple or value that can be used for sorting.
69
+ """
70
+ if item is None:
71
+ return (0, "")
72
+
73
+ elif isinstance(item, bool):
74
+ return (1, item)
75
+
76
+ elif isinstance(item, (int, float)):
77
+ return (2, (item,)) # Tuple to ensure float and int are sorted together
78
+
79
+ elif isinstance(item, datetime):
80
+ return (3, item.timestamp())
81
+
82
+ elif isinstance(item, str):
83
+ # Check if the whole string is a date
84
+ for date_format in date_formats:
85
+ try:
86
+ # Use the same sort order as for datetime objects
87
+ return (3, datetime.strptime(item, date_format).timestamp())
88
+ except ValueError:
89
+ continue
90
+
91
+ # Check if the whole string is a number
92
+ try:
93
+ return (4, float(item))
94
+ except Exception:
95
+ # Proceed with natural sorting
96
+ return (5, tuple(int(text) if text.isdigit() else text.lower() for text in re.split(r"(\d+)", item)))
97
+
98
+ else:
99
+ # For unknown types, attempt to convert to string, or place at end
100
+ try:
101
+ return (6, f"{item}".lower())
102
+ except Exception:
103
+ return (7, item) # If conversion fails, place at the very end
104
+
105
+
106
+ def sort_selection(
107
+ data: list[list[object]],
108
+ reverse: bool = False,
109
+ key: Callable | None = None,
110
+ row_wise: bool = False,
111
+ ) -> list[list[object]]:
112
+ if not data or not isinstance(data[0], list):
113
+ raise ValueError("Data must be a list of lists.")
114
+
115
+ if key is None:
116
+ key = natural_sort_key
117
+
118
+ if row_wise:
119
+ return [sorted(row, key=key, reverse=reverse) for row in data]
120
+ else:
121
+ return list(
122
+ zip(
123
+ *(
124
+ sorted(
125
+ (row[col] for row in data),
126
+ key=key,
127
+ reverse=reverse,
128
+ )
129
+ for col in range(len(data[0]))
130
+ )
131
+ )
132
+ )
133
+
134
+
135
+ def sort_column(
136
+ data: list[list[object]] | list[object] | AnyIter[object],
137
+ column: int = 0,
138
+ reverse: bool = False,
139
+ key: Callable | None = None,
140
+ ) -> list[list[object]] | list[object]:
141
+ if not data:
142
+ return data
143
+
144
+ if key is None:
145
+ key = natural_sort_key
146
+
147
+ if isinstance(data, list) and isinstance(data[0], list):
148
+ return sorted(data, key=lambda row: key(row[column]) if len(row) > column else key(None), reverse=reverse)
149
+ else:
150
+ return sorted(data, reverse=reverse, key=key)
151
+
152
+
153
+ def sort_row(
154
+ data: list[list[object]] | list[object] | AnyIter[object],
155
+ row: int = 0,
156
+ reverse: bool = False,
157
+ key: Callable | None = None,
158
+ ) -> list[list[object]]:
159
+ if not data:
160
+ return data
161
+
162
+ if key is None:
163
+ key = natural_sort_key
164
+
165
+ if isinstance(data, list) and isinstance(data[0], list):
166
+ if 0 <= row < len(data):
167
+ data[row] = sorted(data[row], key=key, reverse=reverse)
168
+ return data
169
+ else:
170
+ raise IndexError(f"Row index {row} out of range for data with {len(data)} rows.")
171
+ else:
172
+ return sorted(data, reverse=reverse, key=key)
173
+
174
+
175
+ def sort_rows_by_column(
176
+ data: list[list[object]],
177
+ column: int = 0,
178
+ reverse: bool = False,
179
+ key: Callable | None = None,
180
+ ) -> tuple[list[tuple[int, list[object]]], dict[int, int]]:
181
+ if not data:
182
+ return data, {}
183
+
184
+ # Check if data is a list of lists
185
+ if not isinstance(data[0], list):
186
+ raise ValueError("Data must be a list of lists for row sorting.")
187
+
188
+ if key is None:
189
+ key = natural_sort_key
190
+
191
+ # Use a generator expression for sorting to avoid creating an intermediate list
192
+ sorted_indexed_data = sorted(
193
+ ((i, row) for i, row in enumerate(data)),
194
+ key=lambda item: key(item[1][column]) if len(item[1]) > column else key(None),
195
+ reverse=reverse,
196
+ )
197
+
198
+ # Return sorted rows [(old index, row), ...] and create the mapping dictionary
199
+ return sorted_indexed_data, {old: new for new, (old, _) in enumerate(sorted_indexed_data)}
200
+
201
+
202
+ def sort_columns_by_row(
203
+ data: list[list[object]],
204
+ row: int = 0,
205
+ reverse: bool = False,
206
+ key: Callable | None = None,
207
+ ) -> tuple[list[int], dict[int, int]]:
208
+ if not data:
209
+ return data, {}
210
+
211
+ # Check if data is a list of lists
212
+ if not isinstance(data[0], list):
213
+ raise ValueError("Data must be a list of lists for column sorting.")
214
+
215
+ if row >= len(data) or row < 0:
216
+ raise IndexError(f"Row index {row} out of range for data with {len(data)} rows.")
217
+
218
+ if key is None:
219
+ key = natural_sort_key
220
+
221
+ # Get sorting indices based on the elements of the specified row
222
+ sort_indices = sorted(range(len(data[row])), key=lambda i: key(data[row][i]), reverse=reverse)
223
+ sort_indices_set = set(sort_indices)
224
+
225
+ new_data = []
226
+ for row_data in data:
227
+ # Sort the columns based on sort_indices, then append any additional elements from longer rows
228
+ sorted_part = [row_data[i] for i in sort_indices if i < len(row_data)]
229
+ unsorted_part = [elem for idx, elem in enumerate(row_data) if idx not in sort_indices_set]
230
+ new_data.append(sorted_part + unsorted_part)
231
+
232
+ return sort_indices, {old: new for old, new in zip(range(len(data[row])), sort_indices)}
233
+
234
+
235
+ def _sort_node_children(
236
+ node: Node,
237
+ tree: dict[str, Node],
238
+ reverse: bool,
239
+ key: Callable,
240
+ ) -> Generator[Node, None, None]:
241
+ sorted_children = sorted(
242
+ (tree[child_iid] for child_iid in node.children if child_iid in tree),
243
+ key=lambda child: key(child.text),
244
+ reverse=reverse,
245
+ )
246
+ for child in sorted_children:
247
+ yield child
248
+ if child.children: # If the child node has children
249
+ yield from _sort_node_children(child, tree, reverse, key)
250
+
251
+
252
+ def sort_tree_view(
253
+ _row_index: list[Node],
254
+ tree_rns: dict[str, int],
255
+ tree: dict[str, Node],
256
+ key: Callable = natural_sort_key,
257
+ reverse: bool = False,
258
+ ) -> tuple[list[Node], dict[int, int]]:
259
+ if not _row_index or not tree_rns or not tree:
260
+ return [], {}
261
+
262
+ if key is None:
263
+ key = natural_sort_key
264
+
265
+ # Create the index map and sorted nodes list
266
+ mapping = {}
267
+ sorted_nodes = []
268
+ new_index = 0
269
+
270
+ # Sort top-level nodes
271
+ for node in sorted(
272
+ (node for node in _row_index if node.parent == ""),
273
+ key=lambda node: key(node.text),
274
+ reverse=reverse,
275
+ ):
276
+ iid = node.iid
277
+ mapping[tree_rns[iid]] = new_index
278
+ sorted_nodes.append(node)
279
+ new_index += 1
280
+
281
+ # Sort children recursively
282
+ for descendant_node in _sort_node_children(node, tree, reverse, key):
283
+ mapping[tree_rns[descendant_node.iid]] = new_index
284
+ sorted_nodes.append(descendant_node)
285
+ new_index += 1
286
+
287
+ return sorted_nodes, mapping
288
+
289
+
290
+ class TestNaturalSort(unittest.TestCase):
291
+ def test_none_first(self):
292
+ self.assertEqual(natural_sort_key(None), (0, ""))
293
+
294
+ def test_booleans_order(self):
295
+ self.assertLess(natural_sort_key(False), natural_sort_key(True))
296
+
297
+ def test_numbers_order(self):
298
+ self.assertLess(natural_sort_key(5), natural_sort_key(10))
299
+ self.assertLess(natural_sort_key(5.5), natural_sort_key(6))
300
+
301
+ def test_datetime_order(self):
302
+ dt1 = datetime(2023, 1, 1)
303
+ dt2 = datetime(2023, 1, 2)
304
+ self.assertLess(natural_sort_key(dt1), natural_sort_key(dt2))
305
+
306
+ def test_string_natural_sort(self):
307
+ items = ["item2", "item10", "item1"]
308
+ sorted_items = sorted(items, key=natural_sort_key)
309
+ self.assertEqual(sorted_items, ["item1", "item2", "item10"])
310
+
311
+ def test_date_string_recognition(self):
312
+ # Test various date formats
313
+ date_str1 = "01/01/2023"
314
+ date_str2 = "2023-01-01"
315
+ date_str3 = "Jan 1, 2023"
316
+
317
+ dt = datetime(2023, 1, 1)
318
+
319
+ self.assertEqual(natural_sort_key(date_str1)[0], 3)
320
+ self.assertEqual(natural_sort_key(date_str2)[0], 3)
321
+ self.assertEqual(natural_sort_key(date_str3)[0], 3)
322
+ self.assertEqual(natural_sort_key(date_str1)[1], natural_sort_key(dt)[1]) # Timestamps should match
323
+
324
+ def test_unknown_types(self):
325
+ # Here we use a custom class for testing unknown types
326
+ class Unknown:
327
+ pass
328
+
329
+ unknown = Unknown()
330
+ self.assertEqual(natural_sort_key(unknown)[0], 5) # Success case, string conversion works
331
+
332
+ def test_unknown_types_failure(self):
333
+ # Create an object where string conversion fails
334
+ class Unconvertible:
335
+ def __str__(self):
336
+ raise Exception("String conversion fails")
337
+
338
+ def __repr__(self):
339
+ raise Exception("String conversion fails")
340
+
341
+ unconvertible = Unconvertible()
342
+ self.assertEqual(natural_sort_key(unconvertible)[0], 6) # Failure case, string conversion fails
343
+
344
+
345
+ def test_sort_selection():
346
+ # Test case 1: Mixed types, no reverse
347
+ data1 = [[1, "b"], [3, "a"]]
348
+ sorted_data1 = sort_selection(data1)
349
+ print(f"Test 1 - No reverse: {data1} -> {sorted_data1}")
350
+
351
+ # Test case 2: Mixed types, with reverse
352
+ data2 = [[1, "b"], [3, "a"]]
353
+ sorted_data2 = sort_selection(data2, reverse=True)
354
+ print(f"Test 2 - With reverse: {data2} -> {sorted_data2}")
355
+
356
+ # Test case 3: All numbers
357
+ data3 = [[2, 1], [4, 3]]
358
+ sorted_data3 = sort_selection(data3)
359
+ print(f"Test 3 - All numbers: {data3} -> {sorted_data3}")
360
+
361
+ # Test case 4: With None values
362
+ data4 = [[None, "b"], ["a", None]]
363
+ sorted_data4 = sort_selection(data4)
364
+ print(f"Test 4 - With None: {data4} -> {sorted_data4}")
365
+
366
+
367
+ if __name__ == "__main__":
368
+ test_sort_selection()
369
+ unittest.main()
tksheet/text_editor.py CHANGED
@@ -4,7 +4,7 @@ import tkinter as tk
4
4
  from collections.abc import Callable
5
5
  from typing import Literal
6
6
 
7
- from .constants import ctrl_key, rc_binding
7
+ from .constants import align_helper, ctrl_key, rc_binding
8
8
  from .functions import convert_align
9
9
  from .other_classes import DotDict
10
10
 
@@ -92,11 +92,7 @@ class TextEditorTkText(tk.Text):
92
92
  command=self.undo,
93
93
  **menu_kwargs,
94
94
  )
95
- align = convert_align(align)
96
- if align == "w":
97
- self.align = "left"
98
- elif align == "e":
99
- self.align = "right"
95
+ self.align = align_helper[convert_align(align)]
100
96
  self.delete(1.0, "end")
101
97
  self.insert(1.0, text)
102
98
  self.yview_moveto(1)
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Iterable, Iterator
4
- from typing import Tuple, Union
4
+ from typing import Literal, Tuple, Union
5
5
 
6
6
  from .other_classes import Span
7
7
 
@@ -17,4 +17,13 @@ CreateSpanTypes = Union[
17
17
  Span,
18
18
  ]
19
19
 
20
+ CellPropertyKey = Literal[
21
+ "format",
22
+ "highlight",
23
+ "dropdown",
24
+ "checkbox",
25
+ "readonly",
26
+ "align",
27
+ ]
28
+
20
29
  AnyIter = Iterable | Iterator
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tksheet
3
- Version: 7.3.4
4
- Summary: Tkinter table / sheet widget
3
+ Version: 7.4.0
4
+ Summary: Tkinter table / sheet and treeview widget
5
5
  Author-email: ragardner <github@ragardner.simplelogin.com>
6
6
  License: Copyright (c) 2019 ragardner and open source contributors
7
7
 
@@ -26,7 +26,7 @@ License: Copyright (c) 2019 ragardner and open source contributors
26
26
  Project-URL: Homepage, https://github.com/ragardner/tksheet
27
27
  Project-URL: Bug Reports, https://github.com/ragardner/tksheet/issues
28
28
  Project-URL: Funding, https://github.com/ragardner
29
- Keywords: tkinter,table,widget,sheet,grid,tk
29
+ Keywords: tkinter,table,widget,tree,treeview,sheet,grid,tk
30
30
  Classifier: Development Status :: 5 - Production/Stable
31
31
  Classifier: Intended Audience :: Developers
32
32
  Classifier: Topic :: Software Development :: Libraries
@@ -46,7 +46,7 @@ License-File: LICENSE.txt
46
46
  <img width="45%"src="https://github.com/user-attachments/assets/08e77e89-00ea-4c52-9222-9bd421284360" alt="tksheet treeview">
47
47
  </p>
48
48
 
49
- # <div align="center">tksheet - python tkinter table widget</div>
49
+ # <div align="center">tksheet - python tkinter table + treeview widget</div>
50
50
 
51
51
  [![PyPI version shields.io](https://img.shields.io/pypi/v/tksheet.svg)](https://pypi.python.org/pypi/tksheet/) ![python](https://img.shields.io/badge/python-3.8|3.9|3.10|3.11|3.12|3.13-blue) [![License: MIT](https://img.shields.io/badge/License-MIT%20-blue.svg)](https://github.com/ragardner/tksheet/blob/master/LICENSE.txt)
52
52
 
@@ -84,31 +84,24 @@ License-File: LICENSE.txt
84
84
 
85
85
  This library is maintained with the help of **[others](https://github.com/ragardner/tksheet/graphs/contributors)**. If you would like to contribute please read this [help section](https://github.com/ragardner/tksheet/wiki/Version-7#contributing).
86
86
 
87
- ## **Changes for versions `7`+:**
88
-
89
- - **ALL** `extra_bindings()` event objects have changed, information [here](https://github.com/ragardner/tksheet/wiki/Version-7#bind-specific-table-functionality).
90
- - The bound function for `extra_bindings()` with `"edit_cell"`/`"end_edit_cell"` **no longer** requires a return value and no longer sets the cell to the return value. Use [this](https://github.com/ragardner/tksheet/wiki/Version-7#validate-user-cell-edits) instead.
91
- - `edit_cell_validation` has been removed and replaced with the function `edit_validation()`, information [here](https://github.com/ragardner/tksheet/wiki/Version-7#validate-user-cell-edits).
92
- - Only Python versions >= `3.8` are supported.
93
- - `tksheet` file names have been changed.
94
- - Many other smaller changes, see the [changelog](https://github.com/ragardner/tksheet/blob/master/docs/CHANGELOG.md) for more information.
95
-
96
87
  ## **Features**
97
88
 
98
89
  - Smoothly display and modify tabular data
99
90
  - [Edit cells directly](https://github.com/ragardner/tksheet/wiki/Version-7#table-functionality-and-bindings)
100
91
  - Cell values can potentially be [any class](https://github.com/ragardner/tksheet/wiki/Version-7#data-formatting), the default is any class with a `__str__` method
101
92
  - [Drag and drop columns and rows](https://github.com/ragardner/tksheet/wiki/Version-7#table-functionality-and-bindings)
93
+ - [Editable Treeview mode](https://github.com/ragardner/tksheet/wiki/Version-7#treeview-mode) with working drag and drop, undo, etc.
102
94
  - Multiple line header and index cells
103
95
  - [Expand row heights and column widths](https://github.com/ragardner/tksheet/wiki/Version-7#table-functionality-and-bindings)
104
96
  - [Change fonts and font size (not for individual cells)](https://github.com/ragardner/tksheet/wiki/Version-7#text-font-and-alignment)
105
97
  - [Change any colors in the sheet](https://github.com/ragardner/tksheet/wiki/Version-7#sheet-appearance)
106
- - [Treeview mode](https://github.com/ragardner/tksheet/wiki/Version-7#treeview-mode)
98
+
107
99
  - [Dropdown boxes](https://github.com/ragardner/tksheet/wiki/Version-7#dropdown-boxes)
108
100
  - [Check boxes](https://github.com/ragardner/tksheet/wiki/Version-7#check-boxes)
109
101
  - [Progress bars](https://github.com/ragardner/tksheet/wiki/Version-7#progress-bars)
110
102
  - [Hide rows and/or columns](https://github.com/ragardner/tksheet/wiki/Version-7#example-header-dropdown-boxes-and-row-filtering)
111
103
  - [Left `"w"`, Center `"center"` or Right `"e"` text alignment for any cell/row/column](https://github.com/ragardner/tksheet/wiki/Version-7#text-font-and-alignment)
104
+ - [In-built natural sorting](https://github.com/ragardner/tksheet/wiki/Version-7#sorting-the-table)
112
105
  - [Optional built-in find window](https://github.com/ragardner/tksheet/wiki/Version-7#table-functionality-and-bindings)
113
106
 
114
107
  ```python
@@ -140,4 +133,11 @@ sheet.delete_columns(columns=[0, 3], undo=True)
140
133
 
141
134
  ![tksheet treeview](https://github.com/user-attachments/assets/159ab987-7612-4db7-98de-1f30c9680247)
142
135
 
136
+ ## **Changes for versions `7`+:**
143
137
 
138
+ - **ALL** `extra_bindings()` event objects have changed, information [here](https://github.com/ragardner/tksheet/wiki/Version-7#bind-specific-table-functionality).
139
+ - The bound function for `extra_bindings()` with `"edit_cell"`/`"end_edit_cell"` **no longer** requires a return value and no longer sets the cell to the return value. Use [this](https://github.com/ragardner/tksheet/wiki/Version-7#validate-user-cell-edits) instead.
140
+ - `edit_cell_validation` has been removed and replaced with the function `edit_validation()`, information [here](https://github.com/ragardner/tksheet/wiki/Version-7#validate-user-cell-edits).
141
+ - Only Python versions >= `3.8` are supported.
142
+ - `tksheet` file names have been changed.
143
+ - Many other smaller changes, see the [changelog](https://github.com/ragardner/tksheet/blob/master/docs/CHANGELOG.md) for more information.
@@ -0,0 +1,22 @@
1
+ tksheet/__init__.py,sha256=cz7KFN4KnaMiO7QT74IP09ovReyBmHQwhSbnMAofV70,2241
2
+ tksheet/colors.py,sha256=dHhmdFuQDlwohDHsAfT9VdrKoSl_R33L72a3HCin5zo,51591
3
+ tksheet/column_headers.py,sha256=kWCK1fTf0poj5vilqxh99r6gJ4-bBaIJfB-oNbH7oE8,103245
4
+ tksheet/constants.py,sha256=PkvAtdYXSOiOO5zuxqrObvGIVV2sEE0enN8YLhI8zCc,3922
5
+ tksheet/find_window.py,sha256=JfkgpGluSng3bKMBneDNQg-AJmBcmCW7JIhtYbSUZaE,8036
6
+ tksheet/formatters.py,sha256=21ZkMaDIJNUtjvtlAbPl8Y19I9nDxue-JJegw6hblz8,10551
7
+ tksheet/functions.py,sha256=tVwMLWsW4n5M-RCL8DrLMOXygDhBP26wz4nEQq1gkUo,52734
8
+ tksheet/main_table.py,sha256=s7d3q8x35dSWuMcviGkn0g4c2PsJqGLhD319tbaW-yc,357255
9
+ tksheet/other_classes.py,sha256=ADybikLipEG4NABXx8bGVAovJJhWcomQOWTorzS1CPU,16581
10
+ tksheet/row_index.py,sha256=F64DrvSBmq89JVhYomSRzyQtS4nWAeWDcqnPL8cgsGI,133222
11
+ tksheet/sheet.py,sha256=2_fMNnJqwvEkYsrDuyxFC68l6XZROzZVjG2FrSZc7IU,284689
12
+ tksheet/sheet_options.py,sha256=RAQX9NF8Bvauj4fHW-8TGtZG6cRVSzwGxSUKplg3c28,9625
13
+ tksheet/sorting.py,sha256=SMspUgdcmzUOFjhck8brcVhDTwDK2Uw_du9TWkGXJMI,12295
14
+ tksheet/text_editor.py,sha256=ZLVF-0WxDin5qUAJ5r7dmsdwvhyEoxw0PlPvi_AGNPE,7328
15
+ tksheet/themes.py,sha256=AoNAxibnQi04MN0Zpbn9-kyDnkiiV8TDNWP9FYjpuf0,18473
16
+ tksheet/tksheet_types.py,sha256=8JQVlA6N9jEZTEAytbcyuhOGuNE4fUPxYhTqoidxEE0,588
17
+ tksheet/top_left_rectangle.py,sha256=KhTT-rBUwQTgaHjSwL83cL5_71k2L1B7gxkSxZlTSK8,8598
18
+ tksheet-7.4.0.dist-info/LICENSE.txt,sha256=ndbcCPe9SlHfweE_W2RAueWUe2k7yudyxYLq6WjFdn4,1101
19
+ tksheet-7.4.0.dist-info/METADATA,sha256=5vqJyNyD80FP4mSSpaU1osuXCm2GOIahE6vYz8p99-8,7966
20
+ tksheet-7.4.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
21
+ tksheet-7.4.0.dist-info/top_level.txt,sha256=my61PXCcck_HHAc9cq3NAlyAr3A3FXxCy9gptEOaCN8,8
22
+ tksheet-7.4.0.dist-info/RECORD,,
@@ -1,21 +0,0 @@
1
- tksheet/__init__.py,sha256=R9-cO_1-zGTQ17K8KDglbM9MacYfa-IM6BzqSlQhFxc,2241
2
- tksheet/colors.py,sha256=dHhmdFuQDlwohDHsAfT9VdrKoSl_R33L72a3HCin5zo,51591
3
- tksheet/column_headers.py,sha256=ukYtRorIpq6gTIYHQrQdGqMKD0ybA5FI-56qt8-Cqww,100361
4
- tksheet/constants.py,sha256=-NWaHEEpcQ0YHn4yGDf8SSffjceiEOayGqnEYw6VwPM,3507
5
- tksheet/find_window.py,sha256=JfkgpGluSng3bKMBneDNQg-AJmBcmCW7JIhtYbSUZaE,8036
6
- tksheet/formatters.py,sha256=21ZkMaDIJNUtjvtlAbPl8Y19I9nDxue-JJegw6hblz8,10551
7
- tksheet/functions.py,sha256=kxL7dutuL_hTdoaK4rBidd3VGsWvYx1PAB7J_5oP8qg,46509
8
- tksheet/main_table.py,sha256=vyqJPdzEQMmQXg4RQgFp3T3IuWpYouxibsnYsB4zP5c,342095
9
- tksheet/other_classes.py,sha256=Jdy11Q_Hwx2x9j3xTMTEUR6mc16nPVy_zrv38ZmpM2Y,16411
10
- tksheet/row_index.py,sha256=VV6Dy-NHiN7XVSeB5AmCzsfM1mO5tARcKaVe9o8pWDU,109036
11
- tksheet/sheet.py,sha256=FbFb4Abyiv5-DfqBNQqvaeZ6gubF613hX7Ggz48i6A0,290363
12
- tksheet/sheet_options.py,sha256=FOCZgSDnED10UjKJXhvfaRr2sNFnga2_dFHU_0cLJ6U,7856
13
- tksheet/text_editor.py,sha256=4r6JJnzwfqAcYxdtBpPVRgePk_ijhMT0-2EgcD1fKJ0,7416
14
- tksheet/themes.py,sha256=AoNAxibnQi04MN0Zpbn9-kyDnkiiV8TDNWP9FYjpuf0,18473
15
- tksheet/top_left_rectangle.py,sha256=KhTT-rBUwQTgaHjSwL83cL5_71k2L1B7gxkSxZlTSK8,8598
16
- tksheet/types.py,sha256=e1w3Op6RrRdG4u_e1ZAieCVf_lGu2CBQKIB_vKuiYyE,448
17
- tksheet-7.3.4.dist-info/LICENSE.txt,sha256=ndbcCPe9SlHfweE_W2RAueWUe2k7yudyxYLq6WjFdn4,1101
18
- tksheet-7.3.4.dist-info/METADATA,sha256=FokcL8Lyih0jrvJ4C7J1MSSgVXQXUcbXhxmR2yV_XpM,7781
19
- tksheet-7.3.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
20
- tksheet-7.3.4.dist-info/top_level.txt,sha256=my61PXCcck_HHAc9cq3NAlyAr3A3FXxCy9gptEOaCN8,8
21
- tksheet-7.3.4.dist-info/RECORD,,