- A Kivy
canvas is not the space in which you paint.
The major problems I had at the beginning with the
canvas were due to its name. Particularly considering all the buzz about HTML5
canvas. I initially visualize the
canvas as the cloth that painters paint. Since all the widgets has one
canvas, I started to get confused and asked why did the
Widget seem to share the same canvas?
The answer needs, first, to clarify what is the
canvas? (2nd bullet point) and where is it that the widgets share? (3rd bullet point)
- A Kivy
canvas is a set of instructions!
canvas is basically a container of instructions. More like a recipe that tells you how to draw, rather than a place to draw. In other words the bitmap is not saved (like .bmp), but the vectors (like .gif). The
canvas of the
Widget basically keep the instructions to draw that performs the drawing.
- The widgets share the same coordinate space, not the same
The final answer to my previous question is that all the widgets have a different
canvas but all the
canvas draw in exactly the same drawing space, i.e. the coordinate space. Moreover, the drawing space is not limited to the position and size of the widget. The 0,0 of the drawing space is always (except for the RelativeLayout and the ScatterLayout ) the bottom-left corner. If we want to draw a Rectangle that covers the whole area of the
Widget we should do something like this:
Notice that we need to specify explicitly that the size and pos of the Rectangle is the same of the Widget. There lies...
- ... the little secret of RelativeLayout.
The previous code doesn't work with
RelativeLayout. Instead we actually use the coordinate (0,0) to achieve the same result:
RelativeLayout includes a Translate instruction:
That instruction is going to translate the
canvas to the
self.pos position. In other words, the (0,0) is now
PopMatrix to control the effects of sharing the same drawing space.
Sharing the same drawing space has its consequences. The problem is that the instructions of one
canvas may affect the next one. If I rotate the drawing space, the drawing space will be rotated until some other instructions rotate it back again. It is up to us to guarantee that the context of the drawing space remains the same when the
canvas stop drawings. Kivy provides two instructions
RelativeLayout uses a
PushMatrix before the instructions of the
canvas and a
PopMatrix after it.
PushMatrix saves the current context state (rotations, translations, zoomings) and the
PopMatrix recovers it and let thing as they were before.
- The color is an exception.
PopMatrix doesn't have an effect over the
It doesn't matter which was the previous
Rectangle and and the
Line would be red (rgb: 1,0,0).
- The order in which
canvas instructions are executed.
The execution order follow a traversal order. There is recursivity involved but, let's stick with what you have to consider. When you are using a
canvas, the instructions of all the
canvas of the parent and grandparents were executed. Also the instructions of the
canvas of the first added siblings. The instructions of the
canvas children hasn't been executed yet. Neither the ones of the siblings added after. When you are coding you actually perceive a normal top-down execution and thinking that way is ok for most of the cases. When you start using dynamic interfaces, adding and removing children, strange things seem to happen and you will need to remember the order of execution. Perhaps, more important is to know how to do to manipulate this order.
- Drawing after the children with
What happens if we want to execute instructions after the children were added. In that case we have another
canvas.after) that is called after traverse all the children.
canvas execution order is as follows:
(2) All the
Notice that all the
canvas of the
Button are executed in between
- What about
You probably notice that there is a third
canvas.before). It executes just before the
canvas. I found the
canvas.before particularly useful when
(1) I need to manipulate the
Color of an instance and, (2) I need to manipulate inheritance. Let's say I have
MyWidget that draws a
So I can modify the
Color of a particular instance doing something like this:
mywidget = MyWidget
If I use
canvas instead of
RedWidget, the instruction is going to be executed after the Rectangle, so it won't apply to the Rectangle.
(2) Similarly, when I need to manipulate inheritance. Let's say I have MyWidget that draws a Rectangle inside. I can do the following to change the color of the base class Rectangle:
RedWidget inherits from
MyWidget and I would have the same problem if I just use the
canvas. Of course, similar applications goes for the
- Drawing on top of a
This works for any other
Widget and not just for the
Label but it is probably the most common case. If you want to draw over the
Button background you can use the
canvas.after. Otherwise the background will cover your drawings:
The rectangle of the previous code will actually cover its text.
This post turned out to be longer than I was expected. I hope I didn't make any mistake in my explanation. If so, please let me know.