tksheet 7.2.17__py3-none-any.whl → 7.2.19__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tksheet/sheet.py CHANGED
@@ -19,6 +19,7 @@ from itertools import (
19
19
  product,
20
20
  repeat,
21
21
  )
22
+ from operator import attrgetter
22
23
  from timeit import default_timer
23
24
  from tkinter import ttk
24
25
  from typing import Literal
@@ -27,6 +28,7 @@ from .column_headers import ColumnHeaders
27
28
  from .functions import (
28
29
  add_highlight,
29
30
  add_to_options,
31
+ alpha2idx,
30
32
  consecutive_ranges,
31
33
  convert_align,
32
34
  data_to_displayed_idxs,
@@ -110,8 +112,8 @@ class Sheet(tk.Frame):
110
112
  header: None | list[object] = None,
111
113
  row_index: None | list[object] = None,
112
114
  index: None | list[object] = None,
113
- default_header: Literal["letters", "numbers", "both"] = "letters",
114
- default_row_index: Literal["letters", "numbers", "both"] = "numbers",
115
+ default_header: Literal["letters", "numbers", "both"] | None = "letters",
116
+ default_row_index: Literal["letters", "numbers", "both"] | None = "numbers",
115
117
  data_reference: None | Sequence[Sequence[object]] = None,
116
118
  data: None | Sequence[Sequence[object]] = None,
117
119
  # either (start row, end row, "rows"), (start column, end column, "rows") or
@@ -2906,10 +2908,16 @@ class Sheet(tk.Frame):
2906
2908
  set_readonly(self.MT.cell_options, (r, c), readonly)
2907
2909
  elif span.kind == "row":
2908
2910
  for r in rows:
2909
- set_readonly(self.MT.row_options, r, readonly)
2911
+ if index:
2912
+ set_readonly(self.RI.cell_options, r, readonly)
2913
+ if table:
2914
+ set_readonly(self.MT.row_options, r, readonly)
2910
2915
  elif span.kind == "column":
2911
2916
  for c in cols:
2912
- set_readonly(self.MT.col_options, c, readonly)
2917
+ if header:
2918
+ set_readonly(self.CH.cell_options, c, readonly)
2919
+ if table:
2920
+ set_readonly(self.MT.col_options, c, readonly)
2913
2921
  return span
2914
2922
 
2915
2923
  # Text Font and Alignment
@@ -4151,6 +4159,7 @@ class Sheet(tk.Frame):
4151
4159
  canvas: Literal[
4152
4160
  "all",
4153
4161
  "row_index",
4162
+ "index",
4154
4163
  "header",
4155
4164
  "top_left",
4156
4165
  "x_scrollbar",
@@ -4173,7 +4182,7 @@ class Sheet(tk.Frame):
4173
4182
  self.yscroll_showing = True
4174
4183
  self.xscroll_disabled = False
4175
4184
  self.yscroll_disabled = False
4176
- elif canvas == "row_index":
4185
+ elif canvas in ("row_index", "index"):
4177
4186
  self.RI.grid(row=1, column=0, sticky="nswe")
4178
4187
  self.MT["yscrollcommand"] = self.yscroll.set
4179
4188
  self.RI["yscrollcommand"] = self.yscroll.set
@@ -4422,14 +4431,107 @@ class Sheet(tk.Frame):
4422
4431
  and event.widget == self.TL
4423
4432
  )
4424
4433
 
4434
+ def props(
4435
+ self,
4436
+ row: int,
4437
+ column: int | str,
4438
+ key: None
4439
+ | Literal[
4440
+ "format",
4441
+ "highlight",
4442
+ "dropdown",
4443
+ "checkbox",
4444
+ "readonly",
4445
+ "align",
4446
+ ] = None,
4447
+ cellops: bool = True,
4448
+ rowops: bool = True,
4449
+ columnops: bool = True,
4450
+ ) -> dict:
4451
+ """
4452
+ Retrieve options (properties - props)
4453
+ from a single cell in the main table
4454
+
4455
+ Also retrieves any row or column options
4456
+ impacting that cell
4457
+ """
4458
+ if isinstance(column, str):
4459
+ column = alpha2idx(column)
4460
+ if key is not None:
4461
+ return self.MT.get_cell_kwargs(
4462
+ datarn=row,
4463
+ datacn=column,
4464
+ key=key,
4465
+ cell=cellops,
4466
+ row=rowops,
4467
+ column=columnops,
4468
+ )
4469
+ if cellops and (row, column) in self.MT.cell_options:
4470
+ return self.MT.cell_options[(row, column)]
4471
+ if rowops and row in self.MT.row_options:
4472
+ return self.MT.row_options[row]
4473
+ if columnops and column in self.MT.col_options:
4474
+ return self.MT.col_options[column]
4475
+ return {}
4476
+
4477
+ def index_props(
4478
+ self,
4479
+ row: int,
4480
+ key: None
4481
+ | Literal[
4482
+ "format",
4483
+ "highlight",
4484
+ "dropdown",
4485
+ "checkbox",
4486
+ "readonly",
4487
+ "align",
4488
+ ] = None,
4489
+ ) -> dict:
4490
+ """
4491
+ Retrieve options (properties - props)
4492
+ from a cell in the index
4493
+ """
4494
+ if key is not None:
4495
+ return self.RI.get_cell_kwargs(
4496
+ datarn=row,
4497
+ key=key,
4498
+ )
4499
+ return self.RI.cell_options[row] if row in self.RI.cell_options else {}
4500
+
4501
+ def header_props(
4502
+ self,
4503
+ column: int | str,
4504
+ key: None
4505
+ | Literal[
4506
+ "format",
4507
+ "highlight",
4508
+ "dropdown",
4509
+ "checkbox",
4510
+ "readonly",
4511
+ "align",
4512
+ ] = None,
4513
+ ) -> dict:
4514
+ """
4515
+ Retrieve options (properties - props)
4516
+ from a cell in the header
4517
+ """
4518
+ if isinstance(column, str):
4519
+ column = alpha2idx(column)
4520
+ if key is not None:
4521
+ return self.CH.get_cell_kwargs(
4522
+ datacn=column,
4523
+ key=key,
4524
+ )
4525
+ return self.CH.cell_options[column] if column in self.CH.cell_options else {}
4526
+
4425
4527
  def get_cell_options(
4426
4528
  self,
4427
4529
  key: None | str = None,
4428
- canvas: Literal["table", "row_index", "header"] = "table",
4530
+ canvas: Literal["table", "row_index", "index", "header"] = "table",
4429
4531
  ) -> dict:
4430
4532
  if canvas == "table":
4431
4533
  target = self.MT.cell_options
4432
- elif canvas == "row_index":
4534
+ elif canvas in ("row_index", "index"):
4433
4535
  target = self.RI.cell_options
4434
4536
  elif canvas == "header":
4435
4537
  target = self.CH.cell_options
@@ -4715,8 +4817,10 @@ class Sheet(tk.Frame):
4715
4817
  open_ids: Iterator[str] | None = None,
4716
4818
  safety: bool = True,
4717
4819
  ncols: int | None = None,
4820
+ lower: bool = False,
4718
4821
  include_iid_column: bool = True,
4719
4822
  include_parent_column: bool = True,
4823
+ include_text_column: bool = True,
4720
4824
  ) -> Sheet:
4721
4825
  self.reset(cell_options=False, column_widths=False, header=False, redraw=False)
4722
4826
  if text_column is None:
@@ -4727,7 +4831,12 @@ class Sheet(tk.Frame):
4727
4831
  for rn, row in enumerate(data):
4728
4832
  if safety and ncols > (lnr := len(row)):
4729
4833
  row += self.MT.get_empty_row_seq(rn, end=ncols, start=lnr)
4730
- iid, pid = row[iid_column].lower(), row[parent_column].lower()
4834
+ if lower:
4835
+ iid = row[iid_column].lower()
4836
+ pid = row[parent_column].lower()
4837
+ else:
4838
+ iid = row[iid_column]
4839
+ pid = row[parent_column]
4731
4840
  if safety:
4732
4841
  if not iid:
4733
4842
  continue
@@ -4736,7 +4845,7 @@ class Sheet(tk.Frame):
4736
4845
  x = 1
4737
4846
  while iid in tally_of_ids:
4738
4847
  new = f"{row[iid_column]}_DUPLICATED_{x}"
4739
- iid = new.lower()
4848
+ iid = new.lower() if lower else new
4740
4849
  x += 1
4741
4850
  tally_of_ids[iid] += 1
4742
4851
  row[iid_column] = new
@@ -4755,14 +4864,14 @@ class Sheet(tk.Frame):
4755
4864
  else:
4756
4865
  self.RI.tree[iid].parent = ""
4757
4866
  self.RI.tree_rns[iid] = rn
4758
- for n in self.RI.tree.values():
4759
- if n.parent is None:
4760
- n.parent = ""
4761
- newrow = self.MT.get_empty_row_seq(len(data), ncols)
4762
- newrow[iid_column] = n.iid
4763
- self.RI.tree_rns[n.iid] = len(data)
4764
- data.append(newrow)
4765
-
4867
+ if safety:
4868
+ for n in self.RI.tree.values():
4869
+ if n.parent is None:
4870
+ n.parent = ""
4871
+ newrow = self.MT.get_empty_row_seq(len(data), ncols)
4872
+ newrow[iid_column] = n.iid
4873
+ self.RI.tree_rns[n.iid] = len(data)
4874
+ data.append(newrow)
4766
4875
  insert_rows = partial(
4767
4876
  self.insert_rows,
4768
4877
  idx=0,
@@ -4773,37 +4882,22 @@ class Sheet(tk.Frame):
4773
4882
  push_ops=push_ops,
4774
4883
  redraw=False,
4775
4884
  )
4776
-
4777
- if include_iid_column and include_parent_column:
4778
- insert_rows(rows=[[self.RI.tree[iid]] + data[self.RI.tree_rns[iid]] for iid in self.get_children()])
4779
-
4780
- elif include_iid_column and not include_parent_column:
4781
- exclude = {parent_column}
4782
- insert_rows(
4783
- rows=[
4784
- [self.RI.tree[iid]] + [e for i, e in enumerate(data[self.RI.tree_rns[iid]]) if i not in exclude]
4785
- for iid in self.get_children()
4786
- ]
4787
- )
4788
-
4789
- elif include_parent_column and not include_iid_column:
4790
- exclude = {iid_column}
4885
+ exclude = set()
4886
+ if not include_iid_column:
4887
+ exclude.add(iid_column)
4888
+ if not include_parent_column:
4889
+ exclude.add(parent_column)
4890
+ if isinstance(text_column, int) and not include_text_column:
4891
+ exclude.add(text_column)
4892
+ if exclude:
4791
4893
  insert_rows(
4792
4894
  rows=[
4793
4895
  [self.RI.tree[iid]] + [e for i, e in enumerate(data[self.RI.tree_rns[iid]]) if i not in exclude]
4794
- for iid in self.get_children()
4896
+ for iid in self.get_nodes()
4795
4897
  ]
4796
4898
  )
4797
-
4798
- elif not include_iid_column and not include_parent_column:
4799
- exclude = {iid_column, parent_column}
4800
- insert_rows(
4801
- rows=[
4802
- [self.RI.tree[iid]] + [e for i, e in enumerate(data[self.RI.tree_rns[iid]]) if i not in exclude]
4803
- for iid in self.get_children()
4804
- ]
4805
- )
4806
-
4899
+ else:
4900
+ insert_rows(rows=[[self.RI.tree[iid]] + data[self.RI.tree_rns[iid]] for iid in self.get_nodes()])
4807
4901
  self.MT.all_rows_displayed = False
4808
4902
  self.MT.displayed_rows = list(range(len(self.MT._row_index)))
4809
4903
  self.RI.tree_rns = {n.iid: i for i, n in enumerate(self.MT._row_index)}
@@ -4840,7 +4934,7 @@ class Sheet(tk.Frame):
4840
4934
  deselect_all=False,
4841
4935
  data_indexes=True,
4842
4936
  )
4843
- open_ids = set(filter(self.exists, map(str.lower, open_ids)))
4937
+ open_ids = set(filter(self.exists, open_ids))
4844
4938
  self.RI.tree_open_ids = set()
4845
4939
  if open_ids:
4846
4940
  self.show_rows(
@@ -4924,58 +5018,129 @@ class Sheet(tk.Frame):
4924
5018
  values: None | list[object] = None,
4925
5019
  create_selections: bool = False,
4926
5020
  ) -> str:
4927
- if iid is None:
5021
+ """
5022
+ Insert an item into the treeview
5023
+ """
5024
+ if not iid:
4928
5025
  i = 0
4929
5026
  while (iid := f"{num2alpha(i)}") in self.RI.tree:
4930
5027
  i += 1
4931
- iid, pid = iid.lower(), parent.lower()
4932
- if not iid:
4933
- raise ValueError("iid cannot be empty string.")
4934
5028
  if iid in self.RI.tree:
4935
5029
  raise ValueError(f"iid '{iid}' already exists.")
4936
- if iid == pid:
4937
- raise ValueError(f"iid '{iid}' cannot be equal to parent '{pid}'.")
4938
- if pid and pid not in self.RI.tree:
5030
+ if iid == parent:
5031
+ raise ValueError(f"iid '{iid}' cannot be equal to parent '{parent}'.")
5032
+ if parent and parent not in self.RI.tree:
4939
5033
  raise ValueError(f"parent '{parent}' does not exist.")
4940
5034
  if text is None:
4941
5035
  text = iid
4942
- parent_node = self.RI.tree[pid] if parent else ""
5036
+ parent_node = self.RI.tree[parent] if parent else ""
4943
5037
  self.RI.tree[iid] = Node(text, iid, parent_node)
4944
5038
  if parent_node:
4945
5039
  if isinstance(index, int):
4946
- idx = self.RI.tree_rns[pid] + index + 1
4947
- for count, cid in enumerate(self.get_children(pid)):
4948
- if count >= index:
4949
- break
4950
- idx += sum(1 for _ in self.RI.get_iid_descendants(cid))
4951
- self.RI.tree[pid].children.insert(index, self.RI.tree[iid])
5040
+ datarn = self.RI.tree_rns[parent] + index + 1
5041
+ datarn += sum(
5042
+ sum(1 for _ in self.RI.get_iid_descendants(cid)) for cid in islice(self.get_children(parent), index)
5043
+ )
5044
+ self.RI.tree[parent].children.insert(index, self.RI.tree[iid])
4952
5045
  else:
4953
- idx = self.RI.tree_rns[pid] + sum(1 for _ in self.RI.get_iid_descendants(pid)) + 1
4954
- self.RI.tree[pid].children.append(self.RI.tree[iid])
5046
+ datarn = self.RI.tree_rns[parent] + sum(1 for _ in self.RI.get_iid_descendants(parent)) + 1
5047
+ self.RI.tree[parent].children.append(self.RI.tree[iid])
4955
5048
  else:
4956
5049
  if isinstance(index, int):
4957
- idx = index
4958
- if index:
4959
- for count, cid in enumerate(self.get_children("")):
4960
- if count >= index:
4961
- break
4962
- idx += sum(1 for _ in self.RI.get_iid_descendants(cid)) + 1
5050
+ datarn = index
5051
+ if index and (datarn := self.top_index_row(datarn)) is None:
5052
+ datarn = len(self.MT._row_index)
4963
5053
  else:
4964
- idx = len(self.MT._row_index)
5054
+ datarn = len(self.MT._row_index)
4965
5055
  if values is None:
4966
5056
  values = []
4967
5057
  self.insert_rows(
4968
- idx=idx,
4969
5058
  rows=[[self.RI.tree[iid]] + values],
5059
+ idx=datarn,
4970
5060
  row_index=True,
4971
5061
  create_selections=create_selections,
4972
5062
  fill=False,
4973
5063
  )
4974
- self.RI.tree_rns[iid] = idx
4975
- if pid and (pid not in self.RI.tree_open_ids or not self.item_displayed(pid)):
4976
- self.hide_rows(idx, deselect_all=False, data_indexes=True)
5064
+ self.RI.tree_rns[iid] = datarn
5065
+ if parent and (parent not in self.RI.tree_open_ids or not self.item_displayed(parent)):
5066
+ self.hide_rows(datarn, deselect_all=False, data_indexes=True)
4977
5067
  return iid
4978
5068
 
5069
+ def bulk_insert(
5070
+ self,
5071
+ data: list[list[object]],
5072
+ parent: str = "",
5073
+ index: None | int | Literal["end"] = None,
5074
+ iid_column: int | None = None,
5075
+ text_column: int | None | str = None,
5076
+ create_selections: bool = False,
5077
+ include_iid_column: bool = True,
5078
+ include_text_column: bool = True,
5079
+ ) -> dict[str, int]:
5080
+ """
5081
+ Insert multiple items into the treeview at once, under the same parent
5082
+ """
5083
+ to_insert = []
5084
+ pid = parent
5085
+ if pid and pid not in self.RI.tree:
5086
+ raise ValueError(f"parent '{parent}' does not exist.")
5087
+ parent_node = self.RI.tree[pid] if parent else ""
5088
+ if parent_node:
5089
+ if isinstance(index, int):
5090
+ datarn = self.RI.tree_rns[pid] + index + 1
5091
+ datarn += sum(
5092
+ sum(1 for _ in self.RI.get_iid_descendants(cid)) for cid in islice(self.get_children(pid), index)
5093
+ )
5094
+ else:
5095
+ datarn = self.RI.tree_rns[pid] + sum(1 for _ in self.RI.get_iid_descendants(pid)) + 1
5096
+ else:
5097
+ if isinstance(index, int):
5098
+ datarn = index
5099
+ if index and (datarn := self.top_index_row(datarn)) is None:
5100
+ datarn = len(self.MT._row_index)
5101
+ else:
5102
+ datarn = len(self.MT._row_index)
5103
+ i = 0
5104
+ rns_to_add = {}
5105
+ for rn, r in enumerate(data, start=datarn):
5106
+ if iid_column is None:
5107
+ while (iid := f"{num2alpha(i)}") in self.RI.tree:
5108
+ i += 1
5109
+ else:
5110
+ iid = r[iid_column]
5111
+ self.RI.tree[iid] = Node(
5112
+ r[text_column] if isinstance(text_column, int) else text_column if isinstance(text_column, str) else "",
5113
+ iid,
5114
+ parent_node,
5115
+ )
5116
+ if parent_node:
5117
+ if isinstance(index, int):
5118
+ self.RI.tree[pid].children.insert(index, self.RI.tree[iid])
5119
+ else:
5120
+ self.RI.tree[pid].children.append(self.RI.tree[iid])
5121
+ exclude = set()
5122
+ if isinstance(iid_column, int) and not include_iid_column:
5123
+ exclude.add(iid_column)
5124
+ if isinstance(text_column, int) and not include_text_column:
5125
+ exclude.add(text_column)
5126
+ if exclude:
5127
+ to_insert.append([self.RI.tree[iid]] + [e for c, e in enumerate(r) if c not in exclude])
5128
+ else:
5129
+ to_insert.append([self.RI.tree[iid]] + r)
5130
+ rns_to_add[iid] = rn
5131
+ self.insert_rows(
5132
+ rows=to_insert,
5133
+ idx=datarn,
5134
+ row_index=True,
5135
+ create_selections=create_selections,
5136
+ fill=False,
5137
+ )
5138
+ for iid, rn in rns_to_add.items():
5139
+ self.RI.tree_rns[iid] = rn
5140
+ if pid and (pid not in self.RI.tree_open_ids or not self.item_displayed(pid)):
5141
+ self.hide_rows(range(datarn, datarn + len(to_insert)), deselect_all=False, data_indexes=True)
5142
+ return rns_to_add
5143
+
4979
5144
  def item(
4980
5145
  self,
4981
5146
  item: str,
@@ -4990,12 +5155,11 @@ class Sheet(tk.Frame):
4990
5155
  If no options are set then returns DotDict of options for item
4991
5156
  Else returns Sheet
4992
5157
  """
4993
- if not (item := item.lower()) or item not in self.RI.tree:
5158
+ if item not in self.RI.tree:
4994
5159
  raise ValueError(f"Item '{item}' does not exist.")
4995
5160
  if isinstance(iid, str):
4996
5161
  if iid in self.RI.tree:
4997
5162
  raise ValueError(f"Cannot rename '{iid}', it already exists.")
4998
- iid = iid.lower()
4999
5163
  self.RI.tree[item].iid = iid
5000
5164
  self.RI.tree[iid] = self.RI.tree.pop(item)
5001
5165
  self.RI.tree_rns[iid] = self.RI.tree_rns.pop(item)
@@ -5040,9 +5204,9 @@ class Sheet(tk.Frame):
5040
5204
 
5041
5205
  def itemrow(self, item: str) -> int:
5042
5206
  try:
5043
- return self.RI.tree_rns[item.lower()]
5207
+ return self.RI.tree_rns[item]
5044
5208
  except Exception:
5045
- raise ValueError(f"item '{item.lower()}' does not exist.")
5209
+ raise ValueError(f"item '{item}' does not exist.")
5046
5210
 
5047
5211
  def rowitem(self, row: int, data_index: bool = False) -> str | None:
5048
5212
  try:
@@ -5053,12 +5217,21 @@ class Sheet(tk.Frame):
5053
5217
  return None
5054
5218
 
5055
5219
  def get_children(self, item: None | str = None) -> Generator[str]:
5220
+ if item is None:
5221
+ for iid in self.get_children(""):
5222
+ yield iid
5223
+ yield from self.RI.get_iid_descendants(iid)
5224
+ elif item == "":
5225
+ yield from map(attrgetter("iid"), self.RI.gen_top_nodes())
5226
+ else:
5227
+ yield from (n.iid for n in self.RI.tree[item].children)
5228
+
5229
+ def get_nodes(self, item: None | str = None) -> Generator[str]:
5056
5230
  if item is None:
5057
5231
  for n in self.RI.tree.values():
5058
5232
  if not n.parent:
5059
5233
  yield n.iid
5060
- for iid in self.RI.get_iid_descendants(n.iid):
5061
- yield iid
5234
+ yield from self.RI.get_iid_descendants(n.iid)
5062
5235
  elif item == "":
5063
5236
  yield from (n.iid for n in self.RI.tree.values() if not n.parent)
5064
5237
  else:
@@ -5071,7 +5244,6 @@ class Sheet(tk.Frame):
5071
5244
  rows_to_del = []
5072
5245
  iids_to_del = []
5073
5246
  for item in unpack(items):
5074
- item = item.lower()
5075
5247
  if item not in self.RI.tree:
5076
5248
  continue
5077
5249
  rows_to_del.append(self.RI.tree_rns[item])
@@ -5109,9 +5281,9 @@ class Sheet(tk.Frame):
5109
5281
  'parent' can be an empty str which will put the item at top level
5110
5282
  Performance is not great
5111
5283
  """
5112
- if (item := item.lower()) and item not in self.RI.tree:
5284
+ if item not in self.RI.tree:
5113
5285
  raise ValueError(f"Item '{item}' does not exist.")
5114
- if (parent := parent.lower()) and parent not in self.RI.tree:
5286
+ if parent and parent not in self.RI.tree:
5115
5287
  raise ValueError(f"Parent '{parent}' does not exist.")
5116
5288
  mapping = {}
5117
5289
  to_show = []
@@ -5188,11 +5360,8 @@ class Sheet(tk.Frame):
5188
5360
  item_node.parent = parent_node
5189
5361
  parent_node.children.insert(index, item_node)
5190
5362
  else:
5191
- if index is None:
5192
- new_r = self.top_index_row((sum(1 for _ in self.RI.gen_top_nodes()) - 1))
5193
- else:
5194
- if (new_r := self.top_index_row(index)) is None:
5195
- new_r = self.top_index_row((sum(1 for _ in self.RI.gen_top_nodes()) - 1))
5363
+ if index is None or (new_r := self.top_index_row(index)) is None:
5364
+ new_r = self.top_index_row(sum(1 for _ in self.RI.gen_top_nodes()) - 1)
5196
5365
  if item_r < new_r:
5197
5366
  r_ctr = (
5198
5367
  new_r
@@ -5225,15 +5394,15 @@ class Sheet(tk.Frame):
5225
5394
  reattach = move
5226
5395
 
5227
5396
  def exists(self, item: str) -> bool:
5228
- return item.lower() in self.RI.tree
5397
+ return item in self.RI.tree
5229
5398
 
5230
5399
  def parent(self, item: str) -> str:
5231
- if (item := item.lower()) not in self.RI.tree:
5400
+ if item not in self.RI.tree:
5232
5401
  raise ValueError(f"Item '{item}' does not exist.")
5233
5402
  return self.RI.tree[item].parent.iid if self.RI.tree[item].parent else self.RI.tree[item].parent
5234
5403
 
5235
5404
  def index(self, item: str) -> int:
5236
- if (item := item.lower()) not in self.RI.tree:
5405
+ if item not in self.RI.tree:
5237
5406
  raise ValueError(f"Item '{item}' does not exist.")
5238
5407
  if not self.RI.tree[item].parent:
5239
5408
  find_node = self.RI.tree[item]
@@ -5245,7 +5414,7 @@ class Sheet(tk.Frame):
5245
5414
  Check if an item (row) is currently displayed on the sheet
5246
5415
  - Does not check if the item is visible to the user
5247
5416
  """
5248
- if (item := item.lower()) not in self.RI.tree:
5417
+ if item not in self.RI.tree:
5249
5418
  raise ValueError(f"Item '{item}' does not exist.")
5250
5419
  return self.RI.tree_rns[item] in self.MT.displayed_rows
5251
5420
 
@@ -5256,7 +5425,7 @@ class Sheet(tk.Frame):
5256
5425
  - Unlike the ttk treeview 'see' function
5257
5426
  this function does **NOT** scroll to the item
5258
5427
  """
5259
- if (item := item.lower()) not in self.RI.tree:
5428
+ if item not in self.RI.tree:
5260
5429
  raise ValueError(f"Item '{item}' does not exist.")
5261
5430
  if self.RI.tree[item].parent:
5262
5431
  self.show_rows(
@@ -5270,7 +5439,7 @@ class Sheet(tk.Frame):
5270
5439
  """
5271
5440
  Scrolls to an item and ensures that it is displayed
5272
5441
  """
5273
- if (item := item.lower()) not in self.RI.tree:
5442
+ if item not in self.RI.tree:
5274
5443
  raise ValueError(f"Item '{item}' does not exist.")
5275
5444
  self.display_item(item, redraw=False)
5276
5445
  self.see(
@@ -5288,19 +5457,29 @@ class Sheet(tk.Frame):
5288
5457
  self.MT._row_index[self.MT.displayed_rows[rn]].iid for rn in self.get_selected_rows(get_cells_as_rows=cells)
5289
5458
  ]
5290
5459
 
5291
- def selection_set(self, *items, redraw: bool = True) -> Sheet:
5292
- if any(item.lower() in self.RI.tree for item in unpack(items)):
5293
- boxes_to_hide = tuple(self.MT.selection_boxes)
5294
- self.selection_add(*items, redraw=False)
5295
- for iid in boxes_to_hide:
5296
- self.MT.hide_selection_box(iid)
5460
+ @property
5461
+ def tree_selected(self) -> str | None:
5462
+ """
5463
+ Get the iid at the currently selected box
5464
+ """
5465
+ if selected := self.selected:
5466
+ return self.rowitem(selected.row)
5467
+ return None
5468
+
5469
+ def selection_set(self, *items, run_binding: bool = True, redraw: bool = True) -> Sheet:
5470
+ boxes_to_hide = tuple(self.MT.selection_boxes)
5471
+ self.selection_add(*items, run_binding=False, redraw=False)
5472
+ for iid in boxes_to_hide:
5473
+ self.MT.hide_selection_box(iid)
5474
+ if run_binding:
5475
+ self.MT.run_selection_binding("rows")
5297
5476
  return self.set_refresh_timer(redraw)
5298
5477
 
5299
- def selection_add(self, *items, redraw: bool = True) -> Sheet:
5478
+ def selection_add(self, *items, run_binding: bool = True, redraw: bool = True) -> Sheet:
5300
5479
  to_open = []
5301
5480
  quick_displayed_check = set(self.MT.displayed_rows)
5302
5481
  for item in unpack(items):
5303
- if self.RI.tree_rns[(item := item.lower())] not in quick_displayed_check and self.RI.tree[item].parent:
5482
+ if self.RI.tree_rns[item] not in quick_displayed_check and self.RI.tree[item].parent:
5304
5483
  to_open.extend(list(self.RI.get_iid_ancestors(item)))
5305
5484
  if to_open:
5306
5485
  self.show_rows(
@@ -5312,7 +5491,7 @@ class Sheet(tk.Frame):
5312
5491
  sorted(
5313
5492
  bisect_left(
5314
5493
  self.MT.displayed_rows,
5315
- self.RI.tree_rns[item.lower()],
5494
+ self.RI.tree_rns[item],
5316
5495
  )
5317
5496
  for item in unpack(items)
5318
5497
  )
@@ -5326,12 +5505,13 @@ class Sheet(tk.Frame):
5326
5505
  set_current=True,
5327
5506
  ext=True,
5328
5507
  )
5329
- self.MT.run_selection_binding("rows")
5508
+ if run_binding:
5509
+ self.MT.run_selection_binding("rows")
5330
5510
  return self.set_refresh_timer(redraw)
5331
5511
 
5332
5512
  def selection_remove(self, *items, redraw: bool = True) -> Sheet:
5333
5513
  for item in unpack(items):
5334
- if (item := item.lower()) not in self.RI.tree:
5514
+ if item not in self.RI.tree:
5335
5515
  continue
5336
5516
  try:
5337
5517
  self.deselect(bisect_left(self.MT.displayed_rows, self.RI.tree_rns[item]), redraw=False)
@@ -5344,7 +5524,7 @@ class Sheet(tk.Frame):
5344
5524
  add = []
5345
5525
  remove = []
5346
5526
  for item in unpack(items):
5347
- if (item := item.lower()) in self.RI.tree:
5527
+ if item in self.RI.tree:
5348
5528
  if item in selected:
5349
5529
  remove.append(item)
5350
5530
  else:
@@ -5353,6 +5533,9 @@ class Sheet(tk.Frame):
5353
5533
  self.selection_add(*add, redraw=False)
5354
5534
  return self.set_refresh_timer(redraw)
5355
5535
 
5536
+ def descendants(self, item: str, check_open: bool = False) -> Generator[str]:
5537
+ return self.RI.get_iid_descendants(item, check_open=check_open)
5538
+
5356
5539
  # Functions not in docs
5357
5540
 
5358
5541
  def event_generate(self, *args, **kwargs) -> None:
@@ -5942,7 +6125,7 @@ class Sheet(tk.Frame):
5942
6125
  row: int | Literal["all"] = 0,
5943
6126
  column: int = 0,
5944
6127
  cells: list[tuple[int, int]] = [],
5945
- canvas: Literal["table", "row_index", "header"] = "table",
6128
+ canvas: Literal["table", "row_index", "index", "header"] = "table",
5946
6129
  all_: bool = False,
5947
6130
  redraw: bool = True,
5948
6131
  ) -> Sheet:
@@ -5975,7 +6158,7 @@ class Sheet(tk.Frame):
5975
6158
  for k in self.MT.cell_options:
5976
6159
  if "highlight" in self.MT.cell_options[k]:
5977
6160
  del self.MT.cell_options[k]["highlight"]
5978
- elif canvas == "row_index":
6161
+ elif canvas in ("row_index", "index"):
5979
6162
  if cells and not all_:
5980
6163
  for r in cells:
5981
6164
  try:
@@ -6005,10 +6188,13 @@ class Sheet(tk.Frame):
6005
6188
  del self.CH.cell_options[c]["highlight"]
6006
6189
  return self.set_refresh_timer(redraw)
6007
6190
 
6008
- def get_highlighted_cells(self, canvas: str = "table") -> dict | None:
6191
+ def get_highlighted_cells(
6192
+ self,
6193
+ canvas: Literal["table", "row_index", "index", "header"] = "table",
6194
+ ) -> dict | None:
6009
6195
  if canvas == "table":
6010
6196
  return {k: v["highlight"] for k, v in self.MT.cell_options.items() if "highlight" in v}
6011
- elif canvas == "row_index":
6197
+ elif canvas in ("row_index", "index"):
6012
6198
  return {k: v["highlight"] for k, v in self.RI.cell_options.items() if "highlight" in v}
6013
6199
  elif canvas == "header":
6014
6200
  return {k: v["highlight"] for k, v in self.CH.cell_options.items() if "highlight" in v}