Search code examples
pythonpython-3.xclasskivy

Why the class button does not work the same as the kv file button?


When clicking on the button generated by the *.KV file, the buttons are generated in a grid (2). When clicking on the button of the "Create" class, the buttons are generated superimposed.

How do I solve the overlay buttons problem?

test.py

from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button

class Test(App):
    def build(self):
        return Window()

class Window(GridLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.cols = 2
        self.col_force_default = True
        self.col_default_width = 200
        self.row_force_default = True
        self.row_default_height = 40

        self.add_widget((Button(text="Create",
                       on_release=Window.addWidget)))

    def addWidget(self):
        count = 0
        while count < 10:
            count = count + 1
            self.add_widget(Button(text=str(count)))
Test().run()

test.kv

<Window>:
    BoxLayout:
        Button:
            on_release:root.addWidget()

Solution

  • In your python code the line:

    on_release=Window.addWidget)))
    

    arranges for Window.addWidget() to be called when the Button is released. When an on_release is specified, the Button that was release is passed as an argument to the specified method. So, when the Button is released, the actual call is executed as:

    Window.addWidget(button_instance)
    

    Now the addWidget() method of your Window class is an instance method, meaning that it expects the first argument passed to it is the self (the instance of the Window class). Since you are not calling it properly as an instance method, the passed in button_instance is mistaken for the Window class instance, and the created Buttons are added to the button_instance instead of the Window instance.

    You can fix this by correctly calling the addWidget() method like this:

    on_release=lambda button: self.addWidget())))
    

    The lambda is just to remove the added button_instance argument.

    A different fix is to add *args to the addWidget(), like this:

    def addWidget(self, *args):
    

    Then the on_release line is simpler:

    on_release=self.addWidget)))