Reputation: 432
I have been trying to get my labels to auto-update themselves for quite a while now and I have read over a dozen StackOverflow questions but to no avail.
I have an global object that contains an integer value that I want to be displayed with a label inside of one of my widget classes.
The widget class looks like this:
class Battle(Widget):
def __init__(self, **kwargs):
super(Battle, self).__init__(**kwargs)
#Enemy Stats
self.enemyBar = BoxLayout(orientation="horizontal", size=(Window.width, Window.height/8), center_y = Window.height - self.height/2)
self.enemyBar.add_widget(Label(text=enemy.name))
#Enemy Health Label
health_label = Label(text=str(enemy.health))
self.enemyBar.add_widget(health_label)
self.add_widget(self.enemyBar)
def update_health(instance, value):
health_label.text = str(enemy.health) #<-- Error happens here
enemy.bind(health=update_health)
When the value of enemy.health
is changed in the program, I want my label to change as well. I do not want to use any of the kivy language because I prefer having just one main python file.
The enemy object is created with an entity class. Here is the entity code:
class entity(Widget):
#entity creation
health = NumericProperty()
def __init__(self, health):
self.health = health
When I run the code I press a button that calls a function that changes the enemy health and then I get an error:
global name 'health_label' is not defined
Somehow, when the update_health function is called, the program doesn't see the health_label variable that was created in the init.
Upvotes: 0
Views: 7900
Reputation: 59
To add to what inclement said, you can add various types of bindings to the elements you make. Just keep in mind that there's a different way to do it for the elements generating an action, such as a button. Here is an example of what I used to create a button to search for nearby Bluetooth devices (but the only thing you have to think about are the methods I use) :
size = dp(50)
b = Button(text="Start Research", size_hint=(1, None), size=(1,size))
self.bind(searching_devices=b.setter("disabled"))
b.bind(on_press=self.start_searching)
self.add_widget(b)
Here searching_devices is a BooleanProperty and start_searching() is a function. As you can see, the disabled property, which 'gives' an information to the button is binded with a setter, just as the text element inclement talked about. For an action which is generated by using the component like the on_press property, you bind it directly without the setter.
Upvotes: 0
Reputation: 29460
You need to use the bind method, something like the following
health_label = Label(text=enemy.health)
self.enemyBar.add_widget(health_label)
def update_health(instance, value):
health_label.text = str(enemy.health)
enemy.bind(health=update_health)
This is just a basic example off the top of my head, it can be made neater depending on the structure of your program. If enemy.health is a string, you can just do enemy.bind(health=health_label.setter('text'))
.
For this to work, the health attribute must be a kivy property:
class Enemy(Something):
health = StringProperty('')
In your code it seems that enemy is a global object. There is quite likely a better way to do that. Also, I recommend using kv language - there is not really any special value in putting your code in one file (indeed, it's commonly considered bad practice once it gets non-trivial), and kv makes a lot of things easier where python just isn't suited to certain tasks.
Upvotes: 5