Whistleroosh
Whistleroosh

Reputation: 99

Kivy - AttributeError: 'NoneType' object has no attribute 'add_widget'

I've been trying to write a simple python app using Kivy but, because I specialize more in backend than frontend, I struggle a lot. In advance I want to warn that I will paste a lot of code in this post, but please do not feel discouraged because it's the most basic kivy code possible. My project has the following structure:

structure

main.py looks like this and starts the whole app:

import source.app.application

def main():
    source.app.application.Main().run()

if __name__ == '__main__':
    main()

application.py is responsible for general look of the app:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import ObjectProperty

from .main_screen.main_screen import MainScreen
from .search_screen.search_screen import SearchScreen
from .view_screen.view_screen import ViewScreen

Builder.load_file('source/app/application.kv')

class MultiScreen(ScreenManager):
    pass

class HomeButton(BoxLayout):
    pass

class SearchButton(BoxLayout):
    pass

class ViewButton(BoxLayout):
    pass

class Area(BoxLayout):
    multi_screen = ObjectProperty(None)

    def __init__(self, *args, **kargs):
        super().__init__(*args, **kargs)

    def change_to_home(self):
        self.multi_screen.transition.direction = 'right'
        self.multi_screen.current = 'main'

    def change_to_search(self):
        if self.multi_screen.current == 'main':
            self.multi_screen.transition.direction = 'left'

        else: self.multi_screen.transition.direction = 'right'

        self.multi_screen.current = 'search'

    def change_to_view(self):
        self.multi_screen.transition.direction = 'left'
        self.multi_screen.current = 'view'

class Main(App):
    def build(self):
        return Area()

And here is application.kv:

#:kivy 2.0.0

<Area>:
    orientation: 'vertical'
    multi_screen: multi_screen
    MultiScreen:
        id: multi_screen
        size_hint: 1, 895
        MainScreen:
            name: 'main'
        SearchScreen:
            name: 'search'
        ViewScreen:
            name: 'view'

    Widget:
        size_hint: 1, 5
        canvas:
            Color:
                rgba: 0, 0, 1, 1
            Rectangle:
                pos: self.pos
                size: self.size

    BoxLayout:
        size_hint: 1, 100
        spacing: 0
        HomeButton:
            Button:
                text: 'Home'
                on_release: root.change_to_home()
        SearchButton:
            Button: 
                text: 'Search'
                on_release: root.change_to_search()
        ViewButton:
            Button: 
                text: 'View'
                on_release: root.change_to_view()

Now the problem is with main_screen.py. It implements class MainScreen which is meant to be used as a screen in screen manager from application.py. It looks like this:

from kivy.uix.screenmanager import Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.clock import Clock

from source.database.database_communicator import DatabaseCommunicator
from source.database.database_entry import Entry

Builder.load_file('source/app/main_screen/main_screen.kv')

class MainScreen(Screen):
    scroll_box_layout = ObjectProperty(None)

    def __init__(self, *args, **kargs):

        super().__init__(*args, **kargs)
        #self._communicator = DatabaseCommunicator('database')
        #self.discovered, self.undiscovered = self._communicator.get_list_of_words()
        self.total_viewed = 0
        self._build_box_layout()

    def _choose_entry(self):
        x = Entry('hello', 10)
        return x

    def _build_box_layout(self):
        entry = self._choose_entry()

        for i in range(100):
            self.scroll_box_layout.add_widget(Button(text=str(i), size_hint_y=None, height=40))

_choose_entry and _build_box_layout will have different body in the future, but for now they just fill scrollview with buttons. And at last here is main_screen.kv:

#:kivy 2.0.0

<MainScreen>:
    scroll_box_layout: scroll_box_layout
    ScrollView:
        BoxLayout:
            id: scroll_box_layout
            orientation: 'vertical'

When I run main.py I have the following error:

AttributeError: 'NoneType' object has no attribute 'add_widget'

I checked for similar problems in stackoverflow and tried solutions like:

None of it worked. It must be something with when ObjectProperty links object from .py file to object from .kv file. Unfortunately, I have too little experience with kivy to find the cause of this problem. I would be delighted if you could help me.

EDIT. Here is full traceback

 Traceback (most recent call last):
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/main.py", line 7, in <module>
     main()
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/main.py", line 4, in main
     source.app.application.Main().run()
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/vocab/lib/python3.9/site-packages/kivy/app.py", line 949, in run
     self._run_prepare()
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/vocab/lib/python3.9/site-packages/kivy/app.py", line 919, in _run_prepare
     root = self.build()
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/source/app/application.py", line 50, in build
     return Area()
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/source/app/application.py", line 30, in __init__
     super().__init__(*args, **kargs)
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/vocab/lib/python3.9/site-packages/kivy/uix/boxlayout.py", line 145, in __init__
     super(BoxLayout, self).__init__(**kwargs)
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/vocab/lib/python3.9/site-packages/kivy/uix/layout.py", line 76, in __init__
     super(Layout, self).__init__(**kwargs)
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/vocab/lib/python3.9/site-packages/kivy/uix/widget.py", line 359, in __init__
     self.apply_class_lang_rules(
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/vocab/lib/python3.9/site-packages/kivy/uix/widget.py", line 463, in apply_class_lang_rules
     Builder.apply(
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/vocab/lib/python3.9/site-packages/kivy/lang/builder.py", line 541, in apply
     self._apply_rule(
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/vocab/lib/python3.9/site-packages/kivy/lang/builder.py", line 663, in _apply_rule
     self._apply_rule(
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/vocab/lib/python3.9/site-packages/kivy/lang/builder.py", line 659, in _apply_rule
     child = cls(__no_builder=True)
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/source/app/main_screen/main_screen.py", line 21, in __init__
     self._build_box_layout()
   File "/home/whistleroosh/Desktop/minimalistic_english_vocabulary_app/source/app/main_screen/main_screen.py", line 31, in _build_box_layout
     self.scroll_box_layout.add_widget(Button(text=str(i), size_hint_y=None, height=40))
 AttributeError: 'NoneType' object has no attribute 'add_widget'

Upvotes: 1

Views: 1150

Answers (1)

John Anderson
John Anderson

Reputation: 38822

I think you are just calling self._build_box_layout() too soon (before the scroll_box_layout property is set). Try replacing:

self._build_box_layout()

with:

Clock.schedule_once(lambda dt: self._build_box_layout())

Upvotes: 1

Related Questions