Thursday, July 3, 2014

43. ListView

ListView allows us, to view lists of data. As in most cases, here, the individual list items are selectable.




The root widget is based on BoxLayout. We use ListProperty, to hold the ListView data, to be displayed. Whenever, a particular list item is selected, its text will be placed in the StringProperty.




In the root, the list to be displayed by ListView, is in the variable, my_data. Initially, it is empty and thus the ListView, would be empty at startup. The StringProperty called selected_value, holds the individual list item button's text. The change() function will be called, from the kv file, whenever a particular list item is selected.




The app class has to return the root widget. It should be noted that, inside the kv file, root always refers to top of current tree, and not necessarily this class, unless rules are being defined, for the root widget.


# ex43.py

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ListProperty, StringProperty

class Ex43(BoxLayout):
    my_data = ListProperty([])
    selected_value = StringProperty('Select a button')
    def change(self,change):
        self.selected_value = 'Selected: {}'.format(change.text)

class Ex43App(App):
    def build(self):
        return Ex43()

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



In the kv file, we need two imports. The first is for the ListAdapter, which is needed to connect to the Python List. The second import is required since we want each of the list items to be selectable, and based on the Button. A rule is defined for ListItemButton to go to the change() function when a press is detected.




The root is based on BoxLayout. It will have a 'vertical' orientation. It will have three rows, the first is BoxLayout, and is shown here. The inner BoxLayout defines three items which will be arranged 'horizontally', the default orientation. The first item is a Label. Next, we have a TextInput with an id of add_box. The button is the last item and will add one string to the list, my_data.




ListView takes 80% of the height. We define the adapter as pointing to my_data. The canvas.before is used to make this item an orange color.




The bluish label indicates the selected value.


# ex43.kv

#: import ListAdapter kivy.adapters.listadapter.ListAdapter
#: import ListItemButton kivy.uix.listview.ListItemButton

<ListItemButton>:
    on_press: app.root.change(*args)
    
<Ex43>:
    orientation: 'vertical'
    BoxLayout:
        size_hint_y: .1
        Label:
            text: 'Enter String'
        TextInput:
            id: add_box
            multiline: False
            size_hint_x: 5
        Button:
            text: 'Add'
            on_press: root.my_data.append(add_box.text)
    ListView:
        size_hint_y: .8
        adapter:
            ListAdapter(data=root.my_data,
            selection_mode='single',
            allow_empty_selection=False,
            cls=ListItemButton)
        canvas.before:
            Color:
                rgb: 1,.5,0
            Rectangle:
                pos: self.pos
                size: self.size
    Label:
        size_hint_y: .1
        canvas.before:
            Color:
                rgb: 0,.5,1
            Rectangle:
                pos: self.pos
                size: self.size
        text: root.selected_value
        
            



In this result, we entered 7 lines. Then the last line was selected, and the label in the bottom shows the status. In this example, the items were added by typing. In a real application, they could be data, from a database, or a website.




5 comments:

  1. :
    on_press: app.root.change(*args)

    I found this section of code particularly useful for my code!

    ReplyDelete
  2. Hello! I have two listview A and B, I want to select Item in B when I select Item in A. ¿Any idea? I try with
    def selectItem(self,change):
    self.index_current = change.index
    if not self.my_lv_width.adapter.selection:
    self.my_lv_width.adapter.selection.append(self.my_lv_width.adapter.data[change.index])
    self.my_lv_width._trigger_reset_populate()
    But, doesn't work
    thank you!

    ReplyDelete
  3. Hi. Thank you for the tutorial. I am trying to get your code to work opening and saving a text file. I can get it to open a text file (into a list) and it show in the ListView but i cannot get it to save the list back into the text file. I keep getting the following error:
    line 37, in
    on_press: root.my_data.saveList()
    AttributeError: 'ObservableList' object has no attribute 'saveList'

    Here is the 43.py code.........

    # ex43.py

    from kivy.app import App
    from kivy.uix.boxlayout import BoxLayout
    from kivy.properties import ListProperty, StringProperty

    class Ex43(BoxLayout):
    with open("sh_list.txt") as f:
    my_list = f.read().splitlines()
    print(my_list)
    my_data = ListProperty(my_list)
    f.close()
    selected_valuetop = StringProperty('')
    selected_valuebottom = StringProperty('Select a button')

    def add_item(self):
    pass

    def delete_item(self):
    pass

    def sort_items(self):
    pass

    def clear_input(self, change):
    pass

    def change(self,change):
    self.selected_valuebottom = change.text
    self.selected_valuetop = change.text

    def saveList(self, my_list):
    f = open("sh_list.txt", "w")
    for item in my_list:
    f.write("%s\n" % item)
    print(my_list)
    change='saved'
    self.selected_valuebottom = change


    class Ex43App(App):
    def build(self):
    return Ex43()

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

    AND HERE IS THE KV code....

    # ex43.kv

    #: import ListAdapter kivy.adapters.listadapter.ListAdapter
    #: import ListItemButton kivy.uix.listview.ListItemButton

    :
    size: (100, '48dp')
    on_press: app.root.change(*args)

    :
    orientation: 'vertical'
    BoxLayout:
    size_hint_y: .07
    Label:
    text: "Pepe's Shopping List"
    BoxLayout:
    size_hint_y: .06
    Label:
    text: "Item"
    TextInput:
    id: add_box
    multiline: False
    size_hint_x: 2
    text: root.selected_valuetop
    focus: True
    Button:
    text: 'Add'
    on_press: root.my_data.append(add_box.text); add_box.text=""; add_box.focus=True
    Button:
    text: 'Delete'
    on_press: root.my_data.remove(add_box.text); add_box.text=""; add_box.focus=True
    Button:
    text: 'Sort'
    on_press: root.my_data.sort()
    Button:
    text: 'SAVE'
    on_press: root.my_data.saveList()
    ListView:
    size_hint_y: .8
    adapter:
    ListAdapter(data=root.my_data,
    selection_mode='single',
    allow_empty_selection=False,
    cls=ListItemButton)
    canvas.before:
    Color:
    rgb: 1,.5,0
    Rectangle:
    pos: self.pos
    size: self.size
    Label:
    size_hint_y: .05
    canvas.before:
    Color:
    rgb: 0,.5,1
    Rectangle:
    pos: self.pos
    size: self.size
    text: root.selected_valuebottom

    I cant seem to find a solution and is hoping you can help me.

    Many thanks.

    ReplyDelete
  4. Python:

    Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging.

    https://www.emexotechnologies.com/online-courses/python-training-in-electronic-city/

    ReplyDelete
  5. Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging.

    Python Training in electronic city

    ReplyDelete