Search code examples
pythonkivykivy-language

How to get, use and change values from a .kv file


So, I've been working on a very simple kivy app and am having trouble with the backend. How would you go about getting the values from text_input and changing label values? I've watched a few tutorials but their methods vary and I get error messages. Here is my python code:

import kivy
from kivy.app import App

kivy.require('1.9.1')


class MyWindowApp(App):
    pass


window = MyWindowApp()
window.run()

And here is the .kv file:

Screen:
    side: side
    FloatLayout:
    Label:
        text: "Side:"
        pos_hint: {"x": 0.1, "y": 0.7}
        text_size: self.size
    Label:
        text: "Volume:"
        pos_hint: {"x": 0.1, "y": 0.65}
        text_size: self.size
    Label:
        text: "Surface Area:"
        pos_hint: {"x": 0.1, "y": 0.6}
        text_size: self.size
    TextInput:
        size_hint: (.4, None)
        height: 26
        multiline: False
        pos_hint: {"x": 0.24, "y": 0.7}
        id: side
    Label:
        text: "0cm"
        id: volume
        pos_hint: {"x": 0.27, "y": 0.65}
        text_size: self.size
    Label:
        text: "0cm"
        id: surface_area
        pos_hint: {"x": 0.355, "y": 0.6}
        text_size: self.size

Solution

  • As mentioned in the other answer, place Builder.load_file or Builder.load_string in your build method.

    To handle changes e.g. if the user hits enter in the side input use on_text_validate: app.on_side_change(self) or place a button to trigger the calculation and use on_press method.

    The on_side_change method in your class MyApp will handle the change. It will be called once the user hits enter. See the example code below, for a basic calculation.

    For getting/setting values from the labels/inputs you can use ObjectProperty or StringProperty from kivy.properties. In the below code I'm using StringProperty. Important you need the StringProperty in your app class and use it in the kv file.

    For the mode of your calculation I've added a property, so you can use it in your calculation method. The mode is also used in the kv file so it's displaying the current mode.

    The switcher dictionary is the Python way to do a switch/case statement.

    Example code

    from kivy.app import App
    from kivy.lang import Builder
    from kivy.properties import StringProperty
    
    kv = """
    Screen:
        side: side
        GridLayout:
            rows: 1
            cols:2
            spacing:0
    
            GridLayout:
                rows: 5
                cols:1
                Button:
                    text: "Cube"
                    # on_press: app.mode = self.text
                    on_press: app.setMode(self)
                Button:
                    text: "Cuboid"
                    on_press: app.setMode(self)
                Button:
                    text: "Cylinder"
                    on_press: app.setMode(self)
                Button:
                    text: "Cone"
                    on_press: app.setMode(self)
                Button:
                    text: "Sphere"
                    on_press: app.setMode(self)
    
            FloatLayout:
                Label:
                    text: "The Volume and surface area of a {}:".format(app.mode)
                    pos_hint: {"x":0.1, "y":0.8}
                    text_size: self.size
                Label:
                    text:"Side:"
                    pos_hint: {"x":0.1, "y":0.7}
                    text_size: self.size
                Label:
                    text:"Volume:"                
                    pos_hint: {"x":0.1, "y":0.65}
                    text_size: self.size
                Label:
                    text:"Surface Area:"                
                    pos_hint: {"x":0.1, "y":0.6}
                    text_size: self.size
                TextInput:
                    size_hint: (.4, None)
                    height: 26
                    multiline: False
                    pos_hint: {"x":0.24, "y":0.7}
                    id: side
                    text: app.sideText
                    on_text_validate: app.on_side_change(self)
                Label:                
                    text: app.volume
                    pos_hint: {"x":0.27, "y":0.65}
                    text_size: self.size
                Label:
                    text: app.area
                    pos_hint: {"x":0.355, "y":0.6}
                    text_size: self.size
    """
    
    class MyApp(App):
        sideText = StringProperty("")
        area = StringProperty("0 cm²")
        volume = StringProperty("0 cm³")
        mode = StringProperty("Cube")
    
        def build(self):
            return Builder.load_string(kv)
    
        def setMode(self, btn):
            self.mode = btn.text
    
        def on_side_change(self, instance):
            print(instance.text)
            result = 0
    
            try:
                value = float(instance.text)
            except:
                # failed to convert 
                return
    
            def cubeCalc(val):
                return {
                    "volume": val * val * val,
                    "area": val * val
                }
    
            switcher = {
                "Cube": cubeCalc
            }
            method = switcher.get(self.mode, "Unknown mode")
            if method is not "Unknown mode":
                result = method(value) # result is a dictionary with volume & area
                #print(result)
                print(self.volume)
                self.volume = "{:.2f} cm³".format(result["volume"])
                self.area = "{:.2f} cm²".format(result["area"])
    
    if __name__ == "__main__":
        MyApp().run()