I have been puzzled by the way Kivy deal with transformations (Rotate, Translate and Scale). The Kivy documentation says that "each widget in Kivy already have by default their Canvas"[1] but that doesn't seem the case. At least in practice, it seems that all widgets share the same canvas as if it were a Reference.

Here is the very basic main.py

# main.py
from kivy.app import App

class Example(Widget):
    pass

class ExampleApp(App):
    def build(self):
        return Example()

ExampleApp().run()

Now we have this very simple python code. There are 2 buttons, both of 200x200 pixels. I want the first next to each other, so I position the first in the (0,0) coordinate and the second one in the (200,0) coordinate. Also, I want to circles in the first Button so I use the canvas.after for that. I draw one circle of radio 50px, translate the canvas of the Button (which is a Widget) 100px (the diameter of the first circle), and draw exactly the same circle again after the tanslation so I have one circle next to each other.

<Example>:
    Button:
        pos: [0,0]
        size_hint: [None, None]
        size: [200,200]
        canvas.after:
            Line:
                circle: [50, 100, 50]
            Translate:
                x: 100
            Line:
                circle: [50, 100, 50]
    Button:
        pos: [200,0]
        size_hint: [None, None]
        size: [200,200]
        background_color: [0,0,1,1]

I executed this with python main.py --size=500x100. The following Figure shows my result.

one

How come there is a black space in between both of them? I was expecting for this instead:

two

The Translate is affecting the whole drawing space of the window. This is a very control environment so we can guess what is happening but in real scenarios things can get completely misleading. A rotation could be the end of your interface. At this point we have two options:

Option 1 - Undo all the transformations:

Make sure to undo all the transformations we applied to the canvas. This is why it seems that the canvas is share by all the Widgets.

<Example>:
    Button:
        pos: [0,0]
        size_hint: [None, None]
        size: [200,200]
        canvas.after:
            Line:
                circle: [50, 100, 50]
            Translate:
                x: 100
            Line:
                circle: [50, 100, 50]
            Translate:
                x: -100
    Button:
        pos: [200,0]
        size_hint: [None, None]
        size: [200,200]
        background_color: [0,0,1,1]

Option 2 - Use the RelativeLayout:

If we embed the button inside the RelativeLayout, our problems are also solved.

<Example>:
    RelativeLayout:
        pos: [0,0]
        Button:
            pos: [0,0]
            size_hint: [None, None]
            size: [200,200]
            canvas.after:
                Line:
                    circle: [50, 100, 50]
                Translate:
                    x: 100
                Line:
                    circle: [50, 100, 50]
    Button:
        pos: [200,0]
        size_hint: [None, None]
        size: [200,200]
        background_color: [0,0,1,1]

This brings me to a question I am still trying to find. What exactly a RelativeLayout does? The documentation talks about relative positioning [2] but I was not expecting the impact in the whole transformation sets of Rotate, Translate and Scale. The first two could be considered positioning in certain way. Not that clear for scaling.

EDIT: I found and answer for this and posted it here. It leads to another option:

Option 3 - Using PushMatrix and PopMatrix

 

[1] http://kivy.org/docs/api-kivy.graphics.html

[2]http://kivy.org/docs/api-kivy.uix.relativelayout.html?highlight=relativelayout#kivy.uix.relativelayout.RelativeLayout

Leave a reply