0

I'm trying to enable/disable Textinput in kivy. multiple TextInput are there. (1) when I will click on a TextInput, that particular TextInput will be editable. (2) By default everything will be set on disable mode. (3) scrollbar should be there, as suppose there are some hundreds of inputs are there.(I was unable to bring that). (4) one more problem I am facing is: the text of TextInput is not properly visible when there are hundreds of inputs. so is there any option to set a default size so that it will not affect whether there is only 2-3 inputs or 100s of inputs. (5) the values at TextInput and label should be dynamic, should be stored at variable globally. @PalimPalim already helped me for the existing code. Thank you everyone.

from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
kivy.uix.scrollview import ScrollView
from kivy.properties import StringProperty

ROWS = ['ac', 'asd', 'kjhgf', 'b' ,'bn', 'sdf', 'ytrwd', 'hfs' ,'erf', ...]

Builder.load_string("""

<Test>:
    do_default_tab: False

    TabbedPanelItem:
        text: 'page1'
        scrollView:
            size_hint: (None, None)
            size: (400, 400)
            Table:
                padding: 50, 50, 50, 50
                orientation: 'vertical'

<Row>:
    spacing: 50
    size_hint: 1, .9
    txt: txtinpt.text
    Label:
        text: root.txt
    TextInput:
        id: txtinpt
        text: root.txt

    Button:
        text: 'save'

""")
class Table(BoxLayout):
    def __init__(self, **kwargs):
        super(Table, self).__init__(**kwargs)
        for row in ROWS:
            self.add_widget(Row(row))



class Row(BoxLayout):
    txt = StringProperty()
    def __init__(self, row, **kwargs):
        super(Row, self).__init__(**kwargs)
        self.txt = row

class ScrollableLabel(ScrollView):
    text = StringProperty('')

class Test(TabbedPanel):
    pass

class MyApp(App):

    def build(self):
        test = Test()
        return test


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

kivy gui

1 Answer 1

1

Ok, first, you want to make the number of rows dynamic, and potentially big, so you don't want to create and manage all the rows yourself, this is very slow both at populate time and when scrolling, you want to use RecyclevView.

RecycleView takes a list of dictionaries, containing the data for your widgets, and a class to display them, will create as much of these as needed to fill the visible part of your scrollview (which it manages), and feed the right data to the right line when you scroll, positionning them to give the illusion of an infinite stream, this allows you to effortlessly manage hundreds of thousands of rows, while your current way usually becomes unusable after a few hundreds of items.

Usage is simple, you already have a list of items, but just with text inside, let's convert that to a list of dicts, and put it in the app for easy reference from kv.

class MyApp(App):
    data = ListProperty()

    def build(self):
        self.data = [{'row_id': i, 'text': x} for i, x in enumerate(ROWS)]
        test = Test()
        return test

now, let's replace your ScrollView with a RecycleView

#:import Factory kivy.factory.Factory

<Test>:
    do_default_tab: False

    TabbedPanelItem:
        text: 'page1'
        RecycleView:
            size_hint: (None, None)
            size: (400, 400)
            data: app.data
            viewclass: Factory.Row
            RecycleBoxLayout:
                padding: 50, 50, 50, 50
                size_hint_y: None
                size: self.minimum_size
                default_size_hint: 1, None
                default_size: 0, dp(36)  # width is overriden by size_hint_x
                orientation: 'vertical'

We size things automatically, here the RecycleView takes all the available space (you can resize the window to give it less/more space), and the RecycleBoxLayout inside is as big as it needs to, computing from the size of its rows, which is defined statically, to avoid the effect described in your (4). So the scrollbar appears when needed, i.e, when the RecycleBoxLayout is bigger than the RecycleView.

Now, fix the Row class to be usable with proper sizing and usage in the recycleview.

<Row>:
    spacing: 50
    text: txtinpt.text
    Label:
        text: root.text

    TextInput:
        id: txtinpt
        text: root.text

    Button:
        text: 'save'
        on_press:
            app.data[root.row_id]['text'] = root.text

and

class Row(BoxLayout):
    text = StringProperty()
    row_id = NumericProperty()

here the idea is that saving will edit the source data in app, using the row id to know where to put it, pretty straightforward.

Now, i didn't really get the bit about disabling textinput, do you mean focused/unfocused? because if you disable textinputs, touching them won't give them focus, and so (1) won't be realised, if you disable textinputs, you need to give the user a way to enable them.

full program

from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ListProperty, NumericProperty
from kivy.lang import Builder
from kivy.uix.scrollview import ScrollView

ROWS = ['ac', 'asd', 'kjhgf', 'b', 'bn', 'sdf', 'ytrwd', 'hfs', 'erf', 'boo']
# now make a lot more of them, to see performances, yep, 100k items...
ROWS = ROWS * 10000

Builder.load_string("""
#:import Factory kivy.factory.Factory
#:import dp kivy.metrics.dp

<Test>:
    do_default_tab: False

    TabbedPanelItem:
        text: 'page1'
        RecycleView:
            # size_hint: (None, None)
            # size: (400, 400)
            data: app.data
            viewclass: Factory.Row
            RecycleBoxLayout:
                padding: 50, 50, 50, 50
                orientation: 'vertical'
                size_hint: 1, None
                size: self.minimum_size
                default_size_hint: 1, None
                default_size: 0, dp(36)

<Row>:
    spacing: 50
    text: txtinpt.text
    Label:
        text: root.text

    TextInput:
        id: txtinpt
        text: root.text

    Button:
        text: 'save'
        on_press:
            app.data[root.row_id]['text'] = root.text

""")


class Row(BoxLayout):
    text = StringProperty()
    row_id = NumericProperty()


class Test(TabbedPanel):
    pass


class MyApp(App):
    data = ListProperty()

    def build(self):
        self.data = [{'row_id': i, 'text': x} for i, x in enumerate(ROWS)]
        test = Test()
        return test


if __name__ == '__main__':
    MyApp().run()
Sign up to request clarification or add additional context in comments.

2 Comments

Removing the update of the label is easy, just remove the "text: txtinpt.text" line in <Row> rule, it'll only update when you save the value now. For the other things, add properties to your Row class, and pass them in the dict using the same name, it'll work automatically.
when I'm adding more TextInput in the Row rule, it's showing "Invalid data after declaration" and when passing the properties in the dict , it's showing : "ValueError: need more than 2 values to unpack" in the enumeration process.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.