How to change row color when mouse over? Tkinter Treeview

Viewed 1157

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.

2 Answers

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

Here is an example to do it. You may further improve this code.

import tkinter as tk
from tkinter import ttk


def Enter_event(event):

    for x in tree.get_children():
        tree.tag_configure(tree.item(x)['tags'], background='')

    tree.tag_configure(tree.item(tree.identify_row(event.y))['tags'], background='yellow')

root = tk.Tk()
style = ttk.Style() 
  
  
style.configure('W.TButton') 

tree = ttk.Treeview(root, style = 'W.TButton')

tree.pack()
for i in range(10):
    tree.insert("", "end", text="Item %s" % i, tags=i)
    tree.tag_bind(i, '<Motion>', Enter_event)
        
root.mainloop()

Explanation:

use set each cell a tag then bind each tag to motion event and call event handler.

  • tree.item(x)['tags'] returns the tag of specified iid.

  • tree.get_children() returns all the children iid

  • tree.identify_row(event.y) returns the row at y position.

  • tree.tag_configure(tree.item(tree.identify_row(event.y))['tags'], background='yellow') will change the background of the specified tag.

I tried Enter and leave event but it's not being recognized. (Also, note setting style configure to 'Treeview' doesn't work for me. If it does for any of the readers pls inform me)

Related