Search code examples
pythontkintertreeviewttkttkwidgets

How to change row color when mouse over? Tkinter Treeview


I have been working with the Tkinter ttk.Treeview widget lately and I have been able to change a lot the widget's style using a ttk.Style, but sadly I am unable to find a solution to this problem:

How can I change the item's colour when the cursor/mouse is over?

Like the activebackground option of Tkinter.Button. Like the VS CODE treeview: When you are navigating the explorer, the file/ folder under the cursor changes the background colour.


Solution

  • You can control the color of a row with a tag, so the first part of the solution is to define a tag to highlight a row:

    tree.tag_configure('highlight', background='lightblue')
    

    Next, write a method that will remove that tag from all items in the tree and then add it for the item under the cursor. The underlying tk widget has methods for adding and removing tags but those methods aren't exposed, so we'll need to directly call the underlying tk code.

    def highlight_row(event):
        tree = event.widget
        item = tree.identify_row(event.y)
        tree.tk.call(tree, "tag", "remove", "highlight")
        tree.tk.call(tree, "tag", "add", "highlight", item)
    

    Finally, bind the function to the <Motion> event:

    tree.bind("<Motion>", highlight_row)
    

    Here is a complete working example:

    import tkinter as tk
    from tkinter import ttk
    
    
    def highlight_row(event):
        tree = event.widget
        item = tree.identify_row(event.y)
        tree.tk.call(tree, "tag", "remove", "highlight")
        tree.tk.call(tree, "tag", "add", "highlight", item)
    
    root = tk.Tk()
    
    tree = ttk.Treeview(root, style = 'W.TButton')
    vsb = ttk.Scrollbar(root, command=tree.yview)
    tree.configure(yscrollcommand=vsb.set)
    
    vsb.pack(side="right", fill="y")
    tree.pack(side="left", fill="both", expand=True)
    
    tree.tag_configure('highlight', background='lightblue')
    tree.bind("<Motion>", highlight_row)
    
    
    for i in range(100):
        tree.insert("", "end", text=f"Item #{i+1}")
        tree.tag_bind(i, '<Motion>', highlight_row)
    
    root.mainloop()
    

    screenshot