Addison
Addison

Reputation: 423

How to dynamically change label text in Kivy

So I am making a calculator/unit conversion app, and want a label to display the calculation dynamically as the text input value changes. For instance, lets say that the calculation part of this is a simply multiply by 3. So, The user inputs, say, 5 into the text input. Then, they click the Calculate button, which will multiply it by 3. Then, I want a label to display this calculation. Currently, I can't figure out how to get that to happen! Heres my code:

import kivy
from kivy.app import App

from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.floatlayout import  FloatLayout

FLOAT_LAYOUT = FloatLayout(size=(300, 300))

title_label = Label(text='0',
                  font_size=20,
                  pos_hint={'x': .4, 'y': .8},
                  size_hint=(.2, .2))

text_box = TextInput(multiline=False,
                     font_size=20,
                     pos_hint={'x': .4, 'y': .3},
                     size_hint=(.2, .2))

calculate_button = Button(text='Calculate',
                          font_size=20,
                          pos_hint={'x': .4, 'y': .1},
                          size_hint=(.2, .1))

class calculator_app(App):

    def build(self):
        FLOAT_LAYOUT.add_widget(title_label)
        FLOAT_LAYOUT.add_widget(text_box)
        FLOAT_LAYOUT.add_widget(calculate_button)
        return FLOAT_LAYOUT

    def calculate(self):
        title_label.text = str(float(text_box.text)*3)


calculator_object = calculator_app()
calculator_object.run()

calculate_button.bind(on_press=calculator_object.calculate())

Clearly I am doing something wrong. Is .bind(on_press=...) not the right way to go about this? Thanks in advance!

EDIT: Here is the error message:

 AssertionError: None is not callable

This pertains the the line: calculate_button.bind(on_press=calculator_object.calculate())

Upvotes: 0

Views: 2151

Answers (2)

ikolim
ikolim

Reputation: 16031

  1. Move button binding statement to before invoking either run() method or return FLOAT_LAYOUT
  2. Add an argument called instance / obj into calculate() method

Event dispatcher » bind()

In general, property callbacks are called with 2 arguments (the object and the property’s new value) and event callbacks with one argument (the object).

Snippet - Option 1: Button Bind before run() method

class calculator_app(App):

    def build(self):
        FLOAT_LAYOUT.add_widget(title_label)
        FLOAT_LAYOUT.add_widget(text_box)
        FLOAT_LAYOUT.add_widget(calculate_button)
        return FLOAT_LAYOUT

    def calculate(self, instance):
        if len(text_box.text) > 0:
            title_label.text = str(float(text_box.text)*3)


calculator_object = calculator_app()
calculate_button.bind(on_press=calculator_object.calculate)
calculator_object.run()

Snippet - Option 2: Button Bind inside build() method

class calculator_app(App):

    def build(self):
        FLOAT_LAYOUT.add_widget(title_label)
        FLOAT_LAYOUT.add_widget(text_box)
        FLOAT_LAYOUT.add_widget(calculate_button)
        calculate_button.bind(on_press=self.calculate)
        return FLOAT_LAYOUT

    def calculate(self, instance):
        if len(text_box.text) > 0:
            title_label.text = str(float(text_box.text)*3)


calculator_object = calculator_app()
calculator_object.run()

Upvotes: 1

eyllanesc
eyllanesc

Reputation: 243955

You have the following errors:

  • What is placed after run() will only run when the window is closed, and the goal of the GUI is to run everything while the GUI is open.

  • On the other hand, the bind method requires that you give the name of the callback, not the callback evaluated as you are doing.

  • bind passes parameters that must be received by the callback, such as the instance that generates the call, in this case the button, so you should place those values ​​as arguments.

  • The TextInput can receive values ​​that are not numbers, for example in the case that the TextInput is empty, that could cause problems so a possible solution is to use the exceptions.

Solution:

import kivy
from kivy.app import App

from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.floatlayout import  FloatLayout

FLOAT_LAYOUT = FloatLayout(size=(300, 300))

title_label = Label(text='0',
                  font_size=20,
                  pos_hint={'x': .4, 'y': .8},
                  size_hint=(.2, .2))

text_box = TextInput(multiline=False,
                     font_size=20,
                     pos_hint={'x': .4, 'y': .3},
                     size_hint=(.2, .2)
                     )

calculate_button = Button(text='Calculate',
                          font_size=20,
                          pos_hint={'x': .4, 'y': .1},
                          size_hint=(.2, .1))

class calculator_app(App):
    def build(self):
        FLOAT_LAYOUT.add_widget(title_label)
        FLOAT_LAYOUT.add_widget(text_box)
        FLOAT_LAYOUT.add_widget(calculate_button)
        return FLOAT_LAYOUT

    def calculate(self, *args):
        print(args)
        try:
            title_label.text = str(float(text_box.text)*3)
        except ValueError:
            print("Not a float")



calculator_object = calculator_app()
calculate_button.bind(on_press=calculator_object.calculate)
calculator_object.run()

Upvotes: 1

Related Questions