Kivy rectangles are stuck at bottom left

Viewed 349

I am trying to add buttons to a kivy gridlayout with a rectangle shape above each of them. But all the rectangles seem to stack up at the bottom left of the screen, nowhere near the buttons. I think multiple rectangles are being created, but all of them are placed above each other, and not over the button. How do I fix it?

main.py:

from kivymd.app import MDApp
from kivy.app import App
from kivy.graphics import Ellipse, Rectangle, Color
from kivy.uix.button import ButtonBehavior, Button
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
from kivy.metrics import dp, sp

Window.size = (550, 730)


class Recta(Button):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        with self.canvas.after:
            Color(1., 1., 0)
            Rectangle(pos=self.pos, size=(self.width, self.height))


class Grid(GridLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.rows = 8
        self.cols = 8
        self.size_hint = None, None

        if Window.height > Window.width:
            self.width = Window.width
            self.height = Window.width

        elif Window.height < Window.width:
            self.width = Window.height
            self.height = Window.height

        elif Window.height == Window.width:
            self.width = Window.width
            self.height = Window.height

        self.pos_hint = {"center_x": 0.5, "center_y": 0.5}

        for i in range(64):
            self.add_widget(Recta())


class TestApp(MDApp):
    def build(self):
        grid_w = Grid()
        return grid_w


if __name__ == '__main__':
    TestApp().run()
1 Answers

The problem is that you are drawing the Rectangle in the __init__() method using pos and size of the Recta widget. In the __init__(), the pos and size of a widget have not yet been set, and are still the default values of (0,0) and (100,100).

You can fix this in either of two ways. The easiest is just to define the Rectangle using kv like this:

Builder.load_string('''
<Recta>:
    canvas.after:
        Color:
            rgb: 1,1,0
        Rectangle:
            pos: self.pos
            size: self.size
''')

and remove the drawing of the Rectangle from the __init__() method

The second way is to create the bindings (that kv does automatically) like this:

class Recta(Button):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        with self.canvas.after:
            Color(1., 1., 0)
            self.rect = Rectangle(pos=self.pos, size=(self.width, self.height))
        self.bind(pos=self.update)
        self.bind(size=self.update)

    def update(self, *args):
        self.rect.pos = self.pos
        self.rect.size = self.size
Related