Kivy - Graphics



The Kivy framework is equipped with powerful graphics capabilities built on top of OpenGL and SDL instructions. Kivy, like many of the graphics toolkits, provides a canvas to render graphics. However, Kivy's canvas is different from Canvas object found in other frameworks, for example the Canvas in HTML5, or even the Canvas in Python's TKinter library. Let us try to understand how using graphics in Kivy is different.

In Kivy a Graphics Canvas is a set of drawing instructions that define the graphical representation of a widget, which you can see as an unlimited drawing board. Although each GUI widget in Kivy library has a Canvas, all the Kivy widgets share the same coordinate space, which is where you can draw. The coordinate space is not restricted to the size of the window or the application window, so that we can even draw outside of the visible area.

There are two types of graphic instructions in Kivy −

  • Vertex instructions − Instructions used to draw basic geometric shapes like lines, rectangles, ellipses etc. are called vertex instructions.

  • Context instructions − These instructions don't draw anything but manipulate the whole coordinate space, so to add colors to it, rotate, translate and scale it.

The effect of a certain context instruction on a canvas object − for example if we apply rotate instruction to a button's canvas − will be visible in all the subsequent graphics instructions because all widgets share in the coordinate space.

Vertex Instructions

OpenGL uses vertices to describe its graphic objects. Vertex instructions are instructions that draw graphics. Common vertex instructions include Point, Line, Triangle, Rectangle, Ellipse, Mesh, Bezier, etc. The chapter Kivy - Drawing explains the use of drawing instructions in detail.

Context Instructions

Kivy's Graphics instructions include Rotate, Translate, and Scale instructions. They are context instructions that are applied to the vertex instructions, which are displayed in the coordinate space

Rotate

The Rotate instruction works in the context of an object's canvas. The object is rotated in given angle and along given axis.

with self.canvas:
   rotate = Rotate(angle=45)

The rotation is performed as per the parameters passed to it.

  • angle − Property for getting/setting the angle of the rotation. Ranes between 0 to 360

  • axis − Property for getting/setting the axis of the rotation. The format of axis is (x, y, z).

  • origin − Origin of the rotation. The format of the origin can be either (x, y) or (x, y, z).

  • set(float angle, float ax, float ay, float az) − This function sets the angle and axis of rotation.

Scale

The Scale instruction works in the context of an object's canvas. It changes the size of the object along the given axes as per the scale ratio.

with self.canvas:
   s=Scale(2,2,1)

A Scale instruction Create using one or three arguments −

Scale(x, y, z)

Following parameters are accepted by Scale instruction −

origin − Origin of the scale. The format of the origin can be either (x, y) or (x, y, z).

  • x − Property for getting/setting the scale on the X axis.

  • y − Property for getting/setting the scale on the Y axis.

  • z − Property for getting/setting the scale on Z axis.

  • xyz − 3 tuple scale vector in 3D in x, y, and z axis.

The scaling and rotation instructions under the canvas of a particular object causes the entire canvas to change because the canvases share the same coordinate space. Therefore, to retrieve the position and state of the other widgets, there are PushMatrix and PopMatrix instructions.

  • PushMatrix saves the current coordinate space context and.

  • PopMatrix retrieves the last saved coordinate space context.

Therefore, the transformation instructions (Scale, Rotate, and Translate) surrounded by PushMatrix and PopMatrix won't affect the rest of the interface.

Example

We start with placing a label and two buttons on the app window. We use a FloatLAyout that gives us a freedom to place the widgets at the specific coordinates and of specified size.

The Label is provided with a background color by drawing a colored rectangle on its canvas −

self.lbl = Label(
   text= 'Hello World', font_size=24,
   halign='center',size_hint=(.2, .1),
   pos=(300, 250)
)
with self.lbl.canvas:
   Color(0, 1, 0, 0.25)
   Rectangle(pos=self.lbl.pos, size=(200,50))

The button caption Rotate is bound to the update_rotate() method that applies a 45 degree rotation. Note that the PushMatrix stores the coordinates space before rotation and PopMatrix is called after the rotation.

   def update_rotate(self, instance):
      with self.lbl.canvas.before:
         PushMatrix()
         rotate = Rotate(angle=45)
      with self.lbl.canvas.after:
         PopMatrix()
         rotate.origin = self.lbl.center

The Scale button when pressed, causes the Rotate button to be scaled along the X and Y axis. The update_scale() method for the purpose is as below −

def update_scale(self, instance):
   with self.btn.canvas.before:
      PushMatrix()
      s = Scale(2, 2, 1)

   with self.btn.canvas.after:
      PopMatrix()
      s.origin = self.btn.center

The complete example code is as follows −

from kivy.app import App
from kivy.graphics import *
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.core.window import Window

Window.size = (720, 400)

class RotationApp(App):
   def update_rotate(self, instance):
      with self.lbl.canvas.before:
         PushMatrix()
         rotate = Rotate(angle=45)
      with self.lbl.canvas.after:
         PopMatrix()
         rotate.origin = self.lbl.center
   
   def update_scale(self, instance):
      with self.btn.canvas.before:
         PushMatrix()
         s = Scale(2, 2, 1)
      with self.btn.canvas.after:
         PopMatrix()
         s.origin = self.btn.center
   
   def build(self):
      root = FloatLayout()
      self.lbl = Label(
         text='Hello World', font_size=24,
         halign='center', size_hint=(.2, .1),
         pos=(300, 250)
      )
      with self.lbl.canvas:
         Color(0, 1, 0, 0.25)
         Rectangle(pos=self.lbl.pos, size=(200, 50))
      self.btn = Button(
         text='Rotate', size_hint=(None, None),
         pos_hint={'center_x': .3, 'center_y': .1}
      )
      root.add_widget(self.lbl)
      self.btn1 = Button(
         text='Scale', size_hint=(None, None),
         pos_hint={'center_x': .6, 'center_y': .1}
      )
      self.btn.bind(on_press=self.update_rotate)
      self.btn1.bind(on_press=self.update_scale)
      root.add_widget(self.btn)
      root.add_widget(self.btn1)
      return root
      
RotationApp().run()

Output

Run the above code. When you click the "Rotate" button, the label "Hello World" will rotate by 45 degrees.

Kivy Graphics

Click the Scale button. It will change the dimension of Rotate button along the X and Y axes.

Kivy Button Rotate
Advertisements