'tkinter Highlight a row of a label table

I am trying to highlight an entire row of a table created with buttons.

The code already highlights the button the mouse is over, but I need to highlight not only this button, but also the entire row to emulate a real table.

I know this is easier to do with a tree view, but for multiple reasons in my GUI, I am forced to use a button based table instead of a tree view.

from tkinter import *

root = Tk()

class HoverButton(Button):
    def __init__(self, master, **kw):
        Button.__init__(self,master=master,**kw)
        self.defaultBackground = self["background"]
        self.bind("<Enter>", self.on_enter)
        self.bind("<Leave>", self.on_leave)

    def on_enter(self, e):
        self['background'] = self['activebackground']

    def on_leave(self, e):
        self['background'] = self.defaultBackground

columns = ("column 1","column 2","column 3", "column 4")
first_row = ("data 1", "data 2", "data 3", "data 4")

for columns_headings in range(len(columns)):
    l = HoverButton(root, text=columns[columns_headings], relief=RIDGE, activebackground="yellow")
    l.grid(row=0, column=columns_headings, sticky=NSEW)    

for first_row_data in range(len(first_row)):
    g = HoverButton(root, text=first_row[first_row_data], relief=RIDGE, activebackground="yellow")
    g.grid(row=1, column=first_row_data, sticky=NSEW)  

mainloop()


Solution 1:[1]

You can use .grid_info() to get the row of the hovered button and then update all the buttons in same row (get by .grid_slaves(row=...):

def on_enter(self, e):
    row = self.grid_info()['row']
    for w in self.master.grid_slaves(row=row):
        if isinstance(w, HoverButton):
            w['bg'] = w['activebackground']

def on_leave(self, e):
    row = self.grid_info()['row']
    for w in self.master.grid_slaves(row=row):
        if isinstance(w, HoverButton):
            w['bg'] = w.defaultBackground

Solution 2:[2]

If you will keep buttons on 2D list with rows and columns then you can simply access other buttons in the same row. Every button need also variable to keep its row number.

from tkinter import *

root = Tk()

class HoverButton(Button):
    
    def __init__(self, master, **kw):
        Button.__init__(self,master=master,**kw)
        self.defaultBackground = self["background"]
        self.bind("<Enter>", self.on_enter)
        self.bind("<Leave>", self.on_leave)

    def on_enter(self, e):
        #self['background'] = self['activebackground']
        for b in buttons[self.row_number]:
            b['background'] = self['activebackground']

    def on_leave(self, e):
        #self['background'] = self.defaultBackground
        for b in buttons[self.row_number]:
            b['background'] = self.defaultBackground

columns = ("column 1","column 2","column 3", "column 4")
first_row = ("data 1", "data 2", "data 3", "data 4")

buttons = []  # list 2D for all buttons

row = []
for columns_headings in range(len(columns)):
    l = HoverButton(root, text=columns[columns_headings], relief=RIDGE, activebackground="yellow")
    l.row_number = 0
    l.grid(row=0, column=columns_headings, sticky=NSEW)    
    row.append(l)
buttons.append(row)    
    
row = []
for first_row_data in range(len(first_row)):
    g = HoverButton(root, text=first_row[first_row_data], relief=RIDGE, activebackground="yellow")
    g.row_number = 1
    g.grid(row=1, column=first_row_data, sticky=NSEW)  
    row.append(g)
buttons.append(row)    

mainloop()

EDIT:

Version with more rows - which need extra for-loop.

import tkinter as tk  # PEP8: `import *` is not preferred

# --- classes ---  # PEP8: all classes directly after imports

class HoverButton(tk.Button):
    
    def __init__(self, master, **kw):
        super().__init__(master=master, **kw)
        self.defaultBackground = self["background"]
        self.bind("<Enter>", self.on_enter)
        self.bind("<Leave>", self.on_leave)

    def on_enter(self, e):
        #self['background'] = self['activebackground']
        for b in buttons[self.row_number]:
            b['background'] = self['activebackground']

    def on_leave(self, e):
        #self['background'] = self.defaultBackground
        for b in buttons[self.row_number]:
            b['background'] = self.defaultBackground

# --- main ---

root = tk.Tk()


columns = ("column 1","column 2","column 3", "column 4")
rows = [
    ("data A1", "data A2", "data A3", "data A4"),
    ("data B1", "data B2", "data B3", "data B4"),
    ("data C1", "data C2", "data C3", "data C4"),
]

buttons = []

row = []
for col_number, text in enumerate(columns):
    l = HoverButton(root, text=text, relief='ridge', activebackground="yellow")
    l.row_number = 0
    #l.col_number = col_number
    l.grid(row=0, column=col_number, sticky='nsew')    
    row.append(l)
buttons.append(row)    
    
for row_number, row_data in enumerate(rows, 1):
    row = []
    for col_number, text in enumerate(row_data):
        g = HoverButton(root, text=text, relief='ridge', activebackground="yellow")
        g.row_number = row_number
        #g.col_number = col_number
        g.grid(row=row_number, column=col_number, sticky='nsew')  
        row.append(g)
    buttons.append(row)    

root.mainloop()

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 acw1668
Solution 2