You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将ttk.Treeview的项重新附加或移回原位置?

问题描述

现有示例脚本可实现:鼠标左键释放时分离ttk.Treeview控件的根项,点击ttk.Button时重新附加分离的项。但目前通过解析项文本的方式实现重新附加,不仅繁琐,还存在项文本不含索引时无法使用的问题。

引用ttk.Treeview的move(或reattach)方法文档:

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) 

文档中明确提到If item was detached it is reattached(若项已分离,则会被重新附加)。

因此想请教:是否存在更简便的方法,无需依赖当前的文本解析方式,就能将分离的项重新附加回原索引位置?

当前脚本:

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()
解决方案

核心思路是在分离项的时候,提前记录每个项的原父节点和原索引位置,这样恢复时直接调用move方法即可,完全不需要解析文本。

修改后的脚本如下:

import tkinter as tk
from tkinter import ttk

class App:
    def __init__(self):
        # 用字典存储分离项的原父节点和原索引,键为项的iid
        self.detached_info = {}
        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()
        tree = event.widget
        for item in selected_items:
            # 获取项的原父节点
            parent = tree.parent(item)
            # 获取项在父节点下的原索引
            index = tree.index(item)
            # 存储到字典中
            self.detached_info[item] = (parent, index)
        # 分离选中的项
        tree.detach(*selected_items)

    def reinsert_items(self):
        tree = self.tree
        # 遍历复制的字典键,避免修改字典时影响遍历
        for iid in list(self.detached_info.keys()):
            parent, index = self.detached_info.pop(iid)
            # 用move方法将项移回原位置,自动重新附加
            tree.move(iid, parent, index)

if __name__ == "__main__":
    app = App()

关键说明:

  • 用self.detached_info字典替代原来的集合,每个分离项的iid对应它的原父节点和原索引,这两个值可以通过tree.parent(item)和tree.index(item)直接获取,完全不依赖项的文本内容。
  • 恢复时,直接从字典中取出原父节点和索引,调用tree.move(iid, parent, index)即可,根据文档说明,分离状态的项会被自动重新附加到指定位置。
  • 遍历字典时用list(self.detached_info.keys()),避免在遍历过程中修改字典(删除键)导致的异常。

内容的提问来源于stack exchange,提问作者Sun Bear

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 12:12:09