tksheet 7.3.2__tar.gz → 7.3.3__tar.gz

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.
Files changed (25) hide show
  1. {tksheet-7.3.2/tksheet.egg-info → tksheet-7.3.3}/PKG-INFO +1 -1
  2. {tksheet-7.3.2 → tksheet-7.3.3}/pyproject.toml +1 -1
  3. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/__init__.py +1 -1
  4. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/functions.py +103 -13
  5. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/main_table.py +122 -119
  6. {tksheet-7.3.2 → tksheet-7.3.3/tksheet.egg-info}/PKG-INFO +1 -1
  7. {tksheet-7.3.2 → tksheet-7.3.3}/LICENSE.txt +0 -0
  8. {tksheet-7.3.2 → tksheet-7.3.3}/README.md +0 -0
  9. {tksheet-7.3.2 → tksheet-7.3.3}/setup.cfg +0 -0
  10. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/colors.py +0 -0
  11. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/column_headers.py +0 -0
  12. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/constants.py +0 -0
  13. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/find_window.py +0 -0
  14. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/formatters.py +0 -0
  15. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/other_classes.py +0 -0
  16. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/row_index.py +0 -0
  17. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/sheet.py +0 -0
  18. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/sheet_options.py +0 -0
  19. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/text_editor.py +0 -0
  20. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/themes.py +0 -0
  21. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/top_left_rectangle.py +0 -0
  22. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet/types.py +0 -0
  23. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet.egg-info/SOURCES.txt +0 -0
  24. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet.egg-info/dependency_links.txt +0 -0
  25. {tksheet-7.3.2 → tksheet-7.3.3}/tksheet.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tksheet
3
- Version: 7.3.2
3
+ Version: 7.3.3
4
4
  Summary: Tkinter table / sheet widget
5
5
  Author-email: ragardner <github@ragardner.simplelogin.com>
6
6
  License: Copyright (c) 2019 ragardner and open source contributors
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
  name = "tksheet"
7
7
  description = "Tkinter table / sheet widget"
8
8
  readme = "README.md"
9
- version = "7.3.2"
9
+ version = "7.3.3"
10
10
  authors = [{ name = "ragardner", email = "github@ragardner.simplelogin.com" }]
11
11
  requires-python = ">=3.8"
12
12
  license = {file = "LICENSE.txt"}
@@ -4,7 +4,7 @@
4
4
  tksheet - A Python tkinter table widget
5
5
  """
6
6
 
7
- __version__ = "7.3.2"
7
+ __version__ = "7.3.3"
8
8
 
9
9
  from .colors import (
10
10
  color_map,
@@ -21,6 +21,9 @@ from itertools import islice, repeat
21
21
  from typing import Literal
22
22
 
23
23
  from .colors import color_map
24
+ from .constants import (
25
+ symbols_set,
26
+ )
24
27
  from .formatters import (
25
28
  to_bool,
26
29
  )
@@ -35,9 +38,6 @@ from .other_classes import (
35
38
  from .types import (
36
39
  AnyIter,
37
40
  )
38
- from .constants import (
39
- symbols_set,
40
- )
41
41
 
42
42
  unpickle_obj = pickle.loads
43
43
 
@@ -590,17 +590,8 @@ def move_elements_by_mapping(
590
590
  # old_idxs = {new index: old index, ...}
591
591
  if old_idxs is None:
592
592
  old_idxs = dict(zip(new_idxs.values(), new_idxs))
593
-
594
- res = [0] * len(seq)
595
-
596
593
  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)
602
-
603
- return res
594
+ return [seq[old_idxs[i]] if i in old_idxs else next(remaining_values) for i in range(len(seq))]
604
595
 
605
596
 
606
597
  def move_elements_to(
@@ -734,6 +725,105 @@ def diff_gen(seq: list[float]) -> Generator[int]:
734
725
  )
735
726
 
736
727
 
728
+ def gen_coords(
729
+ start_row: int,
730
+ start_col: int,
731
+ end_row: int,
732
+ end_col: int,
733
+ reverse: bool = False,
734
+ ) -> Generator[tuple[int, int]]:
735
+ if reverse:
736
+ for r in reversed(range(start_row, end_row)):
737
+ for c in reversed(range(start_col, end_col)):
738
+ yield (r, c)
739
+ else:
740
+ for r in range(start_row, end_row):
741
+ for c in range(start_col, end_col):
742
+ yield (r, c)
743
+
744
+
745
+ def box_gen_coords(
746
+ start_row: int,
747
+ start_col: int,
748
+ total_cols: int,
749
+ total_rows: int,
750
+ reverse: bool = False,
751
+ ) -> Generator[tuple[int, int]]:
752
+ if reverse:
753
+ # yield start cell
754
+ yield (start_row, start_col)
755
+ # yield any remaining cells in the starting row before the start column
756
+ if start_col:
757
+ for col in reversed(range(start_col)):
758
+ yield (start_row, col)
759
+ # yield any cells above start row
760
+ for row in reversed(range(start_row)):
761
+ for col in reversed(range(total_cols)):
762
+ yield (row, col)
763
+ # yield cells from bottom of table upward
764
+ for row in range(total_rows - 1, start_row, -1):
765
+ for col in reversed(range(total_cols)):
766
+ yield (row, col)
767
+ # yield any remaining cells in start row
768
+ for col in range(total_cols - 1, start_col, -1):
769
+ yield (start_row, col)
770
+ else:
771
+ # Yield cells from the start position to the end of the current row
772
+ for col in range(start_col, total_cols):
773
+ yield (start_row, col)
774
+ # yield from the next row to the last row
775
+ for row in range(start_row + 1, total_rows):
776
+ for col in range(total_cols):
777
+ yield (row, col)
778
+ # yield from the beginning up to the start
779
+ for row in range(start_row):
780
+ for col in range(total_cols):
781
+ yield (row, col)
782
+ # yield any remaining cells in the starting row before the start column
783
+ for col in range(start_col):
784
+ yield (start_row, col)
785
+
786
+
787
+ def next_cell(
788
+ start_row: int,
789
+ start_col: int,
790
+ end_row: int,
791
+ end_col: int,
792
+ row: int,
793
+ col: int,
794
+ reverse: bool = False,
795
+ ) -> tuple[int, int]:
796
+ if reverse:
797
+ col -= 1
798
+ if col < start_col:
799
+ col = end_col - 1
800
+ row -= 1
801
+ if row < start_row:
802
+ row = end_row - 1
803
+ else:
804
+ col += 1
805
+ if col == end_col:
806
+ col = start_col
807
+ row += 1
808
+ if row == end_row:
809
+ row = start_row
810
+ return row, col
811
+
812
+
813
+ def is_last_cell(
814
+ start_row: int,
815
+ start_col: int,
816
+ end_row: int,
817
+ end_col: int,
818
+ row: int,
819
+ col: int,
820
+ reverse: bool = False,
821
+ ) -> bool:
822
+ if reverse:
823
+ return row == start_row and col == start_col
824
+ return row == end_row - 1 and col == end_col - 1
825
+
826
+
737
827
  def zip_fill_2nd_value(x: AnyIter[object], o: object) -> Generator[object, object]:
738
828
  return zip(x, repeat(o))
739
829
 
@@ -67,6 +67,7 @@ from .functions import (
67
67
  add_to_displayed,
68
68
  b_index,
69
69
  bisect_in,
70
+ box_gen_coords,
70
71
  box_is_single_cell,
71
72
  cell_right_within_box,
72
73
  color_tup,
@@ -78,6 +79,7 @@ from .functions import (
78
79
  event_has_char_key,
79
80
  event_opens_dropdown_or_checkbox,
80
81
  float_to_int,
82
+ gen_coords,
81
83
  gen_formatted,
82
84
  get_data_from_clipboard,
83
85
  get_new_indexes,
@@ -86,6 +88,7 @@ from .functions import (
86
88
  insert_items,
87
89
  int_x_iter,
88
90
  is_iterable,
91
+ is_last_cell,
89
92
  is_type_int,
90
93
  len_to_idx,
91
94
  mod_event_val,
@@ -93,6 +96,7 @@ from .functions import (
93
96
  mod_span_widget,
94
97
  move_elements_by_mapping,
95
98
  new_tk_event,
99
+ next_cell,
96
100
  rounded_box_coords,
97
101
  span_idxs_post_move,
98
102
  stored_event_dict,
@@ -547,19 +551,25 @@ class MainTable(tk.Canvas):
547
551
  self.find_window.tktext.focus_set()
548
552
  return "break"
549
553
 
550
- def find_see_and_set(self, coords: tuple[int, int] | None, just_see: bool = False) -> tuple[int, int]:
554
+ def find_see_and_set(
555
+ self,
556
+ coords: tuple[int, int, int | None] | None,
557
+ just_see: bool = False,
558
+ ) -> tuple[int, int]:
551
559
  if coords:
560
+ row, column, item = coords
552
561
  if not self.all_rows_displayed:
553
- coords = (self.disprn(coords[0]), coords[1])
562
+ row = self.disprn(row)
554
563
  if not self.all_columns_displayed:
555
- coords = (coords[0], self.dispcn(coords[1]))
564
+ column = self.dispcn(column)
556
565
  if not just_see:
557
566
  if self.find_window.window.find_in_selection:
558
- self.set_currently_selected(*coords)
567
+ self.set_currently_selected(row, column, item=item)
559
568
  else:
560
- self.select_cell(*coords, redraw=False)
569
+ self.select_cell(row, column, redraw=False)
561
570
  if not self.see(
562
- *coords,
571
+ row,
572
+ column,
563
573
  keep_yscroll=False,
564
574
  keep_xscroll=False,
565
575
  bottom_right_corner=False,
@@ -569,78 +579,6 @@ class MainTable(tk.Canvas):
569
579
  self.refresh()
570
580
  return coords
571
581
 
572
- def find_gen_all_cells(
573
- self,
574
- start_row: int,
575
- start_col: int,
576
- total_cols: int | None = None,
577
- reverse: bool = False,
578
- ) -> Generator[tuple[int, int]]:
579
- if total_cols is None:
580
- total_cols = self.total_data_cols(include_header=False)
581
- total_rows = self.total_data_rows(include_index=False)
582
- if reverse:
583
- # yield start cell
584
- yield (start_row, start_col)
585
- # yield any remaining cells in the starting row before the start column
586
- if start_col:
587
- for col in reversed(range(start_col)):
588
- yield (start_row, col)
589
- # yield any cells above start row
590
- for row in reversed(range(start_row)):
591
- for col in reversed(range(total_cols)):
592
- yield (row, col)
593
- # yield cells from bottom of table upward
594
- for row in range(total_rows - 1, start_row, -1):
595
- for col in reversed(range(total_cols)):
596
- yield (row, col)
597
- # yield any remaining cells in start row
598
- for col in range(total_cols - 1, start_col, -1):
599
- yield (start_row, col)
600
- else:
601
- # Yield cells from the start position to the end of the current row
602
- for col in range(start_col, total_cols):
603
- yield (start_row, col)
604
- # yield from the next row to the last row
605
- for row in range(start_row + 1, total_rows):
606
- for col in range(total_cols):
607
- yield (row, col)
608
- # yield from the beginning up to the start
609
- for row in range(start_row):
610
- for col in range(total_cols):
611
- yield (row, col)
612
- # yield any remaining cells in the starting row before the start column
613
- for col in range(start_col):
614
- yield (start_row, col)
615
-
616
- def find_get_start_coords(
617
- self,
618
- plus_one: bool = True,
619
- within: None | list = None,
620
- reverse: bool = False,
621
- ) -> tuple[int, int, int]:
622
- selected = self.selected
623
- if not selected:
624
- return 0, 0, 0
625
- max_row = len(self.row_positions) - 2
626
- max_col = len(self.col_positions) - 2
627
- row, col = selected.row, selected.column
628
- if plus_one and within:
629
- curridx = bisect_left(within, (row, col)) + (1 if not reverse else -1)
630
- return row, col, curridx % len(within)
631
- if plus_one:
632
- if reverse:
633
- if col == 0:
634
- col = max_col
635
- row = max_row if row == 0 else row - 1
636
- else:
637
- col -= 1
638
- else:
639
- col = (col + 1) % (max_col + 1)
640
- if col == 0:
641
- row = (row + 1) % (max_row + 1)
642
- return row, col, 0
643
-
644
582
  def find_match(self, find: str, r: int, c: int) -> bool:
645
583
  return (
646
584
  not find
@@ -653,41 +591,104 @@ class MainTable(tk.Canvas):
653
591
  )
654
592
  )
655
593
 
656
- def find_within_sels(
594
+ def find_within_match(self, find: str, r: int, c: int) -> bool:
595
+ if not self.all_rows_displayed:
596
+ r = self.datarn(r)
597
+ if not self.all_columns_displayed:
598
+ c = self.datacn(c)
599
+ return self.find_match(find, r, c)
600
+
601
+ def find_within_current_box(
657
602
  self,
603
+ current_box: SelectionBox,
658
604
  find: str,
659
- reverse: bool = False,
660
- ) -> tuple[int, int] | None:
661
- sels = self.PAR.get_selected_cells(
662
- get_rows=True,
663
- get_columns=True,
664
- sort_by_row=True,
665
- sort_by_column=True,
605
+ reverse: bool,
606
+ ) -> None | tuple[int, int]:
607
+ start_row, start_col = next_cell(
608
+ *current_box.coords,
609
+ self.selected.row,
610
+ self.selected.column,
611
+ reverse=reverse,
666
612
  )
667
- _, _, from_idx = self.find_get_start_coords(plus_one=True, within=sels, reverse=reverse)
668
- for r, c in chain(islice(sels, from_idx, None), islice(sels, 0, from_idx)):
669
- if not self.all_rows_displayed:
670
- r = self.datarn(r)
671
- if not self.all_columns_displayed:
672
- c = self.datacn(c)
673
- if self.find_match(find, r, c):
674
- return (r, c)
613
+ _, _, r2, c2 = current_box.coords
614
+ for r, c in box_gen_coords(start_row, start_col, c2, r2, reverse=reverse):
615
+ if self.find_within_match(find, r, c):
616
+ return (r, c, current_box.fill_iid)
617
+ return None
618
+
619
+ def find_within_non_current_boxes(
620
+ self,
621
+ current_id: int,
622
+ find: str,
623
+ reverse: bool,
624
+ ) -> None | tuple[int, int]:
625
+ if reverse:
626
+ # iterate backwards through selection boxes from the box before current
627
+ idx = next(i for i, k in enumerate(reversed(self.selection_boxes)) if k == current_id)
628
+ for item, box in chain(
629
+ islice(reversed(self.selection_boxes.items()), idx + 1, None),
630
+ islice(reversed(self.selection_boxes.items()), 0, idx),
631
+ ):
632
+ for r, c in gen_coords(*box.coords, reverse=reverse):
633
+ if self.find_within_match(find, r, c):
634
+ return (r, c, item)
635
+ else:
636
+ # iterate forwards through selection boxes from the box after current
637
+ idx = next(i for i, k in enumerate(self.selection_boxes) if k == current_id)
638
+ for item, box in chain(
639
+ islice(self.selection_boxes.items(), idx + 1, None),
640
+ islice(self.selection_boxes.items(), 0, idx),
641
+ ):
642
+ for r, c in gen_coords(*box.coords, reverse=reverse):
643
+ if self.find_within_match(find, r, c):
644
+ return (r, c, item)
645
+ return None
646
+
647
+ def find_within(
648
+ self,
649
+ find: str,
650
+ reverse: bool = False,
651
+ ) -> tuple[int, int, int] | None:
652
+ current_box = self.selection_boxes[self.selected.fill_iid]
653
+ current_id = self.selected.fill_iid
654
+ if is_last_cell(*current_box.coords, self.selected.row, self.selected.column, reverse=reverse):
655
+ if coord := self.find_within_non_current_boxes(current_id=current_id, find=find, reverse=reverse):
656
+ return coord
657
+ if coord := self.find_within_current_box(current_box=current_box, find=find, reverse=reverse):
658
+ return coord
659
+ else:
660
+ if coord := self.find_within_current_box(current_box=current_box, find=find, reverse=reverse):
661
+ return coord
662
+ if coord := self.find_within_non_current_boxes(current_id=current_id, find=find, reverse=reverse):
663
+ return coord
675
664
  return None
676
665
 
677
666
  def find_all_cells(
678
667
  self,
679
668
  find: str,
680
669
  reverse: bool = False,
681
- ) -> tuple[int, int] | None:
682
- row, col, _ = self.find_get_start_coords(plus_one=True, reverse=reverse)
670
+ ) -> tuple[int, int, None] | None:
671
+ if self.selected:
672
+ row, col = next_cell(
673
+ 0,
674
+ 0,
675
+ len(self.row_positions) - 1,
676
+ len(self.col_positions) - 1,
677
+ self.selected.row,
678
+ self.selected.column,
679
+ reverse=reverse,
680
+ )
681
+ else:
682
+ row, col = 0, 0
683
683
  row, col = self.datarn(row), self.datacn(col)
684
- return next(
684
+ result = next(
685
685
  (
686
686
  (r, c)
687
- for r, c in self.find_gen_all_cells(
687
+ for r, c in box_gen_coords(
688
688
  start_row=row,
689
689
  start_col=col,
690
690
  total_cols=self.total_data_cols(include_header=False),
691
+ total_rows=self.total_data_rows(include_index=False),
691
692
  reverse=reverse,
692
693
  )
693
694
  if (
@@ -698,13 +699,16 @@ class MainTable(tk.Canvas):
698
699
  ),
699
700
  None,
700
701
  )
702
+ if result:
703
+ return result + (None,)
704
+ return None
701
705
 
702
706
  def find_next(self, event: tk.Misc | None = None) -> Literal["break"]:
703
707
  find = self.find_window.get().lower()
704
708
  if not self.find_window.open:
705
709
  self.open_find_window(focus=False)
706
710
  if self.find_window.window.find_in_selection:
707
- self.find_see_and_set(self.find_within_sels(find))
711
+ self.find_see_and_set(self.find_within(find))
708
712
  else:
709
713
  self.find_see_and_set(self.find_all_cells(find))
710
714
  return "break"
@@ -714,7 +718,7 @@ class MainTable(tk.Canvas):
714
718
  if not self.find_window.open:
715
719
  self.open_find_window(focus=False)
716
720
  if self.find_window.window.find_in_selection:
717
- self.find_see_and_set(self.find_within_sels(find, reverse=True))
721
+ self.find_see_and_set(self.find_within(find, reverse=True))
718
722
  else:
719
723
  self.find_see_and_set(self.find_all_cells(find, reverse=True))
720
724
  return "break"
@@ -1216,25 +1220,24 @@ class MainTable(tk.Canvas):
1216
1220
  event_data["selection_boxes"] = boxes
1217
1221
  if not try_binding(self.extra_begin_delete_key_func, event_data, "begin_delete"):
1218
1222
  return
1219
- for r1, c1, r2, c2 in boxes:
1220
- for r in range(r1, r2):
1221
- for c in range(c1, c2):
1222
- datarn, datacn = self.datarn(r), self.datacn(c)
1223
- val = self.get_value_for_empty_cell(datarn, datacn)
1224
- if (
1225
- not self.edit_validation_func
1226
- or not validation
1227
- or (
1228
- self.edit_validation_func
1229
- and (val := self.edit_validation_func(mod_event_val(event_data, val, (r, c)))) is not None
1230
- )
1231
- ):
1232
- event_data = self.event_data_set_cell(
1233
- datarn,
1234
- datacn,
1235
- val,
1236
- event_data,
1237
- )
1223
+ for box in boxes:
1224
+ for r, c in gen_coords(*box):
1225
+ datarn, datacn = self.datarn(r), self.datacn(c)
1226
+ val = self.get_value_for_empty_cell(datarn, datacn)
1227
+ if (
1228
+ not self.edit_validation_func
1229
+ or not validation
1230
+ or (
1231
+ self.edit_validation_func
1232
+ and (val := self.edit_validation_func(mod_event_val(event_data, val, (r, c)))) is not None
1233
+ )
1234
+ ):
1235
+ event_data = self.event_data_set_cell(
1236
+ datarn,
1237
+ datacn,
1238
+ val,
1239
+ event_data,
1240
+ )
1238
1241
  if event_data["cells"]["table"]:
1239
1242
  self.refresh()
1240
1243
  self.undo_stack.append(stored_event_dict(event_data))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tksheet
3
- Version: 7.3.2
3
+ Version: 7.3.3
4
4
  Summary: Tkinter table / sheet widget
5
5
  Author-email: ragardner <github@ragardner.simplelogin.com>
6
6
  License: Copyright (c) 2019 ragardner and open source contributors
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes