How to create a new instance of a class defined in kv file from main.py

365 views
Skip to first unread message

Juan Francisco López-Egea Martínez

unread,
Feb 8, 2019, 5:24:25 AM2/8/19
to Kivy users support
Hello to the forum.

I'm trying to create some instances from a class I've defined in .kv file.

This is about a ScrollViewClass, parenting a gridLayout with size_hint to None and some buttons (the instance class I want to create) to fill that gridLayout to get ScrollView really scrolling.

The point is, the number of buttons/text the gridLayout must contain, is dynamic, it can be 10, 20 or 100, nevermind. The point is add/remove an instance when needed.

This is a very basic idea about home savings/spends, economy app.

Here the basic code:

main.py:


from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty


class MoneyControl(Widget):
    mainScrollView
= ObjectProperty(None)#This references an INSTACE YET CREATED from kv file
    childGridLayout
= ObjectProperty(None)
   
   
# Just a for to create 100 instances of button.
    buttonsList
= [None]
   
for i in range(100):
        buttonsList
.append(Tbutton())#What should I put here in place of "Tbutton()" to get a new tButton instance? OR how can I "import" the class from .kv file, and not a single instance?
        buttonList
[-1].parent(childGridLayout)
   
print buttonsList


class MoneyControlApp(App):
   
def build(self):
        prog
= MoneyControl()
       
return prog

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




And the .kv file:


#:kivy 1.0.9

<Tbutton@Button>:# nameYouWant @ class
    text
: "Test Text"
    size_hint_y
: None
    height
: 40
    background_normal
: ""
    background_color
: 0.7, 0.9, 0.7, 1.0

<ScView@ScrollView>:
    size_hint
: None, None
   
#size: root.width, root.height
   
#top: 0
   
#center_x: root.width
    pos
: 100,200

<Glo@GridLayout>:
    cols
:1
    spacing
:0
    size_hint
: None, None
   
#size_hint_x: None
   
#size_hint_y: None
    height
: self.minimum_height

<MoneyControl@Widget>:
    mainScrollView
:rollerresults
    childGridLayout
:resultsBlock

   
ScView:
        id
:rollerresults
        size
: 800, 600

       
Glo:
            id
:resultsBlock

#            Tbutton:#This list of buttons, from main.py
#            Tbutton:#To be able to add/remove as user needs.
#            Tbutton: ....




ZenCODE

unread,
Feb 8, 2019, 9:02:15 AM2/8/19
to Kivy users support
You can use the Factory class for that. Once the kv file is loaded, you can instantiate any class defined in it via Factory.MyClass().

Juan Francisco López-Egea Martínez

unread,
Feb 8, 2019, 5:28:47 PM2/8/19
to Kivy users support
Ok, I'm gonna study factory class and make some tests.

ZenCODE, thanks for pointing me in the right way, really thank you very much.

Juan Francisco López-Egea Martínez

unread,
Feb 9, 2019, 5:40:39 AM2/9/19
to Kivy users support
Humm.... I've read about factory and modified main.py in consecuence in this this way:

main.py

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty
from kivy.factory import Factory



class MoneyControl(Widget):
    mainScrollView
= ObjectProperty(None)#This references an INSTACE YET CREATED from kv file
    childGridLayout
= ObjectProperty(None)

   
Factory.register('buttonFromKvFile', cls=Tbutton)#Here Factory register the class "Tbutton" from KvFile with the name of "buttonFromKvFile"
   
Tbutton1 = Factory.buttonFromKvFile()#Here we create an INSTANCE called Tbutton1 from Tbutton class?

   
   
# Just a for to create 100 instances of button.
    buttonsList
= [None]
   
for i in range(100):
        buttonsList
.append(Tbutton())#What should I put here in place of "Tbutton()" to get a new tButton instance? OR how can I "import" the class from .kv file, and not a single instance?
        buttonList
[-1].parent(childGridLayout)
   
print buttonsList


class MoneyControlApp(App):
   
def build(self):
        prog
= MoneyControl()
       
return prog

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



But it says:

NameError: name 'Tbutton' is not defined


So, factory is not reading Tbutton class defined in .kv file?. What am I missing?

ZenCODE

unread,
Feb 9, 2019, 8:57:28 AM2/9/19
to Kivy users support
Are you loading the kv file?

from kivy.lang import Builder
Builder.load_file(myfile.kv)


and do that before you use the factory.

Juan Francisco López-Egea Martínez

unread,
Feb 10, 2019, 7:52:53 AM2/10/19
to Kivy users support
Same results after load kv file with Builder, the  NameError: name 'Tbutton' is not defined error.


from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty
from kivy.factory import Factory
from kivy.lang import Builder #Used for import rules from several .kv files


class MoneyControl(Widget):
   
Builder.load_file("moneycontrol.kv")#"moneycontrol.kv" is a path/file relative to main.py, string type.

    mainScrollView
= ObjectProperty(None)#This references an INSTACE YET CREATED from kv file
    childGridLayout
= ObjectProperty(None)
   
Factory.register('buttonFromKvFile', cls=Tbutton)#Here Factory register the class "Tbutton" from KvFile with the name of "buttonFromKvFile"
   
Tbutton1 = Factory.buttonFromKvFile()#Here we create an INSTANCE called Tbutton1 from Tbutton class?
   
   
# Just a for to create 100 instances of button.
    buttonsList
= [None]
   
for i in range(100):
        buttonsList
.append(Tbutton())#What should I put here in place of "Tbutton()" to get a new tButton instance? OR how can I "import" the class from .kv file, and not a single instance?
        buttonList
[-1].parent(childGridLayout)
   
print buttonsList


class MoneyControlApp(App):
   
def build(self):
        prog
= MoneyControl()
       
return prog

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



Builder bad applied?, or simply that was not the problem?, because I think kv file was applying correctly, since haves the same name (in lowercase) than app name...

So, ideas?.

ZenCODE

unread,
Feb 11, 2019, 5:47:51 AM2/11/19
to Kivy users support
What is the exact error? Perhaps post your complete code so I can try running it...

Thanks

Juan Francisco López-Egea Martínez

unread,
Feb 11, 2019, 6:18:59 AM2/11/19
to Kivy users support
Zen, is posted, the .kv file is in first post, main.py with the modifications you told me is in the previous post to this one

Juan Francisco López-Egea Martínez

unread,
Feb 11, 2019, 6:36:30 AM2/11/19
to Kivy users support
The error is, still unable to find the class defined in kv file from main.py to instantiate it. Is a little bit up to, in the previous message.

Zen, do you really have some time to take it a look?
I'm grateful at least, to find somebody can help and put me in the right way, but I have the sensation you have not much time for this, as if other worries and problems would be distracting or giving you very little time to help here, in spite of you really want to help, no doubt about this for my side.

Hope all to be going well there.
Man, take your time for your needs, you have no obligation with me or anybody else, and when you can, if you want, come back and give us a hand.

Thanks for your time, really.

ZenCODE

unread,
Feb 11, 2019, 7:41:31 AM2/11/19
to Kivy users support
That is not the exact error. It's you rewriting the part of the error that you think is important. The full error message will contain a complete traceback,the line number and other important information....

I wanted you to repost the code as it is now, as I don't want to have to go back and read each post to re-assemble the changes you have made. Just post the code as it is now and I will fix it and repost. It really should be trivial to fix, so work with me on this one :-)

Cheers

ZenCODE

unread,
Feb 11, 2019, 11:56:46 AM2/11/19
to Kivy users support
You were missing the "super" call.


# Just a for to create 100 instances of button.
def __init__(self, **kwargs):
super(MoneyControl, self).__init__(**kwargs)

buttonsList = [None]
for i in range(100):
        button = Factory.Tbutton()
buttonsList.append(button)
self.childGridLayout.add_widget(button)

Juan Francisco López-Egea Martínez

unread,
Feb 11, 2019, 12:35:29 PM2/11/19
to Kivy users support
Yes, it now works. Everything. Wonderful. to have forgotten "Super" haves no forgiveness, very python-newbie, my fault.

Thanks Zen. Awesome. I owe you a beer, no doubt.


You're on right, I was asking myself...
but, if I've posted it!, and I have clear about we're talking about..

But probably from your perspective is more as...
I swear I already helped that guy -one of many-, what was him talking about?... ah yes, make instances from a class defined in .kv file, yes... but where was exactly the problem...  Ouh, I forgot tell something at the previous person I have just answered, wait a minute...  ..ok, I'm back... but.. where is the code? how many tweaks have we done?... be patient, stay friendly, request and explain the things... for nth time to nth person.

So, sorry, I'll take that into account the next time (yes, I'm afraid there will be, I promise I'll try to avoid it, but...). From now on I'll post everything when we had modified things and posts started to stay a little bit in the past to avoid to be searching.

Thanks one more time, a greeting ZenCODE!.

ZenCODE

unread,
Feb 11, 2019, 3:06:22 PM2/11/19
to Kivy users support
Ha. No worries. I should have include the super when I mentioned calling the __init__. But glad you are sorted. Go forth and code! :-)
Reply all
Reply to author
Forward
0 new messages