fiacobelli
fiacobelli

Reputation: 1990

Dynamically change the background of a label in kivy

I am trying to dynamically change the background color of a Label in Kivy with this simple program. it is intended to produce a grid of red and black cells. However, all I get is a red cell in position (7,0) (I am printing the positions).

import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.graphics import Color, Rectangle

class LabelX(Label):
    def set_bgcolor(self,r,b,g,o,*args):
        self.canvas.after.clear()
        with self.canvas.after:
            Color(r,g,b,o)
            Rectangle(pos=self.pos,size=self.size)


class MyGrid(GridLayout):

    def __init__(self,cols,rows,**kwargs):
        super(MyGrid,self).__init__(**kwargs)
        self.cols = cols
        for i in range(rows):
            for j in range(cols):
                l = LabelX(text=str(i)+","+str(j))
                if (i*rows+j)%2:
                    l.set_bgcolor(1,0,0,1)
                else:
                    l.set_bgcolor(0,0,0,1)
                self.add_widget(l)



class GridApp(App):

    def build(self):
        g = MyGrid(8,8)
        return g

if __name__=="__main__":
    GridApp().run()

Any ideas on how to get a red/black checker board?

Upvotes: 0

Views: 2696

Answers (2)

eyllanesc
eyllanesc

Reputation: 243887

If you print self.pos using the following:

with self.canvas.after:
    [...]
    print(self.pos)
    [...]

only the values [0, 0] are obtained, and that leads to the conclusion that all the rectangles are drawn in that position, so they are superimposed, and that is what you observe.

For example, to verify this we pass a parameter more to the function set_bgcolor () that will be the index of the loop and we will use it as a parameter to locate the position:

    def set_bgcolor(self, r, b, g, o, i):
        [..]
            Rectangle(pos=[100*i, 100*i],size=self.size)

class MyGrid(GridLayout):
    def __init__(self,cols,rows,**kwargs):
        [...]
                if (i*rows+j)%2:
                    l.set_bgcolor(1,0,0,1, i)
                else:
                    l.set_bgcolor(0,0,0,1, i)
                self.add_widget(l)

We get the following:

enter image description here

also if we change the size of the window the rectangle does not change either:

enter image description here

So if you want the Rectangle to be the bottom of the Label, the position and size of the Rectangle should be the same as the Label, so you have to make a binding with both properties between the Label and Rectangle. You must also use canvas.before instead of canvas.after otherwise the Rectangle will be drawn on top of the Label.

class LabelX(Label):
    def set_bgcolor(self,r,b,g,o):
        self.canvas.before.clear()
        with self.canvas.before:
            Color(r,g,b,o)
            self.rect = Rectangle(pos=self.pos,size=self.size)

        self.bind(pos=self.update_rect,
                  size=self.update_rect)

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

enter image description here

enter image description here

enter image description here

The advantage of my solution is that it is independent of the external widget or layout since the position and size of the Rectangle only depends on the Label.

Upvotes: 2

John Anderson
John Anderson

Reputation: 38822

The problem is that each labels position is (0,0) and size is (100,100) until the app is actually built. so all your canvas drawing is done at those positions and sizes. You can get the checkerboard effect by waiting until after the positions and sizes are assigned, then doing the checkerboard effect. I do this with a Clock.schedule_once:

from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.graphics import Color, Rectangle
from kivy.clock import Clock
from kivy.core.window import Window

class LabelX(Label):
    def set_bgcolor(self,r,b,g,o,*args):
        self.canvas.after.clear()
        with self.canvas.after:
            Color(r,g,b,o)
            Rectangle(pos=self.pos,size=self.size)


class MyGrid(GridLayout):

    def __init__(self,cols,rows,**kwargs):
        super(MyGrid,self).__init__(**kwargs)
        self.cols = cols
        for i in range(rows):
            for j in range(cols):
                l = LabelX(text=str(i)+","+str(j))
                l.rowcol = (i,j)
                self.add_widget(l)



class GridApp(App):

    def build(self):
        self.g = MyGrid(8,8)
        Window.bind(size=self.checkerboard)
        return self.g

    def checkerboard(self, *args):
        for l in self.g.children:
            count = l.rowcol[0] + l.rowcol[1]
            if count % 2:
                l.set_bgcolor(1, 0, 0, 1)
            else:
                l.set_bgcolor(0, 0, 0, 1 )


if __name__=="__main__":
    app = GridApp()
    Clock.schedule_once(app.checkerboard, 1)
    app.run()

Upvotes: 1

Related Questions