Search code examples
pythonpython-3.xtkintertreeview

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


How do I amend (e.g. add/remove) the values of the option tags in a ttk.Treeview? Is there any built in tkinter/widget methods that I can use?

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

tree.insert('', 'end', iid=1, values=(1,1), tags='1')
...
  1. How do I add the value "NEWTAG" to tags='1'? tags should have the values '1' & "NEWTAG".
  2. How do I add the replace '1' with "NEWTAG"? tags value should finally be "NEWTAG".
  3. If the value of tags is already "1' and "NEWTAG", how do I remove "NEWTAG" so that tags="1"?

Solution

  • According to the tcl documentation, a ttk.Treeview widget does have commands to add and remove a tag from a node or a list of nodes. However, these methods are not provided in the official tkinter wrapper; see the Treeview class in /usr/lib/python3.8/tkinter/ttk.py.

    Building on the comment by @JasonYang and answer by @CoolCloud, the test code below illustrates how to implement a wrapper to access the tag add and tag remove commands of the ttk.Treeview widget. Hopes this example can benefit tkinter users.

    Test Code: Please see the section on # Methods to add and remove tag(s). For an explanation on why the ._w attribute of a widget is used, please read this question.

    #!/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()
    

    Remark: In both tag add and tag remove methods, the items argument must be a list object and not a string object. The element of the items argument must be a string object. In the event a string object with white space is passed in as items, a _tkinter.TclError would occur.