1

I am new to kivy and need help with the ListView and ScreenManager. I managed to use the ListView on a single screen, but I am trying to use it with two screens via ScreenManager: I have a ListView in MainScreen and made a button to navigate to ProfileScreen, where I will enter values in the TextInput "abc" and "defe" and want them to be submitted to the ListView. When I run the .py file with this code it works, but when I enter the values on ProfileScreen and press "Okay", it crashes and says "ProfileScreen" has no attribute "student_list". If I change the parameter of ProfileScreen(Screen) to ProfileScreen(MainScreen) it works, but the content of the page ProfileScreen is inherited from MainScreen, which I don't want.

How can I solve this issue? I would appreciate any kind of help, thanks in advance.

This is my code:

studentdb.py, studentdb.kv

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.uix.listview import ListItemButton
from kivy.uix.screenmanager import ScreenManager, Screen

class StudentListButton(ListItemButton):
    pass

class Manager(ScreenManager):
    main_screen = ObjectProperty()
    profile_screen = ObjectProperty()

class MainScreen(Screen):
    first_name_text_input= ObjectProperty()
    last_name_text_input= ObjectProperty()
    student_list = ObjectProperty()

    def submit_student(self):
        # Get the student name from the TextInputs
        student_name = self.first_name_text_input.text + " " + self.last_name_text_input.text

        # Add the student to the ListView
        self.student_list.adapter.data.extend([student_name])

        # Reset the ListView
        self.student_list._trigger_reset_populate()

    def delete_student(self, *args):
        if self.student_list.adapter.selection:

            # Get the text from the item selected
            selection = self.student_list.adapter.selection[0].text

            # Remove the matching item
            self.student_list.adapter.data.remove(selection)

            # Reset the ListView
            self.student_list._trigger_reset_populate()

    def replace_student(self, *args):
        # If a list item is selected
        if self.student_list.adapter.selection:

            # Get the text from the item selected
            selection = self.student_list.adapter.selection[0].text

            # Remove the matching item
            self.student_list.adapter.data.remove(selection)

            # Get the student name from the TextInputs
            student_name = self.first_name_text_input.text + " " + self.last_name_text_input.text

            # Add the updated data to the list
            self.student_list.adapter.data.extend([student_name])

            # Reset the ListView
            self.student_list._trigger_reset_populate()

class ProfileScreen(Screen):
    abc_text_input=ObjectProperty()
    def_text_input=ObjectProperty()

    def okay(self):
        abc_name = self.abc_text_input.text + " " + self.def_text_input.text

        # Add the student to the ListView
        self.student_list.adapter.data.extend([abc_name])

        # Reset the ListView
        self.student_list._trigger_reset_populate()

class StudentDBApp(App):
    def build(self):
        return Manager()

if __name__=="__main__":
    StudentDBApp().run()
------------------------------------------------------------------
#: import main studentdb
#: import ListAdapter kivy.adapters.listadapter.ListAdapter
#: import ListItemButton kivy.uix.listview.ListItemButton

<MainScreen>:
    first_name_text_input: first_name
    last_name_text_input: last_name
    student_list: student_list
    BoxLayout:
        orientation: "vertical"
        BoxLayout:
            Label:
                text:"First Name:"
            TextInput:
                id: first_name
            Label:
                text:"Last Name:"
            TextInput:
                id: last_name
        BoxLayout:
            Button:
                text:"Submit"
                on_release: root.submit_student()
            Button:
                text:"Delete"
                on_release: root.delete_student()
            Button:
                text:"Replace"
                on_release: root.replace_student()
            Button:
                text: "New"
                on_release: root.manager.current="profile_screen"
        ListView:
            id: student_list
            adapter:
                ListAdapter(data=['Doug Smith'], cls=main.StudentListButton)

<ProfileScreen>:
    abc_text_input: abc
    def_text_input: defe
    BoxLayout:
        orientation: "vertical"
        BoxLayout:
            Button:
                text:"Back"
                on_release: root.manager.current="main_screen"
        BoxLayout:
            Label:
                text:"abc"
            TextInput:
                id: abc
            Label:
                text: "def"
            TextInput:
                id: defe
        BoxLayout:
            Button:
                text: "Okay"
                on_release: root.okay()

<Manager>:
    id: screen_manager
    main_screen: main_screen
    profile_screen: profile_screen

    MainScreen:
        id: main_screen
        name:"main_screen"
        manager: screen_manager

    ProfileScreen:
        id: profile_screen
        name: "profile_screen"
        manager: screen_manager
eyllanesc
  • 235,170
  • 19
  • 170
  • 241

2 Answers2

1

The error in your case is that student_list is an attribute of MainScreen and not ProfileScreen so you can not access it by self, I recommend reading the following: What is the purpose of self?

In your case, the solution is to access the correct Screen through the ScreenManager:

def okay(self):
    abc_name = self.abc_text_input.text + " " + self.def_text_input.text
    main_screen = self.manager.get_screen('main_screen')

    # Add the student to the ListView
    main_screen.student_list.adapter.data.extend([abc_name])

    # Reset the ListView
    main_screen.student_list._trigger_reset_populate()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thank you so much, I'm new to Python and Kivy in general so I am having a really hard time with small details like this. I understand it better now, cheers :) – Kadir Karadavut May 17 '18 at 12:58
0

Since you already have the screens hooked up to ObjectProperty in the ScreenManager, the solution is

self.manager.main_screen...

Note

You might want to replace ListView with RecycleView because ListView has been deprecated.

List View

Deprecated since version 1.10.0: ListView has been deprecated, use RecycleView instead.

Snippets

def okay(self):
    abc_name = self.abc_text_input.text + " " + self.def_text_input.text

    # Add the student to the ListView
    self.manager.main_screen.student_list.adapter.data.extend([abc_name])

    # Reset the ListView
    self.manager.main_screen.student_list._trigger_reset_populate()

Output

Img01

ikolim
  • 15,721
  • 2
  • 19
  • 29
  • Thanks for your reply. Seems like there are always multiple ways to solve a problem in coding :) Well I thought about using RecycleView, though there are not enough guides and tutorials about it and the examples on kivy's website are just not enough for me to figure it out since I'm new to kivy, so till then I would rather try to stick to the current stuff until I have more knowledge . I can't even use ListView properly yet, I am trying to add multiple columns to the ListView to seperate the values, but I have no idea how to do it to be honest. – Kadir Karadavut May 17 '18 at 18:44