user3060854
user3060854

Reputation: 933

add - remove widgets in kivy

I experience some difficulties in adding or removing widgets in kivy. This is the case:

The main form should contain two of three widgets, Widget1, Widget2, and Widget3. Pressing the button of Widget1, Widget2 should be removed and Widget3 should appear.

This is the main.py file:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.factory import Factory

class TableApp(App):

    def on_pause(self): return True

    def on_resume(self): pass

class Form(BoxLayout):
    def click(self, instance, arg):
        print 'this is the Form class'
        print 'this is my arg ... ', arg
        print 'this is the button pressed ... ', instance
        print 'these are the children of the Form class:', self.children
        Form().remove_widget(Widget2)
        Form().add_widget(Widget3)

class Widget1(BoxLayout):

    def click(self, instance, arg):
        print 'this is the Widget 1'
        print 'this is my arg ... ', arg
        print 'this is my instance', instance, '\n'
        Factory.Form().click(instance,arg)

class Widget2(BoxLayout):
    pass

class Widget3(BoxLayout):
    pass

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

and this is the .kv file:

#:import Factory kivy.factory.Factory
Form:

<Form>:
    orientation: 'vertical'

    Widget1:    
    Widget2:

<Widget1>:
    Button:
        text: "Widget 1 Button" 
        on_press: root.click(self, 'arg')

<Widget2>:
    Button:
        text: 'Widget 2 Button'

<Widget3>:
    Button:
        text: 'Widget 3 Button'

In the class Form I check that Widgets1 and 2 are children of the class:

print 'these are the children of the Form class:', self.children

and I get:

these are the children of the Form class: [<__main__.Widget2 object at 0x7fe5833317c0>, <__main__.Widget1 object at 0x7fe5833316e0>]

So when I try to remove an existing child and add a new one I get:

TypeError: descriptor 'unbind' of 'kivy._event.EventDispatcher' object needs an argument

Could somebody help? Thank you.

Upvotes: 1

Views: 4912

Answers (2)

Peter Badida
Peter Badida

Reputation: 12199

The only reason you see children in that print log is because they are used in kv lang. This doesn't work at all:

Form().remove_widget(Widget2)
Form().add_widget(Widget3)

Let's do a little bit of drawing:

App                   |  "Form()" called:              # this is how Form (A) gets created
 |                    |     |
 |- Form (A)          |  new Form instance created     # this is the click event beginning
     |                |       |                         
     |- Widget1       |       |- remove_widget of -> Form()                     
     |     |          |                                |                        
     |     |- Button  |    new Form instance created  -|                        
     |                |        |                       |
     |- Widget2       |        |- remove_widget(class) |
           |          |             ^                  |
           |- Button  |             |- crash           |
                      |                                |
                      |    new Form instance created  -|

The left part is what kv file does, the right part is you/python code. Let's call the already available (via kv) Form a form A:

  1. Form A is an instance of class Form and has a click method
  2. You click on the A instance's graphical representation i.e. in the BoxLayout's area
  3. There's nothing wrong with prints, so it'll pass just OK, instance will be probably Button instance (and you should really use *args, read about (un)packing arguments)
  4. In form A the Form() lines get noticed and that's the right side of my drawing
    • each Form() will create a new instance (not related to form A) of class Form (those brackets)
    • when a new instance is created and initialized, it'll call remove_widget with parameter Widget2
      • that's no instance, that's a class, therefore it can't be "unbind"
      • even if you'd use the right thing - an instance, it'd still crash because there' no such child in a newly created Form instance. The children are in form A

Upvotes: 1

Yoav Glazner
Yoav Glazner

Reputation: 8041

Most of the problems ocurr because you created new widgets instead of using the existing ones.

Here is a working example (look at the comments):

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.factory import Factory #no need for this


class Form(BoxLayout):
    def click(self, instance, arg):
        print ('this is the Form class')
        print ('this is my arg ... ', arg)
        print ('this is the button pressed ... ', instance)
        print ('these are the children of the Form class:', self.children)
        #notice! here we use self, not Form() which will make a new Form instance
        self.remove_widget(self.children[0]) #should be Widget2
        self.add_widget(Widget3()) #Here we really want a new widget

class Widget1(BoxLayout):

    def click(self, instance, arg):
        print ('this is the Widget 1')
        print ('this is my arg ... ', arg)
        print ('this is my instance', instance, '\n')
        self.parent.click(instance,arg) #changed to use the existing From instance instead of tring to create a new one

class Widget2(BoxLayout):
    pass

class Widget3(BoxLayout):
    pass



class TableApp(App):


    def on_pause(self): return True

    def on_resume(self): pass

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

Upvotes: 3

Related Questions