tksheet 7.4.12__py3-none-any.whl → 7.4.14__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/row_index.py CHANGED
@@ -3,10 +3,11 @@ 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
9
- from operator import itemgetter
8
+ from itertools import cycle, islice, repeat
9
+ from math import ceil
10
+ from re import findall
10
11
  from typing import Any, Literal
11
12
 
12
13
  from .colors import color_map
@@ -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)
@@ -1057,25 +1058,48 @@ class RowIndex(tk.Canvas):
1057
1058
  def get_cell_dimensions(self, datarn: int) -> tuple[int, int]:
1058
1059
  txt = self.cell_str(datarn, fix=False)
1059
1060
  if txt:
1060
- self.MT.txt_measure_canvas.itemconfig(self.MT.txt_measure_canvas_text, text=txt, font=self.ops.index_font)
1061
- b = self.MT.txt_measure_canvas.bbox(self.MT.txt_measure_canvas_text)
1062
- w = b[2] - b[0] + 7
1063
- h = b[3] - b[1] + 5
1061
+ lines = findall(r"[^\n]+", txt)
1062
+ h = self.MT.index_txt_height * len(lines) + 5
1063
+ w = max(sum(self.wrap_get_char_w(c) for c in line) for line in lines) + 8
1064
1064
  else:
1065
1065
  w = self.ops.default_row_index_width
1066
1066
  h = self.MT.min_row_height
1067
- if self.get_cell_kwargs(datarn, key="dropdown") or self.get_cell_kwargs(datarn, key="checkbox"):
1067
+ # self.get_cell_kwargs not used here to boost performance
1068
+ if (datarn in self.cell_options and "dropdown" in self.cell_options[datarn]) or (
1069
+ datarn in self.cell_options and "checkbox" in self.cell_options[datarn]
1070
+ ):
1068
1071
  w += self.MT.index_txt_height + 2
1069
1072
  if self.ops.treeview:
1070
1073
  if datarn in self.cell_options and "align" in self.cell_options[datarn]:
1071
1074
  align = self.cell_options[datarn]["align"]
1072
1075
  else:
1073
1076
  align = self.align
1074
- if align.endswith("w"):
1077
+ if align[-1] == "w":
1075
1078
  w += self.MT.index_txt_height
1076
1079
  w += self.get_iid_indent(self.MT._row_index[datarn].iid) + 10
1077
1080
  return w, h
1078
1081
 
1082
+ def get_cell_width(self, datarn: int) -> int:
1083
+ txt = self.cell_str(datarn, fix=False)
1084
+ if txt:
1085
+ w = max(sum(self.wrap_get_char_w(c) for c in line) for line in findall(r"[^\n]+", txt)) + 8
1086
+ else:
1087
+ w = self.ops.default_row_index_width
1088
+ # self.get_cell_kwargs not used here to boost performance
1089
+ if (datarn in self.cell_options and "dropdown" in self.cell_options[datarn]) or (
1090
+ datarn in self.cell_options and "checkbox" in self.cell_options[datarn]
1091
+ ):
1092
+ w += self.MT.index_txt_height + 2
1093
+ if self.ops.treeview:
1094
+ if datarn in self.cell_options and "align" in self.cell_options[datarn]:
1095
+ align = self.cell_options[datarn]["align"]
1096
+ else:
1097
+ align = self.align
1098
+ if align[-1] == "w":
1099
+ w += self.MT.index_txt_height
1100
+ w += self.get_iid_indent(self.MT._row_index[datarn].iid) + 10
1101
+ return w
1102
+
1079
1103
  def get_wrapped_cell_height(self, datarn: int) -> int:
1080
1104
  n_lines = max(
1081
1105
  1,
@@ -1160,10 +1184,7 @@ class RowIndex(tk.Canvas):
1160
1184
  self.MT.recreate_all_selection_boxes()
1161
1185
  return height
1162
1186
 
1163
- def get_index_text_width(
1164
- self,
1165
- only_rows: Iterator[int] | None = None,
1166
- ) -> int:
1187
+ def get_index_text_width(self, only_rows: Iterator[int] | None = None) -> int:
1167
1188
  self.fix_index()
1168
1189
  w = self.ops.default_row_index_width
1169
1190
  if (not self.MT._row_index and isinstance(self.MT._row_index, list)) or (
@@ -1179,7 +1200,7 @@ class RowIndex(tk.Canvas):
1179
1200
  iterable = range(len(self.MT.data))
1180
1201
  else:
1181
1202
  iterable = self.MT.displayed_rows
1182
- if (new_w := max(map(itemgetter(0), map(self.get_cell_dimensions, iterable)), default=w)) > w:
1203
+ if (new_w := max(map(self.get_cell_width, iterable), default=w)) > w:
1183
1204
  w = new_w
1184
1205
  if w > self.ops.max_index_width:
1185
1206
  w = int(self.ops.max_index_width)
@@ -1234,18 +1255,18 @@ class RowIndex(tk.Canvas):
1234
1255
  def auto_set_index_width(self, end_row: int, only_rows: list) -> bool:
1235
1256
  if not isinstance(self.MT._row_index, int) and not self.MT._row_index:
1236
1257
  if self.ops.default_row_index == "letters":
1237
- new_w = self.MT.get_txt_w(f"{num2alpha(end_row)}", self.index_font) + 20
1258
+ new_w = sum(self.wrap_get_char_w(c) for c in num2alpha(end_row)) + 20
1238
1259
  elif self.ops.default_row_index == "numbers":
1239
- new_w = self.MT.get_txt_w(f"{end_row}", self.index_font) + 20
1260
+ new_w = sum(self.wrap_get_char_w(c) for c in str(end_row)) + 20
1240
1261
  elif self.ops.default_row_index == "both":
1241
- new_w = self.MT.get_txt_w(f"{end_row + 1} {num2alpha(end_row)}", self.index_font) + 20
1262
+ new_w = sum(self.wrap_get_char_w(c) for c in f"{end_row + 1} {num2alpha(end_row)}") + 20
1242
1263
  elif self.ops.default_row_index is None:
1243
1264
  new_w = 20
1244
1265
  elif self.ops.auto_resize_row_index is True:
1245
1266
  new_w = self.get_index_text_width(only_rows=only_rows)
1246
1267
  else:
1247
1268
  new_w = None
1248
- if new_w is not None and (sheet_w_x := floor(self.PAR.winfo_width() * 0.7)) < new_w:
1269
+ if new_w is not None and (sheet_w_x := int(self.PAR.winfo_width() * 0.7)) < new_w:
1249
1270
  new_w = sheet_w_x
1250
1271
  if new_w and (self.current_width - new_w > 20 or new_w - self.current_width > 3):
1251
1272
  if self.MT.find_window.open:
@@ -1305,7 +1326,6 @@ class RowIndex(tk.Canvas):
1305
1326
  sr,
1306
1327
  fill=fill,
1307
1328
  outline=self.ops.index_fg if has_dd and self.ops.show_dropdown_borders else "",
1308
- tag="s",
1309
1329
  )
1310
1330
  tree_arrow_fg = txtfg
1311
1331
  elif not kwargs:
@@ -1319,7 +1339,6 @@ class RowIndex(tk.Canvas):
1319
1339
  sr,
1320
1340
  fill=self.ops.index_selected_rows_bg,
1321
1341
  outline=self.ops.index_fg if has_dd and self.ops.show_dropdown_borders else "",
1322
- tag="s",
1323
1342
  )
1324
1343
  elif "cells" in selections and r in selections["cells"]:
1325
1344
  txtfg = self.ops.index_selected_cells_fg
@@ -1331,7 +1350,6 @@ class RowIndex(tk.Canvas):
1331
1350
  sr,
1332
1351
  fill=self.ops.index_selected_cells_bg,
1333
1352
  outline=self.ops.index_fg if has_dd and self.ops.show_dropdown_borders else "",
1334
- tag="s",
1335
1353
  )
1336
1354
  else:
1337
1355
  txtfg = self.ops.index_fg
@@ -1346,7 +1364,6 @@ class RowIndex(tk.Canvas):
1346
1364
  y2: float,
1347
1365
  fill: str,
1348
1366
  outline: str,
1349
- tag: str | tuple[str],
1350
1367
  ) -> bool:
1351
1368
  coords = (x1, y1, x2, y2)
1352
1369
  if self.hidd_high:
@@ -1355,10 +1372,9 @@ class RowIndex(tk.Canvas):
1355
1372
  if showing:
1356
1373
  self.itemconfig(iid, fill=fill, outline=outline)
1357
1374
  else:
1358
- self.itemconfig(iid, fill=fill, outline=outline, tag=tag, state="normal")
1359
- self.tag_raise(iid)
1375
+ self.itemconfig(iid, fill=fill, outline=outline, state="normal")
1360
1376
  else:
1361
- iid = self.create_rectangle(coords, fill=fill, outline=outline, tag=tag)
1377
+ iid = self.create_rectangle(coords, fill=fill, outline=outline)
1362
1378
  self.disp_high[iid] = True
1363
1379
  return True
1364
1380
 
@@ -1367,18 +1383,17 @@ class RowIndex(tk.Canvas):
1367
1383
  points: tuple[float],
1368
1384
  fill: str,
1369
1385
  width: int,
1370
- tag: str | tuple[str],
1371
1386
  ) -> None:
1372
1387
  if self.hidd_grid:
1373
1388
  t, sh = self.hidd_grid.popitem()
1374
1389
  self.coords(t, points)
1375
1390
  if sh:
1376
- self.itemconfig(t, fill=fill, width=width, tag=tag)
1391
+ self.itemconfig(t, fill=fill, width=width)
1377
1392
  else:
1378
- self.itemconfig(t, fill=fill, width=width, tag=tag, state="normal")
1393
+ self.itemconfig(t, fill=fill, width=width, state="normal")
1379
1394
  self.disp_grid[t] = True
1380
1395
  else:
1381
- self.disp_grid[self.create_line(points, fill=fill, width=width, tag=tag)] = True
1396
+ self.disp_grid[self.create_line(points, fill=fill, width=width)] = True
1382
1397
 
1383
1398
  def redraw_tree_arrow(
1384
1399
  self,
@@ -1386,7 +1401,6 @@ class RowIndex(tk.Canvas):
1386
1401
  y1: float,
1387
1402
  y2: float,
1388
1403
  fill: str,
1389
- tag: str | tuple[str],
1390
1404
  indent: float,
1391
1405
  has_children: bool = False,
1392
1406
  open_: bool = False,
@@ -1394,7 +1408,7 @@ class RowIndex(tk.Canvas):
1394
1408
  ) -> None:
1395
1409
  mod = (self.MT.index_txt_height - 1) if self.MT.index_txt_height % 2 else self.MT.index_txt_height
1396
1410
  small_mod = int(mod / 5)
1397
- mid_y = floor(self.MT.min_row_height / 2)
1411
+ mid_y = int(self.MT.min_row_height / 2)
1398
1412
  if has_children:
1399
1413
  # up arrow
1400
1414
  if open_:
@@ -1459,16 +1473,15 @@ class RowIndex(tk.Canvas):
1459
1473
  if sh:
1460
1474
  self.itemconfig(t, fill=fill if has_children else self.ops.index_grid_fg)
1461
1475
  else:
1462
- self.itemconfig(t, fill=fill if has_children else self.ops.index_grid_fg, tag=tag, state="normal")
1463
- self.lift(t)
1476
+ self.itemconfig(t, fill=fill if has_children else self.ops.index_grid_fg, state="normal")
1464
1477
  else:
1465
1478
  t = self.create_line(
1466
1479
  points,
1467
1480
  fill=fill if has_children else self.ops.index_grid_fg,
1468
- tag=tag,
1469
1481
  width=2,
1470
1482
  capstyle=tk.ROUND,
1471
1483
  joinstyle=tk.BEVEL,
1484
+ tag="lift",
1472
1485
  )
1473
1486
  self.disp_tree_arrow[t] = True
1474
1487
 
@@ -1480,17 +1493,16 @@ class RowIndex(tk.Canvas):
1480
1493
  y2: float,
1481
1494
  fill: str,
1482
1495
  outline: str,
1483
- tag: str | tuple[str],
1484
1496
  draw_outline: bool = True,
1485
1497
  draw_arrow: bool = True,
1486
1498
  open_: bool = False,
1487
1499
  ) -> None:
1488
1500
  if draw_outline and self.ops.show_dropdown_borders:
1489
- self.redraw_highlight(x1 + 1, y1 + 1, x2, y2, fill="", outline=self.ops.index_fg, tag=tag)
1501
+ self.redraw_highlight(x1 + 1, y1 + 1, x2, y2, fill="", outline=self.ops.index_fg)
1490
1502
  if draw_arrow:
1491
1503
  mod = (self.MT.index_txt_height - 1) if self.MT.index_txt_height % 2 else self.MT.index_txt_height
1492
1504
  small_mod = int(mod / 5)
1493
- mid_y = floor(self.MT.min_row_height / 2)
1505
+ mid_y = int(self.MT.min_row_height / 2)
1494
1506
  if open_:
1495
1507
  # up arrow
1496
1508
  points = (
@@ -1517,16 +1529,15 @@ class RowIndex(tk.Canvas):
1517
1529
  if sh:
1518
1530
  self.itemconfig(t, fill=fill)
1519
1531
  else:
1520
- self.itemconfig(t, fill=fill, tag=tag, state="normal")
1521
- self.lift(t)
1532
+ self.itemconfig(t, fill=fill, state="normal")
1522
1533
  else:
1523
1534
  t = self.create_line(
1524
1535
  points,
1525
1536
  fill=fill,
1526
- tag=tag,
1527
1537
  width=2,
1528
1538
  capstyle=tk.ROUND,
1529
1539
  joinstyle=tk.BEVEL,
1540
+ tag="lift",
1530
1541
  )
1531
1542
  self.disp_dropdown[t] = True
1532
1543
 
@@ -1538,7 +1549,6 @@ class RowIndex(tk.Canvas):
1538
1549
  y2: float,
1539
1550
  fill: str,
1540
1551
  outline: str,
1541
- tag: str | tuple[str],
1542
1552
  draw_check: bool = False,
1543
1553
  ) -> None:
1544
1554
  points = rounded_box_coords(x1, y1, x2, y2)
@@ -1548,10 +1558,9 @@ class RowIndex(tk.Canvas):
1548
1558
  if sh:
1549
1559
  self.itemconfig(t, fill=outline, outline=fill)
1550
1560
  else:
1551
- self.itemconfig(t, fill=outline, outline=fill, tag=tag, state="normal")
1552
- self.lift(t)
1561
+ self.itemconfig(t, fill=outline, outline=fill, state="normal")
1553
1562
  else:
1554
- t = self.create_polygon(points, fill=outline, outline=fill, tag=tag, smooth=True)
1563
+ t = self.create_polygon(points, fill=outline, outline=fill, smooth=True, tag="lift")
1555
1564
  self.disp_checkbox[t] = True
1556
1565
  if draw_check:
1557
1566
  # draw filled box
@@ -1566,10 +1575,9 @@ class RowIndex(tk.Canvas):
1566
1575
  if sh:
1567
1576
  self.itemconfig(t, fill=fill, outline=outline)
1568
1577
  else:
1569
- self.itemconfig(t, fill=fill, outline=outline, tag=tag, state="normal")
1570
- self.lift(t)
1578
+ self.itemconfig(t, fill=fill, outline=outline, state="normal")
1571
1579
  else:
1572
- t = self.create_polygon(points, fill=fill, outline=outline, tag=tag, smooth=True)
1580
+ t = self.create_polygon(points, fill=fill, outline=outline, smooth=True, tag="lift")
1573
1581
  self.disp_checkbox[t] = True
1574
1582
 
1575
1583
  def configure_scrollregion(self, last_row_line_pos: float) -> bool:
@@ -1587,15 +1595,15 @@ class RowIndex(tk.Canvas):
1587
1595
  return False
1588
1596
 
1589
1597
  def wrap_get_char_w(self, c: str) -> int:
1590
- self.MT.txt_measure_canvas.itemconfig(
1591
- self.MT.txt_measure_canvas_text,
1592
- text=_test_str + c,
1593
- font=self.index_font,
1594
- )
1595
- b = self.MT.txt_measure_canvas.bbox(self.MT.txt_measure_canvas_text)
1596
1598
  if c in self.MT.char_widths[self.index_font]:
1597
1599
  return self.MT.char_widths[self.index_font][c]
1598
1600
  else:
1601
+ self.MT.txt_measure_canvas.itemconfig(
1602
+ self.MT.txt_measure_canvas_text,
1603
+ text=_test_str + c,
1604
+ font=self.index_font,
1605
+ )
1606
+ b = self.MT.txt_measure_canvas.bbox(self.MT.txt_measure_canvas_text)
1599
1607
  wd = b[2] - b[0] - self.index_test_str_w
1600
1608
  self.MT.char_widths[self.index_font][c] = wd
1601
1609
  return wd
@@ -1634,6 +1642,35 @@ class RowIndex(tk.Canvas):
1634
1642
  self.current_width,
1635
1643
  scrollpos_bot,
1636
1644
  )
1645
+
1646
+ if (self.ops.show_horizontal_grid or self.height_resizing_enabled) and row_pos_exists:
1647
+ xend = self.current_width - 6
1648
+ points = [
1649
+ self.current_width - 1,
1650
+ y_stop - 1,
1651
+ self.current_width - 1,
1652
+ scrollpos_top - 1,
1653
+ -1,
1654
+ scrollpos_top - 1,
1655
+ ]
1656
+ for r in range(grid_start_row, grid_end_row):
1657
+ draw_y = self.MT.row_positions[r]
1658
+ if r and self.height_resizing_enabled:
1659
+ self.visible_row_dividers[r] = (1, draw_y - 2, xend, draw_y + 2)
1660
+ points.extend(
1661
+ (
1662
+ -1,
1663
+ draw_y,
1664
+ self.current_width,
1665
+ draw_y,
1666
+ -1,
1667
+ draw_y,
1668
+ -1,
1669
+ self.MT.row_positions[r + 1] if len(self.MT.row_positions) - 1 > r else draw_y,
1670
+ )
1671
+ )
1672
+ self.redraw_gridline(points=points, fill=self.ops.index_grid_fg, width=1)
1673
+
1637
1674
  sel_cells_bg = (
1638
1675
  self.ops.index_selected_cells_bg
1639
1676
  if self.ops.index_selected_cells_bg.startswith("#")
@@ -1673,13 +1710,13 @@ class RowIndex(tk.Canvas):
1673
1710
  else:
1674
1711
  align = self.align
1675
1712
  if dropdown_kwargs:
1676
- max_width = self.current_width - self.MT.index_txt_height - 2
1677
- if align.endswith("w"):
1713
+ max_width = self.current_width - self.MT.index_txt_height - 5
1714
+ if align[-1] == "w":
1678
1715
  draw_x = 3
1679
- elif align.endswith("e"):
1716
+ elif align[-1] == "e":
1680
1717
  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)
1718
+ elif align[-1] == "n":
1719
+ draw_x = (self.current_width - self.MT.index_txt_height) / 2
1683
1720
  self.redraw_dropdown(
1684
1721
  0,
1685
1722
  rtopgridln,
@@ -1687,29 +1724,28 @@ class RowIndex(tk.Canvas):
1687
1724
  rbotgridln - 1,
1688
1725
  fill=fill if dropdown_kwargs["state"] != "disabled" else self.ops.index_grid_fg,
1689
1726
  outline=fill,
1690
- tag="dd",
1691
1727
  draw_outline=not dd_drawn,
1692
1728
  draw_arrow=True,
1693
1729
  open_=dd_coords == r,
1694
1730
  )
1695
1731
  else:
1696
1732
  max_width = self.current_width - 2
1697
- if align.endswith("w"):
1733
+ if align[-1] == "w":
1698
1734
  draw_x = 3
1699
- elif align.endswith("e"):
1735
+ elif align[-1] == "e":
1700
1736
  draw_x = self.current_width - 3
1701
- elif align.endswith("n"):
1702
- draw_x = floor(self.current_width / 2)
1737
+ elif align[-1] == "n":
1738
+ draw_x = self.current_width / 2
1703
1739
  if (
1704
1740
  (checkbox_kwargs := self.get_cell_kwargs(datarn, key="checkbox"))
1705
1741
  and not dropdown_kwargs
1706
1742
  and max_width > self.MT.index_txt_height + 1
1707
1743
  ):
1708
1744
  box_w = self.MT.index_txt_height + 1
1709
- if align.endswith("w"):
1745
+ if align[-1] == "w":
1710
1746
  draw_x += box_w + 3
1711
- elif align.endswith("n"):
1712
- draw_x += ceil(box_w / 2) + 1
1747
+ elif align[-1] == "n":
1748
+ draw_x += box_w / 2 + 1
1713
1749
  max_width -= box_w + 4
1714
1750
  try:
1715
1751
  draw_check = (
@@ -1726,13 +1762,12 @@ class RowIndex(tk.Canvas):
1726
1762
  rtopgridln + self.MT.index_txt_height + 3,
1727
1763
  fill=fill if checkbox_kwargs["state"] == "normal" else self.ops.index_grid_fg,
1728
1764
  outline="",
1729
- tag="cb",
1730
1765
  draw_check=draw_check,
1731
1766
  )
1732
1767
  if treeview and isinstance(self.MT._row_index, list) and len(self.MT._row_index) > datarn:
1733
1768
  iid = self.MT._row_index[datarn].iid
1734
1769
  max_width -= self.MT.index_txt_height
1735
- if align.endswith("w"):
1770
+ if align[-1] == "w":
1736
1771
  draw_x += self.MT.index_txt_height + 3
1737
1772
  level, indent = self.get_iid_level_indent(iid)
1738
1773
  draw_x += indent + 5
@@ -1741,7 +1776,6 @@ class RowIndex(tk.Canvas):
1741
1776
  rtopgridln,
1742
1777
  rbotgridln - 1,
1743
1778
  fill=tree_arrow_fg,
1744
- tag="ta",
1745
1779
  indent=indent,
1746
1780
  has_children=bool(self.MT._row_index[datarn].children),
1747
1781
  open_=self.MT._row_index[datarn].iid in self.tree_open_ids,
@@ -1763,7 +1797,7 @@ class RowIndex(tk.Canvas):
1763
1797
  wrap=wrap,
1764
1798
  start_line=start_line,
1765
1799
  )
1766
- if align.endswith(("w", "e")):
1800
+ if align[-1] == "w" or align[-1] == "e":
1767
1801
  if self.hidd_text:
1768
1802
  iid, showing = self.hidd_text.popitem()
1769
1803
  self.coords(iid, draw_x, draw_y)
@@ -1792,18 +1826,18 @@ class RowIndex(tk.Canvas):
1792
1826
  fill=fill,
1793
1827
  font=font,
1794
1828
  anchor=align,
1795
- tags="t",
1829
+ tag="lift",
1796
1830
  )
1797
1831
  self.disp_text[iid] = True
1798
1832
  else:
1799
- for text in gen_lines:
1833
+ for line in gen_lines:
1800
1834
  if self.hidd_text:
1801
1835
  iid, showing = self.hidd_text.popitem()
1802
1836
  self.coords(iid, draw_x, draw_y)
1803
1837
  if showing:
1804
1838
  self.itemconfig(
1805
1839
  iid,
1806
- text=text,
1840
+ text=line,
1807
1841
  fill=fill,
1808
1842
  font=font,
1809
1843
  anchor=align,
@@ -1811,7 +1845,7 @@ class RowIndex(tk.Canvas):
1811
1845
  else:
1812
1846
  self.itemconfig(
1813
1847
  iid,
1814
- text=text,
1848
+ text=line,
1815
1849
  fill=fill,
1816
1850
  font=font,
1817
1851
  anchor=align,
@@ -1821,42 +1855,15 @@ class RowIndex(tk.Canvas):
1821
1855
  iid = self.create_text(
1822
1856
  draw_x,
1823
1857
  draw_y,
1824
- text=text,
1858
+ text=line,
1825
1859
  fill=fill,
1826
1860
  font=font,
1827
1861
  anchor=align,
1828
- tags="t",
1862
+ tag="lift",
1829
1863
  )
1830
1864
  self.disp_text[iid] = True
1831
1865
  draw_y += self.MT.header_txt_height
1832
1866
 
1833
- xend = self.current_width - 6
1834
- if (self.ops.show_horizontal_grid or self.height_resizing_enabled) and row_pos_exists:
1835
- points = [
1836
- self.current_width - 1,
1837
- y_stop - 1,
1838
- self.current_width - 1,
1839
- scrollpos_top - 1,
1840
- -1,
1841
- scrollpos_top - 1,
1842
- ]
1843
- for r in range(grid_start_row, grid_end_row):
1844
- draw_y = self.MT.row_positions[r]
1845
- if r and self.height_resizing_enabled:
1846
- self.visible_row_dividers[r] = (1, draw_y - 2, xend, draw_y + 2)
1847
- points.extend(
1848
- (
1849
- -1,
1850
- draw_y,
1851
- self.current_width,
1852
- draw_y,
1853
- -1,
1854
- draw_y,
1855
- -1,
1856
- self.MT.row_positions[r + 1] if len(self.MT.row_positions) - 1 > r else draw_y,
1857
- )
1858
- )
1859
- self.redraw_gridline(points=points, fill=self.ops.index_grid_fg, width=1, tag="h")
1860
1867
  for dct in (
1861
1868
  self.hidd_text,
1862
1869
  self.hidd_high,
@@ -1869,7 +1876,7 @@ class RowIndex(tk.Canvas):
1869
1876
  if showing:
1870
1877
  self.itemconfig(iid, state="hidden")
1871
1878
  dct[iid] = False
1872
- self.tag_raise("t")
1879
+ self.tag_raise("lift")
1873
1880
  if self.disp_resize_lines:
1874
1881
  self.tag_raise("rh")
1875
1882
  return True
@@ -2729,16 +2736,17 @@ class RowIndex(tk.Canvas):
2729
2736
  )
2730
2737
  # deal with displayed mapping
2731
2738
  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
2739
+ with suppress(Exception):
2740
+ if new_loc_is_displayed:
2741
+ if disp_insert_row is None:
2742
+ if new_parent or insert_row > move_to_row:
2743
+ disp_insert_row = self.MT.disprn(self.rns[move_to_iid]) + 1
2744
+ else:
2745
+ disp_insert_row = self.MT.disprn(self.rns[move_to_iid])
2746
+ if (disp_from_row := self.MT.try_disprn(self.rns[item])) is not None:
2747
+ event_data["moved"]["rows"]["displayed"] = {disp_from_row: disp_insert_row}
2736
2748
  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}
2749
+ event_data["moved"]["rows"]["displayed"] = {(): disp_insert_row}
2742
2750
 
2743
2751
  if any(self.move_pid_causes_recursive_loop(self.MT._row_index[r].iid, new_parent) for r in moved_rows):
2744
2752
  event_data["moved"]["rows"] = {}
@@ -2759,7 +2767,7 @@ class RowIndex(tk.Canvas):
2759
2767
  event_data["moved"]["rows"]["data"],
2760
2768
  )
2761
2769
  data_new_idxs = event_data["moved"]["rows"]["data"]
2762
- data_old_idxs = dict(zip(data_new_idxs.values(), data_new_idxs))
2770
+ data_old_idxs = {v: k for k, v in data_new_idxs.items()}
2763
2771
 
2764
2772
  if () in event_data["moved"]["rows"]["displayed"]:
2765
2773
  del event_data["moved"]["rows"]["displayed"][()]
@@ -2988,15 +2996,6 @@ class RowIndex(tk.Canvas):
2988
2996
  if not parent_node.children:
2989
2997
  self.tree_open_ids.discard(parent_node.iid)
2990
2998
 
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
2999
  def move_pid_causes_recursive_loop(self, to_move_iid: str, move_to_parent: str) -> bool:
3001
3000
  # if the parent the item is being moved under is one of the item's descendants
3002
3001
  # then it is a recursive loop
@@ -3017,8 +3016,8 @@ class RowIndex(tk.Canvas):
3017
3016
  include_parent_column: bool = True,
3018
3017
  include_text_column: bool = True,
3019
3018
  ) -> None:
3020
- index = self.MT._row_index
3021
3019
  data_rns = {}
3020
+ tree = {}
3022
3021
  if text_column is None:
3023
3022
  text_column = iid_column
3024
3023
  if not isinstance(ncols, int):
@@ -3030,33 +3029,27 @@ class RowIndex(tk.Canvas):
3030
3029
  else:
3031
3030
  iid = row[iid_column]
3032
3031
  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]
3032
+ if iid in tree:
3033
+ tree[iid].text = row[text_column] if isinstance(text_column, int) else text_column[rn]
3035
3034
  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
- )
3035
+ tree[iid] = Node(
3036
+ text=row[text_column] if isinstance(text_column, int) else text_column[rn],
3037
+ iid=iid,
3038
+ parent="",
3042
3039
  )
3043
- self.rns[iid] = len(index) - 1
3044
3040
  data_rns[iid] = rn
3045
3041
  if pid:
3046
- if pid in self.rns:
3047
- index[self.rns[pid]].children.append(iid)
3042
+ if pid in tree:
3043
+ tree[pid].children.append(iid)
3048
3044
  else:
3049
- index.append(
3050
- Node(
3051
- text=pid,
3052
- iid=pid,
3053
- children=[iid],
3054
- )
3045
+ tree[pid] = Node(
3046
+ text=pid,
3047
+ iid=pid,
3048
+ children=[iid],
3055
3049
  )
3056
- self.rns[pid] = len(index) - 1
3057
- index[self.rns[iid]].parent = pid
3050
+ tree[iid].parent = pid
3058
3051
  else:
3059
- index[self.rns[iid]].parent = ""
3052
+ tree[iid].parent = ""
3060
3053
  exclude = set()
3061
3054
  if not include_iid_column:
3062
3055
  exclude.add(iid_column)
@@ -3065,18 +3058,35 @@ class RowIndex(tk.Canvas):
3065
3058
  if isinstance(text_column, int) and not include_text_column:
3066
3059
  exclude.add(text_column)
3067
3060
  rows = []
3061
+ ctr = 0
3068
3062
  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)
3063
+ for iid, node in tree.items():
3064
+ if node.parent == "":
3065
+ row = [tree[iid]]
3066
+ row.extend(e for i, e in enumerate(data[data_rns[iid]]) if i not in exclude)
3067
+ rows.append(row)
3068
+ self.rns[iid] = ctr
3069
+ ctr += 1
3070
+ for diid in self._build_get_descendants(iid, tree):
3071
+ row = [tree[diid]]
3072
+ row.extend(e for i, e in enumerate(data[data_rns[diid]]) if i not in exclude)
3073
+ rows.append(row)
3074
+ self.rns[diid] = ctr
3075
+ ctr += 1
3073
3076
  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 = {}
3077
+ for iid, node in tree.items():
3078
+ if node.parent == "":
3079
+ row = [tree[iid]]
3080
+ row.extend(data[data_rns[iid]])
3081
+ rows.append(row)
3082
+ self.rns[iid] = ctr
3083
+ ctr += 1
3084
+ for diid in self._build_get_descendants(iid, tree):
3085
+ row = [tree[diid]]
3086
+ row.extend(data[data_rns[diid]])
3087
+ rows.append(row)
3088
+ self.rns[diid] = ctr
3089
+ ctr += 1
3080
3090
  self.PAR.insert_rows(
3081
3091
  rows=rows,
3082
3092
  idx=0,
@@ -3091,7 +3101,6 @@ class RowIndex(tk.Canvas):
3091
3101
  )
3092
3102
  self.MT.all_rows_displayed = False
3093
3103
  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
3104
  if open_ids:
3096
3105
  self.PAR.tree_set_open(open_ids=open_ids)
3097
3106
  else:
@@ -3118,8 +3127,8 @@ class RowIndex(tk.Canvas):
3118
3127
  include_parent_column: bool = True,
3119
3128
  include_text_column: bool = True,
3120
3129
  ) -> None:
3121
- index = self.MT._row_index
3122
3130
  data_rns = {}
3131
+ tree = {}
3123
3132
  iids_missing_rows = set()
3124
3133
  if text_column is None:
3125
3134
  text_column = iid_column
@@ -3146,43 +3155,36 @@ class RowIndex(tk.Canvas):
3146
3155
  x += 1
3147
3156
  tally_of_ids[iid] += 1
3148
3157
  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]
3158
+ if iid in tree:
3159
+ tree[iid].text = row[text_column] if isinstance(text_column, int) else text_column[rn]
3151
3160
  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
- )
3161
+ tree[iid] = Node(
3162
+ text=row[text_column] if isinstance(text_column, int) else text_column[rn],
3163
+ iid=iid,
3164
+ parent="",
3158
3165
  )
3159
- self.rns[iid] = len(index) - 1
3160
3166
  if iid in iids_missing_rows:
3161
3167
  iids_missing_rows.discard(iid)
3162
3168
  data_rns[iid] = rn
3163
- if iid == pid or self.build_pid_causes_recursive_loop(iid, pid):
3169
+ if iid == pid or self.build_pid_causes_recursive_loop(iid, pid, tree):
3164
3170
  row[parent_column] = ""
3165
3171
  pid = ""
3166
3172
  if pid:
3167
- if pid in self.rns:
3168
- index[self.rns[pid]].children.append(iid)
3173
+ if pid in tree:
3174
+ tree[pid].children.append(iid)
3169
3175
  else:
3170
- index.append(
3171
- Node(
3172
- text=pid,
3173
- iid=pid,
3174
- children=[iid],
3175
- )
3176
+ tree[pid] = Node(
3177
+ text=pid,
3178
+ iid=pid,
3179
+ children=[iid],
3176
3180
  )
3177
3181
  iids_missing_rows.add(pid)
3178
- self.rns[pid] = len(index) - 1
3179
- index[self.rns[iid]].parent = pid
3182
+ tree[iid].parent = pid
3180
3183
  else:
3181
- index[self.rns[iid]].parent = ""
3184
+ tree[iid].parent = ""
3182
3185
  empty_rows = {}
3183
3186
  for iid in iids_missing_rows:
3184
- node = index[self.rns[iid]]
3185
- node.parent = ""
3187
+ node = tree[iid]
3186
3188
  newrow = self.MT.get_empty_row_seq(len(data), ncols)
3187
3189
  newrow[iid_column] = node.iid
3188
3190
  empty_rows[node.iid] = newrow
@@ -3194,24 +3196,41 @@ class RowIndex(tk.Canvas):
3194
3196
  if isinstance(text_column, int) and not include_text_column:
3195
3197
  exclude.add(text_column)
3196
3198
  rows = []
3199
+ ctr = 0
3197
3200
  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)
3201
+ for iid, node in tree.items():
3202
+ if node.parent == "":
3203
+ row = [tree[iid]]
3204
+ if iid in empty_rows:
3205
+ row.extend(e for i, e in enumerate(empty_rows[iid]) if i not in exclude)
3206
+ else:
3207
+ row.extend(e for i, e in enumerate(data[data_rns[iid]]) if i not in exclude)
3208
+ rows.append(row)
3209
+ self.rns[iid] = ctr
3210
+ ctr += 1
3211
+ for diid in self._build_get_descendants(iid, tree):
3212
+ row = [tree[diid]]
3213
+ row.extend(e for i, e in enumerate(data[data_rns[diid]]) if i not in exclude)
3214
+ rows.append(row)
3215
+ self.rns[diid] = ctr
3216
+ ctr += 1
3205
3217
  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 = {}
3218
+ for iid, node in tree.items():
3219
+ if node.parent == "":
3220
+ row = [tree[iid]]
3221
+ if iid in empty_rows:
3222
+ row.extend(empty_rows[iid])
3223
+ else:
3224
+ row.extend(data[data_rns[iid]])
3225
+ rows.append(row)
3226
+ self.rns[iid] = ctr
3227
+ ctr += 1
3228
+ for diid in self._build_get_descendants(iid, tree):
3229
+ row = [tree[diid]]
3230
+ row.extend(data[data_rns[diid]])
3231
+ rows.append(row)
3232
+ self.rns[diid] = ctr
3233
+ ctr += 1
3215
3234
  self.PAR.insert_rows(
3216
3235
  rows=rows,
3217
3236
  idx=0,
@@ -3226,7 +3245,6 @@ class RowIndex(tk.Canvas):
3226
3245
  )
3227
3246
  self.MT.all_rows_displayed = False
3228
3247
  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
3248
  if open_ids:
3231
3249
  self.PAR.tree_set_open(open_ids=open_ids)
3232
3250
  else:
@@ -3237,3 +3255,29 @@ class RowIndex(tk.Canvas):
3237
3255
  data_indexes=True,
3238
3256
  row_heights=row_heights is not False,
3239
3257
  )
3258
+
3259
+ def _build_get_descendants(self, iid: str, tree: dict[str, Node]) -> Generator[str]:
3260
+ stack = [iter(tree[iid].children)]
3261
+ while stack:
3262
+ top_iterator = stack[-1]
3263
+ try:
3264
+ ciid = next(top_iterator)
3265
+ yield ciid
3266
+ if tree[ciid].children:
3267
+ stack.append(iter(tree[ciid].children))
3268
+ except StopIteration:
3269
+ stack.pop()
3270
+
3271
+ def build_pid_causes_recursive_loop(self, iid: str, pid: str, tree: dict[str, Node]) -> bool:
3272
+ # check descendants
3273
+ for diid in self._build_get_descendants(iid, tree):
3274
+ if diid == pid:
3275
+ return True
3276
+ # check ancestors
3277
+ current_iid = iid
3278
+ while tree[current_iid].parent:
3279
+ parent_iid = tree[current_iid].parent
3280
+ if parent_iid == pid:
3281
+ return True
3282
+ current_iid = parent_iid
3283
+ return False