Search code examples
python-2.7kivykivy-language

Python/Kivy : Focus one TextInput to another TextInput using enter key


  1. How to move focus from name TextInput to first column (id:test1) of dynamic add row using press enter key?
  2. When press enter in second column (id:test2) of dynamic row then new row add . How to focus first column of every row when add new row dynamic?

test.py

from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty

Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (500, 300)

class User(Screen):

    def add_more(self):
        self.ids.rows.add_row()


class Row(BoxLayout):
    button_text = StringProperty("")


class Rows(BoxLayout):
    row_count = 0

    def __init__(self, **kwargs):
        super(Rows, self).__init__(**kwargs)
        self.add_row()

    def add_row(self):
        self.row_count += 1
        self.add_widget(Row(button_text=str(self.row_count)))


class Test(App):

    def build(self):
        return self.root


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

test.kv

<Button@Button>:
    font_size: 15
    font_name: 'Verdana'


<TextInput@TextInput>:
    font_size: 15
    font_name: 'Verdana'
    padding_y: 3


<Row>:
    size_hint_y: None
    height: self.minimum_height
    height: 40

    Button:
        text: root.button_text
        size_hint_x: None
        top: 200

    TextInput:
        id:test1
        text: ' '
        width: 300
        multiline: False
        on_text_validate: test2.focus = True

    TextInput:
        id:test2
        text: ' '
        width: 300
        multiline: False
        on_text_validate: app.root.add_more()

<Rows>:
    size_hint_y: None
    height: self.minimum_height
    orientation: "vertical"

User:
    BoxLayout:
        orientation: "vertical"
        GridLayout:
            cols: 2
            padding: 20, 20
            spacing: 10, 10

            Label:
                text: "Name"
                text_size: self.size
                valign: 'middle'
            TextInput:
                id:name
                multiline: False
                text_size: self.size

        ScrollView:
            Rows:
                id: rows

Solution

  • I have added the following:

    1. Clock.schedule_once to ensure that the ids are defined and set focus on name (TextInput).
    2. ObjectProperty because

    it is generally regarded as ‘best practice’ to use the ObjectProperty. This creates a direct reference, provides faster access and is more explicit.

    Programming Guide » Kv language » Referencing Widgets

    Example

    main.py

    from kivy.uix.screenmanager import Screen
    from kivy.app import App
    from kivy.core.window import Window
    from kivy.uix.boxlayout import BoxLayout
    from kivy.properties import StringProperty, ObjectProperty
    from kivy.clock import Clock
    
    Window.clearcolor = (0.5, 0.5, 0.5, 1)
    Window.size = (500, 300)
    
    
    class User(Screen):
        name = ObjectProperty(None)
        rows = ObjectProperty(None)
    
        def __init__(self, **kwargs):
            super(User, self).__init__(**kwargs)
            Clock.schedule_once(self.set_name_focus, 1)
    
        def set_name_focus(self, *args):
            self.name.focus = True
    
        def on_enter_text_input(self):
            self.rows.row.test1.focus = True
    
        def add_more(self):
            self.rows.add_row()
    
    
    class Row(BoxLayout):
        button_text = StringProperty("")
    
    
    class Rows(BoxLayout):
        row_count = 0
        row = ObjectProperty(None)
    
        def __init__(self, **kwargs):
            super(Rows, self).__init__(**kwargs)
            self.add_row()
    
        def add_row(self):
            self.row_count += 1
            self.row = Row(button_text=str(self.row_count))
            self.add_widget(self.row)
    
    
    class Test(App):
    
        def build(self):
            return self.root
    
    
    if __name__ == '__main__':
        Test().run()
    

    test.kv

    #:kivy 1.10.0
    
    <Button@Button>:
        font_size: 15
        font_name: 'Verdana'
    
    
    <TextInput@TextInput>:
        font_size: 15
        font_name: 'Verdana'
        padding_y: 3
    
    
    <Row>:
        test1: test1
        size_hint_y: None
        height: self.minimum_height
        height: 40
    
        Button:
            text: root.button_text
            size_hint_x: None
            top: 200
    
        TextInput:
            id:test1
            focus: True
            text: ' '
            width: 300
            multiline: False
            on_text_validate: test2.focus = True
    
        TextInput:
            id:test2
            text: ' '
            width: 300
            multiline: False
            on_text_validate: app.root.add_more()
    
    <Rows>:
        size_hint_y: None
        height: self.minimum_height
        orientation: "vertical"
    
    User:
        name: name
        rows: rows
        BoxLayout:
            orientation: "vertical"
            GridLayout:
                cols: 2
                padding: 20, 20
                spacing: 10, 10
    
                Label:
                    text: "Name"
                    text_size: self.size
                    valign: 'middle'
                TextInput:
                    id:name
                    multiline: False
                    text_size: self.size
                    on_text_validate: root.on_enter_text_input()
    
            ScrollView:
                Rows:
                    id: rows
    

    Output

    Img01 - App Startup Img02 - test1-TextInput @ Row 1 Img03 - test2-TextInput @ Row 1 Img04 - test1-TextInput @ Row 2