Elliott Johnson
Elliott Johnson

Reputation: 59

tkinter button with changing txt when clicked

So I know what the problem is, I just don't know how to fix it: self.health gets stored in the variable once and doesn't re-read after that. I've tried using: @property. But I only just learnt about @property yesterday, so either 1: I'm not using it properly, or 2: it can't be used in this situation.

import tkinter as tk


class button_health:
    def __init__(self):
        self.health = 5
    def hit(self, event):
        self.health -= 1
bob = button_health()

window = tk.Tk()
button = tk.Button(window, text = bob.health #I want this to update)
button.bind("<Button-1>", bob.hit)
button.pack()

window.mainloop()

What I'm aiming for is for the code to produce a simple tkinter button on screen which starts off saying "5", then when you click it, says "4", then click "3" etc.

Upvotes: 0

Views: 2196

Answers (3)

mhawke
mhawke

Reputation: 87074

Use a Tkinter IntVar variable to track changes to the health value. Hook that variable up to the button label using the textvariable attribute.

import tkinter as tk

class button_health:
    def __init__(self, health=5):
        self.health = tk.IntVar()
        self.health.set(health)

    def hit(self, event=None):
        if self.health.get() >= 1:    # assuming that you can't have negative health
            self.health.set(self.health.get() - 1)

window = tk.Tk()
bob = button_health(8)
button = tk.Button(window, textvariable=bob.health, command=bob.hit)
#button = tk.Button(window, textvariable=bob.health)
#button.bind('<Button-1>', bob.hit)
button.pack()

window.mainloop()

Another way is to create your own button class as a subclass of Button and hook up an IntVar as a member of the class. This way you can easily create multiple independent buttons with different health values:

import tkinter as tk

class HealthButton(tk.Button):
    def __init__(self, window, health=5, *args, **kwargs):
        self.health = tk.IntVar()
        self.health.set(health)
        super(HealthButton, self).__init__(window, *args, textvariable=self.health, command=self.hit, **kwargs)

    def hit(self):
        if self.health.get() >= 1:
            self.health.set(self.health.get() - 1)

window = tk.Tk()
buttons = [HealthButton(window, i) for i in range(10,15)]
for b in buttons:
    b.pack()

window.mainloop()

Upvotes: 1

Taku
Taku

Reputation: 33714

There is an argument called command in tk.Button. Binding the button-1 to a function isn't the best way to check if the user clicked the button. And even if you used bind, it should be binded to a function, not a class. To change a button's text, you can set button['text'] to something.

import tkinter as tk


def button_health():
    global health
    health -= 1
    button['text'] = str(health)

health = 5
window = tk.Tk()
button = tk.Button(window, text = health , command = button_health)
button.pack()

window.mainloop()

You can also avoid using a global statement by doing this:

import tkinter as tk

def button_health(but):
    but.health -= 1
    but['text'] = str(but.health)

window = tk.Tk()
button = tk.Button(window)
button.health = 5
button['text'] = str(button.health)
button['command'] = lambda: button_health(button)
button.pack()

window.mainloop()

another advantage for doing it this way is that it can keep the health of the button independent, so if you have multiple buttons, this will keep the counters for all the buttons different.

Upvotes: 1

eyllanesc
eyllanesc

Reputation: 243947

Use button['text'] or button.config(text={text})

class button_health:
    def __init__(self):
        self.health = 5
    def hit(self, event):
        self.health -= 1
        button['text'] = str(self.health)

or

class button_health:
    def __init__(self):
        self.health = 5
    def hit(self, event):
        self.health -= 1
        button.config(text= str(self.health))

Upvotes: 1

Related Questions