Abyssal Platypus
Abyssal Platypus

Reputation: 31

How can I call a Kivy TextInput into my Python Function?

I am trying to call text input from the Kivy TextInput widget into a Python function that will do heavy lifting for math and have it output on a Label widget next to the TextInput widget.

I'm very new to Kivy but my professor mentioned it and have been messing around with what it can do. Currently I'm having an issue saying that the specific variable that I'm trying to call doesn't exist. I have tried running a function in the Kivy code that would turn the input into an int for the Python function to call but it got messy real fast. Before I break what I have any further I'm asking advice. I'm not going to include the full code since its very long but these are the snippets that are being used.

import abilities
import races
import os
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button

class AbilityDisplay(GridLayout):

    def __init__(self, **kwargs):
        super(AbilityDisplay, self).__init__(**kwargs)
        self.cols = 3
        if os.path.isfile("character1.txt"):
            with open("character1.txt", "r") as f:
                d = f.read().split(",")
                character_name = d[0]
                char_str = d[1]

        else:
            character_name = ''
            char_str = ''

        self.add_widget(Label(text = 'Name'))
        self.name = TextInput(text = character_name, multiline = False)
        self.add_widget(self.name)
        self.add_widget(Label())
        self.add_widget(Label(text = 'Strength'))
        self.strength = TextInput(text = char_str, multiline = False) 
        self.add_widget(self.strength)
        self.add_widget(Label(text = str(Player.p_strength())))

        self.save = Button(text = 'Save')
        self.save.bind(on_press = self.save_state)
        self.add_widget(Label())
        self.add_widget(self.save)

    def save_state(self, instance):
        character_name = self.name.text
        char_str = self.strength.text

        print(f"Saving {character_name}.")

        with open("character1.txt", "w") as f:
            f.write(f"{character_name},{char_str}")


class TheApp(App):

    def build(self):
        return AbilityDisplay()


class Player():

    def p_strength():
        strength_input = int(AbilityDisplay.strength())
        racial_bonus = 0
        s_total = strength_input + racial_bonus
        s_modifier = abilities.strength(s_total)
        return s_modifier


if __name__ == '__main__':
    TheApp().run()

There is a bit more code that involves other "abilities" and the code for the math is in the abilities module that I built. The code above is intended to take the player input from the widget run it through the Player class which will do call the abilities module for the math. Then the last label widget will display the output number. If I input say 18 for the math the label should show 4. The problem is that I am getting an error code on starting the program saying that AbilityDisplay has no attribute. I'm having a hard time making this work without breaking the code entirely.

For those who play D&D this should look familiar.

Edit: I realized a few lines were missing.

Upvotes: 0

Views: 578

Answers (1)

ikolim
ikolim

Reputation: 16041

Root Cause - NoneType Error

During instantiation of Label widget, it tried to populate Label's text by invoking p_strength() method, but Kivy has not completed its building process. Therefore, the attributes have value of None.

Solution - bind strength / TextInput

The solution is to make the following enhancements.

class AbilityDisplay(GridLayout)

  • Use on_text_validate (when player press 'enter' key) event to bind strength / TextInput to invoke a new method, on_enter()
  • Implement a new method, on_enter() to invoke p_strength() method
  • Instantiate Label widget and assign it to self.result
  • Replace Label(text = str(Player.p_strength())) with self.result

method p_strength()

  • Add parameters, self and strength to p_strength() method
  • Replace AbilityDisplay.strength() with strength

Snippets

class AbilityDisplay(GridLayout):

    def __init__(self, **kwargs):
        super(AbilityDisplay, self).__init__(**kwargs)
        self.cols = 3
        ...
        self.add_widget(Label(text='Strength'))
        self.strength = TextInput(text=char_str, multiline=False)
        self.strength.bind(on_text_validate=self.on_enter)
        self.add_widget(self.strength)

        self.result = Label()
        self.add_widget(self.result)
        ...

    def on_enter(self, instance):
        # update result's text
        self.result.text = str(Player().p_strength(instance.text))
...
class Player():

    def p_strength(self, strength):
        strength_input = int(strength)
        racial_bonus = 0
        s_total = strength_input + racial_bonus
        s_modifier = abilities.strength(s_total)
        return s_modifier

Upvotes: 1

Related Questions