Kivy - Canvas



Unlike some of the other GUI toolkits (such as TKinter for example), Kivy doesn't have an independent Canvas widget. Instead, you need to use the canvas that each Widget in Kivy already has by default. When you create a widget, you can create all the instructions needed for drawing on its canvas.

We need to note 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 the bottom-left corner.

The "canvas.before" property particularly useful when you need to manipulate the Color of an instance. On the other hand, canvas.after is used to execute any instructions after the children were added. It is called after traverse all the children.

Canvas in Kivy is a set of drawing instructions. To draw, you will need a Canvas object and Instruction objects. For example, to draw a rectangle on a label's canvas −

from kivy.graphics import *
with label.canvas:
   Color(1., 0, 0)    # Add a red color

   # Add a rectangle
   Rectangle(pos=(10, 10), size=(500, 500))

If you have to do the same by not using Python's context manager −

label.canvas.add(Color(1., 0, 0))
label.canvas.add(Rectangle(size=(500, 500)))

You can draw shapes such as rectangle, ellipse, line and Beizer on any widget's canvas, including the Widget object itself. (Widget class is the base for all the other visual widgets in Kivy)

To draw a rectangle on the canvas, we need to specify the pos and size attributes −

from kivy.graphics import Rectangle
Rectangle(**kwargs)

Parameters

  • pos − List of X and Y coordinate values specifying Position of the rectangle, in the format (x, y).

  • size − List specifying the width and height of the rectangle, in the format (width, height).

The following code draws a rectangle on the canvas of a Widget object −

widget=Widget()

with widget.canvas:
   Color(0,0,1,1)
   Rectangle(pos=(50,300), size_hint=(None, None), size=(300,200))

The instructions Color and Rectangle are automatically added to the canvas object and will be used when the window is drawn.

Syntax

You may want to use the "kv" language syntax −

Widget:
   canvas:
      color:
         rgb: 0,0,1
      Rectangle:
         pos: self.pos
         size: self.size

A Label object in Kivy doesn't have a backgound color property. To get around this, you can draw a rectangle fille with the desired color on its canvas to lend background.

lbl = Label(text='Hello World', font_size=24)
   with lbl.canvas.before:
      Color(1,1,0)
      Rectangle(pos=lbl.pos, size=lbl.size)

Or, using the "kv" script −

Label:
   text:'Hello World'
   font_size:24
   canvas.before:
      Color:
         rgb: (1,1,0)
      Rectangle:
         pos:self.pos
         size=:self.size

Syntax

You can draw an ellipse on the canvas using the following syntax −

from kivy.graphics import Ellipse
Ellipse(*args, **kwargs)

Parameters

  • segments − Defines how many segments are needed for drawing the ellipse. More the segments, drawing will be smoother if you have many segments.

  • angle_start − float, defaults to 0.0 and Specifies the starting angle, in degrees, of the disk portion.

  • angle_end − float, defaults to 360.0, specifies the ending angle, in degrees, of the disk portion.

  • angle_end − End angle of the ellipse in degrees, defaults to 360.

  • angle_start − Start angle of the ellipse in degrees, defaults to 0.

  • pos − the position on the canvas coordinate system

  • size − the X and Y radii of the ellipse. If both are equal, it becomes a circle.

You can also draw an image directly on the canvas, by assigning the image file as the source property of rectangle or ellipse.

Example 1

The following code implements all the drawing instructions explained above to display a rectangle, an ellipse and an image on a widget canvas. It also places a label with a rectangle having background color drawn on its canvas.

from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.graphics import *
from kivy.core.window import Window

Window.size = (720,400)

class canvasdemoapp(App):
   def build(self):
      widget=Widget()

      # rectangle on canvas
      with widget.canvas:
         Color(0,0,1,1)
         Rectangle(
            pos=(50,300), size_hint=(None, None),
            size=(300,200)
         )
         Color(0.5, .2, 0.4, 1)
         d = 100

         # ellipse
         Ellipse(pos=(600,100), size=(d+75, d))
         Color(.5,.5,.5)

         # image
         Rectangle(source='kivy-logo.png', pos=(50,100))
         Color(1,1,1)
         Rectangle(source='TPlogo.png', pos=(300, 100))

         # label with background
         lbl = Label(
            text='Hello World', font_size=24,
            pos=(Window.width/2, 300), size =(200,200),
            color=(0,0,1,1)
         )
         with lbl.canvas.before:
            Color(1,1,0)
            Rectangle(pos=lbl.pos, size=lbl.size)
         widget.add_widget(lbl)
         btn=Button(
            text='Button', font_size=24,
            background_color= (.8, .4, .3, 1),
            pos=(500,10)
         )
         widget.add_widget(btn)
      return widget
canvasdemoapp().run()

Output

When you run this code, it will produce the following output −

Kivy Canvas

Example 2

The Widget object responds to all the touch events (such as on_touch_down and on_touch_move, etc.).

In the example below, a callback on the on_touch_down event on a Widget object obtains the coordinates where the touch event occurred and draws a circle (ellipse of equal X and Y radii) with random values of RGB colours.

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import *
import random
from kivy.core.window import Window

Window.size = (720,300)
class widget(Widget):
   def on_touch_down(self, touch):
      colorR = random.randint(0, 255)
      colorG = random.randint(0, 255)
      colorB = random.randint(0, 255)
   
      self.canvas.add(Color(
         rgb=(colorR / 255.0, colorG / 255.0, colorB / 255.0)
      ))
   
      d = 30
      self.canvas.add(Ellipse(
         pos=(touch.x - d / 2, touch.y - d / 2),
         size=(d, d)
      ))
      
class circlesapp(App):
   def build(self):
      return widget()
circlesapp().run()

Output

Run the program and generate touch_down event at different places. Circles will be painted at locations.

Kivy Canvas Circle
Advertisements