Astrodude11
Astrodude11

Reputation: 159

Changing Button Appearance On Click

In my GUI application I create a button for every item in a given list. When a button is pressed the background of said button turns green and it appears as "sunken". The other buttons appear raised and with the default color. My issue is that only the last button created changes color and appearance. I believe this is because it contains the callback function.

I desire that after the initial button press, when a user presses a different button that the new button turns green and appears sunken and the previous button becomes raised and the default color. My guess is that I need to create a list of all created buttons and apply logic to it in the callback function for my desired behavior. But how so?

# ---callback functions---
def select_button():
    btn.config(bg='green', activebackground='green', relief=SUNKEN)

# ---main---
# List of buttons to be created
list = ['A', 'B', 'C']
buttons = []

# Create buttons
for i, name in enumerate(list, 2):
    btn = Button(root, text=name, command=select_button)
    btn.grid(row=i, column=0, sticky=W)
    buttons.append(btn)

Upvotes: 2

Views: 11670

Answers (2)

Avandale
Avandale

Reputation: 252

I had a similar issue and ended up defining a function within a function to solve my problem. In your case, we would get this :

import tkinter as tk

def select(btn):
    def select_button():
        for other_btn in buttons:
            deactivate(other_btn)
        btn.config(bg='green', activebackground='green', relief=tk.SUNKEN)
    return select_button

def deactivate(btn):
    btn.config(bg='gray', activebackground='gray', relief=tk.RAISED)

list_ = ['A', 'B', 'C']
buttons = []

root = tk.Tk()

for i, name in enumerate(list_, 2):
    btn = tk.Button(root, text=name) 
    btn['command'] = select(btn)
    btn.grid(row=i, column=0, sticky=tk.W)
    buttons.append(btn)

root.mainloop()

The important part here is the fact that we bypass the difficulty of not being able to pass arguments to button['command'] by defining a function which directly integrates those arguments instead.

Upvotes: 0

furas
furas

Reputation: 142631

It was so many times on Stackoverflow so I don't know if I should write it again.

Button has command= to assign function but it can assign function without arguments. If you need with arguments you have to use lambda. I assign function with reference to button so function can use correct button and change it.

lambda in for-loop needs arg=btn and select_button(arg) because direct select_button(btn) will use last button in all functions.


As for changing previous button to original color you can use variable to remember previusly clicked button and then you can easily change it color.

Problem can be to find oryginal color of button so I copy it from new clicked button.

import tkinter as tk

# --- functions ---

def select_button(widget):
    global previously_clicked

    if previously_clicked:
        previously_clicked['bg'] = widget['bg']
        previously_clicked['activebackground'] = widget['activebackground']
        previously_clicked['relief'] = widget['relief']

    widget['bg'] = 'green'
    widget['activebackground'] = 'green'
    widget['relief'] = 'sunken'

    previously_clicked = widget

# --- main ---

names = ['Button A', 'Button B', 'Button C']

root = tk.Tk()

previously_clicked = None

for i, name in enumerate(names, 2):
    btn = tk.Button(root, text=name)

    btn.config(command=lambda arg=btn:select_button(arg))
    #btn['command'] = lambda arg=btn:select_button(arg)

    btn.grid(row=i, column=0, sticky='w')

root.mainloop()

EDIT: you can also use Radiobutton with some options to do the same - and without function:

see: http://effbot.org/tkinterbook/radiobutton.htm

import tkinter as tk

# --- functions ---

def select_button():
    print('value:', v.get())  # selected value

# --- main ---

names = ['Button A', 'Button B', 'Button C']

root = tk.Tk()

v = tk.IntVar()  # variable for selected value

for i, name in enumerate(names, 2):
    btn = tk.Radiobutton(root, text=name, variable=v, value=i)  # assign variable and value

    btn['indicatoron'] = 0  # display button instead of radiobutton
    btn['selectcolor'] = 'green' # color after selection

    btn['command'] = select_button  # function without variables

    btn.grid(row=i, column=0, sticky='w', ipadx=5, ipady=5)

root.mainloop()

Upvotes: 4

Related Questions