I am trying to build an application that loads data from a JSON file and adds it to an MDList object. I would like the loaded items to take the user to a specific page when clicked. My implementation of the __init__
finction is shown bellow:
def __init__(self, **kw):
super().__init__(**kw)
json_data_object = JsonData("data.json")
# adds list item from JSON file to MDlist object
for i in json_data_object.data["lists"]:
loaded_object = ListItemWithoutCheckbox(text="[b]" + i["list_name"] + "[/b]")
self.ids["Container"].add_widget(loaded_object)
self.manager.add_widget(ToDoListPage(name=str(loaded_object.text)))
loaded_object.bind(on_release= lambda x : self.change_screen(loaded_object.text))
The first half of the for loop works as intended, adding the loaded objects to the MDList
object. However the second half returns an AttributeError
:
AttributeError: 'NoneType' object has no attribute 'add_widget'
I have a theory that this is due to the __init__
function running before the screen is added to the ScreenManager()
object in the MainApp()
class, shown below, but have no concrete proof of this nor any ideas for how to get around the issues.
class MainApp(MDApp):
def build(self):
# Setting theme to my favorite theme
self.theme_cls.theme_style = "Dark"
Builder.load_file("styling.kv")
sm = ScreenManager()
sm.add_widget(ToDoListView(name="ListView"))
return sm
I will keep trying to work on the issue but I am struggling to come up with new ideas, so any help is greatly appreciated.
If I manage to crack this I will post my solution to this issue!
Thanks for any help :)
EDIT:
I have added a method to attempt to add the on_release functionality after the __init__
method has run. On printing self.parent
I still receive a None
value. I have also attempted to use the Clock
module of kivy as suggested in the comments by John Anderson but I am still receiving the same AttributeError
shown earlier in this question. My edited code now looks like this:
Class initialisation:
def __init__(self, **kw):
super().__init__(**kw)
json_data_object = JsonData("data.json")
self.loaded_items = []
# adds list item from JSON file to MDlist object
for i in json_data_object.data["lists"]:
loaded_object = ListItemWithoutCheckbox(text="[b]" + i["list_name"] + "[/b]")
self.ids["Container"].add_widget(loaded_object)
self.loaded_items.append(loaded_object)
def load_tasks(self, loaded_objects):
for i in loaded_objects:
# To test if i can access the screen manager
print(self.parent)
self.manager.add_widget(ToDoListPage(name=str(i.text)))
object.bind(on_release= lambda x : self.change_screen(i.text))
Main app class:
class MainApp(MDApp):
def build(self):
# Setting theme to my favorite theme
self.theme_cls.theme_style = "Dark"
Builder.load_file("styling.kv")
list_view_screen = ToDoListView(name = "ListView")
list_view_screen.load_tasks(list_view_screen.loaded_items)
sm = ScreenManager()
sm.add_widget(list_view_screen)
return sm
Thank you so much for even checking out this question!
any help is greatly appreciated :)
happy to say i finally found a solution to the issue. By abstracting the functionality of the __init__
method into another method and making a partial call in kivy's Clock
object, the function could utilise the screen manager object.
def __init__(self, sm,**kw):
super().__init__(**kw)
Clock.schedule_once(partial(self.load_tasks, sm))
def load_tasks(self, sm, *largs):
json_data_object = JsonData("data.json")
self.loaded_items = []
for i in json_data_object.data["lists"]:
self.add_loaded_item_to_list(i)
for i in self.loaded_items:
self.bind_on_release_to_loaded_item(sm, i)
def bind_on_release_to_loaded_item(self, sm, loaded_item):
self.manager.add_widget(ToDoListPage(name = loaded_item.text))
loaded_item.bind(on_release= lambda x: self.change_screen(loaded_item.text))
def add_loaded_item_to_list(self, loaded_item):
loaded_object = ListItemWithoutCheckbox(text="[b]" + loaded_item["list_name"] + "[/b]")
self.ids["Container"].add_widget(loaded_object)
self.loaded_items.append(loaded_object)
The MainApp
class reverted to its original:
class MainApp(MDApp):
def build(self):
# Setting theme to my favorite theme
self.theme_cls.theme_style = "Dark"
Builder.load_file("styling.kv")
sm = ScreenManager()
sm.add_widget(ToDoListView(sm,name = "ListView"))
return sm
Thanks again to John Anderson for helping me get to this solution :)