I have been insisting in the importance of PushMatrix and PopMatrix to control the use of the canvas in Kivy in:
- 10 things you should know about the Kivy canvas
- Rotate, Translate, Scale and RelativeLayout in Kivy – undercovered!
- Rotate, Translate, Scale and RelativeLayout in Kivy
In today's post I explain a very specific example of how to rotate an Image without affecting the whole interface. I start with the common problem that the novice will experience and explain how to fix it:
1. The Initial Interface:
This is the screenshot of the interface we are going to work with. We want to rotate the image on the top-right of the layout.

This is the respective code:
from kivy.app import App from kivy.uix.gridlayout import GridLayout from kivy.lang import Builder Builder.load_string(""" <KivyImage@Image>: source: 'kivy.png' pos: self.parent.pos size_hint: .5,.4 canvas: Color: rgba: 1,0,0,.5 Line: rectangle: self.x, self.y, self.width, self.height <TestApp>: cols: 2 Button: text: "Place Holder 1" FloatLayout: KivyImage: Button: text: "Place Holder 2" """) class TestApp(App, GridLayout): def build(self): return self if __name__ == "__main__": TestApp().run()
The interface is very simple. A GridLayout
with 2 columns and 3 widgets. A Button
(Place Holder 1), a FloatLayout
with an Image inside and another Button
(Place Holder 2). The FloatLayout
contains the KivyImage
class that we are going to be working with. The KivyImage
also contains a rectangular border as a reference to follow our notations.
2. The Problem:
The instruction we need to use for rotating is Rotate
. We use three parameters for it: axis
that sets the axis are we going to use for the rotation, usually z since; angle
that sets the amount of degrees we want to rotate. After using the Rotate
instruction, our code will look like this:
<KivyImage@Image>: source: 'kivy.png' pos: self.parent.pos size_hint: .5,.4 canvas: Rotate: axis: 0,0,1 angle: 60 origin: self.center Color: rgba: 1,0,0,.5 Line: rectangle: self.x, self.y, self.width, self.height
Here is the result:

As you can see, after applying the instruction, everything is rotated (including the last Button
). This means, that when we use context instructions (Scale
, Rotate
and Translate
), the whole coordinate space is affected.
More over, our image didn't Rotate just the Rectangle!
3. Avoiding a global rotation:
We need to control the rotation, so the whole interface after the Rotate instruction is not affected. One way would be manually un-rotate with something like:
Rotate: axis: 0,0,1 angle: -60 origin: self.center
Of course, that will be quite tedious. Instead, we are going to use two important instructions: PushMatrix
, that saves the current context, and PopMatrix
, that recovers the last saved context. Now the code looks like this:
<KivyImage@Image>: source: 'kivy.png' pos: self.parent.pos size_hint: .5,.4 canvas: PushMatrix Rotate: axis: 0,0,1 angle: 60 origin: self.center Color: rgba: 1,0,0,.5 Line: rectangle: self.x, self.y, self.width, self.height PopMatrix
And our new output should look like the following:

Almost there, except that our images continues in the same original position. Let's finally Rotate the image.
4. Rotate the image for once!:
Sorry, I tell you why the image is not rotated. In the 2nd step, even though the whole context where rotated, the image didn't. The only explanation is that the Rotate
where executed after the image is display on the screen.
Somehow, we need to guarantee that our Rotate
instruction gets executed before. There is three sets of canvas instructions: canvas.before
, canvas
and canvas.after
. Their names are intuitive, so let's reorganize our instructions:
<<KivyImage@Image>: source: 'kivy.png' pos: self.parent.pos size_hint: .5,.4 canvas.before: PushMatrix Rotate: axis: 0,0,1 angle: 60 origin: self.center canvas: Color: rgba: 1,0,0,.5 Line: rectangle: self.x, self.y, self.width, self.height canvas.after: PopMatrix
And finally we got our image rotated without affecting the rest of the interface:

It is not strictly necessary to move the PopMatrix
to the canvas.after
section. The difference would be in the children of the image. In this case, it doesn't have. But in case it has children, then they will be rotated if the PopMatrix
is in the canvas.after
section.
Read more about the canvas in 10 things you should know about the Kivy canvas.
Thank you for writing this article! Helped me solve my problem.