I want to multiply 2 numbers, performing it exclusively in the .kv file.
So I created this minimalistic main.py
file:
from kivy.app import App
class Test(App):
pass
Test().run()
and this minimalistic test.kv
file:
My 1st approach:
BoxLayout:
TextInput:
id: num_1
text: "8"
TextInput:
id: num_2
text: "4"
Label:
text: f"Product: {int(num_1.text) * int(num_2.text)}"
After launching the application, I obtained the error
>> 9: text: f"Procuct: {int(num_1.text) * int(num_2.text)}" ... ValueError: invalid literal for int() with base 10: ''
My 2nd approach – I changed only the order of widgets, putting the Label before TextInputs:
BoxLayout:
Label:
text: f"Procuct: {int(num_1.text) * int(num_2.text)}"
TextInput:
id: num_1
text: "8"
TextInput:
id: num_2
text: "4"
and I obtained the expected result
(which correctly reflects changes in TextInputs).
So the question is: Why my first approach didn't work?
Note:
I'm not interested in a better solution or some workaround, I want only understand, why my first test.kv
file don't work.
EDIT:
If in my 1st, not working approach, I change the text of Label to num_1.text + num_2.text
(concatenation of strings), it works!
The issue is the order that kivy.builder
does things. It creates widgets from top to bottom in your kv
file, but it assigns attributes of those widgets in the reverse order (I have no idea why). Here is a slight modification of your code that shows the order of things:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
kv = '''
BoxLayout:
MyTextInput:
id: num_1
text: "8"
MyTextInput:
id: num_2
text: "4"
MyLabel:
text: "Product: " + str(int(num_1.text) * int(num_2.text)) if num_1.text != '' and num_2.text != '' else 'unknown'
'''
class MyTextInput(TextInput):
def __init__(self, **kwargs):
super(MyTextInput, self).__init__(**kwargs)
print('Called TextInput init, text =', self.text)
def on_text(self, instance, new_text):
print('on_text:')
print('\ttext set to', new_text)
class MyLabel(Label):
def __init__(self, **kwargs):
super(MyLabel, self).__init__(**kwargs)
print('Called Label init, text = ', self.text)
def on_text(self, instance, new_text):
print('on_text:')
print('\ttext set to', new_text)
class Test(App):
def build(self):
return Builder.load_string(kv)
Test().run()