tksheet 7.4.12__py3-none-any.whl → 7.4.13__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 CHANGED
@@ -4,7 +4,7 @@
4
4
  tksheet - A Python tkinter table widget
5
5
  """
6
6
 
7
- __version__ = "7.4.12"
7
+ __version__ = "7.4.13"
8
8
 
9
9
  from .colors import (
10
10
  color_map,
tksheet/column_headers.py CHANGED
@@ -5,7 +5,7 @@ from collections import defaultdict
5
5
  from collections.abc import Callable, Hashable, Iterator, Sequence
6
6
  from functools import partial
7
7
  from itertools import cycle, islice, repeat
8
- from math import ceil, floor
8
+ from math import ceil
9
9
  from operator import itemgetter
10
10
  from typing import Any, Literal
11
11
 
@@ -741,7 +741,7 @@ class ColumnHeaders(tk.Canvas):
741
741
  if size < self.ops.min_column_width:
742
742
  new_col_pos = ceil(self.MT.col_positions[self.rsz_w - 1] + self.ops.min_column_width)
743
743
  elif size > self.ops.max_column_width:
744
- new_col_pos = floor(self.MT.col_positions[self.rsz_w - 1] + self.ops.max_column_width)
744
+ new_col_pos = int(self.MT.col_positions[self.rsz_w - 1] + self.ops.max_column_width)
745
745
  increment = new_col_pos - self.MT.col_positions[self.rsz_w]
746
746
  self.MT.col_positions[self.rsz_w + 1 :] = [
747
747
  e + increment for e in islice(self.MT.col_positions, self.rsz_w + 1, None)
@@ -1373,7 +1373,7 @@ class ColumnHeaders(tk.Canvas):
1373
1373
  if draw_arrow:
1374
1374
  mod = (self.MT.header_txt_height - 1) if self.MT.header_txt_height % 2 else self.MT.header_txt_height
1375
1375
  small_mod = int(mod / 5)
1376
- mid_y = floor(self.MT.min_header_height / 2)
1376
+ mid_y = int(self.MT.min_header_height / 2)
1377
1377
  if open_:
1378
1378
  # up arrow
1379
1379
  points = (
@@ -1552,13 +1552,13 @@ class ColumnHeaders(tk.Canvas):
1552
1552
  else:
1553
1553
  align = self.align
1554
1554
  if kwargs:
1555
- max_width = crightgridln - cleftgridln - txt_h - 2
1556
- if align.endswith("w"):
1555
+ max_width = crightgridln - cleftgridln - txt_h - 5
1556
+ if align[-1] == "w":
1557
1557
  draw_x = cleftgridln + 2
1558
- elif align.endswith("e"):
1558
+ elif align[-1] == "e":
1559
1559
  draw_x = crightgridln - 5 - txt_h
1560
- elif align.endswith("n"):
1561
- draw_x = cleftgridln + ceil((crightgridln - cleftgridln - txt_h) / 2)
1560
+ elif align[-1] == "n":
1561
+ draw_x = cleftgridln + (crightgridln - cleftgridln - txt_h) / 2
1562
1562
  self.redraw_dropdown(
1563
1563
  cleftgridln,
1564
1564
  0,
@@ -1573,18 +1573,18 @@ class ColumnHeaders(tk.Canvas):
1573
1573
  )
1574
1574
  else:
1575
1575
  max_width = crightgridln - cleftgridln - 2
1576
- if align.endswith("w"):
1576
+ if align[-1] == "w":
1577
1577
  draw_x = cleftgridln + 2
1578
- elif align.endswith("e"):
1578
+ elif align[-1] == "e":
1579
1579
  draw_x = crightgridln - 2
1580
- elif align.endswith("n"):
1581
- draw_x = cleftgridln + floor((crightgridln - cleftgridln) / 2)
1580
+ elif align[-1] == "n":
1581
+ draw_x = cleftgridln + (crightgridln - cleftgridln) / 2
1582
1582
  if (kwargs := self.get_cell_kwargs(datacn, key="checkbox")) and max_width > txt_h + 1:
1583
1583
  box_w = txt_h + 1
1584
- if align.endswith("w"):
1584
+ if align[-1] == "w":
1585
1585
  draw_x += box_w + 3
1586
- elif align.endswith("n"):
1587
- draw_x += ceil(box_w / 2) + 1
1586
+ elif align[-1] == "n":
1587
+ draw_x += box_w / 2 + 1
1588
1588
  max_width -= box_w + 4
1589
1589
  try:
1590
1590
  draw_check = (
@@ -1606,9 +1606,9 @@ class ColumnHeaders(tk.Canvas):
1606
1606
  )
1607
1607
  if (
1608
1608
  max_width < self.MT.header_txt_width
1609
- or (align.endswith("w") and draw_x > scrollpos_right)
1610
- or (align.endswith("e") and cleftgridln + 5 > scrollpos_right)
1611
- or (align.endswith("n") and cleftgridln + 5 > scrollpos_right)
1609
+ or (align[-1] == "w" and draw_x > scrollpos_right)
1610
+ or (align[-1] == "e" and cleftgridln + 5 > scrollpos_right)
1611
+ or (align[-1] == "n" and cleftgridln + 5 > scrollpos_right)
1612
1612
  ):
1613
1613
  continue
1614
1614
  text = self.cell_str(datacn, fix=False)
tksheet/functions.py CHANGED
@@ -793,6 +793,11 @@ def move_elements_by_mapping_gen(
793
793
  return (seq[old_idxs[i]] if i in old_idxs else next(remaining_values) for i in range(len(seq)))
794
794
 
795
795
 
796
+ def move_fast(seq: list[Any], new_idxs: dict[int, int], old_idxs: dict[int, int]) -> list[Any]:
797
+ remaining_values = (e for i, e in enumerate(seq) if i not in new_idxs)
798
+ return [seq[old_idxs[i]] if i in old_idxs else next(remaining_values) for i in range(len(seq))]
799
+
800
+
796
801
  def move_elements_to(
797
802
  seq: list[Any],
798
803
  move_to: int,
@@ -819,10 +824,17 @@ def get_new_indexes(
819
824
  returns {old idx: new idx, ...}
820
825
  """
821
826
  offset = sum(1 for i in to_move if i < move_to)
822
- new_idxs = dict(zip(to_move, range(move_to - offset, move_to - offset + len(to_move))))
823
- if get_inverse:
824
- return new_idxs, dict(zip(new_idxs.values(), new_idxs))
825
- return new_idxs
827
+ correct_move_to = move_to - offset
828
+ if not get_inverse:
829
+ return {elem: correct_move_to + i for i, elem in enumerate(to_move)}
830
+ else:
831
+ new_idxs = {}
832
+ old_idxs = {}
833
+ for i, elem in enumerate(to_move):
834
+ value = correct_move_to + i
835
+ new_idxs[elem] = value
836
+ old_idxs[value] = elem
837
+ return new_idxs, old_idxs
826
838
 
827
839
 
828
840
  def insert_items(
@@ -833,7 +845,7 @@ def insert_items(
833
845
  """
834
846
  seq: list[Any]
835
847
  to_insert: keys are ints sorted, representing list indexes to insert items.
836
- Values are any, e.g. {1: 200, 0: 200}
848
+ Values are any, e.g. {0: 200, 1: 200}
837
849
  """
838
850
  if to_insert:
839
851
  if seq_len_func and next(reversed(to_insert)) >= len(seq) + len(to_insert):
tksheet/main_table.py CHANGED
@@ -8,7 +8,6 @@ from collections import defaultdict, deque
8
8
  from collections.abc import Callable, Generator, Hashable, Iterator, Sequence
9
9
  from functools import partial
10
10
  from itertools import accumulate, chain, cycle, filterfalse, islice, repeat
11
- from math import ceil, floor
12
11
  from operator import itemgetter
13
12
  from re import IGNORECASE, escape, sub
14
13
  from tkinter import TclError
@@ -75,8 +74,8 @@ from .functions import (
75
74
  mod_event_val,
76
75
  mod_span,
77
76
  mod_span_widget,
78
- move_elements_by_mapping,
79
77
  move_elements_by_mapping_gen,
78
+ move_fast,
80
79
  new_tk_event,
81
80
  next_cell,
82
81
  push_n,
@@ -323,7 +322,7 @@ class MainTable(tk.Canvas):
323
322
  and kwargs["total_rows"] > 0
324
323
  and kwargs["total_cols"] > 0
325
324
  ):
326
- self.data = [list(repeat("", kwargs["total_cols"])) for i in range(kwargs["total_rows"])]
325
+ self.data = [list(repeat("", kwargs["total_cols"])) for _ in range(kwargs["total_rows"])]
327
326
  _header = kwargs["header"] if kwargs["header"] is not None else kwargs["headers"]
328
327
  if isinstance(_header, int):
329
328
  self._headers = _header
@@ -1498,11 +1497,6 @@ class MainTable(tk.Canvas):
1498
1497
  event_data: EventDataDict | None = None,
1499
1498
  ) -> tuple[dict[int, int], dict[int, int], EventDataDict]:
1500
1499
  self.saved_column_widths = {}
1501
- if not isinstance(totalcols, int):
1502
- totalcols = max(data_new_idxs.values(), default=0)
1503
- if totalcols:
1504
- totalcols += 1
1505
- totalcols = self.equalize_data_row_lengths(at_least_cols=totalcols)
1506
1500
  if not event_data:
1507
1501
  event_data = self.new_event_dict("move_columns", state=True)
1508
1502
  if not event_data["moved"]["columns"]:
@@ -1539,26 +1533,26 @@ class MainTable(tk.Canvas):
1539
1533
  )
1540
1534
  else:
1541
1535
  self.recreate_all_selection_boxes()
1536
+
1542
1537
  if move_data:
1543
- self.data = list(
1544
- map(
1545
- move_elements_by_mapping,
1546
- self.data,
1547
- repeat(data_new_idxs),
1548
- repeat(data_old_idxs),
1549
- ),
1550
- )
1538
+ if not isinstance(totalcols, int):
1539
+ totalcols = max(data_new_idxs.values(), default=0)
1540
+ if totalcols:
1541
+ totalcols += 1
1542
+ totalcols = self.equalize_data_row_lengths(at_least_cols=totalcols)
1543
+
1544
+ self.data = [move_fast(k, data_new_idxs, data_old_idxs) for k in self.data]
1551
1545
  maxidx = len_to_idx(totalcols)
1552
1546
  self.CH.fix_header(maxidx)
1553
1547
  if isinstance(self._headers, list) and self._headers:
1554
- self._headers = move_elements_by_mapping(self._headers, data_new_idxs, data_old_idxs)
1548
+ self._headers = move_fast(self._headers, data_new_idxs, data_old_idxs)
1555
1549
  maxidx = self.get_max_column_idx(maxidx)
1556
- full_new_idxs = self.get_full_new_idxs(
1550
+ full_new_idxs, full_old_idxs = self.get_full_new_idxs(
1557
1551
  max_idx=maxidx,
1558
1552
  new_idxs=data_new_idxs,
1559
1553
  old_idxs=data_old_idxs,
1554
+ get_inverse=True,
1560
1555
  )
1561
- full_old_idxs = dict(zip(full_new_idxs.values(), full_new_idxs))
1562
1556
  self.tagged_cells = {
1563
1557
  tags: {(k[0], full_new_idxs[k[1]]) for k in tagged} for tags, tagged in self.tagged_cells.items()
1564
1558
  }
@@ -1731,14 +1725,14 @@ class MainTable(tk.Canvas):
1731
1725
  data_old_idxs: dict[int, int],
1732
1726
  maxidx: int,
1733
1727
  ) -> None:
1734
- self.data = move_elements_by_mapping(
1728
+ self.data = move_fast(
1735
1729
  self.data,
1736
1730
  data_new_idxs,
1737
1731
  data_old_idxs,
1738
1732
  )
1739
1733
  self.RI.fix_index(maxidx)
1740
1734
  if isinstance(self._row_index, list) and self._row_index:
1741
- self._row_index = move_elements_by_mapping(self._row_index, data_new_idxs, data_old_idxs)
1735
+ self._row_index = move_fast(self._row_index, data_new_idxs, data_old_idxs)
1742
1736
 
1743
1737
  def move_rows_adjust_options_dict(
1744
1738
  self,
@@ -1790,12 +1784,12 @@ class MainTable(tk.Canvas):
1790
1784
  else:
1791
1785
  self.move_rows_data(data_new_idxs, data_old_idxs, maxidx)
1792
1786
  maxidx = self.get_max_row_idx(maxidx)
1793
- full_new_idxs = self.get_full_new_idxs(
1787
+ full_new_idxs, full_old_idxs = self.get_full_new_idxs(
1794
1788
  max_idx=maxidx,
1795
1789
  new_idxs=data_new_idxs,
1796
1790
  old_idxs=data_old_idxs,
1791
+ get_inverse=True,
1797
1792
  )
1798
- full_old_idxs = dict(zip(full_new_idxs.values(), full_new_idxs))
1799
1793
  self.tagged_cells = {
1800
1794
  tags: {(full_new_idxs[k[0]], k[1]) for k in tagged} for tags, tagged in self.tagged_cells.items()
1801
1795
  }
@@ -1956,6 +1950,7 @@ class MainTable(tk.Canvas):
1956
1950
  max_idx: int,
1957
1951
  new_idxs: dict[int, int],
1958
1952
  old_idxs: None | dict[int, int] = None,
1953
+ get_inverse: bool = False,
1959
1954
  ) -> dict[int, int]:
1960
1955
  # return a dict of all row or column indexes
1961
1956
  # old indexes and new indexes, not just the
@@ -1963,13 +1958,18 @@ class MainTable(tk.Canvas):
1963
1958
  # {old index: new index, ...}
1964
1959
  # all the way from 0 to max_idx
1965
1960
  if old_idxs is None:
1966
- old_idxs = dict(zip(new_idxs.values(), new_idxs))
1967
- return dict(
1968
- zip(
1969
- move_elements_by_mapping_gen(tuple(range(max_idx + 1)), new_idxs, old_idxs),
1970
- range(max_idx + 1),
1971
- )
1972
- )
1961
+ old_idxs = {v: k for k, v in new_idxs.items()}
1962
+ if get_inverse:
1963
+ d = {}
1964
+ d_in = {}
1965
+ for v, k in enumerate(move_elements_by_mapping_gen(tuple(range(max_idx + 1)), new_idxs, old_idxs)):
1966
+ d[k] = v
1967
+ d_in[v] = k
1968
+ return d, d_in
1969
+ else:
1970
+ return {
1971
+ k: v for v, k in enumerate(move_elements_by_mapping_gen(tuple(range(max_idx + 1)), new_idxs, old_idxs))
1972
+ }
1973
1973
 
1974
1974
  def undo(self, event: Any = None) -> None | EventDataDict:
1975
1975
  if not self.undo_stack:
@@ -4047,7 +4047,7 @@ class MainTable(tk.Canvas):
4047
4047
  self.yview_scroll(-1, "units")
4048
4048
  self.RI.yview_scroll(-1, "units")
4049
4049
  self.y_move_synced_scrolls("moveto", self.yview()[0])
4050
- self.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
4050
+ self.main_table_redraw_grid_and_text(redraw_header=False, redraw_row_index=True)
4051
4051
 
4052
4052
  def shift_mousewheel(self, event: Any) -> None:
4053
4053
  if event.delta < 0 or event.num == 5:
@@ -4060,7 +4060,7 @@ class MainTable(tk.Canvas):
4060
4060
  self.xview_scroll(-1, "units")
4061
4061
  self.CH.xview_scroll(-1, "units")
4062
4062
  self.x_move_synced_scrolls("moveto", self.xview()[0])
4063
- self.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
4063
+ self.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=False)
4064
4064
 
4065
4065
  def ctrl_mousewheel(self, event: Any) -> None:
4066
4066
  if event.delta < 0 or event.num == 5:
@@ -4441,7 +4441,7 @@ class MainTable(tk.Canvas):
4441
4441
  self.cells_cache = self._redraw_precache_cells(disprn, disprn + 1, 0, len(self.col_positions) - 1)
4442
4442
  if not (align := self.get_cell_kwargs(datarn, datacn, key="align")):
4443
4443
  align = self.align
4444
- if align.endswith("w"):
4444
+ if align[-1] == "w":
4445
4445
  max_width += sum(
4446
4446
  self._overflow(
4447
4447
  self.cells_cache,
@@ -4449,7 +4449,7 @@ class MainTable(tk.Canvas):
4449
4449
  datarn,
4450
4450
  )
4451
4451
  )
4452
- elif align.endswith("e"):
4452
+ elif align[-1] == "e":
4453
4453
  max_width += sum(
4454
4454
  self._overflow(
4455
4455
  self.cells_cache,
@@ -4768,6 +4768,7 @@ class MainTable(tk.Canvas):
4768
4768
  self,
4769
4769
  rows: list[int] | tuple[int],
4770
4770
  create_ops: bool = True,
4771
+ tree: bool = True,
4771
4772
  ) -> None:
4772
4773
  self.tagged_cells = {
4773
4774
  tags: {(push_n(r, rows), c) for (r, c) in tagged} for tags, tagged in self.tagged_cells.items()
@@ -4777,7 +4778,8 @@ class MainTable(tk.Canvas):
4777
4778
  self.tagged_rows = {tags: {push_n(r, rows) for r in tagged} for tags, tagged in self.tagged_rows.items()}
4778
4779
  self.row_options = {push_n(r, rows): v for r, v in self.row_options.items()}
4779
4780
  self.RI.cell_options = {push_n(r, rows): v for r, v in self.RI.cell_options.items()}
4780
- self.RI.rns = {k: push_n(r, rows) for k, r in self.RI.rns.items()}
4781
+ if tree:
4782
+ self.RI.rns = {k: push_n(r, rows) for k, r in self.RI.rns.items()}
4781
4783
  # if there are named spans where rows were added
4782
4784
  # add options to gap which was created by adding rows
4783
4785
  totalcols = None
@@ -5044,7 +5046,7 @@ class MainTable(tk.Canvas):
5044
5046
  event_data["added"]["rows"] = {
5045
5047
  "table": {},
5046
5048
  "index": {},
5047
- "row_heights": {rn: default_height for rn in range(len(self.row_positions) - 1, maxrn + 1)},
5049
+ "row_heights": dict.fromkeys(range(len(self.row_positions) - 1, maxrn + 1), default_height),
5048
5050
  }
5049
5051
  if not from_undo:
5050
5052
  self.set_row_positions(
@@ -5168,7 +5170,7 @@ class MainTable(tk.Canvas):
5168
5170
  event_data["added"]["columns"] = {
5169
5171
  "table": {},
5170
5172
  "header": {},
5171
- "column_widths": {cn: default_width for cn in range(len(self.col_positions) - 1, maxcn + 1)},
5173
+ "column_widths": dict.fromkeys(range(len(self.col_positions) - 1, maxcn + 1), default_width),
5172
5174
  }
5173
5175
  if not from_undo:
5174
5176
  self.set_col_positions(
@@ -5181,6 +5183,7 @@ class MainTable(tk.Canvas):
5181
5183
  self.adjust_options_post_add_rows(
5182
5184
  rows=tuple(rows),
5183
5185
  create_ops=create_ops,
5186
+ tree=tree,
5184
5187
  )
5185
5188
  event_data["added"]["rows"] = {
5186
5189
  "table": rows,
@@ -6084,7 +6087,7 @@ class MainTable(tk.Canvas):
6084
6087
  if draw_arrow:
6085
6088
  mod = (self.table_txt_height - 1) if self.table_txt_height % 2 else self.table_txt_height
6086
6089
  small_mod = int(mod / 5)
6087
- mid_y = floor(self.min_row_height / 2)
6090
+ mid_y = int(self.min_row_height / 2)
6088
6091
  if open_:
6089
6092
  # up arrow
6090
6093
  points = (
@@ -6283,19 +6286,25 @@ class MainTable(tk.Canvas):
6283
6286
  # self.get_cell_kwargs not used here to boost performance
6284
6287
  if t in self.cell_options and "dropdown" in self.cell_options[t]:
6285
6288
  cells["dropdown"][t] = self.cell_options[t]["dropdown"]
6289
+
6286
6290
  elif datarn in self.row_options and "dropdown" in self.row_options[datarn]:
6287
6291
  cells["dropdown"][t] = self.row_options[datarn]["dropdown"]
6292
+
6288
6293
  elif datacn in self.col_options and "dropdown" in self.col_options[datacn]:
6289
6294
  cells["dropdown"][t] = self.col_options[datacn]["dropdown"]
6295
+
6290
6296
  else:
6291
6297
  if t in self.cell_options and "checkbox" in self.cell_options[t]:
6292
6298
  cells["checkbox"][t] = self.cell_options[t]["checkbox"]
6299
+
6293
6300
  elif datarn in self.row_options and "checkbox" in self.row_options[datarn]:
6294
6301
  cells["checkbox"][t] = self.row_options[datarn]["checkbox"]
6302
+
6295
6303
  elif datacn in self.col_options and "checkbox" in self.col_options[datacn]:
6296
6304
  cells["checkbox"][t] = self.col_options[datacn]["checkbox"]
6297
6305
 
6298
6306
  cells[t] = self.cell_str(datarn, datacn, get_displayed=True)
6307
+
6299
6308
  return cells
6300
6309
 
6301
6310
  def wrap_get_char_w(self, c: str) -> int:
@@ -6380,6 +6389,7 @@ class MainTable(tk.Canvas):
6380
6389
  if changed_w:
6381
6390
  for widget in (self, self.RI, self.CH, self.TL):
6382
6391
  widget.update_idletasks()
6392
+ return
6383
6393
  # important vars
6384
6394
  x_stop = min(last_col_line_pos, scrollpos_right)
6385
6395
  y_stop = min(last_row_line_pos, scrollpos_bot)
@@ -6452,14 +6462,14 @@ class MainTable(tk.Canvas):
6452
6462
  else:
6453
6463
  alternate_color = None
6454
6464
  dont_blend = ()
6455
- if not self.PAR.ops.show_selected_cells_border:
6465
+ if self.PAR.ops.show_selected_cells_border:
6466
+ override = ()
6467
+ else:
6456
6468
  override = (
6457
6469
  color_tup(self.PAR.ops.table_selected_cells_fg),
6458
6470
  color_tup(self.PAR.ops.table_selected_columns_fg),
6459
6471
  color_tup(self.PAR.ops.table_selected_rows_fg),
6460
6472
  )
6461
- else:
6462
- override = ()
6463
6473
  allow_overflow = self.PAR.ops.allow_cell_overflow
6464
6474
  wrap = self.PAR.ops.table_wrap
6465
6475
  cells = self._redraw_precache_cells(
@@ -6509,12 +6519,12 @@ class MainTable(tk.Canvas):
6509
6519
  kws = cells["dropdown"][t] if t in cells["dropdown"] else None # noqa: SIM401
6510
6520
  if kws:
6511
6521
  max_width = crightgridln - cleftgridln - self.table_txt_height - 5
6512
- if align.endswith("w"):
6522
+ if align[-1] == "w":
6513
6523
  draw_x = cleftgridln + 2
6514
- elif align.endswith("e"):
6524
+ elif align[-1] == "e":
6515
6525
  draw_x = crightgridln - 5 - self.table_txt_height
6516
- elif align.endswith("n"):
6517
- draw_x = cleftgridln + ceil((crightgridln - cleftgridln - self.table_txt_height) / 2)
6526
+ elif align[-1] == "n":
6527
+ draw_x = cleftgridln + (crightgridln - cleftgridln - self.table_txt_height) / 2
6518
6528
  self.redraw_dropdown(
6519
6529
  cleftgridln,
6520
6530
  rtopgridln,
@@ -6528,49 +6538,43 @@ class MainTable(tk.Canvas):
6528
6538
  )
6529
6539
  else:
6530
6540
  max_width = crightgridln - cleftgridln - 2
6531
- if align.endswith("w"):
6541
+ if align[-1] == "w":
6532
6542
  draw_x = cleftgridln + 2
6533
- elif align.endswith("e"):
6543
+ elif align[-1] == "e":
6534
6544
  draw_x = crightgridln - 2
6535
- elif align.endswith("n"):
6536
- draw_x = cleftgridln + floor((crightgridln - cleftgridln) / 2)
6537
-
6538
- kws = cells["checkbox"][t] if t in cells["checkbox"] else None # noqa: SIM401
6539
- if kws and max_width > self.table_txt_height + 1:
6540
- box_w = self.table_txt_height + 1
6541
- if align.endswith("w"):
6542
- draw_x += box_w + 3
6543
- elif align.endswith("n"):
6544
- draw_x += ceil(box_w / 2) + 1
6545
- max_width -= box_w + 4
6546
- try:
6547
- draw_check = bool(self.data[datarn][datacn])
6548
- except Exception:
6549
- draw_check = False
6550
- self.redraw_checkbox(
6551
- cleftgridln + 2,
6552
- rtopgridln + 2,
6553
- cleftgridln + self.table_txt_height + 3,
6554
- rtopgridln + self.table_txt_height + 3,
6555
- fill=fill if kws["state"] == "normal" else self.PAR.ops.table_grid_fg,
6556
- outline="",
6557
- draw_check=draw_check,
6558
- )
6545
+ elif align[-1] == "n":
6546
+ draw_x = cleftgridln + (crightgridln - cleftgridln) / 2
6547
+
6548
+ if t in cells["checkbox"]:
6549
+ kws = cells["checkbox"][t]
6550
+ if max_width > self.table_txt_height + 1:
6551
+ box_w = self.table_txt_height + 1
6552
+ if align[-1] == "w":
6553
+ draw_x += box_w + 3
6554
+ elif align[-1] == "n":
6555
+ draw_x += box_w / 2 + 1
6556
+ max_width -= box_w + 4
6557
+ try:
6558
+ draw_check = bool(self.data[datarn][datacn])
6559
+ except Exception:
6560
+ draw_check = False
6561
+ self.redraw_checkbox(
6562
+ cleftgridln + 2,
6563
+ rtopgridln + 2,
6564
+ cleftgridln + self.table_txt_height + 3,
6565
+ rtopgridln + self.table_txt_height + 3,
6566
+ fill=fill if kws["state"] == "normal" else self.PAR.ops.table_grid_fg,
6567
+ outline="",
6568
+ draw_check=draw_check,
6569
+ )
6559
6570
  text = cells[t]
6560
- if (
6561
- not text
6562
- or (align.endswith("w") and draw_x > scrollpos_right)
6563
- or (align.endswith("e") and cleftgridln + 5 > scrollpos_right)
6564
- or (align.endswith("n") and cleftgridln + 5 > scrollpos_right)
6565
- ):
6571
+ if not text or (align[-1] == "w" and draw_x > scrollpos_right) or cleftgridln + 5 > scrollpos_right:
6566
6572
  continue
6567
6573
  if allow_overflow and not kws:
6568
- if align.endswith("w"):
6574
+ if align[-1] == "w":
6569
6575
  max_width += sum(self._overflow(cells, range(c + 1, text_end_col), datarn))
6570
- elif align.endswith("e"):
6576
+ elif align[-1] == "e":
6571
6577
  max_width += sum(self._overflow(cells, reversed(range(text_start_col, c)), datarn))
6572
- elif align.endswith("n"):
6573
- ...
6574
6578
  if max_width <= 1:
6575
6579
  continue
6576
6580
  start_line = max(0, int((scrollpos_top - rtopgridln) / self.table_txt_height))
@@ -6584,7 +6588,7 @@ class MainTable(tk.Canvas):
6584
6588
  wrap=wrap,
6585
6589
  start_line=start_line,
6586
6590
  )
6587
- if align.endswith(("w", "e")):
6591
+ if align[-1] == "w" or align[-1] == "e":
6588
6592
  if self.hidd_text:
6589
6593
  iid, showing = self.hidd_text.popitem()
6590
6594
  self.coords(iid, draw_x, draw_y)
@@ -6617,15 +6621,15 @@ class MainTable(tk.Canvas):
6617
6621
  )
6618
6622
  self.disp_text[iid] = True
6619
6623
 
6620
- elif align.endswith("n"):
6621
- for text in gen_lines:
6624
+ elif align[-1] == "n":
6625
+ for t in gen_lines:
6622
6626
  if self.hidd_text:
6623
6627
  iid, showing = self.hidd_text.popitem()
6624
6628
  self.coords(iid, draw_x, draw_y)
6625
6629
  if showing:
6626
6630
  self.itemconfig(
6627
6631
  iid,
6628
- text=text,
6632
+ text=t,
6629
6633
  fill=fill,
6630
6634
  font=font,
6631
6635
  anchor=align,
@@ -6633,7 +6637,7 @@ class MainTable(tk.Canvas):
6633
6637
  else:
6634
6638
  self.itemconfig(
6635
6639
  iid,
6636
- text=text,
6640
+ text=t,
6637
6641
  fill=fill,
6638
6642
  font=font,
6639
6643
  anchor=align,
@@ -6643,7 +6647,7 @@ class MainTable(tk.Canvas):
6643
6647
  iid = self.create_text(
6644
6648
  draw_x,
6645
6649
  draw_y,
6646
- text=text,
6650
+ text=t,
6647
6651
  fill=fill,
6648
6652
  font=font,
6649
6653
  anchor=align,
@@ -6668,11 +6672,11 @@ class MainTable(tk.Canvas):
6668
6672
  self.tag_raise(box.bd_iid)
6669
6673
  if self.selected:
6670
6674
  self.tag_raise(self.selected.iid)
6671
- self.tag_raise("t")
6672
6675
  if self.RI.disp_resize_lines:
6673
6676
  self.tag_raise("rh")
6674
6677
  if self.CH.disp_resize_lines:
6675
6678
  self.tag_raise("rw")
6679
+ self.tag_raise("t")
6676
6680
  if redraw_header and self.show_header:
6677
6681
  self.CH.redraw_grid_and_text(
6678
6682
  last_col_line_pos=last_col_line_pos,
@@ -6781,7 +6785,7 @@ class MainTable(tk.Canvas):
6781
6785
  box = next(iter(reversed(self.selection_boxes.values())))
6782
6786
  r1, c1, r2, c2 = box.coords
6783
6787
  if r2 - r1 == 1 and c2 - c1 == 1:
6784
- self.itemconfig(box.fill_iid, state="hidden")
6788
+ box.state = "hidden"
6785
6789
  self.set_currently_selected(item=box.fill_iid)
6786
6790
 
6787
6791
  def coords_and_type(self, item: int) -> tuple:
@@ -7117,9 +7121,10 @@ class MainTable(tk.Canvas):
7117
7121
 
7118
7122
  def get_redraw_selections(self, startr: int, endr: int, startc: int, endc: int) -> dict:
7119
7123
  d = defaultdict(set)
7124
+ ignore_hidd_current = not self.PAR.ops.show_selected_cells_border
7120
7125
  for _, box in self.get_selection_items():
7121
7126
  r1, c1, r2, c2 = box.coords
7122
- if box.state == "normal":
7127
+ if box.state == "normal" or ignore_hidd_current:
7123
7128
  if box.type_ == "cells":
7124
7129
  for r in range(startr, endr):
7125
7130
  for c in range(startc, endc):
@@ -8081,23 +8086,49 @@ class MainTable(tk.Canvas):
8081
8086
  cell value is not in datatypes kwarg
8082
8087
  if get displayed is true then Nones are replaced by
8083
8088
  """
8084
- kwargs = self.get_cell_kwargs(datarn, datacn, key=None)
8085
8089
  if get_displayed:
8086
- if kwargs and "dropdown" in kwargs:
8087
- if kwargs["dropdown"]["text"] is not None:
8088
- return f"{kwargs['dropdown']['text']}"
8089
- elif kwargs and "checkbox" in kwargs:
8090
- return f"{kwargs['checkbox']['text']}"
8090
+ # check for dropdown
8091
+ if (datarn, datacn) in self.cell_options and "dropdown" in self.cell_options[(datarn, datacn)]:
8092
+ kws = self.cell_options[(datarn, datacn)]["dropdown"]
8093
+ elif datarn in self.row_options and "dropdown" in self.row_options[datarn]:
8094
+ kws = self.row_options[datarn]["dropdown"]
8095
+ elif datacn in self.col_options and "dropdown" in self.col_options[datacn]:
8096
+ kws = self.col_options[datacn]["dropdown"]
8097
+ else:
8098
+ kws = None
8099
+ if kws and kws["text"] is not None:
8100
+ return f"{kws['text']}"
8101
+
8102
+ # check for checkbox
8103
+ if (datarn, datacn) in self.cell_options and "checkbox" in self.cell_options[(datarn, datacn)]:
8104
+ kws = self.cell_options[(datarn, datacn)]["checkbox"]
8105
+ elif datarn in self.row_options and "checkbox" in self.row_options[datarn]:
8106
+ kws = self.row_options[datarn]["checkbox"]
8107
+ elif datacn in self.col_options and "checkbox" in self.col_options[datacn]:
8108
+ kws = self.col_options[datacn]["checkbox"]
8109
+ else:
8110
+ kws = None
8111
+ if kws:
8112
+ return f"{kws['text']}"
8091
8113
  try:
8092
8114
  value = self.data[datarn][datacn]
8093
8115
  except Exception:
8094
8116
  value = ""
8095
- if "format" in kwargs:
8096
- if kwargs["format"]["formatter"] is None:
8117
+ # check for format
8118
+ if (datarn, datacn) in self.cell_options and "format" in self.cell_options[(datarn, datacn)]:
8119
+ kws = self.cell_options[(datarn, datacn)]["format"]
8120
+ elif datarn in self.row_options and "format" in self.row_options[datarn]:
8121
+ kws = self.row_options[datarn]["format"]
8122
+ elif datacn in self.col_options and "format" in self.col_options[datacn]:
8123
+ kws = self.col_options[datacn]["format"]
8124
+ else:
8125
+ kws = None
8126
+ if kws:
8127
+ if kws["formatter"] is None:
8097
8128
  if get_displayed:
8098
- return data_to_str(value, **kwargs["format"])
8129
+ return data_to_str(value, **kws)
8099
8130
  else:
8100
- return f"{get_data_with_valid_check(value, **kwargs['format'])}"
8131
+ return f"{get_data_with_valid_check(value, **kws)}"
8101
8132
  else:
8102
8133
  if get_displayed:
8103
8134
  # assumed given formatter class has __str__()
@@ -8106,7 +8137,7 @@ class MainTable(tk.Canvas):
8106
8137
  # assumed given formatter class has get_data_with_valid_check()
8107
8138
  return f"{value.get_data_with_valid_check()}"
8108
8139
  else:
8109
- return "" if value is None else value if isinstance(value, str) else f"{value}"
8140
+ return "" if value is None else str(value)
8110
8141
 
8111
8142
  def get_cell_data(
8112
8143
  self,
tksheet/other_classes.py CHANGED
@@ -448,7 +448,7 @@ class Node:
448
448
  self,
449
449
  text: str,
450
450
  iid: str,
451
- parent: str | None = None,
451
+ parent: str = "",
452
452
  children: list[str] | None = None,
453
453
  ) -> None:
454
454
  self.text = text
tksheet/row_index.py CHANGED
@@ -3,9 +3,10 @@ from __future__ import annotations
3
3
  import tkinter as tk
4
4
  from collections import defaultdict
5
5
  from collections.abc import Callable, Generator, Hashable, Iterator, Sequence
6
+ from contextlib import suppress
6
7
  from functools import partial
7
- from itertools import chain, cycle, islice, repeat
8
- from math import ceil, floor
8
+ from itertools import cycle, islice, repeat
9
+ from math import ceil
9
10
  from operator import itemgetter
10
11
  from typing import Any, Literal
11
12
 
@@ -758,7 +759,7 @@ class RowIndex(tk.Canvas):
758
759
  if size < self.MT.min_row_height:
759
760
  new_row_pos = ceil(self.MT.row_positions[self.rsz_h - 1] + self.MT.min_row_height)
760
761
  elif size > self.ops.max_row_height:
761
- new_row_pos = floor(self.MT.row_positions[self.rsz_h - 1] + self.ops.max_row_height)
762
+ new_row_pos = int(self.MT.row_positions[self.rsz_h - 1] + self.ops.max_row_height)
762
763
  increment = new_row_pos - self.MT.row_positions[self.rsz_h]
763
764
  self.MT.row_positions[self.rsz_h + 1 :] = [
764
765
  e + increment for e in islice(self.MT.row_positions, self.rsz_h + 1, None)
@@ -1071,7 +1072,7 @@ class RowIndex(tk.Canvas):
1071
1072
  align = self.cell_options[datarn]["align"]
1072
1073
  else:
1073
1074
  align = self.align
1074
- if align.endswith("w"):
1075
+ if align[-1] == "w":
1075
1076
  w += self.MT.index_txt_height
1076
1077
  w += self.get_iid_indent(self.MT._row_index[datarn].iid) + 10
1077
1078
  return w, h
@@ -1245,7 +1246,7 @@ class RowIndex(tk.Canvas):
1245
1246
  new_w = self.get_index_text_width(only_rows=only_rows)
1246
1247
  else:
1247
1248
  new_w = None
1248
- if new_w is not None and (sheet_w_x := floor(self.PAR.winfo_width() * 0.7)) < new_w:
1249
+ if new_w is not None and (sheet_w_x := int(self.PAR.winfo_width() * 0.7)) < new_w:
1249
1250
  new_w = sheet_w_x
1250
1251
  if new_w and (self.current_width - new_w > 20 or new_w - self.current_width > 3):
1251
1252
  if self.MT.find_window.open:
@@ -1394,7 +1395,7 @@ class RowIndex(tk.Canvas):
1394
1395
  ) -> None:
1395
1396
  mod = (self.MT.index_txt_height - 1) if self.MT.index_txt_height % 2 else self.MT.index_txt_height
1396
1397
  small_mod = int(mod / 5)
1397
- mid_y = floor(self.MT.min_row_height / 2)
1398
+ mid_y = int(self.MT.min_row_height / 2)
1398
1399
  if has_children:
1399
1400
  # up arrow
1400
1401
  if open_:
@@ -1490,7 +1491,7 @@ class RowIndex(tk.Canvas):
1490
1491
  if draw_arrow:
1491
1492
  mod = (self.MT.index_txt_height - 1) if self.MT.index_txt_height % 2 else self.MT.index_txt_height
1492
1493
  small_mod = int(mod / 5)
1493
- mid_y = floor(self.MT.min_row_height / 2)
1494
+ mid_y = int(self.MT.min_row_height / 2)
1494
1495
  if open_:
1495
1496
  # up arrow
1496
1497
  points = (
@@ -1673,13 +1674,13 @@ class RowIndex(tk.Canvas):
1673
1674
  else:
1674
1675
  align = self.align
1675
1676
  if dropdown_kwargs:
1676
- max_width = self.current_width - self.MT.index_txt_height - 2
1677
- if align.endswith("w"):
1677
+ max_width = self.current_width - self.MT.index_txt_height - 5
1678
+ if align[-1] == "w":
1678
1679
  draw_x = 3
1679
- elif align.endswith("e"):
1680
+ elif align[-1] == "e":
1680
1681
  draw_x = self.current_width - 5 - self.MT.index_txt_height
1681
- elif align.endswith("n"):
1682
- draw_x = ceil((self.current_width - self.MT.index_txt_height) / 2)
1682
+ elif align[-1] == "n":
1683
+ draw_x = (self.current_width - self.MT.index_txt_height) / 2
1683
1684
  self.redraw_dropdown(
1684
1685
  0,
1685
1686
  rtopgridln,
@@ -1694,22 +1695,22 @@ class RowIndex(tk.Canvas):
1694
1695
  )
1695
1696
  else:
1696
1697
  max_width = self.current_width - 2
1697
- if align.endswith("w"):
1698
+ if align[-1] == "w":
1698
1699
  draw_x = 3
1699
- elif align.endswith("e"):
1700
+ elif align[-1] == "e":
1700
1701
  draw_x = self.current_width - 3
1701
- elif align.endswith("n"):
1702
- draw_x = floor(self.current_width / 2)
1702
+ elif align[-1] == "n":
1703
+ draw_x = self.current_width / 2
1703
1704
  if (
1704
1705
  (checkbox_kwargs := self.get_cell_kwargs(datarn, key="checkbox"))
1705
1706
  and not dropdown_kwargs
1706
1707
  and max_width > self.MT.index_txt_height + 1
1707
1708
  ):
1708
1709
  box_w = self.MT.index_txt_height + 1
1709
- if align.endswith("w"):
1710
+ if align[-1] == "w":
1710
1711
  draw_x += box_w + 3
1711
- elif align.endswith("n"):
1712
- draw_x += ceil(box_w / 2) + 1
1712
+ elif align[-1] == "n":
1713
+ draw_x += box_w / 2 + 1
1713
1714
  max_width -= box_w + 4
1714
1715
  try:
1715
1716
  draw_check = (
@@ -1732,7 +1733,7 @@ class RowIndex(tk.Canvas):
1732
1733
  if treeview and isinstance(self.MT._row_index, list) and len(self.MT._row_index) > datarn:
1733
1734
  iid = self.MT._row_index[datarn].iid
1734
1735
  max_width -= self.MT.index_txt_height
1735
- if align.endswith("w"):
1736
+ if align[-1] == "w":
1736
1737
  draw_x += self.MT.index_txt_height + 3
1737
1738
  level, indent = self.get_iid_level_indent(iid)
1738
1739
  draw_x += indent + 5
@@ -2729,16 +2730,17 @@ class RowIndex(tk.Canvas):
2729
2730
  )
2730
2731
  # deal with displayed mapping
2731
2732
  event_data["moved"]["rows"]["displayed"] = {}
2732
- if new_loc_is_displayed:
2733
- if disp_insert_row is None:
2734
- if new_parent or insert_row > move_to_row:
2735
- disp_insert_row = self.MT.disprn(self.rns[move_to_iid]) + 1 # TODO: maybe issues here
2733
+ with suppress(Exception):
2734
+ if new_loc_is_displayed:
2735
+ if disp_insert_row is None:
2736
+ if new_parent or insert_row > move_to_row:
2737
+ disp_insert_row = self.MT.disprn(self.rns[move_to_iid]) + 1
2738
+ else:
2739
+ disp_insert_row = self.MT.disprn(self.rns[move_to_iid])
2740
+ if (disp_from_row := self.MT.try_disprn(self.rns[item])) is not None:
2741
+ event_data["moved"]["rows"]["displayed"] = {disp_from_row: disp_insert_row}
2736
2742
  else:
2737
- disp_insert_row = self.MT.disprn(self.rns[move_to_iid])
2738
- if (disp_from_row := self.MT.try_disprn(self.rns[item])) is not None:
2739
- event_data["moved"]["rows"]["displayed"] = {disp_from_row: disp_insert_row}
2740
- else:
2741
- event_data["moved"]["rows"]["displayed"] = {(): disp_insert_row}
2743
+ event_data["moved"]["rows"]["displayed"] = {(): disp_insert_row}
2742
2744
 
2743
2745
  if any(self.move_pid_causes_recursive_loop(self.MT._row_index[r].iid, new_parent) for r in moved_rows):
2744
2746
  event_data["moved"]["rows"] = {}
@@ -2759,7 +2761,7 @@ class RowIndex(tk.Canvas):
2759
2761
  event_data["moved"]["rows"]["data"],
2760
2762
  )
2761
2763
  data_new_idxs = event_data["moved"]["rows"]["data"]
2762
- data_old_idxs = dict(zip(data_new_idxs.values(), data_new_idxs))
2764
+ data_old_idxs = {v: k for k, v in data_new_idxs.items()}
2763
2765
 
2764
2766
  if () in event_data["moved"]["rows"]["displayed"]:
2765
2767
  del event_data["moved"]["rows"]["displayed"][()]
@@ -2988,15 +2990,6 @@ class RowIndex(tk.Canvas):
2988
2990
  if not parent_node.children:
2989
2991
  self.tree_open_ids.discard(parent_node.iid)
2990
2992
 
2991
- def build_pid_causes_recursive_loop(self, iid: str, pid: str) -> bool:
2992
- return any(
2993
- i == pid
2994
- for i in chain(
2995
- self.get_iid_descendants(iid),
2996
- islice(self.get_iid_ancestors(iid), 1, None),
2997
- )
2998
- )
2999
-
3000
2993
  def move_pid_causes_recursive_loop(self, to_move_iid: str, move_to_parent: str) -> bool:
3001
2994
  # if the parent the item is being moved under is one of the item's descendants
3002
2995
  # then it is a recursive loop
@@ -3017,8 +3010,8 @@ class RowIndex(tk.Canvas):
3017
3010
  include_parent_column: bool = True,
3018
3011
  include_text_column: bool = True,
3019
3012
  ) -> None:
3020
- index = self.MT._row_index
3021
3013
  data_rns = {}
3014
+ tree = {}
3022
3015
  if text_column is None:
3023
3016
  text_column = iid_column
3024
3017
  if not isinstance(ncols, int):
@@ -3030,33 +3023,27 @@ class RowIndex(tk.Canvas):
3030
3023
  else:
3031
3024
  iid = row[iid_column]
3032
3025
  pid = row[parent_column]
3033
- if iid in self.rns:
3034
- index[self.rns[iid]].text = row[text_column] if isinstance(text_column, int) else text_column[rn]
3026
+ if iid in tree:
3027
+ tree[iid].text = row[text_column] if isinstance(text_column, int) else text_column[rn]
3035
3028
  else:
3036
- index.append(
3037
- Node(
3038
- text=row[text_column] if isinstance(text_column, int) else text_column[rn],
3039
- iid=iid,
3040
- parent="",
3041
- )
3029
+ tree[iid] = Node(
3030
+ text=row[text_column] if isinstance(text_column, int) else text_column[rn],
3031
+ iid=iid,
3032
+ parent="",
3042
3033
  )
3043
- self.rns[iid] = len(index) - 1
3044
3034
  data_rns[iid] = rn
3045
3035
  if pid:
3046
- if pid in self.rns:
3047
- index[self.rns[pid]].children.append(iid)
3036
+ if pid in tree:
3037
+ tree[pid].children.append(iid)
3048
3038
  else:
3049
- index.append(
3050
- Node(
3051
- text=pid,
3052
- iid=pid,
3053
- children=[iid],
3054
- )
3039
+ tree[pid] = Node(
3040
+ text=pid,
3041
+ iid=pid,
3042
+ children=[iid],
3055
3043
  )
3056
- self.rns[pid] = len(index) - 1
3057
- index[self.rns[iid]].parent = pid
3044
+ tree[iid].parent = pid
3058
3045
  else:
3059
- index[self.rns[iid]].parent = ""
3046
+ tree[iid].parent = ""
3060
3047
  exclude = set()
3061
3048
  if not include_iid_column:
3062
3049
  exclude.add(iid_column)
@@ -3065,18 +3052,35 @@ class RowIndex(tk.Canvas):
3065
3052
  if isinstance(text_column, int) and not include_text_column:
3066
3053
  exclude.add(text_column)
3067
3054
  rows = []
3055
+ ctr = 0
3068
3056
  if exclude:
3069
- for iid in self.PAR.tree_traverse():
3070
- row = [index[self.rns[iid]]]
3071
- row.extend(e for i, e in enumerate(data[data_rns[iid]]) if i not in exclude)
3072
- rows.append(row)
3057
+ for iid, node in tree.items():
3058
+ if node.parent == "":
3059
+ row = [tree[iid]]
3060
+ row.extend(e for i, e in enumerate(data[data_rns[iid]]) if i not in exclude)
3061
+ rows.append(row)
3062
+ self.rns[iid] = ctr
3063
+ ctr += 1
3064
+ for diid in self._build_get_descendants(iid, tree):
3065
+ row = [tree[diid]]
3066
+ row.extend(e for i, e in enumerate(data[data_rns[diid]]) if i not in exclude)
3067
+ rows.append(row)
3068
+ self.rns[diid] = ctr
3069
+ ctr += 1
3073
3070
  else:
3074
- for iid in self.PAR.tree_traverse():
3075
- row = [index[self.rns[iid]]]
3076
- row.extend(data[data_rns[iid]])
3077
- rows.append(row)
3078
- self.MT._row_index = []
3079
- self.rns = {}
3071
+ for iid, node in tree.items():
3072
+ if node.parent == "":
3073
+ row = [tree[iid]]
3074
+ row.extend(data[data_rns[iid]])
3075
+ rows.append(row)
3076
+ self.rns[iid] = ctr
3077
+ ctr += 1
3078
+ for diid in self._build_get_descendants(iid, tree):
3079
+ row = [tree[diid]]
3080
+ row.extend(data[data_rns[diid]])
3081
+ rows.append(row)
3082
+ self.rns[diid] = ctr
3083
+ ctr += 1
3080
3084
  self.PAR.insert_rows(
3081
3085
  rows=rows,
3082
3086
  idx=0,
@@ -3091,7 +3095,6 @@ class RowIndex(tk.Canvas):
3091
3095
  )
3092
3096
  self.MT.all_rows_displayed = False
3093
3097
  self.MT.displayed_rows = list(range(len(self.MT._row_index)))
3094
- self.rns = {n.iid: i for i, n in enumerate(self.MT._row_index)}
3095
3098
  if open_ids:
3096
3099
  self.PAR.tree_set_open(open_ids=open_ids)
3097
3100
  else:
@@ -3118,8 +3121,8 @@ class RowIndex(tk.Canvas):
3118
3121
  include_parent_column: bool = True,
3119
3122
  include_text_column: bool = True,
3120
3123
  ) -> None:
3121
- index = self.MT._row_index
3122
3124
  data_rns = {}
3125
+ tree = {}
3123
3126
  iids_missing_rows = set()
3124
3127
  if text_column is None:
3125
3128
  text_column = iid_column
@@ -3146,43 +3149,36 @@ class RowIndex(tk.Canvas):
3146
3149
  x += 1
3147
3150
  tally_of_ids[iid] += 1
3148
3151
  row[iid_column] = new
3149
- if iid in self.rns:
3150
- index[self.rns[iid]].text = row[text_column] if isinstance(text_column, int) else text_column[rn]
3152
+ if iid in tree:
3153
+ tree[iid].text = row[text_column] if isinstance(text_column, int) else text_column[rn]
3151
3154
  else:
3152
- index.append(
3153
- Node(
3154
- text=row[text_column] if isinstance(text_column, int) else text_column[rn],
3155
- iid=iid,
3156
- parent="",
3157
- )
3155
+ tree[iid] = Node(
3156
+ text=row[text_column] if isinstance(text_column, int) else text_column[rn],
3157
+ iid=iid,
3158
+ parent="",
3158
3159
  )
3159
- self.rns[iid] = len(index) - 1
3160
3160
  if iid in iids_missing_rows:
3161
3161
  iids_missing_rows.discard(iid)
3162
3162
  data_rns[iid] = rn
3163
- if iid == pid or self.build_pid_causes_recursive_loop(iid, pid):
3163
+ if iid == pid or self.build_pid_causes_recursive_loop(iid, pid, tree):
3164
3164
  row[parent_column] = ""
3165
3165
  pid = ""
3166
3166
  if pid:
3167
- if pid in self.rns:
3168
- index[self.rns[pid]].children.append(iid)
3167
+ if pid in tree:
3168
+ tree[pid].children.append(iid)
3169
3169
  else:
3170
- index.append(
3171
- Node(
3172
- text=pid,
3173
- iid=pid,
3174
- children=[iid],
3175
- )
3170
+ tree[pid] = Node(
3171
+ text=pid,
3172
+ iid=pid,
3173
+ children=[iid],
3176
3174
  )
3177
3175
  iids_missing_rows.add(pid)
3178
- self.rns[pid] = len(index) - 1
3179
- index[self.rns[iid]].parent = pid
3176
+ tree[iid].parent = pid
3180
3177
  else:
3181
- index[self.rns[iid]].parent = ""
3178
+ tree[iid].parent = ""
3182
3179
  empty_rows = {}
3183
3180
  for iid in iids_missing_rows:
3184
- node = index[self.rns[iid]]
3185
- node.parent = ""
3181
+ node = tree[iid]
3186
3182
  newrow = self.MT.get_empty_row_seq(len(data), ncols)
3187
3183
  newrow[iid_column] = node.iid
3188
3184
  empty_rows[node.iid] = newrow
@@ -3194,24 +3190,41 @@ class RowIndex(tk.Canvas):
3194
3190
  if isinstance(text_column, int) and not include_text_column:
3195
3191
  exclude.add(text_column)
3196
3192
  rows = []
3193
+ ctr = 0
3197
3194
  if exclude:
3198
- for iid in self.PAR.tree_traverse():
3199
- row = [index[self.rns[iid]]]
3200
- if iid in empty_rows:
3201
- row.extend(e for i, e in enumerate(empty_rows[iid]) if i not in exclude)
3202
- else:
3203
- row.extend(e for i, e in enumerate(data[data_rns[iid]]) if i not in exclude)
3204
- rows.append(row)
3195
+ for iid, node in tree.items():
3196
+ if node.parent == "":
3197
+ row = [tree[iid]]
3198
+ if iid in empty_rows:
3199
+ row.extend(e for i, e in enumerate(empty_rows[iid]) if i not in exclude)
3200
+ else:
3201
+ row.extend(e for i, e in enumerate(data[data_rns[iid]]) if i not in exclude)
3202
+ rows.append(row)
3203
+ self.rns[iid] = ctr
3204
+ ctr += 1
3205
+ for diid in self._build_get_descendants(iid, tree):
3206
+ row = [tree[diid]]
3207
+ row.extend(e for i, e in enumerate(data[data_rns[diid]]) if i not in exclude)
3208
+ rows.append(row)
3209
+ self.rns[diid] = ctr
3210
+ ctr += 1
3205
3211
  else:
3206
- for iid in self.PAR.tree_traverse():
3207
- row = [index[self.rns[iid]]]
3208
- if iid in empty_rows:
3209
- row.extend(empty_rows[iid])
3210
- else:
3211
- row.extend(data[data_rns[iid]])
3212
- rows.append(row)
3213
- self.MT._row_index = []
3214
- self.rns = {}
3212
+ for iid, node in tree.items():
3213
+ if node.parent == "":
3214
+ row = [tree[iid]]
3215
+ if iid in empty_rows:
3216
+ row.extend(empty_rows[iid])
3217
+ else:
3218
+ row.extend(data[data_rns[iid]])
3219
+ rows.append(row)
3220
+ self.rns[iid] = ctr
3221
+ ctr += 1
3222
+ for diid in self._build_get_descendants(iid, tree):
3223
+ row = [tree[diid]]
3224
+ row.extend(data[data_rns[diid]])
3225
+ rows.append(row)
3226
+ self.rns[diid] = ctr
3227
+ ctr += 1
3215
3228
  self.PAR.insert_rows(
3216
3229
  rows=rows,
3217
3230
  idx=0,
@@ -3226,7 +3239,6 @@ class RowIndex(tk.Canvas):
3226
3239
  )
3227
3240
  self.MT.all_rows_displayed = False
3228
3241
  self.MT.displayed_rows = list(range(len(self.MT._row_index)))
3229
- self.rns = {n.iid: i for i, n in enumerate(self.MT._row_index)}
3230
3242
  if open_ids:
3231
3243
  self.PAR.tree_set_open(open_ids=open_ids)
3232
3244
  else:
@@ -3237,3 +3249,29 @@ class RowIndex(tk.Canvas):
3237
3249
  data_indexes=True,
3238
3250
  row_heights=row_heights is not False,
3239
3251
  )
3252
+
3253
+ def _build_get_descendants(self, iid: str, tree: dict[str, Node]) -> Generator[str]:
3254
+ stack = [iter(tree[iid].children)]
3255
+ while stack:
3256
+ top_iterator = stack[-1]
3257
+ try:
3258
+ ciid = next(top_iterator)
3259
+ yield ciid
3260
+ if tree[ciid].children:
3261
+ stack.append(iter(tree[ciid].children))
3262
+ except StopIteration:
3263
+ stack.pop()
3264
+
3265
+ def build_pid_causes_recursive_loop(self, iid: str, pid: str, tree: dict[str, Node]) -> bool:
3266
+ # check descendants
3267
+ for diid in self._build_get_descendants(iid, tree):
3268
+ if diid == pid:
3269
+ return True
3270
+ # check ancestors
3271
+ current_iid = iid
3272
+ while tree[current_iid].parent:
3273
+ parent_iid = tree[current_iid].parent
3274
+ if parent_iid == pid:
3275
+ return True
3276
+ current_iid = parent_iid
3277
+ return False
tksheet/sheet.py CHANGED
@@ -127,7 +127,7 @@ class Sheet(tk.Frame):
127
127
  max_row_height: float = float("inf"),
128
128
  max_header_height: float = float("inf"),
129
129
  max_index_width: float = float("inf"),
130
- after_redraw_time_ms: int = 20,
130
+ after_redraw_time_ms: int = 16,
131
131
  set_all_heights_and_widths: bool = False,
132
132
  zoom: int = 100,
133
133
  align: str = "nw",
@@ -4647,10 +4647,9 @@ class Sheet(tk.Frame):
4647
4647
  if index[rns[item]].children:
4648
4648
  open_ids.add(item)
4649
4649
  if rns[item] in disp_set:
4650
- to_disp = [rns[did] for did in descendants(item, check_open=True)]
4651
- for i in to_disp:
4652
- disp_set.add(i)
4653
- to_open.extend(to_disp)
4650
+ for did in descendants(item, check_open=True):
4651
+ disp_set.add(rns[did])
4652
+ to_open.append(rns[did])
4654
4653
  return to_open
4655
4654
 
4656
4655
  def tree_open(self, *items, redraw: bool = True) -> Sheet:
@@ -4924,7 +4923,7 @@ class Sheet(tk.Frame):
4924
4923
  """
4925
4924
  Also deletes all descendants of items
4926
4925
  """
4927
- rows = list(map(self.RI.rns.get, filter(self.exists, unpack(items))))
4926
+ rows = list(map(self.RI.rns.__getitem__, filter(self.exists, unpack(items))))
4928
4927
  if rows:
4929
4928
  self.del_rows(rows, data_indexes=True, undo=undo)
4930
4929
  return self.set_refresh_timer(rows)
@@ -5138,13 +5137,20 @@ class Sheet(tk.Frame):
5138
5137
  for func in self.bound_events[event]:
5139
5138
  func(data)
5140
5139
 
5141
- def set_refresh_timer(self, redraw: bool = True) -> Sheet:
5140
+ def set_refresh_timer(
5141
+ self,
5142
+ redraw: bool = True,
5143
+ index: bool = True,
5144
+ header: bool = True,
5145
+ ) -> Sheet:
5142
5146
  if redraw and self.after_redraw_id is None:
5143
- self.after_redraw_id = self.after(self.after_redraw_time_ms, self.after_redraw)
5147
+ self.after_redraw_id = self.after(
5148
+ self.after_redraw_time_ms, lambda: self.after_redraw(index=index, header=header)
5149
+ )
5144
5150
  return self
5145
5151
 
5146
- def after_redraw(self) -> None:
5147
- self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
5152
+ def after_redraw(self, index: bool = True, header: bool = True) -> None:
5153
+ self.MT.main_table_redraw_grid_and_text(redraw_header=header, redraw_row_index=index)
5148
5154
  self.after_redraw_id = None
5149
5155
 
5150
5156
  def del_options_using_span(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tksheet
3
- Version: 7.4.12
3
+ Version: 7.4.13
4
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
@@ -63,23 +63,23 @@ License-File: LICENSE.txt
63
63
  <tbody>
64
64
  <tr>
65
65
  <td style="color: LightCoral">Versions 6.x.x &#8594;</td>
66
- <td><a href="https://github.com/ragardner/tksheet/wiki/Version-6">Documentation</a></td>
66
+ <td><a href="https://github.com/ragardner/tksheet/wiki/Version-6" target="_blank">Documentation</a></td>
67
67
  </tr>
68
68
  <tr>
69
69
  <td style="color: lightgreen">Versions 7.x.x &#8594;</td>
70
- <td><a href="https://ragardner.github.io/tksheet/DOCUMENTATION.html">Documentation</a></td>
70
+ <td><a href="https://ragardner.github.io/tksheet/DOCUMENTATION.html" target="_blank">Documentation</a></td>
71
71
  </tr>
72
72
  <tr>
73
- <td align="right" colspan="2"><a href="https://github.com/ragardner/tksheet/blob/master/docs/CHANGELOG.md">Changelog</a></td>
73
+ <td align="right" colspan="2"><a href="https://github.com/ragardner/tksheet/blob/master/docs/CHANGELOG.md" target="_blank">Changelog</a></td>
74
74
  </tr>
75
75
  <tr>
76
- <td align="right" colspan="2"><a href="https://github.com/ragardner/tksheet/wiki/Version-7#asking-questions">Questions</a></td>
76
+ <td align="right" colspan="2"><a href="https://github.com/ragardner/tksheet/wiki/Version-7#asking-questions" target="_blank">Questions</a></td>
77
77
  </tr>
78
78
  <tr>
79
- <td align="right" colspan="2"><a href="https://github.com/ragardner/tksheet/wiki/Version-7#issues">Issues</a></td>
79
+ <td align="right" colspan="2"><a href="https://github.com/ragardner/tksheet/wiki/Version-7#issues" target="_blank">Issues</a></td>
80
80
  </tr>
81
81
  <tr>
82
- <td align="right" colspan="2"><a href="https://github.com/ragardner/tksheet/wiki/Version-7#enhancements-or-suggestions">Suggestions and Contributors</a></td>
82
+ <td align="right" colspan="2"><a href="https://github.com/ragardner/tksheet/wiki/Version-7#enhancements-or-suggestions" target="_blank">Suggestions and Contributors</a></td>
83
83
  </tr>
84
84
  </tbody>
85
85
  </table>
@@ -0,0 +1,22 @@
1
+ tksheet/__init__.py,sha256=CdTPPFo67goThv2unHBQc8yvXaJncIkDk6fZVh6jpEk,2327
2
+ tksheet/colors.py,sha256=dHhmdFuQDlwohDHsAfT9VdrKoSl_R33L72a3HCin5zo,51591
3
+ tksheet/column_headers.py,sha256=Ztr5QvTo3u9ANbZXheECMzRwZ9KvYnQQu-fncOJfNGg,102657
4
+ tksheet/constants.py,sha256=aiDvUSaPtl_TSNtRRJ_p87c9iTBz9ksMd5IsSXzz4Hk,13316
5
+ tksheet/find_window.py,sha256=TvbSqO42cw1o1AO0tQ0Q1iZogouNr4ObMo7JfkZCxgA,19877
6
+ tksheet/formatters.py,sha256=DGcRiMsDJnySNpQcjfiX84oJ7TmOSMdU6u9injIhA4g,10095
7
+ tksheet/functions.py,sha256=qks2dLqBE9GMAuEXaA4qiU-rrL-dzmz4-tkmkp6_ETk,53037
8
+ tksheet/main_table.py,sha256=rm1ZeVRXTv-CwxKirFrssjIz4fvrxNH7pw0JAwUOcH4,364840
9
+ tksheet/other_classes.py,sha256=B2SrUAviztDUOPGoWkcu-AixqAaKwItshoVZPGe1_Tc,16662
10
+ tksheet/row_index.py,sha256=aQOQh6d4x6bGeu4Kn1KMKU5F9hwSFFinAsh5hddC3Ho,141241
11
+ tksheet/sheet.py,sha256=6XOlCb6uhofSwylInVHk2UYufZn4sHXFsQggJ40uAaE,269689
12
+ tksheet/sheet_options.py,sha256=ob-XYgGfBoi2TaFAvUUmlWUkw9xB-yXmJw17gu9__Lw,9948
13
+ tksheet/sorting.py,sha256=zcZPpRtP1h_xJGtGkG3E43H7deKQFnh9cMwZ1B2-aGc,17502
14
+ tksheet/text_editor.py,sha256=rU8Fz0-ltkM63W9io2DoZJPyzUGzCc9Z0qBtc4D1H40,7404
15
+ tksheet/themes.py,sha256=AoNAxibnQi04MN0Zpbn9-kyDnkiiV8TDNWP9FYjpuf0,18473
16
+ tksheet/tksheet_types.py,sha256=1MjXR34EmvP1KfHlLTvKKVnf0VMz_LU_WOM2q4o5hfI,4598
17
+ tksheet/top_left_rectangle.py,sha256=M52IrPIeMoYE3jSpooZmqw_0W5Fz_R-Yu1ZqA685EZ8,8557
18
+ tksheet-7.4.13.dist-info/LICENSE.txt,sha256=ndbcCPe9SlHfweE_W2RAueWUe2k7yudyxYLq6WjFdn4,1101
19
+ tksheet-7.4.13.dist-info/METADATA,sha256=ch2OK72gHX9RudQZpnF4JuYDE3KyR0OQTvl3WYtJpsw,8095
20
+ tksheet-7.4.13.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
21
+ tksheet-7.4.13.dist-info/top_level.txt,sha256=my61PXCcck_HHAc9cq3NAlyAr3A3FXxCy9gptEOaCN8,8
22
+ tksheet-7.4.13.dist-info/RECORD,,
@@ -1,22 +0,0 @@
1
- tksheet/__init__.py,sha256=6B6RTt5-41j4-VcGxzbvcO6Ggfd56Ooy0GwxL_kfLhI,2327
2
- tksheet/colors.py,sha256=dHhmdFuQDlwohDHsAfT9VdrKoSl_R33L72a3HCin5zo,51591
3
- tksheet/column_headers.py,sha256=CIQwGNY0PSWb8q3Yl5lOBCGckZ83BDefFH1e4qKoLfs,102720
4
- tksheet/constants.py,sha256=aiDvUSaPtl_TSNtRRJ_p87c9iTBz9ksMd5IsSXzz4Hk,13316
5
- tksheet/find_window.py,sha256=TvbSqO42cw1o1AO0tQ0Q1iZogouNr4ObMo7JfkZCxgA,19877
6
- tksheet/formatters.py,sha256=DGcRiMsDJnySNpQcjfiX84oJ7TmOSMdU6u9injIhA4g,10095
7
- tksheet/functions.py,sha256=Ffw6yDHj83oubcp4P9VCQhTRWtGkMI2CBob4JFld91E,52567
8
- tksheet/main_table.py,sha256=40MAVfBxQ6FycJcuqm9ZsMOjVYKWhISjht3FFZ-UQOI,363608
9
- tksheet/other_classes.py,sha256=g6LYYHXO9RMy2mN_e-HmGa3Wuzhdxt5voO8-Ug19_Ag,16671
10
- tksheet/row_index.py,sha256=2aiADtsPskfHZeU0JK7fD4kZm-25ooSjO3mguG9vTM4,139671
11
- tksheet/sheet.py,sha256=ZgryLoqfkm2vtJDIVhXAvSUQ8G7TXztiID8Ftt3V2qo,269531
12
- tksheet/sheet_options.py,sha256=ob-XYgGfBoi2TaFAvUUmlWUkw9xB-yXmJw17gu9__Lw,9948
13
- tksheet/sorting.py,sha256=zcZPpRtP1h_xJGtGkG3E43H7deKQFnh9cMwZ1B2-aGc,17502
14
- tksheet/text_editor.py,sha256=rU8Fz0-ltkM63W9io2DoZJPyzUGzCc9Z0qBtc4D1H40,7404
15
- tksheet/themes.py,sha256=AoNAxibnQi04MN0Zpbn9-kyDnkiiV8TDNWP9FYjpuf0,18473
16
- tksheet/tksheet_types.py,sha256=1MjXR34EmvP1KfHlLTvKKVnf0VMz_LU_WOM2q4o5hfI,4598
17
- tksheet/top_left_rectangle.py,sha256=M52IrPIeMoYE3jSpooZmqw_0W5Fz_R-Yu1ZqA685EZ8,8557
18
- tksheet-7.4.12.dist-info/LICENSE.txt,sha256=ndbcCPe9SlHfweE_W2RAueWUe2k7yudyxYLq6WjFdn4,1101
19
- tksheet-7.4.12.dist-info/METADATA,sha256=0WsLK9P5f8h788tKDhCSQjtYad1UD9mDmIOuFz7eNjE,7999
20
- tksheet-7.4.12.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
21
- tksheet-7.4.12.dist-info/top_level.txt,sha256=my61PXCcck_HHAc9cq3NAlyAr3A3FXxCy9gptEOaCN8,8
22
- tksheet-7.4.12.dist-info/RECORD,,