Search code examples
pythonkivy

Keyboard input event bugged with TextInput?


Basically I've been working on a Kivy application with multiple screens, and I'm trying to make it so that it will take you back to the main screen if the escape button is pressed. Things have been going well, as I followed this question's answer, except the fact that when I focus on a TextInput widget and unfocus, the escape key shortcut, or more specifically the on_key_down event isn't called anymore.

Anyone knows what's wrong? Any helps would be appreciated!

Edit: Here's my code, shortened and simplified:

test.py

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
from kivy.config import Config


Config.set('kivy', 'exit_on_escape', 0)

class MainScreen(Screen):
    def __init__(self, **kwargs):
        super(Screen, self).__init__(**kwargs)
        self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
        self._keyboard.bind(on_key_down=self._on_keyboard_down)

    def _keyboard_closed(self):
        self._keyboard.unbind(on_key_down=self._on_keyboard_down)
        self._keyboard = None

    def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
        print(keycode[1])
        if keycode[1] == 'escape':
            if App.get_running_app().root.current == 'Main':
                App.get_running_app().stop()
                Window.close()
            else:
                App.get_running_app().root.current = 'Main'

class UhIDontHaveIdeaForANameBecauseItsAnExampleScreen(Screen):
    pass
     
class Test(App):    
    pass

Test().run()

test.kv

ScreenManager:
    MainScreen:
        id: Main
    UhIDontHaveIdeaForANameBecauseItsAnExampleScreen:
        id: Settings
    
<MainScreen>:
    name: 'Main'
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'idk just press it bruh'
        Button:
            id: butt
            text: 'no clue'
            on_release: app.root.current = 'NoClue'

<UhIDontHaveIdeaForANameBecauseItsAnExampleScreen>:
    name: 'NoClue'
    
    TextInput:
        size_hint: 0.4, 0.1
        pos_hint: {'center_x': 0.5, 'center_y': 0.5}

Solution

  • You bind the main screen to listening to the keyboard when you create it but when you focus the Textinput kivy automatically bind the keyboard to the textinput so the _on_keyboard_down method in the MainScreen there are many solution to do this but we need to bind the main screen to the keyboard again so the _on_keyboard_down start to listening to the keyboard again and we can do this inside the textField with _on_textinput_focused method like bellow

    from kivy.app import App
    from kivy.uix.screenmanager import ScreenManager, Screen
    from kivy.core.window import Window
    from kivy.config import Config
    from kivy.uix.textinput import TextInput
    
    Config.set('kivy', 'exit_on_escape', 0)
    
    
    class MainScreen(Screen):
        def __init__(self, **kwargs):
            super(Screen, self).__init__(**kwargs)
            self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
            self._keyboard.bind(on_key_down=self._on_keyboard_down)
    
        def _keyboard_closed(self):
            self._keyboard.unbind(on_key_down=self._on_keyboard_down)
            self._keyboard = None
    
        def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
            print(keycode[1])
            if keycode[1] == 'escape':
                if App.get_running_app().root.current == 'Main':
                    App.get_running_app().stop()
                    Window.close()
                else:
                    App.get_running_app().root.current = 'Main'
    
    class UhIDontHaveIdeaForANameBecauseItsAnExampleScreen(Screen):
        pass
    
    
    class TextField(TextInput):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
    
        def _on_textinput_focused(self, instance, value, *largs):
            print(instance.focus)
            # test if the text input is focused 
            if not instance.focus:
                app=App.get_running_app()
                app.root.ids.Main._keyboard = Window.request_keyboard(app.root.ids.Main._keyboard_closed, self)
                app.root.ids.Main._keyboard.bind(on_key_down=app.root.ids.Main._on_keyboard_down)
    
    class Test(App):
        pass
    
    
    Test().run()
    
    
    

    and use the TextField in the kv file like this

    
    ScreenManager:
        MainScreen:
            id: Main
        UhIDontHaveIdeaForANameBecauseItsAnExampleScreen:
            id: Settings
        
    <MainScreen>:
        name: 'Main'
        BoxLayout:
            orientation: 'vertical'
            Label:
                text: 'idk just press it bruh'
            Button:
                id: butt
                text: 'no clue'
                on_release: app.root.current = 'NoClue'
    <UhIDontHaveIdeaForANameBecauseItsAnExampleScreen>:
        name: 'NoClue'
    
        TextField:
            size_hint: 0.4, 0.1
            pos_hint: {'center_x': 0.5, 'center_y': 0.5}
    <TextField>: