pythonkivytextinput

Kivy Problem With Text Input on Touchdown Event


When calling function by touchdown event, kivy is iterating all the Text Input widgets of the layout.

I have this code here:

from kivy.core.window import Window
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput

sm = ScreenManager()

class MenuScreen(Screen):  
    def __init__(self, screen_name):  
       super(MenuScreen, self).__init__()  
       self.name = screen_name  
       self.selected_entry = self.last_selected_entry = None  
       self.layout = FloatLayout(size_hint_x=None, size_hint_y=None, size=Window.size)  
       self.add_widget(self.layout)  
       for col in range(4):  
           entry = TextInput(text=f'Entry {col + 1}', size_hint=(None, None), size=(0.22 * Window.width, 0.105 * Window.height), font_size=25, halign='center', unfocus_on_touch=False, keyboard_mode='managed')  
           entry.pos_hint_x = entry.pos_hint_y = None
           entry.center_x = 0.14 * Window.width + col * 0.24 * Window.width  
           entry.center_y = 0.5 * self.layout.height
           entry.bind(on_touch_down=self.identify_selected_entry)
           if col == 0:
               self.selected_entry, self.last_selected_entry, entry.focus = entry, entry, True
           self.layout.add_widget(entry)  

    def identify_selected_entry(self, instance, event=None):
        print(f'Text of clicked entry: {instance.text}')
        if self.last_selected_entry:
            self.last_selected_entry.focus = False
        self.selected_entry = self.last_selected_entry = instance

class MainScreen(App):
    global sm

    def build(self):
        sm.add_widget(MenuScreen('Menu'))
        return sm

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

This is what happens when I click on the fist entry or in a random place of the layout:
Text of clicked entry: Entry 4
Text of clicked entry: Entry 3
Text of clicked entry: Entry 2
Text of clicked entry: Entry 1

And this happens when I click on the last entry:
Text of clicked entry: Entry 4

So kivy is not calling the function only for the entry selected, but instead is iterating all the entries of the layout, from last to fist. I don't know if this is the normal behavior of kivy, but I would like to call the function only for the entry I selected.


Solution

  • The on_touch_down events (as well as others) are distributed to all the widgets. So any widget that wants to determine if the event was touched on itself, must test the position of the touch to determine if the touch was within itself. You can do that in your code by adding a call to collide_point() to your identify_selected_entry():

    def identify_selected_entry(self, instance, event=None):
        if event and instance.collide_point(*event.pos):
            print(f'Text of clicked entry: {instance.text}')
        else:
            return False
        if self.last_selected_entry:
            self.last_selected_entry.focus = False
        self.selected_entry = self.last_selected_entry = instance
        return True