Why am i getting a "KeyError"?

2,041 views
Skip to first unread message

Noob Saibot

unread,
Jun 6, 2013, 12:01:53 AM6/6/13
to kivy-...@googlegroups.com
I'm just trying to create a simple screen that contains a Navigation bar at the top, and a scrolling list at the bottom. When i run the bottom code, i get a "KeyError". Could someone please help me identify where the gap in my understanding is?

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import ObjectProperty

Builder.load_string("""
#:import ScrollView kivy.uix.scrollview
#:import SlideTransition kivy.uix.screenmanager.SlideTransition

<NavBar>:
    BoxLayout:
        orientation: 'horizontal'
        
        Button:
            font_size: 42
            size_hint: .5, 1
            text: 'Edit'

        Button:
            font_size: 42
            size_hint: .5, 1
            text: 'Settings'

<ScrollButton>
    size_hint: None, None
    size: 640, 88
        
<Scroller>:
    ScrollView:
        size_hint: None, None

        GridLayout:
            cols: 1
            height: self.minimum_height
            do_scroll_x: False 
            id: container

<ListsScreen>:
    nb: navbar
    sc: scroller
    canvas:
        Color:
            rgb: .50, .50, .30
        Rectangle:
            size: self.size
            pos: self.pos
            
    BoxLayout:
        height: self.parent.height
        pos_hint: {'top': (1-(20/480))}
        orientation: 'vertical'
        
        NavBar:
            id: navbar
            pos_hint: {'top': 1}
            size_hint: 1, (88/1136)
        
        Scroller:
            id: scroller
            pos: {'top': (1-(128/1136))}
""")

class NavBar(Widget):
    pass

class ScrollButton(Button):
    pass

class Scroller(Widget):
    container = ObjectProperty(None)

class ListsScreen(Screen):
    nb = ObjectProperty(None)
    sc = ObjectProperty(None)

class TestApp(App):
    
    def build(self):
        ''''''
        root = super(TestApp, self).build()
        container = root.ids['container']()
        for i in xrange(30):
            container.add_widget(ScrollButton(text=str(i)))
            
        out = ScreenManager()
        out.add_widget(ListsScreen(name='Insert Name Here'))
        return out

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

Traceback (most recent call last):
   File "C:\Users\Admin\Workspace\Test\src\main.py", line 92, in <module>
     TestApp().run()
   File "C:\Python27\lib\site-packages\kivy\app.py", line 577, in run
     root = self.build()
   File "C:\Users\Admin\Workspace\Test\src\main.py", line 83, in build
     container = root.ids['container']()
 KeyError: 'container'

Thanks in advance. 

Gabriel Pettier

unread,
Jun 6, 2013, 3:57:00 AM6/6/13
to kivy-...@googlegroups.com
Hi

- i see no root widget tree defined in your kv, only rules, so loading
it won't return a root widget.
- the container id is in Scroller, so it's on scroller instances only
that you can get it (ids are local to the rule)

I see this is a modification of the example i put on SO yesterday,
please ask for precisions if you don't fully understand how it worked.

Regards
> --
> You received this message because you are subscribed to the Google Groups "Kivy users support" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

Noob Saibot

unread,
Jun 6, 2013, 8:49:33 PM6/6/13
to kivy-...@googlegroups.com
1. S.O. is very particular about the type of questions you can ask. I don't think Why-won't-this-work questions are appreciated there, so i try to rephrase them as best i can.

2. I recently came across this which made things A LOT easier to understand. My bad...

3. I revised the code and now i'm getting this "Unknown class" error that i don't understand. I think everything is in its place. What am i missing?

Builder.load_string("""
#:import ScrollView kivy.uix.scrollview
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
#:import ScreenManager kivy.uix.screenmanager
#:import Screen kivy.uix.screenmanager

ScreenManager:
    Screen:
        name: 'Lists Screen'
        navbar: navbar
        canvas:
            Color:
                rgb: .50, .50, .30
            Rectangle:
                size: self.size
                pos: self.pos

        BoxLayout:
            pos_hint: {'top': (1-(20/480))}
            orientation: 'vertical'
        
            NavBar:
                id: navbar
                pos_hint: {'top': 1}
                size_hint: 1, (88/1136)
            
            ScrollView:
                size_hint: self.width, None
                pos_hint: {'center_x': .5, 'center_y': .5}
    
                GridLayout:
                    cols: 1
                    height: self.minimum_height
                    do_scroll_x: False
                    id: container

<NavBar>:
    BoxLayout:
        orientation: 'horizontal'
        
        Button:
            font_size: 12
            size_hint: 1, .5
            text: 'Edit'

        Button:
            font_size: 12
            size_hint: 1, .5
            text: 'Settings'

<ScrollButton>
    size_hint: self.width, .5
            
""")

class NavBar(Widget):
    pass

class ScrollButton(Button):
    pass

class TestApp(App):
    navbar = ObjectProperty(None)
    
    def build(self):
        ''''''
        root = super(TestApp, self).build()
        container = root.ids.container
        for in range(30):
            container.add_widget(ScrollButton(text=str(i)))
        return root

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

 Traceback (most recent call last):
   File "C:\Users\Admin\Workspace\Test\src\main.py", line 61, in <module>
     """)
   File "C:\Python27\lib\site-packages\kivy\lang.py", line 1372, in load_string
     self._apply_rule(widget, parser.root, parser.root)
   File "C:\Python27\lib\site-packages\kivy\lang.py", line 1517, in _apply_rule
     self._apply_rule(child, crule, rootrule)
   File "C:\Python27\lib\site-packages\kivy\lang.py", line 1517, in _apply_rule
     self._apply_rule(child, crule, rootrule)
   File "C:\Python27\lib\site-packages\kivy\lang.py", line 1479, in _apply_rule
     cls = Factory_get(cname)
   File "C:\Python27\lib\site-packages\kivy\factory.py", line 92, in __getattr__
     raise FactoryException('Unknown class <%s>' % name)
 kivy.factory.FactoryException: Unknown class <NavBar>

Noob Saibot

unread,
Jun 6, 2013, 9:33:02 PM6/6/13
to kivy-...@googlegroups.com
I guess it's no longer a "Key Error" problem. Should i make a new post?

Noob Saibot

unread,
Jun 6, 2013, 9:50:04 PM6/6/13
to kivy-...@googlegroups.com
And finally, i am running v1.7.1. I shouldn't need to add Factory, right?

Akshay Arora

unread,
Jun 7, 2013, 10:31:30 AM6/7/13
to kivy-...@googlegroups.com
Move the class definition for NavBar and ScrollButton before Builder.load_string(.... and see if that helps.

p.s.
  The guide2 has been merged back into the current programing guide and is more complete.

Regards


On Fri, Jun 7, 2013 at 7:20 AM, Noob Saibot <degu...@purdue.edu> wrote:
And finally, i am running v1.7.1. I shouldn't need to add Factory, right?

--

Noob Saibot

unread,
Jun 7, 2013, 11:01:10 AM6/7/13
to kivy-...@googlegroups.com
BEFORE the Builder.load string? Isn't Builder a means to have the KV file and your python code in one file?

Did you mean before the ScreenManager declaration? Because I did that and it still didn't work...

Akshay Arora

unread,
Jun 7, 2013, 1:15:20 PM6/7/13
to kivy-...@googlegroups.com
You are doing a few things the wrong way `root = super(TestApp, self).build()` without really understanding
the concepts , you should go through the programming guide step by step to get more familiar with the basic
concepts of kivy.


On Fri, Jun 7, 2013 at 8:31 PM, Noob Saibot <degu...@purdue.edu> wrote:
BEFORE the Builder.load string? Isn't Builder a means to have the KV file and your python code in one file?

Did you mean before the ScreenManager declaration? Because I did that and it still didn't work...

Akshay Arora

unread,
Jun 7, 2013, 1:22:39 PM6/7/13
to kivy-...@googlegroups.com
As for why you need to put the class definition before the Builder.load_string(...), you need to
do so as; you access these classes inside your Builder.load_string(...).

You simply can not access a class before declaring it. If you don't in-line the kv lang, then the
kv file with the app class name is auto loaded after the build(...) function of app class is called.
Which us usually the last declaration in a file.

Regards

Akshay Arora

unread,
Jun 7, 2013, 1:33:36 PM6/7/13
to kivy-...@googlegroups.com
Further looking into your code, it's evident that you don't understand size_hint and pos_hint as they are supposed to be used in Kivy.
Please go through the Programming guide and the tutorials. If things still don't  make much sense please go through the examples
 present in kivy_install_dir/examples. Take Special Note of KivyCatalog, make use of it to experiment with size_hint and pos_hint
as outlined in the programming guide.

Regards

Noob Saibot

unread,
Jun 8, 2013, 10:32:13 PM6/8/13
to kivy-...@googlegroups.com
I want you to know that i appreciate you guys helping me with this. I'm new to programming in general and tend to rely heavily on examples (this one in particular). I just hope i don't become a source of frustration for you guys. :-) Your tip to move my `Builder.load_string` function to the bottom of my code fixed that "Unknown class" error, thank you. In the end, thanks to the newly discovered programming guide, i decided to do it without defining a root widget in the kv file and the code does compile.

That being said, you were right about one thing: I really don't understand Kivy's pos/size system...for the life of me, i just can't seem to get it right and i don't know why. Many of your examples deal with layouts of buttons (boxlayout_poshint, for example) which seems pretty straightforward; but when you're trying to layout widgets or other layouts, it all goes to hell.

I'm trying to make a simple screen with a navigation bar at 7.75% the position of the `Screen` from the top and a `ScrollViewer` of multiple buttons right below it (with the possiblity of another navigation bar right below the `ScrollViewer`). After literally hours at this, this is the below is the best i could do.

Is it possible for someone to please just show me how to do this? I know it would be technically "cheating", but it would take seconds for you to do and seeing the "right" way to do it would go A LONG way in reconciling my understanding?

Thanks in advance.

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.scrollview import ScrollView
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.properties import ObjectProperty

class NavBar(GridLayout):
    pass

class ScrollButton(Button):
    pass

class Scroller(ScrollView):
    container = ObjectProperty(None)
    
    def listmaker(self):
        self.container.bind(minimum_height=self.container.setter('height'))
        for i in range(20):
            btn = ScrollButton(text=str(i))
            self.container.add_widget(btn)
        
class ListsScreen(Screen):
    scroller = ObjectProperty(None)

class TestApp(App):
    def build(self):
        ''''''
        ls = ListsScreen()
        ls.scroller.listmaker()
        root = ScreenManager()
        root.add_widget(ls)
        return root

Builder.load_string("""
             
<Scroller>:
    container: container
    GridLayout:
        cols: 1
        do_scroll_x: False
        size_hint: 1, None
        height: self.minimum_height
        id: container

<NavBar>:
    rows: 1
    Button:
        font_size: 12
        text: 'Edit'

    Button:
        font_size: 12
        text: 'Settings'

<ScrollButton>
    size_hint: 1, None

<ListsScreen>:
    name: 'Lists Screen'
    scroller: scroller
    StackLayout:
        orientation: 'tb-rl'
        minimum_height: 1136

        NavBar:
            size_hint: 1,0.078
            
        Scroller:
            id: scroller
            size_hint: 1, None
            pos_hint: {'y': .75}    
            
""")

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

'

Noob Saibot

unread,
Jun 8, 2013, 10:34:07 PM6/8/13
to kivy-...@googlegroups.com
One more question: I noticed that the buttons in my ScrollViewer are not as responsive as the NavBar (i.e., you need to double/tripple click them to activate them). Could that also be a cause of my size/pos issue?


On Wednesday, June 5, 2013 11:01:53 PM UTC-5, Noob Saibot wrote:

Noob Saibot

unread,
Jun 9, 2013, 10:51:19 PM6/9/13
to kivy-...@googlegroups.com
I figured it out. I just wrapped each widget in its own RelativeLayout. Thanks.


On Wednesday, June 5, 2013 11:01:53 PM UTC-5, Noob Saibot wrote:
Reply all
Reply to author
Forward
0 new messages