how to add sub widget using kv?

3,361 views
Skip to first unread message

Michal

unread,
Dec 11, 2011, 7:47:52 PM12/11/11
to Kivy users support
Hello, Kivy is iteresting framework.
I am now solving issue how to change content of part of my root.
I have 2 classes
-------------------------------------------------------------------
class myBasicInfo(FloatLayout):
my_basic_info = ObjectProperty(None)
info_wid = Label() #object for login popup
info_label = Label(text='HHHH')

class myBase(FloatLayout):
basic_layout_wid = ObjectProperty(None) #object of layout
basic_layout_main_place_wid = ObjectProperty(None) #object of main
def do_action(self):
wid = myBasicInfo
self.basic_layout_main_place_wid.add_widget(wid.info_wid)

class myApp(App):
def build(self):
return myBase(info='Hello world')

if __name__ in ('__android__', '__main__'):
myApp().run()
--------------------------------------------------------------------------

and of course kv
--------------------------------------------------------------------------
#:kivy 1.0

<myBasicInfo>:
id: my_basic_info
info_wid: my_info_main

Label:
id: my_info_main
text: 'Not Connected'
height: 20

<myBase>:
id: my_widget
basic_layout_wid: my_basic_layout
basic_layout_main_place_wid: my_main_place

StackLayout:
id: my_basic_layout
anchor_x: 'left'
anchor_y: 'top'
Button:
size: (50, 20)
text: 'Home'
on_press: root.do_action();
StackLayout:
id: my_main_place

------------------------------------------------
What I am trying to do is, when I push a button HOME I want to put
content of myBasicInfo into my_main_place
I can do it by using property of class myBasicInfo.info_label:
def do_action(self):
wid = myBasicInfo
self.basic_layout_main_place_wid.add_widget(wid.info_label)

-----, but I want to use kv definition..
Any help???

Michal

unread,
Dec 14, 2011, 5:10:51 PM12/14/11
to Kivy users support
With greate help from tshirtman (gabriel) it is solved..

to do it from KV 1. register widget by factory
------------main.kv
#:kivy 1.0
#:import SomeWidget main.SomeWidget

# rules for the widget
<SomeWidget>:
BoxLayout:
pos: root.pos
size: root.size
orientation: "vertical"
Label:
text: "hello"
Button:
text: "world"

# root
BoxLayout:
id: layout
Button:
text: "add"
on_press: layout.add_widget(SomeWidget())

------------main.py
#!/usr/bin/env python
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.factory import Factory


class SomeWidget(Widget):
pass

Factory.register('SomeWidget', SomeWidget)

class Main(App):
pass

Main().run()

Michal

unread,
Dec 14, 2011, 5:11:53 PM12/14/11
to Kivy users support
to do add it from py code... just one more linking

------main.py
#!/usr/bin/env python
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.factory import Factory

from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout

class SomeWidget(Widget):
pass

Factory.register('SomeWidget', SomeWidget)

class myOBDBase(BoxLayout):


layout_wid = ObjectProperty(None) #object of layout

def on_click(self):
print "Hello"
self.layout_wid.add_widget(SomeWidget())

class Main(App):
def build(self):
return myOBDBase(info='Hello world')


Main().run()

------main.kv
#:kivy 1.0

#:import SomeWidget main.SomeWidget

# rules for the widget
<SomeWidget>:
BoxLayout:
pos: root.pos
size: root.size
orientation: "vertical"
Label:
text: "hello"
Button:
text: "world"

# root
<myOBDBase>:
layout_wid: layout
BoxLayout:
id: layout

Button:
text: "add KV"
on_press: layout.add_widget(SomeWidget());
Button:
text: "add PY"
on_press: root.on_click()

audiowerk

unread,
Dec 29, 2013, 3:53:21 PM12/29/13
to kivy-...@googlegroups.com
Hi

i'm trying to do the same as explained, but in my case it doesn't work. I hope you can help me.

My python file:
from kivy.app import App

class AddwidgetApp(App):
    def build(self):
pass

AddwidgetApp().run()

and kv

#:kivy 1.0
#:import Button kivy.uix.button

BoxLayout:
    id: test
    Button:
        text: 'Add Device'
        on_press: test.add_widget(Button(text = 'Hello World'))

Thanks for any help!
Message has been deleted

Noob Saibot

unread,
Dec 29, 2013, 6:23:45 PM12/29/13
to kivy-...@googlegroups.com
When you pass build, you're pretty much telling your app not to load. Instead, you would subclass your BoxLayout widget, and have your AddWidgetApp return it during build. Like this:

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.app import App

class MyMainWidget(BoxLayout):
    
#Since there aren't any real callbacks in this widget, we pass.
    
pass

class AddWidgetApp(App):

    
def build(self):
        
#You need to actually DO something in `build` for your app to work.
        app
 = MyMainWidget()
        
return app

Builder.load_string("""
#Here, we design & define the parameters of `MyMainWidget`

<MyMainWidget>:

    id: test

    Button:
        text: 'Add Device'
        on_press: test.add_widget(Button(text='Hello World'))
""")

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

Try running this and see if that works for you.

audiowerk

unread,
Dec 29, 2013, 7:41:01 PM12/29/13
to kivy-...@googlegroups.com
Hi,

thank you for your answer, but that is not true. The app works fine, exept that it breaks with the same error message yours does, when i press the button. Acutally we built the same app, but I defined everything in the kv file.

Here is your version's error message:

   File "_event.pyx", line 281, in kivy._event.EventDispatcher.dispatch (kivy\_event.c:4152)
   File "<string>", line 1, in <module>
 TypeError: 'module' object is not callable

And my version's error message:
   File "_event.pyx", line 281, in kivy._event.EventDispatcher.dispatch (kivy\_event.c:4152)
   File "...\addwidget.kv", line 1, in <module>
     #:kivy 1.0
 TypeError: 'module' object is not callable

This seems to be the same. What does this error Message mean? Thanks for any help!

Noob Saibot

unread,
Dec 29, 2013, 9:19:56 PM12/29/13
to kivy-...@googlegroups.com
Ah. That's because i forgot something important about importing classes from a module in kvlang.

If you add this to the beginning of your kvlang string like so...

Builder.load_string("""
#:import Button kivy.uix.button.Button

...

"""
)

...it works. Try it.

Good Luck

audiowerk

unread,
Dec 30, 2013, 11:13:43 AM12/30/13
to kivy-...@googlegroups.com
Hi,

my version works also. My mistake was that i wrote #:import Button kivy.uix.button instead of #:import Button kivy.uix.button.Button

Thanks for your helping me!

from kivy.lang import Builder
from kivy.app import App

class AddWidgetApp(App):

    def build(self):
        pass

Builder.load_string("""
#:import Button kivy.uix.button.Button

BoxLayout:
    id: test
    Button:
        text: 'Add Device'

audiowerk

unread,
Dec 30, 2013, 12:37:31 PM12/30/13
to kivy-...@googlegroups.com
In the next step I would like to to call a template such as
[MyButton@Button]:
    text: 'My Button'

is there any way to do that?

I also tried to do it by
class MyButton(Button):
pass

Factory.register('MyButton', cls=MyButton)
and

#:import MyButton main.MyButton
 in kv as described in the beginning. But if i try this method, I get the error message
 Unable to import package 'main.MyButton'

Thanks for any help!

Noob Saibot

unread,
Dec 30, 2013, 7:02:27 PM12/30/13
to kivy-...@googlegroups.com
You should read through the kvlang docs here. It explains Templates (and that they have since been depreciated. You should try using Dynamic Widgets instead). Let us know if you have further questions.

audiowerk

unread,
Dec 30, 2013, 11:53:56 PM12/30/13
to kivy-...@googlegroups.com
Thank's for your advice. I changed to Dynamic classes. Nevertheless I get the same results as described.

Question 1:

When i use
<MyButton@Button>:
    text: 'My Button'
and call it with
                Button:
                    text: 'Add Button'
                    on_press: test.add_widget(MyButton(text='Hello World'))
I get
 NameError: name 'MyButton' is not defined

Question 2:

Noob Saibot

unread,
Dec 31, 2013, 1:01:05 PM12/31/13
to kivy-...@googlegroups.com
Question 1 & Question 2:

In kvlang, the convention with binding is... 

property : python code # with `id`s

If you had directly added MyButton to a Widget, there'd be no problem; but because you're using python to bind it in a callback, you get a NameError because MyButton isn't defined in python. It's better practice to do all of your DESIGNING in kvlang, and your CALLBACKS in python.

Do something like this instead:

class MyButton(Button):
   
pass

class Test(BoxLayout):

   
def some_callback(self, *args):
       
self.add_widget(MyButton())

############################################

<MyButton>:
    text
: 'My Button'

<Test>:
    btn
: btn_id

   
Button:
        id
: btn_id
        text
: 'Add Button'
        on_press
: root.some_callback(*args)

This way, you separate responsibilities. If there's a problem with how the widgets are being displayed, check the kvlang. If there's a problem with how the widgets are interacting with each other, touch, etc., check the python. Simpler, no?

Spencer Perkins

unread,
Mar 18, 2015, 6:28:01 AM3/18/15
to kivy-...@googlegroups.com
I'm missing something here. No matter where I try and stick the factory.register(), my CharactersScreen class isn't getting recognized. What am I doing wrong?
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.textinput import TextInput
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.properties import NumericProperty, StringProperty
from kivy.lang import Builder
from kivy.storage.jsonstore import JsonStore
from os.path import join
from kivy.uix.widget import Widget
from kivy.factory import Factory

Builder.load_string('''
#:import random random.random
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
#:import NoTransition kivy.uix.screenmanager.NoTransition
#:import CharactersScreen main.CharactersScreen


<BasicScreen>:
    hue: random()
    canvas:
        Color:
            hsv: self.hue, .5, .3
        Rectangle:
            size: self.size

    Label:
        font_size: 42
        text: root.name
        pos_hint: {'center_y': 0.95}

    BoxLayout:
        size_hint: None, 1
        pos_hint: {'center_y': .5}
        orientation: 'vertical'

        Button:
            text: 'Characters!'
            on_release: root.manager.current = 'Characters'
        Button:
            text: 'Identity'
            on_release: root.manager.current = 'Identity'
        Button:
            text: 'Abilities'
            on_release: root.manager.current = 'Abilities'
        Button:
            text: 'Attacks'
            on_release: root.manager.current = 'Attacks'
        Button:
            text: 'Health'
            on_release: root.manager.current = 'Health'
        Button:
            text: 'Powers'
            on_release: root.manager.current = 'Powers'
        Button:
            text: 'Skills'
            on_release: root.manager.current = 'Skills'
        Button:
            text: 'Feats'
            on_release: root.manager.current = 'Feats'
        Button:
            text: 'Equipment'
            on_release: root.manager.current = 'Equipment'
        Button:
            text: 'Rituals'
            on_release: root.manager.current = 'Rituals'
        Button:
            text: 'Notes'
            on_release: root.manager.current = 'Notes'


<CharactersScreen>:
    Button:
        text: 'Save'
        pos_hint: {'center_x': 0.95, 'center_y': .95}
        size_hint: .1, .1
        on_release: root.save()

    Button:
        text: 'New'
        pos_hint: {'center_x': 0.85, 'center_y': .95}
        size_hint: .1, .1
        on_release: self.createNew()
    Button:
        text: '+'
        size_hint: .05, .05
        pos_hint: {'center_x': 0.34, 'center_y': 0.8}
        on_release: root.createNew()
    TextInput:
        id: newchar
        text: 'enter name here'
        multiline: False
        pos_hint: {'center_x': 0.5, 'center_y': 0.8}
        size_hint: 0.25, None
        height: 30
    StackLayout:
        id: chargrid
        size_hint: .5,.5
        pos_hint: {'center_x': .5,'center_y': .5}
        

    
<IdentityScreen>:
    BoxLayout:
        size_hint: .25, .75
        pos_hint: {'center_x': 0.6,'center_y': .5}
        orientation: 'vertical'

        Button:
            text: 'Name'
            on_release: root.yopo()

        TextInput:
            id: charname
            text: ''
            multiline: False

        Label:
            text: 'Level'

        TextInput:
            id: charlevel
            text: ''
            multiline: False

        Label:
            text: 'XP'

        TextInput:
            id: charxp
            text: ''
            multiline: False

        Label:
            text: 'Race'

        TextInput:
            id: charrace
            text: ''
            multiline: False

        Label:
            text: 'Class'

        TextInput:
            id: charclass
            text: ''
            multiline: False

        Label:
            text: 'Paragon'

        TextInput:
            id: charparagon
            text: ''
            multiline: False

        Label:
            text: 'Epic'

        TextInput:
            id: charepic
            text: ''
            multiline: False

        Label:
            text: 'Misc.'

        TextInput:
            id: charmisc
            text: ''
            multiline: False

<AbilitiesScreen>:
    GridLayout:
        cols: 5
        size_hint: .3, .5
        pos_hint: {'center_x': .5,'center_y': .5}

        Label:
            text: 'Abil'
        Label:
            text: 'Score'
        Label:
            text: 'Mod'
        Label:
            text: 'Defense'
        Label:
            text: 'Score'
        Label:
            text: 'Str'
        TextInput:
            text: ''
            multiline: False
        Label:
            text: '0'
        Label:
            text: 'AC'
        Label:
            text: '15'
        Label:
            text: 'Con'
        TextInput:
            text: ''
            multiline: False
        Label:
            text: '0'
        Label:
            text: 'AC'
        Label:
            text: '15'
        Label:
            text: 'Dex'
        TextInput:
            text: ''
            multiline: False
        Label:
            text: '0'
        Label:
            text: 'AC'
        Label:
            text: '15'
        Label:
            text: 'Int'
        TextInput:
            text: ''
            multiline: False
        Label:
            text: '0'
        Label:
            text: 'AC'
        Label:
            text: '15'
        Label:
            text: 'Wis'
        TextInput:
            text: ''
            multiline: False
        Label:
            text: '0'
        Label:
            text: 'AC'
        Label:
            text: '15'
        Label:
            text: 'Cha'
        TextInput:
            text: ''
            multiline: False
        Label:
            text: '0'
        Label:
            text: 'AC'
        Label:
            text: '15'


ScreenManager:
    id: screen_manager
    CharactersScreen:
        id: characters_screen
    

''')


class BasicScreen(Screen):
    hue = NumericProperty(0)
    def save(self):
        data_dir = App().user_data_dir
        store = JsonStore(join(data_dir, 'dandy.json'))
        cname = self.ids.charname.text
        crace = self.ids.charrace.text
        store.put(cname,race=crace)

class IdentityScreen(BasicScreen):
    pass

class CharactersScreen(BasicScreen):
    def loadem(self):
        data_dir = App().user_data_dir
        store = JsonStore(join(data_dir, 'dandy.json'))
        chars = store.keys()
        for i in range(len(chars)):
            self.add_button(chars[i])
    def loadCharData(self,char):
        data_dir = App().user_data_dir
        store = JsonStore(join(data_dir, 'dandy.json'))
        self.ids.charname.text = 'Dave'
        self.ids.charrace.text = 'Human'
    def createNew(self):
        data_dir = App().user_data_dir
        store = JsonStore(join(data_dir, 'dandy.json'))
        store.put(self.ids.newchar.text)
        self.add_button(self.ids.newchar.text)
    def add_button(self,name):
        btn = Button(text=name)
        self.ids.chargrid.add_widget(btn)
        btn.bind(on_press=self.callback)
    def callback(self,instance):
        self.loadCharData('%s')

        
class AbilitiesScreen(BasicScreen):
    pass

Factory.register('CharactersScreen', CharactersScreen)


class ScreenManagerApp(App):
    title = 'Dandy'
    def build(self):
        pages = ['Attacks','Health','Powers','Skills','Feats','Equipment','Rituals','Notes']
        root = ScreenManager()
        #charscreen = CharactersScreen(name='Characters')
        #Factory.register('CharactersScreen', cls=CharactersScreen)
        #idscreen = IdentityScreen(name='Identity')
        #root.add_widget(charscreen)
        #root.add_widget(idscreen)
        #root.add_widget(AbilitiesScreen(name='Abilities'))
        for i in range(len(pages)):
            root.add_widget(BasicScreen(name=pages[i]))
        #charscreen.loadem()
        return root

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

Spencer Perkins

unread,
Mar 18, 2015, 10:07:19 AM3/18/15
to kivy-...@googlegroups.com
figured it out. alls I had to do was registers AFTER the class but BEFORE the kivy file loaded. loving kivy so far

ZenCODE

unread,
Mar 18, 2015, 1:11:41 PM3/18/15
to kivy-...@googlegroups.com
Nice. Thanks for posting back. And welcome to the Kivy lovers club. Kivy For The Win! :-)

Spencer Perkins

unread,
Mar 19, 2015, 2:07:03 AM3/19/15
to kivy-...@googlegroups.com
thanks, I'm sure I'll be around here a lot

Ezra Calubsing

unread,
Apr 9, 2017, 10:02:48 AM4/9/17
to kivy-...@googlegroups.com
Thank you for this post, it help me.
can i ask one more thing.

how can i limit how many widget can i add.
currently the code that i created base from Michal, I can add widget endlessly.
how can i limit it to 5.

Thank you.

ZenCODE

unread,
Apr 9, 2017, 1:45:57 PM4/9/17
to kivy-...@googlegroups.com
if len(widget.children) < 5:
     # Do crazy stuff
     pass
else:
     # Behave!
    pass

It's not Kivy's job to control stuff like this. If you want to do silly things, that's your right...:-)

ZenCODE

unread,
Apr 12, 2017, 11:43:09 AM4/12/17
to Kivy users support
Ps. Oops, sorry, I did not mean to imply anyone is silly, just wanted to point out a framework should allow the programmer to do what they want (within reason)
Reply all
Reply to author
Forward
0 new messages