Kivy - Drawing App



In this chapter, we shall learn to develop a simple Kivy app that lets the user draw filled rectangles and circles (ellipses) of different dimensions and colors by dragging the mouse button.

The user drags the mouse pointer from the top-left corner to the bottomdown corner of the intended rectangle/ellipse. The approach used in the development of the following code is to capture the mouse coordinates at the touch_down event and touch_up event. Hence we start the program with this code in the build() method of App class −

def build(self):
   self.w= Widget()
   self.w.bind(on_touch_down=self.on_touch_down)
   self.w.bind(on_touch_up=self.on_touch_up)
   return(self.w)

We want to let the user choose to draw a rectangle or a circle. Three toggle buttons for choosing to draw either, or clear the canvas have to be added. Hence, we change the layout of App window to box layout, adding the Widget object on top, and three buttons placed below it.

lo = BoxLayout(orientation='vertical')

self.w = Widget()
self.w.bind(on_touch_down=self.on_touch_down)
self.w.bind(on_touch_up=self.on_touch_up)
lo.add_widget(self.w)

hlo = BoxLayout(orientation='horizontal', size_hint=(1, .1))

self.b2 = ToggleButton(text='Circle', group='mode')
self.b1 = ToggleButton(text='Rectangle', state='down', group='mode')
self.b3 = ToggleButton(text='Clear', group='mode')

hlo.add_widget(self.b1)
hlo.add_widget(self.b2)
hlo.add_widget(self.b3)
lo.add_widget(hlo)

return lo

To draw the desired shape, we need to capture the mouse positions on touch down, and touch up.

The on_touch_down() method is simple −

def on_touch_down(self, *args):
   self.td = args[1].pos

All the processing happens in on_touch_up() method. The captured coordinates of down event and up event are used to calculate the dimensionsof rectangle or circle.

For circle, the x-radius, y-radius and the center are computed as follows −

self.tu=args[1].pos
   xr = (self.tu[0]-self.td[0])/2
   yr = (self.td[1]-self.tu[1])/2
   c=(self.td[0]+xr, self.td[1]-yr)

We need the width and height as well as the top-left corner coordinates for drawing rectangle. The self.td tuple gives top-left point, xr*2 gives the width and yr*2 gives the height.

The shapes are drawn on the widget canvas. We choose a random color for drawing. The text property of the pressed button gives us the shape to be drawn −

color = (random(), random(), random())
with self.w.canvas:
   Color(*color)
   if self.btn=='Rectangle':
      Rectangle(pos=self.td, size=(xr*2,yr*2))
   if self.btn=='Circle':
      Ellipse(pos=(c), size=(xr,yr))

Three toggle buttons are bound to a method. If the caption is Clear, the widget canvas is cleared.

def clear_canvas(self, instance, value):
   self.btn = instance.text
   self.press = True
   if value == 'down' and self.btn == 'Clear':
      self.w.canvas.clear()
   return True

Example

The complete code for the app is given below −

from random import random
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color, Ellipse, Line, Rectangle
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window

Window.size = (720, 400)

class MyPaintApp(App):
   def clear_canvas(self, instance, value):
      self.btn = instance.text
      self.press = True
      if value == 'down' and self.btn == 'Clear':
         self.w.canvas.clear()
      return True

   def on_touch_down(self, *args):
      self.td = args[1].pos

   def on_touch_up(self, *args):
      if self.press == True:
         self.press = False
         return True
      self.tu = args[1].pos
      xr = (self.tu[0] - self.td[0]) / 2
      yr = (self.td[1] - self.tu[1]) / 2
      c = (self.td[0] + xr, self.td[1] - yr)
      color = (random(), random(), random())
      with self.w.canvas:
         Color(*color)
         if self.btn == 'Rectangle':
            Rectangle(pos=self.td, size=(xr * 2, yr * 2))
         if self.btn == 'Circle':
            Ellipse(pos=(c), size=(xr, yr))

   def build(self):
      self.press = False
      self.btn = 'Rectangle'
      
      lo = BoxLayout(orientation='vertical')
      
      self.w = Widget()
      self.w.bind(on_touch_down=self.on_touch_down)
      self.w.bind(on_touch_up=self.on_touch_up)
      lo.add_widget(self.w)
      
      hlo = BoxLayout(orientation='horizontal', size_hint=(1, .1))
      
      self.b2 = ToggleButton(text='Circle', group='mode')
      self.b1 = ToggleButton(text='Rectangle', state='down', group='mode')
      self.b3 = ToggleButton(text='Clear', group='mode')
      self.b1.bind(state=self.clear_canvas)
      self.b2.bind(state=self.clear_canvas)
      self.b3.bind(state=self.clear_canvas)
      
      hlo.add_widget(self.b1)
      hlo.add_widget(self.b2)
      hlo.add_widget(self.b3)
      lo.add_widget(hlo)
      
      return lo
      
MyPaintApp().run()

Output

Run the above code. Choose the shape you want to draw. Drag the mouse from top-left to bottom-right. The rectangles/circles are drawn at drawn at different positions with random colors.

Kivy Drawing App

Click the Clear button to clear the drawings on the canvas.

Advertisements