如何修改(例如 add/remove)ttk.Treeview 标签(tkinter)的值?

How to amend (e.g. add/remove) the values of a ttk.Treeview tag (tkinter)?

如何修改(例如 add/remove)ttk.Treeview 中选项 tags 的值?有没有我可以使用的内置 tkinter/widget 方法?

...
columns = list(range(1,3))
tree = ttk.Treeview(root, columns=columns)

tree.insert('', 'end', iid=1, values=(1,1), tags='1')
...
  1. 如何将值“NEWTAG”添加到 tags='1'tags 应具有值“1”和“NEWTAG”。
  2. 如何添加用“NEWTAG”替换“1”? tags 值最终应该是“NEWTAG”。
  3. 如果 tags 的值已经是“1”和“NEWTAG”,如何删除“NEWTAG”以便 tags="1"?

我认为您需要调用一些 tkinter 未提供的 internet tcl widget 命令(如评论中所述)。为此,我们使用 tk.call 方法。我创建了两个函数并手动将这些函数分配给 Treeview 对象:

def add_tag(new_tag,iid):
    root.tk.call(tree,'tag','add',new_tag,iid)

def replace_tag(new_tag,to_be_replaced,iid=''):
    """If iid is ommited, replaces the old_tag from all the items if it exists"""

    if iid:
        root.tk.call(tree,'tag','add',new_tag,iid)
        root.tk.call(tree,'tag','remove',to_be_replaced,iid)    
    else:
        iids = tree.tag_has(to_be_replaced)
        root.tk.call(tree,'tag','remove',to_be_replaced)
        for i in iids:
            root.tk.call(tree,'tag','add',new_tag,i)

def delete_tag(tag_to_delete,iid=''):
    """If iid is ommited, deletes all the tag from all items"""

    if iid:
        root.tk.call(tree,'tag','remove',tag_to_delete,iid)
    else:
        root.tk.call(tree,'tag','remove',tag_to_delete)

tree.add_tag = add_tag
tree.replace_tag = replace_tag
tree.delete_tag = delete_tag

iids = tree.tag_has('OLDTAG')

# Adding a tag to the first item in the treeview
tree.add_tag('NEWTAG',iids[0]) # Choosing the first item
all_tags = tree.item(iids[0],'tags')
print(f'All tag for the given iid: {all_tags}') # ('OLDTAG', 'NEWTAG')

# Replacing the OLDTAG for all items
tree.replace_tag('OLDEST','OLDTAG')
all_tags = tree.item(iids[0],'tags')
print(f'All tag for the given iid: {all_tags}') # ('NEWTAG', 'OLDEST')

# Delete NEWTAG for the first item
tree.delete_tag('NEWTAG',iids[0])
all_tags = tree.item(iids[0],'tags')
print(f'All tag for the given iid: {all_tags}') # ('OLDEST',)

虽然创建自己的 class 会更好。请注意,'OLDTAG' 是您预先给物品的任何标签。并非万无一失,请随意尝试。

没有任何事件驱动程序的完整代码:

from tkinter import *
from tkinter import ttk

root = Tk()

tree = ttk.Treeview(root,columns=('No.','Name'),show='headings')
tree.pack()

def add_tag(new_tag,iid):
    root.tk.call(tree,'tag','add',new_tag,iid)

def replace_tag(new_tag,to_be_replaced,iid=''):
    """If iid is not specified replaces the old_tag from all the items if it exists"""

    if iid:
        root.tk.call(tree,'tag','add',new_tag,iid)
        root.tk.call(tree,'tag','remove',to_be_replaced,iid)    
    else:
        iids = tree.tag_has(to_be_replaced)
        root.tk.call(tree,'tag','remove',to_be_replaced)
        for i in iids:
            root.tk.call(tree,'tag','add',new_tag,i)

def delete_tag(tag_to_delete,iid=''):
    """If iid is ommited, deletes all the tag from all items"""

    if iid:
        root.tk.call(tree,'tag','remove',tag_to_delete,iid)
    else:
        root.tk.call(tree,'tag','remove',tag_to_delete)

lst = [[1,'Me'],[2,'Myself'],[3,'I']]

for i in ('No.','Name'):
    tree.heading(i,text=i)
    tree.column(i,width=100)

for i in lst:
    tree.insert('','end',values=i,tags='OLDTAG')

iids = tree.tag_has('OLDTAG')

tree.add_tag = add_tag
tree.replace_tag = replace_tag
tree.delete_tag = delete_tag

# Adding a tag to the first item in the treeview
tree.add_tag('NEWTAG',iids[0]) # Choosing the first item
all_tags = tree.item(iids[0],'tags')
print(f'All tag for the given iid: {all_tags}') # ('OLDTAG', 'NEWTAG')

# Replacing the OLDTAG for all items
tree.replace_tag('OLDEST','OLDTAG')
all_tags = tree.item(iids[0],'tags')
print(f'All tag for the given iid: {all_tags}') # ('NEWTAG', 'OLDEST')

# Delete NEWTAG for the first item
tree.delete_tag('NEWTAG',iids[0])
all_tags = tree.item(iids[0],'tags')
print(f'All tag for the given iid: {all_tags}') # ('OLDEST',)

root.mainloop()

根据 tcl documentationttk.Treeview 小部件确实具有向节点或节点列表添加和删除标签的命令。但是,官方的 tkinter 包装器中没有提供这些方法;请参阅 /usr/lib/python3.8/tkinter/ttk.py.

中的 Treeview class

基于@JasonYang 的评论和@CoolCloud 的回答,下面的测试代码说明了如何实现包装器来访问 ttk.Treeviewtag addtag remove 命令小部件。希望这个例子可以让tkinter用户受益。

测试代码:请参阅# Methods to add and remove tag(s)部分。有关为什么使用小部件的 ._w 属性的解释,请阅读

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.ttk as ttk


class App(ttk.Frame):

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent)
        self.parent = parent
        self._create_treeview()
        self._test_tag_add_method()
        self._test_tag_remove_method()

    def _create_treeview(self):
        # Create Treeview
        self.tree = ttk.Treeview(self, column=('A', 'B'), selectmode='none',
                                 height=7)
        self.tree.grid(row=0, column=0, sticky='nsew')

        # Setup column heading
        self.tree.heading('#0', text='Point', anchor='center')
        self.tree.heading('#1', text='x', anchor='center')
        self.tree.heading('#2', text='y', anchor='center')
        # #0, #01, #02 denotes the 0, 1st, 2nd columns

        # Setup column
        self.tree.column('#0', anchor='center', width=50)
        self.tree.column('#1', anchor='center', width=30)
        self.tree.column('#2', anchor='center', width=30)

        # Insert nodes
        data = [(10, 20), (30, 40), (50, 60)]
        for n, d in enumerate(data):
            self.tree.insert('', 'end', iid='n'+str(n), text=str(n), value=d,
                             tag='Current')
        node = 'n5 xxx'
        self.tree.insert('', 'end', iid=node, text=node, value=d, tag='Current')
        print(f"Nodes with 'Current' tag: {self.tree.tag_has('Current')}")

    def _test_tag_add_method(self):
        # Add 'House' to tag of node 'n1'
        tag = 'House'
        self.tag_add(tag, items='n1')  # works but incorrect type for items
        print(f"Nodes with 'House' tag: {self.tree.tag_has(tag)}")
        self.tag_add(tag, items=['n5 xxx'])  # Correct way of submitting items
        print(f"Nodes with 'House' tag: {self.tree.tag_has(tag)}")

        # Add 'NEWTAG' to tag of all nodes
        tag = 'NEWTAG'
        self.tag_add(tag, items=self.tree.get_children())
        print(f"Nodes with 'NEWTAG' tag: {self.tree.tag_has(tag)}")

    def _test_tag_remove_method(self):
        # Remove 'House' to tag of node 'n1'
        tag = 'House'
        self.tag_remove(tag, items='n1')  # works but incorrect type for items
        print(f"Nodes with 'House' tag: {self.tree.tag_has(tag)}")
        self.tag_remove(tag, items=['n5 xxx'])  # Correct way of submitting items
        print(f"Nodes with 'House' tag: {self.tree.tag_has(tag)}")

        # Add 'NEWTAG' to tag of all nodes
        tag = 'NEWTAG'
        self.tag_remove(tag, items=self.tree.get_children())
        print(f"Nodes with 'NEWTAG' tag: {self.tree.tag_has(tag)}")

    ###########################################################################
    # Methods to add and remove tag(s)
    ###########################################################################
    def tag_add(self, tag, items=None):
        '''Adds the specified tag to each of the listed items. If tag is
        already present for a particular item, then the tags for that item are
        unchanged. Note: items refers to the iid of the tree nodes and must be
        a list object.
        '''
        if items is None:
            self.tk.call(self.tree._w, 'tag', 'add', tag)
        else:
            self.tk.call(self.tree._w, 'tag', 'add', tag, items)

    def tag_remove(self, tag, items=None):
        '''Removes the specified tag from each of the listed items. If items is
        omitted, removes tag from each item in the tree. If tag is not present
        for a particular item, then the tags for that item are unchanged.
        Note: items refers to the iid of the tree nodes and must be a list
        object.
        '''
        if items is None:
            self.tk.call(self.tree._w, 'tag', 'remove', tag)
        else:
            self.tk.call(self.tree._w, 'tag', 'remove', tag, items)
    ###########################################################################


if __name__ == '__main__':
    root = tk.Tk()
    app = App(root)
    app.grid(row=0, column=0, sticky='nsew')
    root.rowconfigure(0, weight=1)
    root.columnconfigure(0, weight=1)
    root.mainloop()

备注:tag addtag remove方法中,items参数必须是列表对象,不能是字符串对象。 items 参数的元素必须是字符串对象。如果将带有白色 space 的字符串对象作为 items 传入,则会出现