The following sample script allows the detachment of root item(s) of a ttk.Treeview
widget after the mouse button 1 is released after clicking it and reattaches the detached items when the ttk.Button
widget is released. However, I achieved the reattachments via manipulating the item's text which I find cumbersome and not always possible in situations where its text does not contain any indexing.
Quoting the documentation of the move
(or reattach
) method of the ttk.Treeview
widget
def move(self, item, parent, index):
"""Moves item to position index in parent's list of children.
It is illegal to move an item under one of its descendants. If
index is less than or equal to zero, item is moved to the
beginning, if greater than or equal to the number of children,
it is moved to the end. If item was detached it is reattached."""
self.tk.call(self._w, "move", item, parent, index)
Specifically, If item was detached it is reattached
.
Hence, is there a simpler way to reattach the detached item(s) back to their original index location without having to do what my script is currently doing?
Script:
import tkinter as tk
from tkinter import ttk
class App:
def __init__(self):
self.detached_items = set()
self.root = tk.Tk()
self.tree = ttk.Treeview(self.root)
self.tree.pack(side="top", fill="both")
self.reinsert = ttk.Button(self.root, text= "Reinsert",
command=self.reinsert_items)
self.reinsert.pack(side="top", fill="both")
self.tree.bind("<ButtonRelease-1>", self.detach_selection)
for i in range(10):
self.tree.insert("", "end", text="Item %s" % i)
self.root.mainloop()
def detach_selection(self, event):
selected_items = event.widget.selection()
print(f"{selected_items=}")
for item in selected_items:
self.detached_items.add(item)
event.widget.detach(*selected_items)
def reinsert_items(self):
for iid in self.detached_items.copy():
print(f"{iid=}")
text = self.tree.item(iid)["text"]
print(f"{text=}")
if self.tree.exists(iid):
self.tree.move(iid, "", int(text[5:]))
# self.tree.move(iid, "", "") # "" as index don't work
else:
self.tree.move(iid, "", "end")
self.detached_items.remove(iid)
print(f"{self.detached_items=}")
if __name__ == "__main__":
app = App()
Thanks @acw1668.
I have set up self.items_index
as a reference to the dict
object to store the index of each item as it is being detached. Thereafter, this dict
object is accessed in a reverse order to acquire the corresponding item index to reattach the items.
Note: This method of using a dict
object is guaranteed to work for Python 3.7 and above as dictionary order is guaranteed to be according to the insertion order.
Working script:
import tkinter as tk
from tkinter import ttk
class App:
def __init__(self):
self.items_index = {} # Added
self.root = tk.Tk()
self.tree = ttk.Treeview(self.root)
self.tree.pack(side="top", fill="both")
self.reinsert = ttk.Button(self.root, text= "Reinsert",
command=self.reinsert_items)
self.reinsert.pack(side="top", fill="both")
self.tree.bind("<ButtonRelease-1>", self.detach_selection)
for i in range(10):
self.tree.insert("", "end", text="Item %s" % i)
self.root.mainloop()
def detach_selection(self, event):
print(f"\nDetach")
selected_items = event.widget.selection()
print(f"{selected_items=}")
for item in selected_items:
self.items_index[item] = self.tree.index(item)
event.widget.detach(item)
print(f"{self.items_index=}\n")
def reinsert_items(self):
print(f"\nReinsert")
iids = list(self.items_index.keys())
print(f"{iids=}")
for iid in iids[::-1]:
index = self.items_index[iid]
self.tree.move(iid, "", index)
del self.items_index[iid]
print(f"{self.items_index=}\n")
if __name__ == "__main__":
app = App()