Confused over documented kv-lang "root" versus "class" syntax (brackets, no-brackets, missing colon)

1,001 views
Skip to first unread message

BobB

unread,
Apr 5, 2013, 3:22:28 AM4/5/13
to kivy-...@googlegroups.com
Documentation about "rules" vs. "class" descriptions in kv-lang
1. API section 9.13.2:


"# With the braces it’s a rule; Without them it’s a root widget.
<ClassName>:
prop1: value1
prop2: value2
..."

2. Programming Guide 2 Section 8.5.3

"The root rule is declared by declaring the class of your root widget, without any indentation, followed
by : and will be set as the root attribute of the App instance:
Widget:

A class rule, which defines how any instance of that widget class will be graphically represented is
declared by declaring the name of the class, between < >, followed by ::
<MyWidget>:"

So I would expect every runnable .kv file to have exactly one root widget without angle brackets but with a colon as:
MyRootWidget:
    #content


And there would likely be one or more simple class rules:
<MyClassRule>:
    #content...

Confusion #1:
The Pong Game tutorial has a root widget of PongGame()
returned by PongApp().  But the final associated .kv file reads as thus:

17 <PongGame>:
18     ball: pong_ball
19     player1: player_left
20     player2: player_right
...
Hey! The expected root widget here has brackets!

Confusion #2:
In answering user question "interaction between .kv rules and root widget" (10/02/12) responder qua-non advises: "
For a simple example of how to define rules inside  a .kv look at the examples/demo.showcase/showcase.kv."  Unfortunately, not only is main.py quite complex (apparently Showcase is the root widget class) but showcase.kv is too.  It does not show (to me) a root widget Showcase without brackets.  In addition, there are numerous instances of what appear to be class rules -- because are enclosed brackets -- but are lacking the final ":" to make a correct syntax.

I'm sorry for the length of this, but I'm trying to provide evidence for my confusion.  Is there a simple statement that reconciles the apparent conflict?

BobB

Tyler Conrad

unread,
Apr 5, 2013, 10:28:23 AM4/5/13
to kivy-...@googlegroups.com
Kivy will look for a kv file in the same directory as the main.py that matches the name of the App class without the 'App' part and apply it implicitly as a default.  Another option is to use the output of Builder.loadfile() with a kv file containing a root widget definition as the return value of the Apps build() method.

Its odd that the showcase.kv file has several rules that lack the colon character.  My guess is that this was valid syntax for previous versions.

Akshay Arora

unread,
Apr 5, 2013, 11:01:10 AM4/5/13
to kivy-...@googlegroups.com
.kv files "can have" a root widget. It's not necessary as in you can have kv files with only rules in them.
For example there is not root widget defined in the pong.kv.

If thinking of this in terms of classes::

    # If a Root Widget like MySlider is defined then it's reference
    #  to it's instance will be returned so you can use it in your app.
    MySlider:
        # MySlider is the root widget that has two
        # instances of MyButtons added to it.
        MyButton:
             text: ''one"
        MyButton

    <MyButton>:
        # this is "like" a class definition, that defines how
        # the gui of a Widget looks and behaves
        MyLabel:
            id: lbl1
            # This is adding a instance of  MyLabel to the definition of MyButton
            # so every MyButton will have a instance of MyLabel in it.
            # `root` will refer to MyButton in this case
        MyLabel:
            id: lbl2

    <MyLabel>
        # define how every MyLabel instance will look
        text: 'default_value'


Hope this makes it clear.



--
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.
 
 

Thomas Hansen

unread,
Apr 5, 2013, 11:13:21 AM4/5/13
to kivy-...@googlegroups.com
I'm not sure this is the simplest way to explain, but this is what is actually goes on under the hood:

lets look at what the App class (https://github.com/kivy/kivy/blob/master/kivy/app.py) actually does when we call run() on an instance to start the our it

app.py lines 559 - 570:
def run(self):
       
'''Launches the app in standalone mode.
        '''

       
if not self.built:
           
self.load_config()
           
self.load_kv()
            root
= self.build()
           
if root:
               
self.root = root
       
if self.root:
           
from kivy.core.window import Window
           
Window.add_widget(self.root)
       
#[...]


so if build() hasn't been called manually already, the app will load it's config, call load_kv(), and finally call self.build and set the root widget to teh return value of self.build() (but only if it actually returned something!) 

lets check what load_kv and build do by default to figure out whats happening:


load_kv in in app.py on line 342:

def load_kv(self):
       
# [...] removed a bunch of code to get thge right file name

        root
= Builder.load_file(filename)
       
if root:
           
self.root = root
       
return True





On Friday, April 5, 2013 2:22:28 AM UTC-5, BobB wrote:

Thomas Hansen

unread,
Apr 5, 2013, 11:28:04 AM4/5/13
to kivy-...@googlegroups.com
Sorry...hit shift enter to send by accident before being done...lets try again:

I'm not sure this is the simplest way to explain, but this is what is actually goes on under the hood:

lets look at what the App class (https://github.com/kivy/kivy/blob/master/kivy/app.py) actually does when we call run() on an instance to start the our it

app.py lines 559 - 570:
def run(self):
        
'''Launches the app in standalone mode.
        '''

        
if not self.built:
            
self.load_config()
            
self.load_kv()
            root 
= self.build()
            
if root:
                
self.root = root
        
if self.root:
            
from kivy.core.window import Window
            
Window.add_widget(self.root) 
        
#[...]


so if build() hasn't been called manually already, the app will load it's config, call load_kv(), and finally call self.build and set the root widget to teh return value of self.build() (but only if it actually returned something!) 

lets check what load_kv and build do by default to figure out whats happening:


load_kv in in app.py on line 342:

def load_kv(self):
        
# [...] removed a bunch of code to get thge right file name
        # and make sure its actually there etc.
        root 
= Builder.load_file(filename)

        
if root:
            
self.root = root
        
return True

so load_kv calls Builder.load_file() with the apps kv file.  Builder.load_file() parses a text file and registers all the KV rules so they are applied to any widgets you create from then on.  If there is a root widget in the kv file, Builder.laod_file() will actually instantiate and construct that widget and return it as the return value of Builder.load_file.  Notice how you can actually use Builder.load_file to create an instance of a widget at any time (e.g. you can have a seperate kv file for more complicated widgets that have many subwidgets and binding, and then just create an instance by loading that kv file instead of writing a bunch of python code for hooking them all together).

so at this point, if there is a actual widget instance  defined in the kv file, it will be the apps root widget. 

ok, now lets just check App.build() on line 306:
    def build(self):
       
if not self.root:
           
return Widget()

so actually, if is already a root widget from the kv file....this does nothing. However, alot of times, we will use the build method to controll exactly how our app is constructed.  When we want to construct the root widget and do other related setup in python code, we will overwrite the build method and have it return a widget to use as the apps root widget.

In the pong tutorial, there is actually no widget instance in the kv file, but if you look at PongApp class, you can see how it overwrites the build() method to create a root instance (of type PongApp), start the game, schedules the update function on the clock, and then returns the game widget to be the root widget of the App.
class PongApp(App):
    def build(self):
        game = PongGame()
        game.serve_ball()
        Clock.schedule_interval(game.update, 1.0 / 60.0)
        return game

The <PongGame> section in the kv file that you mention, is not a widget instance, but a rule that defines how to construct all instances of PongGame (so you dont have to write all the hookup code in python in the PongGame widget class)


hope that helps :)

--
Thomas



On Friday, April 5, 2013 2:22:28 AM UTC-5, BobB wrote:

BobB

unread,
Apr 5, 2013, 5:51:40 PM4/5/13
to kivy-...@googlegroups.com
Many thanks to Tyler, qua-non and Thomas for taking so much time to helping a newbie remap his brain to the kivy environment.
The responses were indeed a big help in sorting out the relationships between "build", "load" and user-supplied root widget.
BobB

Pierre Villeneuve

unread,
Nov 23, 2013, 1:40:12 PM11/23/13
to kivy-...@googlegroups.com
I too have been quite confused by the differences between root and class / template rules.  The discussion in this thread really did help clarify a few things for me, but only after I read everything twice and went back and looked at an example .kv file.  Here is a quick rundown of what I learned:
  • Root rules
  • Concrete widget instances are defined in a .kv file via a root rule using a name without brackets, e.g. Widget:.  Child widgets are defined underneath with proper indentation.
  • I believe this is analogous in Python to constructing your own widget tree in the App's build() function and returning the base widget to the caller.

  • Class rules
  • A class rule is defined in a .kv file using a name with angle brackets, e.g. <SomeFancyButton>:.  These rules can also have children widgets and properties.
  • A class rule is simply the definition on how to construct such a widget, if you ever wanted to do so.  An instance of a widget of this type maybe be realized within the root rule of a .kv file, or from within Python code in the App's build() function.
I don't know if this relationship was as confusing to others as it was to me.  Anyhow, just writing it out here helped me better understand, and hopefully others may learn too.

On Friday, April 5, 2013 12:22:28 AM UTC-7, BobB wrote:
Reply all
Reply to author
Forward
0 new messages