Reputation: 99
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:
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
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