Search code examples
pythonpython-3.xkivykivy-language

How to get a kivy variable added to Screen from add widget()


So im trying to get a variable added to screen and the id fro that is in a widget Im going to add, so if for example I have this:

from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty

kv = """
Screen:
    cal:cal
    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
            FloatLayout:
                id:cal
                Label:
                    text:"Side:"
                    pos_hint: {"x":1.1, "y":0.7}
                    text_size: self.size
                Label:
                    text:"Volume:"                
                    pos_hint: {"x":1.1, "y":0.65}
                    text_size: self.size
                Label:
                    text:"Surface Area:"                
                    pos_hint: {"x":1.1, "y":0.6}
                    text_size: self.size
                TextInput:
                    size_hint: (.4, None)
                    height: 26
                    multiline: False
                    pos_hint: {"x":1.24, "y":0.7}
                    id: side
                    text: app.sideText
                    on_text_validate: app.Cube_on_side_change(self)
                Label:                
                    text: app.volume
                    pos_hint: {"x":1.27, "y":0.65}
                    text_size: self.size
                Label:
                    text: app.surface_area
                    pos_hint: {"x":1.355, "y":0.6}
                    text_size: self.size
"""
cube = """
FloatLayout:
    Label:
        text:"Side:"
        pos_hint: {"x":1.1, "y":0.7}
        text_size: self.size
    Label:
        text:"Volume:"                
        pos_hint: {"x":1.1, "y":0.65}
        text_size: self.size
    Label:
        text:"Surface Area:"                
        pos_hint: {"x":1.1, "y":0.6}
        text_size: self.size
    TextInput:
        size_hint: (.4, None)
        height: 26
        multiline: False
        pos_hint: {"x":1.24, "y":0.7}
        id: side
        text: app.sideText
        on_text_validate: app.Cube_on_side_change(self)
    Label:                
        text: app.volume
        pos_hint: {"x":1.27, "y":0.65}
        text_size: self.size
    Label:
        text: app.surface_area
        pos_hint: {"x":1.355, "y":0.6}
        text_size: self.size"""

cuboid = """
FloatLayout:
    id:main
    Label:
        text:"Length:"
        pos_hint: {"x":1.1, "y":0.7}
        text_size: self.size
    Label:
        text:"Breadth:"                
        pos_hint: {"x":1.1, "y":0.65}
        text_size: self.size
    Label:
        text:"Height:"                
        pos_hint: {"x":1.1, "y":0.6}
        text_size: self.size
    TextInput:
        size_hint: (.4, None)
        height: 26
        multiline: False
        pos_hint: {"x":1.24, "y":0.7}
        id: length
        text: app.sideText
        on_text_validate: app.Cuboid_on_side_change(self)
    TextInput:
        size_hint: (.4, None)
        height: 26
        multiline: False
        pos_hint: {"x":1.24, "y":0.65}
        id: breadth
        text: app.sideText
        on_text_validate: app.Cube_on_side_change(self)
    TextInput:
        size_hint: (.4, None)
        height: 26
        multiline: False
        pos_hint: {"x":1.24, "y":0.6}
        id: height
        text: app.sideText
        on_text_validate: app.Cuboid_on_side_change()
    Label:                
        text: "Volume:"
        pos_hint: {"x":1.1, "y":0.55}
        text_size: self.size
    Label:
        text: "Surface Area:"
        pos_hint: {"x":1.1, "y":0.5}
        text_size: self.size
    Label:                
        text: app.volume
        pos_hint: {"x":1.27, "y":0.55}
        text_size: self.size
    Label:
        text: app.surface_area
        pos_hint: {"x":1.355, "y":0.5}
        text_size: self.size"""
cone = """
FloatLayout:
    Label:
        text:"Radius:"
        pos_hint: {"x":1.1, "y":0.7}
        text_size: self.size
    Label:
        text:"Height:"                
        pos_hint: {"x":1.1, "y":0.65}
        text_size: self.size
    TextInput:
        size_hint: (.4, None)
        height: 26
        multiline: False
        pos_hint: {"x":1.24, "y":0.7}
        id: Radius
        text: app.sideText
        on_text_validate: app.Cuboid_on_side_change(self)
    TextInput:
        size_hint: (.4, None)
        height: 26
        multiline: False
        pos_hint: {"x":1.24, "y":0.65}
        id: height
        text: app.sideText
        on_text_validate: app.Cube_on_side_change(self)
    Label:                
        text: "Volume:"
        pos_hint: {"x":1.1, "y":0.6}
        text_size: self.size
    Label:
        text: "Surface Area:"
        pos_hint: {"x":1.1, "y":0.55}
        text_size: self.size
    Label:                
        text: app.volume
        pos_hint: {"x":1.27, "y":0.6}
        text_size: self.size
    Label:
        text: app.surface_area
        pos_hint: {"x":1.355, "y":0.55}
        text_size: self.size
"""
screens = {
    "Cube": cube,
    "Cuboid": cuboid,
    "Cone": cone
}
class MyApp(App):
    sideText = StringProperty("")
    surface_area = StringProperty("0 cm²")
    volume = StringProperty("0 cm³")
    mode = StringProperty("Cube")

    def build(self):
        self.screen = Builder.load_string(kv)

        return self.screen

    def setMode(self, btn):
        self.volume = "0 cm³"
        self.surface_area = "0 cm³"
        self.mode = btn.text
        self.screen.cal.clear_widgets()
        self.screen.cal.add_widget(Builder.load_string(screens[self.mode]))

    def Cube_on_side_change(self, instance):

        try:
            value = float(instance.text)
            num = True
        except ValueError:
            # failed to convert
            num = False
            print("Numbers only idiot.")

        def cubeCalc(val):
            return {
                "volume": val * val * val,
                "surface_area": (val * val) * 6
            }

        if num:
            result = cubeCalc(value)
            self.volume = "{:.2f} cm³".format(result["volume"])
            self.surface_area = "{:.2f} cm²".format(result["surface_area"])

    def Cuboid_on_side_change(self):
        height = self.screen.cal.ids.main.height.text
        try:
            value = float(height)
            num = True
        except ValueError:
            # failed to convert
            num = False
            print("Numbers only idiot.")

        def cubeCalc(val):
            return {
                "volume": val * val * val,
                "surface_area": (val * val) * 6
            }

        if num:
            result = cubeCalc(value)
            self.volume = "{:.2f} cm³".format(result["volume"])
            self.surface_area = "{:.2f} cm²".format(result["surface_area"])


if __name__ == "__main__":
    MyApp().run() 

on_text_validate of the TextInput with id:height in the string cuboid, I want to get the text from the text input with something like: self.screen.main.height.text, however, to do that I would have to add main:main and height:height to Screen. How would I do this?


Solution

  • I suggest refactoring your code. Rather than re-inventing the capabilities of ScreenManager, just use ScreenManager. In your kv string, replace the FloatLayout with:

    ScreenManager:
        CubeScreen:
        CuboidScreen:
        ConeScreen:
    

    and use your additional kv strings (like cube) as the basis for additional rules in your kv string. Something like:

    <CubeScreen>:
        Label:
            text: "The Volume and surface area of a Cube:"
            pos_hint: {"x":0.1, "y":0.8}
            text_size: self.size
            FloatLayout:
                Label:
                    text:"Side:"
                    pos_hint: {"x":0.1, "y":0.7}
                    text_size: self.size
             .
             .
             .
    

    And define each Screen class in your py, like:

    class CubeScreen(Screen):
        def get_cube_area(self):
            .
            .
            .
    

    And in each of the new classes (like CubeScreen) you can define methods for calculating area, volume, etc, and you can access the TextInputs easily using their ids. And your on_text_validate in each Screen can just call the appropriate method from that Screen (like on_text_validate: root.get_cube_area()).