RecursionError when updating multiple Text Fields in Kivy, Python

79 Views Asked by At

we are coding an app that is a converter. So there are 8 text fields in the app (from .kv file). We use on_text parameter to update the other text fields accordingly, but when we update a certain amount of fields, it runs in a loop and crashes the app with a RecursionError. The problem is that the on_text parameter executes the functions inside of it not only when the text inside the field is changed by the user but also when the text parameter is changed from the code. Is there any way to only run it when it's changed by the user, no matter if it's a keyboard or touchscreen, and only inside the KV file without any additional python code?

Here is the part of the code that gives the problem:

BoxLayout:
                    orientation: "vertical"
                    pos: 0, root.height - 30
                    spacing: "30dp"
                    size_hint: .9, .7
                    pos_hint: {"center_x": .5, "center_y": .5}
                    
                    MDTextField:
                        id:millimeterfield
                        hint_text: "Millimeters"
                        on_text:     
                            centimeterfield.text = str(float(millimeterfield.text) / 10).rstrip('0').rstrip('.') if millimeterfield.text != "" else "0"
                            meterfield.text = str(float(millimeterfield.text) / 1000).rstrip('0').rstrip('.') if millimeterfield.text != "" else "0"
                            kilometerfield.text = str(float(millimeterfield.text) / 1000000).rstrip('0').rstrip('.') if millimeterfield.text != "" else "0"
                            inchfield.text = str(float(millimeterfield.text) / 25.4).rstrip('0').rstrip('.') if millimeterfield.text != "" else "0"
                            feetfield.text = str(float(millimeterfield.text) / 305).rstrip('0').rstrip('.') if millimeterfield.text != "" else "0"
                            yardfield.text = str(float(millimeterfield.text) / 914).rstrip('0').rstrip('.') if millimeterfield.text != "" else "0"
                            milefield.text = "0"
                            
                    
                    MDTextField:
                        hint_text: "Centimeters"
                        id: centimeterfield
                        on_text:
                            millimeterfield.text = str(float(centimeterfield.text) * 10).rstrip('0').rstrip('.') if centimeterfield.text != "" else "0"
                            meterfield.text = str(float(centimeterfield.text) / 100).rstrip('0').rstrip('.') if centimeterfield.text != "" else "0"
                            kilometerfield.text = str(float(centimeterfield.text) / 100000).rstrip('0').rstrip('.') if centimeterfield.text != "" else "0"
                            inchfield.text = str(float(centimeterfield.text) / 2.54).rstrip('0').rstrip('.') if centimeterfield.text != "" else "0"
                            feetfield.text = str(float(centimeterfield.text) / 30.48).rstrip('0').rstrip('.') if centimeterfield.text != "" else "0"
                            yardfield.text = str(float(centimeterfield.text) / 91.44).rstrip('0').rstrip('.') if centimeterfield.text != "" else "0"
                            milefield.text = str(float(centimeterfield.text) / 160934).rstrip('0').rstrip('.') if centimeterfield.text != "" else "0"
                    
                    MDTextField:
                        hint_text: "Meters"
                        id: meterfield
                        on_text:
                            millimeterfield.text = str(float(meterfield.text) * 1000).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                            centimeterfield.text = str(float(meterfield.text) * 100).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                            kilometerfield.text = str(float(meterfield.text) / 1000).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                            inchfield.text = str(float(meterfield.text) * 39.37).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                            feetfield.text = str(float(meterfield.text) * 3.281).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                            yardfield.text = str(float(meterfield.text) * 1.094).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                            milefield.text = str(float(meterfield.text) / 1609).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                        
                    MDTextField:
                        hint_text: "Kilometers"
                        id: kilometerfield
                        on_text:
                            millimeterfield.text = str(float(meterfield.text) * 1000).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                            centimeterfield.text = str(float(meterfield.text) * 100).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                            meterfield.text = str(float(meterfield.text) / 1000).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                            inchfield.text = str(float(meterfield.text) * 39.37).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                            feetfield.text = str(float(meterfield.text) * 3.281).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                            yardfield.text = str(float(meterfield.text) * 1.094).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                            milefield.text = str(float(meterfield.text) / 1609).rstrip('0').rstrip('.') if meterfield.text != "" else "0"
                        
                        
                    MDTextField:
                        hint_text: "Inches"
                        id: inchfield
                        on_text:
                            # ---- HERE WE CAN'T PUT ANY MORE CODE BECAUSE IT CRASHES THE APP IN THE PREVIOUS TEXT FIELD UPDATE ----
                            
                        
                    MDTextField:
                        hint_text: "Feet"
                        id: feetfield
                       
                    MDTextField:
                        hint_text: "Yards"
                        id: yardfield
                        
                    MDTextField:
                        hint_text: "Miles"
                        id: milefield

Thank you!

1

There are 1 best solutions below

0
On

To be able to react to text changes by the user, not by code, the easiest solution is to subclass the TextInput (or in your case MDTextInput) and override insert_text.

class CustomTextInput(MDTextInput):
    def insert_text(self, substring, from_undo=False):
         # do whatever you want to do here
         super().insert_text(substring, from_undo)
         # or there if you want to react after the normal change

programmatic changes to text won't trigger this function.

As soon as this class has been loaded beforehand, you can use it in kvlang instead of MDTextInput, if you want to define it in another module without needing to import it, you can register it using Factory

from kivy.factory import Factory

Factory.register('CusomTextInput', mod='python.path.to.module')