I already posted before about the how to use transformations with Kivy. There was still some gaps in the explanation. After a while of being working with Kivy I have a much better understanding of what it is happening so I came with a list of 10 things that you should know about the Kivy canvas
.
- 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 HTML5canvas
. I initially visualize thecanvas
as the cloth that painters paint. Since all the widgets has onecanvas
, I started to get confused and asked why did theWidget
seem to share the same canvas? - A Kivy
canvas
is a set of instructions!A
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). Thecanvas
of theWidget
basically keep the instructions to draw that performs the drawing. - The widgets share the same coordinate space, not the same
canvas
.The final answer to my previous question is that all the widgets have a different
canvas
but all thecanvas
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 theWidget
we should do something like this:... Widget: canvas: Rectangle: pos: self.pos size: self.size
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:... Widget: canvas: Rectangle: pos: 0,0 size: self.size
The
RelativeLayout
includes a Translate instruction:Translate: xy: self.pos
That instruction is going to translate the
canvas
to theself.pos
position. In other words, the (0,0) is nowself.pos
. PushMatrix
andPopMatrix
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
Widget
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 thecanvas
stop drawings. Kivy provides two instructionsPushMatrix
andPopMatrix
.RelativeLayout
uses aPushMatrix
before the instructions of thecanvas
and aPopMatrix
after it.PushMatrix: ... Translate: xy: self.pos ... PopMatrix:
The
PushMatrix
saves the current context state (rotations, translations, zoomings) and thePopMatrix
recovers it and let thing as they were before.- The color is an exception.
That said,
PushMatrix
andPopMatrix
doesn't have an effect over theColor
instruction.PushMatrix: Color: rgb: 1,0,0 Rectangle: ... PopMatrix: Line: ...
It doesn't matter which was the previous
Color
beforePushMatrix
, theRectangle
and and theLine
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
Widget
canvas
, the instructions of all thecanvas
of the parent and grandparents were executed. Also the instructions of thecanvas
of the first added siblings. The instructions of thecanvas
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
canvas.after
What happens if we want to execute instructions after the children were added. In that case we have another
canvas
(calledcanvas.after
) that is called after traverse all the children.Widget: canvas: ... canvas.after: ... Button: canvas.before: ... canvas: ... canvas.after: ...
The
canvas
execution order is as follows:
(1) TheWidget
canvas
(2) All theButton
canvas
(canvas.before
,canvas
,canvas.after
)
(3) TheWidget
canvas.after
Notice that all thecanvas
of theButton
are executed in betweencanvas
andcanvas.after
. - What about
canvas.before
?You probably notice that there is a third
canvas
(canvas.before
). It executes just before thecanvas
. I found thecanvas.before
particularly useful when
(1) I need to manipulate theColor
of an instance and, (2) I need to manipulate inheritance. Let's say I haveMyWidget
that draws aRectangle
inside.<MyWidget@Widget>: canvas: Rectangle: ...
So I can modify the
Color
of a particular instance doing something like this:... mywidget = MyWidget mywidget.canvas.before: Color(rgba=(1,0,0,0))
If I use
canvas
instead ofcanvas.before
inRedWidget
, 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@MyWidget>: canvas.before: Color: rgb: 1,0,0
The
RedWidget
inherits fromMyWidget
and I would have the same problem if I just use thecanvas
. Of course, similar applications goes for thecanvas.after
. - Drawing on top of a
Button
Widget
withcanvas.after
.This works for any other
Widget
and not just for theLabel
but it is probably the most common case. If you want to draw over theButton
background you can use thecanvas.after
. Otherwise the background will cover your drawings:Button: text: canvas.after: Rectangle: pos: self.pos size: self.size
The rectangle of the previous code will actually cover its text.
The answer needs, first, to clarify what is the canvas
? (2nd bullet point) and where is it that the widgets share? (3rd bullet point)
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.
Thanks man this article was very much helpful 🙂
Hi Roberto,
Thanks for these explanations about Canvas in Kivy. I would say that there is not so much documentation about all that… so it’s complex to do something out of the standard (and too much simple) way.
Do you have an idea on how to change the source string of the Reclangle when you have this (when I click on a button, by example) :
ListItemButton:
id: iconListItemButton_id
canvas.before:
Color:
rgba: 1,1,1, 1
Rectangle:
source: “images/ic_action_star.png”
pos: self.pos
size: self.size
I’ve tried this in Python:
theRect = iconButton.canvas.before.get_group(“? ? ?”)
to change the source string, but no name group match on the Reclangle.
Is it a way to get the Reclagle Instruction (and play with it in Python language) ?
Julien
Hi,
Thanks for reading my blog. This seems to be a quite common problem. I hit that wall in different ways. The short answer is that you need to create a property in your python code that you will reference in the kv language. You will use that property to modify the string of the source.
Take a look at this question in stackoverflow: http://stackoverflow.com/questions/12997545/how-do-i-change-the-color-of-my-widget-in-kivy-at-run-time
If you still have problems, ask in stackoverflow.com under kivy tag. I often go to that site but there is a lot of people that can help and you will a quicker reply. Don’t forget to add some more code.
And there is even more complicated versions of your problem, when you don’t use the kv language. The kv language binds events internally.
http://stackoverflow.com/questions/17058098/how-do-i-update-the-color-of-a-dynamically-added-ellipse-not-using-builder
http://stackoverflow.com/questions/17130388/why-do-i-need-to-create-a-new-instance-of-a-line-instead-of-simply-update-or-add
Great article! I couldn’t find much information about canvas.after and canvas.before. Now I get it!
Helpful thanks. I have a question about screen transitions. I’m using canvas to set screen backgrounds but the problem is that unless you use canvas.after, you get black in between screens and not a nice smooth transition. I don’t know why it’s only smooth with canvas.after, I can’t find any documentation.
Problem described here with gifs:
http://stackoverflow.com/questions/39724267/kivy-black-screen-between-transitions/
The problem with filling the screen with canvas.after of course is that it will cover up every other widget. I can’t seem to find a way to have smooth transitions with canvas.before.
Is there a way to use canvas.after but somehow add widgets on top of that? Or do you know another way to solve this problem?
Thanks.
Thanks for your question. I added an answer to the original question (http://stackoverflow.com/a/40093938/1060349). I tested, and I have no problems using `canvas` or `canvas.before`. I also reply your comment in another answer I gave regarding the canvas (http://stackoverflow.com/a/17007957/1060349). I tried to expand on the use of the canvas. There is a couple of sections in my book that deal with the canvas that you might find useful, or you can ask me directly (preferable with a stackoverflow link that includes code sample) when you get stuck in anything.
I hope I had helped, and if not don’t hesitate in asking again. Cheers!
Pingback: Kivy For The Non Computer Scientist – Part 11: Using KV Language To Set Widget Text Size, Text Position And Text And Button Colours | ....brad's blog....
Very informative. Thanks!