Mark One
Mark One

Reputation: 41

Kivy rectangle redraw, self.pos and self.size are (0,0)

I'm using a loop to create 27 label instances. These labels need to be numbered using their Label.text method - This I've achieved.

I also need to be able to re-colour these labels (canvas rectangle) using a python function (based on other inputs). This is the bit that doesn't work

Basically, the init method of the GridLayout creates 29 instances of the custButton() Label. This loop also re-numbers (label.text) the buttons, but the re-coloring of one label does not work.

I've isolated that self.pos and self.size are both returning (0,0) in the xBackground_Color function - this is why the new rectangle doesn't draw after the old one is cleared (the clear works).

Why doesn't self.pos and self.size work from the python side?

I've tried soooooooo many things! Please help if you can.

Here's kvMain.kv :

#:kivy 1.0.9

FloatLayout:
    canvas:
        Color:
            rgba: (64/255, 64/255, 64/255, 1) #Whole screen background
        Rectangle:
            pos: self.pos
            size: self.size
    BoxLayout:
        orientation: 'vertical'
        padding: 10
        FloatLayout: #Dummy widget
        myGrid:


<myGrid>:
    cols: 9
    spacing: 3
    padding: 3
    canvas.before:
        Color:
            rgba: (216/255, 216/255, 216/255, 1)
        Rectangle:
            pos: self.pos
            size: self.size

<custButtom>:
    canvas.before:
        Color:
            rgba: (191/255, 191/255, 191/255, 1)
        Rectangle:
            pos: self.pos
            size: self.size
    font_size: 14
    color: (90/255, 90/255, 90/255, 1)
    text: 'Er' #Don't ever want to see this text. Will set each label in loop
    size: self.texture_size

Here's main.py:

from kivy.app import App
from kivy.lang import Builder

from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label

from kivy.graphics.vertex_instructions import Rectangle, Line
from kivy.graphics.context_instructions import Color

IN_Labels = {}

class custButtom(Label):
    def __init__(self, **kwargs):
        super(custButtom, self).__init__(**kwargs)

    def xtext(self, newText):
        self.text = newText
        self.size = self.texture_size

    def xbackground_color(self, newR, newG, newB, newA):
        print('IN_Labels[14] pos ('+ str(IN_Labels[14].pos[0]) + ',' + str(IN_Labels[14].pos[1]) + ')   size ('+ str(IN_Labels[14].size[0]) + ',' + str(IN_Labels[14].size[1]) + ')')
        print('self pos ('+ str(self.pos[0]) + ',' + str(self.pos[1]) + ')   size ('+ str(self.size[0]) + ',' + str(self.size[1]) + ')')
        self.canvas.before.clear()

        with self.canvas.before:
            Color(newR, newG, newB, newA)
            #Problem is that self.size and self.pos are both (0,0)
            Rectangle(size=self.size, pos=self.pos)


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

        for i in range(0, 27, 1):
            IN_Labels[i] = custButtom()
            self.add_widget(IN_Labels[i])
            #CAN change text, needs more work to get it right
            IN_Labels[i].xtext(str(i))

        # I need to be able to change backgroundcolor programatically
        IN_Labels[14].xbackground_color(1,0,0,1)

class MainApp(App):
    def build(self):
        return kvMain

kvMain = Builder.load_file("kvMain.kv")

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

Upvotes: 0

Views: 2326

Answers (1)

Mark One
Mark One

Reputation: 41

Found it! I think the problem is that the label doesn't really have size and pos at the time at which it's being asked for self.size and self.pos.

Binding a function to resize and reposition it solves the issue.

Thanks to Alexander Taylor for his excellent YouTube videos and his blog where I found the answer: https://blog.kivy.org/2014/10/updating-canvas-instructions-declared-in-python/

kvMain.kv becomes:

#:kivy 1.0.9

FloatLayout:
    canvas:
        Color:
            rgba: (64/255, 64/255, 64/255, 1) #Whole screen background
        Rectangle:
            pos: self.pos
            size: self.size
    BoxLayout:
        orientation: 'vertical'
        padding: 10
        FloatLayout: #Dummy widget
        myGrid:


<myGrid>:
    cols: 9
    spacing: 3
    padding: 3
    canvas.before:
        Color:
            rgba: (216/255, 216/255, 216/255, 1)
        Rectangle:
            pos: self.pos
            size: self.size

<custButtom>:
    #CANVAS INSTRUCTION MOVED TO PYTHON INIT
    font_size: 14
    color: (90/255, 90/255, 90/255, 1)
    text: 'Er' #Don't ever want to see this text. Will set each label in loop
    size: self.texture_size

main.py becomes:

from kivy.app import App
from kivy.lang import Builder

from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label

from kivy.graphics.vertex_instructions import Rectangle, Line
from kivy.graphics.context_instructions import Color

IN_Labels = {}

class custButtom(Label):
    def __init__(self, **kwargs):
        super(custButtom, self).__init__(**kwargs)

        with self.canvas.before:
            Color(191/255, 191/255, 191/255, 1)
            self.rect = Rectangle(pos=self.pos, size=self.size)

        #Ensure that everytime the Rectangle is updated, it's repositioned correctly
        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

    def xtext(self, newText):
        self.text = newText
        self.size = self.texture_size

    def xbackground_color(self, newR, newG, newB, newA):
        print('IN_Labels[14] pos ('+ str(IN_Labels[14].pos[0]) + ',' + str(IN_Labels[14].pos[1]) + ')   size ('+ str(IN_Labels[14].size[0]) + ',' + str(IN_Labels[14].size[1]) + ')')
        print('self pos ('+ str(self.pos[0]) + ',' + str(self.pos[1]) + ')   size ('+ str(self.size[0]) + ',' + str(self.size[1]) + ')')
        self.canvas.before.clear()

        with self.canvas.before:
            Color(newR, newG, newB, newA)
            self.rect = Rectangle(size=self.size, pos=self.pos)


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

        for i in range(0, 27, 1):
            IN_Labels[i] = custButtom()
            self.add_widget(IN_Labels[i])
            #CAN change text, needs more work to get it right
            IN_Labels[i].xtext(str(i))

        # I need to be able to change backgroundcolor programatically
        IN_Labels[14].xbackground_color(1,0,0,1)

class MainApp(App):
    def build(self):
        return kvMain

kvMain = Builder.load_file("kvMain.kv")

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

Upvotes: 1

Related Questions