Getting started with Kivy

This post collates various resources for getting started with the Kivy graphical framework for Python.

Installation

Follow the official installation documentation.

Introductory resources

General resources

Kivy examples

Kivy comes with many examples, with fairly complete coverage of most widgets and other core features. These are very valuable for working out how to use things.

On some platforms, the examples may be installable as a separate package from your operating system’s package manager.

See in particular:

Support channels

Getting started with Kivy

This post collates various resources for getting started with the Kivy graphical framework for Python.

Installation

Follow the official installation documentation.

Introductory resources

General resources

Kivy examples

Kivy comes with many examples, with fairly complete coverage of most widgets and other core features. These are very valuable for working out how to use things.

On some platforms, the examples may be installable as a separate package from your operating system’s package manager.

See in particular:

Support channels

Kivy tutorial 009: Finishing the drawing app

This is number 9 in a series of introductory Kivy tutorials.

Central themes: Passing data between widgets, creating Kivy properties

This tutorial directly follows on from the previous, so start by retrieving the previous code, as below:

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from random import random

class DrawingWidget(Widget):
    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        if not self.collide_point(*touch.pos):
            return

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return

        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]


class Interface(BoxLayout):
    pass

class DrawingApp(App):

    def build(self):
        root_widget = Interface()
        return root_widget

DrawingApp().run()

drawing.kv:

<DrawingWidget>:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size

<ColourSlider@Slider>:
    min: 0
    max: 1
    value: 0.5
    size_hint_y: None
    height: 50


<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    ColourSlider:
        id: red_slider
    ColourSlider:
        id: green_slider
    ColourSlider:
        id: blue_slider
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos

With this code, you should still be able to draw in the DrawingWidget region of the app interface, but the lines still have a random colour each time. Our final task is to make the lines use the colour selected via the sliders.

Let’s start by reassessing where the app’s state should be held. For a start, we need to store somewhere the target colour for the lines. It’s natural to put this inside the DrawingWidget, since this class is what does the drawing and needs to know what colour to use.

The best way to store this data is to use a Kivy property of our own. We’ve made use of many Kivy properties of other widgets already, but this time there isn’t one already created to hold the colour, so it’s time to create one.

Change the DrawingWidget code as follows:

from kivy.properties import ListProperty

class DrawingWidget(Widget):

    target_colour_rgb = ListProperty([0, 0, 0])

    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        if not self.collide_point(*touch.pos):
            return

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return

        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]

That’s all it takes to define a new Kivy property, and it automatically has all the behaviour you’ve seen so far. For instance, if you change the target_colour_rgb of a DrawingWidget instance, an event is automatically dispatched. In fact because this is a ListProperty an event will be dispatched even if we just change the value of an item of the list! There are other types of Kivy property for ensuring correct event dispatching with different types of object (list, dict, int/float, generic objects etc.), which you can find in the documentation.

Note: It may look a little strange that we’ve defined the property at the class level (no reference to self.target_colour_rgb), and it is clearly a ListProperty and not an actual list so how does accessing its values work? The answer is that Kivy properties are descriptors, which are defined at the class level but here are coded to behave like normal lists/ints/whatever when accessed from a class instance. You don’t need to worry about these details, just consider the properties as normal attributes of your objects when accessing them.

As an example of what that really means, lets hook up the property to change so that we can respond to these events. Change your kv file <Interface> rule to read as follows:

<Interface>:
    orientation: 'vertical'
    DrawingWidget:
        target_colour_rgb: red_slider.value, green_slider.value, blue_slider.value  # <- new line
    ColourSlider:
        id: red_slider
    ColourSlider:
        id: green_slider
    ColourSlider:
        id: blue_slider
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos

Note that the only new line here sets the value of target_colour_rgb based on the values of the sliders. We’re once again taking advantage of automatic kv event binding: whenever any of red_slider.value, green_slider.value or blue_slider.value changes then this line will be re-evaluated to update target_colour_rgb. We can add some code to prove that it’s working, via a new method in the DrawingWidget class:

def on_target_colour_rgb(self, instance, value):
    print(f"target_colour_rgb changed to {self.target_colour_rgb}")

A method with this name will be called automatically whenever the target_colour_rgb property changes - this is another handy feature of Kivy event dispatching, instead of binding explicitly this default event method is always available. You can consider that code something like self.bind(target_colour_rgb=self.on_target_colour_rgb) has been automatically run to create the event binding.

Now, run the application and move the values of the sliders. You should see code printed in your terminal every time a slider moves, because every movement updates the value of target_colour_rgb:

target_colour_rgb changed to [0.20853658536585365, 0.6012195121951219, 0.4573170731707317]
target_colour_rgb changed to [0.20853658536585365, 0.6012195121951219, 0.4585365853658537]
target_colour_rgb changed to [0.20853658536585365, 0.6012195121951219, 0.45975609756097563]

Note: The colour changes in this example are very small because you’re getting an update every time the slider moves even a single pixel!

The final step is to make the DrawingWidget use this target colour for the next line it draws. For this we just have to update the on_touch_down method:

def on_touch_down(self, touch):
    super(DrawingWidget, self).on_touch_down(touch)

    if not self.collide_point(*touch.pos):
        return

    with self.canvas:
        Color(*self.target_colour_rgb)  # <- this line changed
        self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

That’s all there is to it! When we make the new Color instruction for the new line, we pass in the current value of our property instead of selecting random values.

Run the app now and every line should match your currently selected colour:

Line drawn using selected colour

For a further example, let’s add a similar method to set the Line width. Again, we add a Kivy property to DrawingWidget:

from kivy.properties import ListProperty, NumericProperty

class DrawingWidget(Widget):
    target_colour_rgb = ListProperty([0, 0, 0])
    target_width_px = NumericProperty(0)

Then in the kv <Interface> rule we add a Slider to select the width, and connect it to the property:

<Interface>:
    orientation: 'vertical'
    DrawingWidget:
        target_colour_rgb: red_slider.value, green_slider.value, blue_slider.value
        target_width_px: width_slider.value
    ColourSlider:
        id: red_slider
    ColourSlider:
        id: green_slider
    ColourSlider:
        id: blue_slider
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: "width: {}".format(width_slider.value)
        Slider:
            id: width_slider
            min: 2
            max: 10
            value: 2

And finally, update the DrawingWidget.on_touch_down to use the currently-selected width for the new Line instruction:

def on_touch_down(self, touch):
    super(DrawingWidget, self).on_touch_down(touch)

    if not self.collide_point(*touch.pos):
        return

    with self.canvas:
        Color(*self.target_colour_rgb)
        self.line = Line(points=[touch.pos[0], touch.pos[1]],
                         width=self.target_width_px)

Note: I’ve added not just a single Slider, but a new BoxLayout to the kv rule, in order to display a Label indicating what the slider is for. Notice how, consistent with everything so far, the Label automatically updates to always show the current value of the Slider.

Run the app and try the drawing. You should now be able to control both the colour and width of every line:

Lines drawn using selected colour and width

With that, the application is fully connected together. We have a UI element for drawing, alongside extra elements for controlling the details of the lines, with data passed around using Kivy properties. These basic ideas are at the heart of all Kivy applications.

This would be a good time to experiment. Try adding or removing widgets, and maybe adding more customisation to the lines.

Full code

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from kivy.properties import ListProperty, NumericProperty

from random import random

class DrawingWidget(Widget):
    target_colour_rgb = ListProperty([0, 0, 0])
    target_width_px = NumericProperty(0)

    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        if not self.collide_point(*touch.pos):
            return

        with self.canvas:
            Color(*self.target_colour_rgb)
            self.line = Line(points=[touch.pos[0], touch.pos[1]],
                            width=self.target_width_px)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return

        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]

    def on_target_colour_rgb(self, instance, value):
        print(f"target_colour_rgb changed to {self.target_colour_rgb}")


class Interface(BoxLayout):
    pass

class DrawingApp(App):

    def build(self):
        root_widget = Interface()
        return root_widget

DrawingApp().run()

drawing.kv:

<DrawingWidget>:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size

<ColourSlider@Slider>:
    min: 0
    max: 1
    value: 0.5
    size_hint_y: None
    height: 50


<Interface>:
    orientation: 'vertical'
    DrawingWidget:
        target_colour_rgb: red_slider.value, green_slider.value, blue_slider.value
        target_width_px: width_slider.value
    ColourSlider:
        id: red_slider
    ColourSlider:
        id: green_slider
    ColourSlider:
        id: blue_slider
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: "width: {:.1f}".format(width_slider.value)
        Slider:
            id: width_slider
            min: 2
            max: 10
            value: 2

Kivy tutorial 009: Finishing the drawing app

This is number 9 in a series of introductory Kivy tutorials.

Central themes: Passing data between widgets, creating Kivy properties

This tutorial directly follows on from the previous, so start by retrieving the previous code, as below:

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from random import random

class DrawingWidget(Widget):
    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        if not self.collide_point(*touch.pos):
            return

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return

        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]


class Interface(BoxLayout):
    pass

class DrawingApp(App):

    def build(self):
        root_widget = Interface()
        return root_widget

DrawingApp().run()

drawing.kv:

<DrawingWidget>:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size

<ColourSlider@Slider>:
    min: 0
    max: 1
    value: 0.5
    size_hint_y: None
    height: 50


<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    ColourSlider:
        id: red_slider
    ColourSlider:
        id: green_slider
    ColourSlider:
        id: blue_slider
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos

With this code, you should still be able to draw in the DrawingWidget region of the app interface, but the lines still have a random colour each time. Our final task is to make the lines use the colour selected via the sliders.

Let’s start by reassessing where the app’s state should be held. For a start, we need to store somewhere the target colour for the lines. It’s natural to put this inside the DrawingWidget, since this class is what does the drawing and needs to know what colour to use.

The best way to store this data is to use a Kivy property of our own. We’ve made use of many Kivy properties of other widgets already, but this time there isn’t one already created to hold the colour, so it’s time to create one.

Change the DrawingWidget code as follows:

from kivy.properties import ListProperty

class DrawingWidget(Widget):

    target_colour_rgb = ListProperty([0, 0, 0])

    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        if not self.collide_point(*touch.pos):
            return

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return

        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]

That’s all it takes to define a new Kivy property, and it automatically has all the behaviour you’ve seen so far. For instance, if you change the target_colour_rgb of a DrawingWidget instance, an event is automatically dispatched. In fact because this is a ListProperty an event will be dispatched even if we just change the value of an item of the list! There are other types of Kivy property for ensuring correct event dispatching with different types of object (list, dict, int/float, generic objects etc.), which you can find in the documentation.

Note: It may look a little strange that we’ve defined the property at the class level (no reference to self.target_colour_rgb), and it is clearly a ListProperty and not an actual list so how does accessing its values work? The answer is that Kivy properties are descriptors, which are defined at the class level but here are coded to behave like normal lists/ints/whatever when accessed from a class instance. You don’t need to worry about these details, just consider the properties as normal attributes of your objects when accessing them.

As an example of what that really means, lets hook up the property to change so that we can respond to these events. Change your kv file <Interface> rule to read as follows:

<Interface>:
    orientation: 'vertical'
    DrawingWidget:
        target_colour_rgb: red_slider.value, green_slider.value, blue_slider.value  # <- new line
    ColourSlider:
        id: red_slider
    ColourSlider:
        id: green_slider
    ColourSlider:
        id: blue_slider
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos

Note that the only new line here sets the value of target_colour_rgb based on the values of the sliders. We’re once again taking advantage of automatic kv event binding: whenever any of red_slider.value, green_slider.value or blue_slider.value changes then this line will be re-evaluated to update target_colour_rgb. We can add some code to prove that it’s working, via a new method in the DrawingWidget class:

def on_target_colour_rgb(self, instance, value):
    print(f"target_colour_rgb changed to {self.target_colour_rgb}")

A method with this name will be called automatically whenever the target_colour_rgb property changes - this is another handy feature of Kivy event dispatching, instead of binding explicitly this default event method is always available. You can consider that code something like self.bind(target_colour_rgb=self.on_target_colour_rgb) has been automatically run to create the event binding.

Now, run the application and move the values of the sliders. You should see code printed in your terminal every time a slider moves, because every movement updates the value of target_colour_rgb:

target_colour_rgb changed to [0.20853658536585365, 0.6012195121951219, 0.4573170731707317]
target_colour_rgb changed to [0.20853658536585365, 0.6012195121951219, 0.4585365853658537]
target_colour_rgb changed to [0.20853658536585365, 0.6012195121951219, 0.45975609756097563]

Note: The colour changes in this example are very small because you’re getting an update every time the slider moves even a single pixel!

The final step is to make the DrawingWidget use this target colour for the next line it draws. For this we just have to update the on_touch_down method:

def on_touch_down(self, touch):
    super(DrawingWidget, self).on_touch_down(touch)

    if not self.collide_point(*touch.pos):
        return

    with self.canvas:
        Color(*self.target_colour_rgb)  # <- this line changed
        self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

That’s all there is to it! When we make the new Color instruction for the new line, we pass in the current value of our property instead of selecting random values.

Run the app now and every line should match your currently selected colour:

Line drawn using selected colour

For a further example, let’s add a similar method to set the Line width. Again, we add a Kivy property to DrawingWidget:

from kivy.properties import ListProperty, NumericProperty

class DrawingWidget(Widget):
    target_colour_rgb = ListProperty([0, 0, 0])
    target_width_px = NumericProperty(0)

Then in the kv <Interface> rule we add a Slider to select the width, and connect it to the property:

<Interface>:
    orientation: 'vertical'
    DrawingWidget:
        target_colour_rgb: red_slider.value, green_slider.value, blue_slider.value
        target_width_px: width_slider.value
    ColourSlider:
        id: red_slider
    ColourSlider:
        id: green_slider
    ColourSlider:
        id: blue_slider
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: "width: {}".format(width_slider.value)
        Slider:
            id: width_slider
            min: 2
            max: 10
            value: 2

And finally, update the DrawingWidget.on_touch_down to use the currently-selected width for the new Line instruction:

def on_touch_down(self, touch):
    super(DrawingWidget, self).on_touch_down(touch)

    if not self.collide_point(*touch.pos):
        return

    with self.canvas:
        Color(*self.target_colour_rgb)
        self.line = Line(points=[touch.pos[0], touch.pos[1]],
                         width=self.target_width_px)

Note: I’ve added not just a single Slider, but a new BoxLayout to the kv rule, in order to display a Label indicating what the slider is for. Notice how, consistent with everything so far, the Label automatically updates to always show the current value of the Slider.

Run the app and try the drawing. You should now be able to control both the colour and width of every line:

Lines drawn using selected colour and width

With that, the application is fully connected together. We have a UI element for drawing, alongside extra elements for controlling the details of the lines, with data passed around using Kivy properties. These basic ideas are at the heart of all Kivy applications.

This would be a good time to experiment. Try adding or removing widgets, and maybe adding more customisation to the lines.

Full code

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from kivy.properties import ListProperty, NumericProperty

from random import random

class DrawingWidget(Widget):
    target_colour_rgb = ListProperty([0, 0, 0])
    target_width_px = NumericProperty(0)

    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        if not self.collide_point(*touch.pos):
            return

        with self.canvas:
            Color(*self.target_colour_rgb)
            self.line = Line(points=[touch.pos[0], touch.pos[1]],
                            width=self.target_width_px)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return

        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]

    def on_target_colour_rgb(self, instance, value):
        print(f"target_colour_rgb changed to {self.target_colour_rgb}")


class Interface(BoxLayout):
    pass

class DrawingApp(App):

    def build(self):
        root_widget = Interface()
        return root_widget

DrawingApp().run()

drawing.kv:

<DrawingWidget>:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size

<ColourSlider@Slider>:
    min: 0
    max: 1
    value: 0.5
    size_hint_y: None
    height: 50


<Interface>:
    orientation: 'vertical'
    DrawingWidget:
        target_colour_rgb: red_slider.value, green_slider.value, blue_slider.value
        target_width_px: width_slider.value
    ColourSlider:
        id: red_slider
    ColourSlider:
        id: green_slider
    ColourSlider:
        id: blue_slider
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: "width: {:.1f}".format(width_slider.value)
        Slider:
            id: width_slider
            min: 2
            max: 10
            value: 2

Kivy tutorial 008: More kv language

This is number 8 in a series of introductory Kivy tutorials.

Central themes: Event binding and canvas instructions in kv language

This tutorial directly follows on from the previous, so start by retrieving the previous code, as below:

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from random import random

class DrawingWidget(Widget):
    def __init__(self):
        super(DrawingWidget, self).__init__()

        with self.canvas:
            Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size,
                                  pos=self.pos)
        self.bind(pos=self.update_rectangle,
                  size=self.update_rectangle)

    def update_rectangle(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size

    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        if not self.collide_point(*touch.pos):
            return

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return

        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]


class Interface(BoxLayout):
    pass

class DrawingApp(App):

    def build(self):
        root_widget = Interface()
        return root_widget

DrawingApp().run()

drawing.kv:

<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 50
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 50
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 50
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:

The first thing to do is draw the coloured Rectangle that the final Widget uses to display an output colour, and for this we need to know how to draw canvas instructions in kv language. The syntax is as below:

Widget:
    canvas:
        Color:
            rgb: 0, 1, 0  # using a fixed colour for now
        Rectangle:
            size: self.size
            pos: self.pos

Run the code, and you’ll see another of kv language’s most important features; automatic event binding. In the original Python code of tutorial 7 we needed an extra .bind(...) call to make the be updated to always be placed within its Widget. In kv language this is not necessary, the dependency on self.size and self.pos is automatically detected, and a binding automatically created!

This is also the generic syntax for canvas instructions; first add canvas: (or canvas.before or canvas.after), then, indent by 4 spaces, and add canvas instructions much like you would Widgets. However, note that canvas instructions are not widgets.

The only thing now missing from the original Python interface implementation in tutorial 7 is having the Sliders automatically update the output colour rectangle. Change the <Interface>: rule to the following:

<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    Slider:
        id: red_slider
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 50
    Slider:
        id: green_slider
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 50
    Slider:
        id: blue_slider
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 50
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos

There are actually only two changes here; we gave each Slider an id declaration, and in the canvas Color referred to the sliders with this name. Giving a widget an id is just like naming it in Python so that you can refer to it elsewhere.

Thanks to kv’s automatic binding, this is all we need to do to have the Color update automatically whenever a slider value changes. Run the code, and you should see that things work exactly as they did in the original Python interface.

We can finish this tutorial with a couple of extra kv conveniences. First, just as we added an automatically updating Rectangle in the Widget kv, we can do the same for the background of the DrawingWidget. Delete the __init__ and update_rectangle methods in the Python DrawingWidget code, and add a new rule in the kv file:

<DrawingWidget>:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size

Second, you might have noticed that there’s a lot of code duplication in each of the Slider rules - we set the same min, max, initial value`, ``size_hint_y` and height for every one. As is normal in Python, it would be natural to abstract this in a new class, so as to set each value only once. You can probably already see how to do this with what we’ve learned so far (make a new class YourSlider(Slider): in the Python and add a new <YourSlider>: rule in the kv), but I’ll note that you can even do this entirely in kv:

<ColourSlider@Slider>:
    min: 0
    max: 1
    value: 0.5
    size_hint_y: None
    height: 50


<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    ColourSlider:
        id: red_slider
    ColourSlider:
        id: green_slider
    ColourSlider:
        id: blue_slider
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos

The new <ColourSlider@Slider>: rule defines a dynamic class, a Python class kv rule without a corresponding Python code definition. This is convenient if you want to do something repeatedly only in kv, and never access it from Python. These Sliders are a good example: they don’t need any extra code, just some default property values, so using a throwaway dynamic class gets that out of the way.

At this point, we’ve reached feature parity with the original Python code, and seen all the basics of kv language. In the next tutorial we’ll finish off the original purpose of all these sliders; letting the user set the colour of line that is drawn by the DrawingWidget.

Next tutorial: Finishing the drawing app

Full code

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from random import random

class DrawingWidget(Widget):
    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        if not self.collide_point(*touch.pos):
            return

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return

        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]


class Interface(BoxLayout):
    pass

class DrawingApp(App):

    def build(self):
        root_widget = Interface()
        return root_widget

DrawingApp().run()

drawing.kv:

<DrawingWidget>:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size

<ColourSlider@Slider>:
    min: 0
    max: 1
    value: 0.5
    size_hint_y: None
    height: 50


<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    ColourSlider:
        id: red_slider
    ColourSlider:
        id: green_slider
    ColourSlider:
        id: blue_slider
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos

Kivy tutorial 008: More kv language

Central themes: Event binding and canvas instructions in kv language

This tutorial directly follows on from the previous, so start by retrieving the previous code, as below:

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from random import random

class DrawingWidget(Widget):
    def __init__(self):
        super(DrawingWidget, self).__init__()

        with self.canvas:
            Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size,
                                  pos=self.pos)
        self.bind(pos=self.update_rectangle,
                  size=self.update_rectangle)

    def update_rectangle(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size

    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        if not self.collide_point(*touch.pos):
            return

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return

        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]


class Interface(BoxLayout):
    pass

class DrawingApp(App):

    def build(self):
        root_widget = Interface()
        return root_widget

DrawingApp().run()

drawing.kv:

<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 80
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 80
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 80
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 80
        Label:
            text: 'output colour:'
        Widget:

The first thing to do is draw the coloured Rectangle that the final Widget uses to display an output colour, and for this we need to know how to draw canvas instructions in kv language. The syntax is as below:

Widget:
    canvas:
        Color:
            rgb: 0, 1, 0  # using a fixed colour for now
        Rectangle:
            size: self.size
            pos: self.pos

Run the code, and you’ll see another of kv language’s most important features; automatic event binding. In the original Python code of tutorial 7 we needed an extra .bind(...) call to make the be updated to always be placed within its Widget. In kv language this is not necessary, the dependency on self.size and self.pos is automatically detected, and a binding automatically created!

This is also the generic syntax for canvas instructions; first add canvas: (or canvas.before or canvas.after), then, indent by 4 spaces, and add canvas instructions much like you would Widgets. However, note that canvas instructions are not widgets.

The only thing now missing from the original Python interface implementation in tutorial 7 is having the Sliders automatically update the output colour rectangle. Change the <Interface>: rule to the following:

<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    Slider:
        id: red_slider
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 80
    Slider:
        id: green_slider
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 80
    Slider:
        id: blue_slider
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 80
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 80
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos

There are actually only two changes here; we gave each Slider an id declaration, and in the canvas Color referred to the sliders with this name. Giving a widget an id is just like naming it in Python so that you can refer to it elsewhere.

Thanks to kv’s automatic binding, this is all we need to do to have the Color update automatically whenever a slider value changes. Run the code, and you should see that things work exactly as they did in the original Python interface.

We can finish this tutorial with a couple of extra kv conveniences. First, just as we added an automatically updating Rectangle in the Widget kv, we can do the same for the background of the DrawingWidget. Delete the __init__ and update_rectangle methods in the Python DrawingWidget code, and add a new rule in the kv file:

<DrawingWidget>:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size

Second, you might have noticed that there’s a lot of code duplication in each of the Slider rules - we set the same min, max, initial value`, ``size_hint_y` and height for every one. As is normal in Python, it would be natural to abstract this in a new class, so as to set each value only once. You can probably already see how to do this with what we’ve learned so far (make a new class YourSlider(Slider): in the Python and add a new <YourSlider>: rule in the kv), but I’ll note that you can even do this entirely in kv:

<ColourSlider@Slider>:
    min: 0
    max: 1
    value: 0.5
    size_hint_y: None
    height: 80


<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    ColourSlider:
        id: red_slider
    ColourSlider:
        id: green_slider
    ColourSlider:
        id: blue_slider
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 80
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos

The new <ColourSlider@Slider>: rule defines a dynamic class, a Python class kv rule without a corresponding Python code definition. This is convenient if you want to do something repeatedly only in kv, and never access it from Python.

At this point, we’ve reached feature parity with the original Python code, and seen all the basics of kv language. In the next tutorial we’ll finish off the original purpose of all these sliders; letting the user set the colour of line that is drawn by the DrawingWidget.

Full code

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from random import random

class DrawingWidget(Widget):
    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        if not self.collide_point(*touch.pos):
            return

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return

        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]


class Interface(BoxLayout):
    pass

class DrawingApp(App):

    def build(self):
        root_widget = Interface()
        return root_widget

DrawingApp().run()

drawing.kv:

<DrawingWidget>:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size

<ColourSlider@Slider>:
    min: 0
    max: 1
    value: 0.5
    size_hint_y: None
    height: 80


<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    ColourSlider:
        id: red_slider
    ColourSlider:
        id: green_slider
    ColourSlider:
        id: blue_slider
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 80
        Label:
            text: 'output colour:'
        Widget:
            canvas:
                Color:
                    rgb: red_slider.value, green_slider.value, blue_slider.value
                Rectangle:
                    size: self.size
                    pos: self.pos

Kivy tutorial 007: Introducing kv language

This is number 7 in a series of introductory Kivy tutorials.

Central themes: kv language, building a gui, integration with Python

The goal of this tutorial will be to build up a simple gui around the DrawingWidget built in the last two tutorials. A nice simple goal would be to let the user select the colour of the lines. Kivy actually has a ColorPicker Widget for this purpose (see the documentation), but we’ll skip that for now in order to continue demonstrating Kivy widget construction.

Note: Since all Kivy widgets are built out of other Widgets and canvas instructions, you might like to think about how you’d build the ColorPicker from scratch.

Let’s start with the code from last time, minus the now-unnecessary red Rectangle:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from random import random

class DrawingWidget(Widget):
    def __init__(self):
        super(DrawingWidget, self).__init__()

        with self.canvas:
            Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size,
                                  pos=self.pos)
        self.bind(pos=self.update_rectangle,
                  size=self.update_rectangle)


    def update_rectangle(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size

    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]


class DrawingApp(App):

    def build(self):
        root_widget = DrawingWidget()
        return root_widget

DrawingApp().run()

I’ll demonstrate adding the new gui components in two ways; first in pure Python as has been demonstrated in previous tutorials, and second using kv language instead. So, here’s a Python implementation of the new features we want, beginning with importing the Widget classes we’ll need:

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider

Slider is a previously-unseen Widget displaying a draggable marker. We’ll be using a Slider for each primary colour (red, blue, green), and using this to set the Color when a Line is drawn.

We can now update the build method of DrawingApp, replacing the root widget and adding the new gui components:

class DrawingApp(App):

    def build(self):
        root_widget = BoxLayout(orientation='vertical')

        drawing_widget = DrawingWidget()

        red_slider = Slider(min=0, max=1, value=0.5,
                            size_hint_y=None, height=50)
        green_slider = Slider(min=0, max=1, value=0.5,
                            size_hint_y=None, height=50)
        blue_slider = Slider(min=0, max=1, value=0.5,
                            size_hint_y=None, height=50)

        colour_row = BoxLayout(orientation='horizontal',
                               size_hint_y=None, height=50)
        colour_label = Label(text='output colour:')
        colour_widget = Widget()

        # We draw a Rectangle on colour_widget exactly the same way as
        # with DrawingWidget, just without making a new class
        with colour_widget.canvas:
            output_colour = Color(red_slider.value,
                                  green_slider.value,
                                  blue_slider.value)
            output_rectangle = Rectangle()
        def update_colour_widget_rect(instance, value):
            output_rectangle.pos = colour_widget.pos
            output_rectangle.size = colour_widget.size
        colour_widget.bind(pos=update_colour_widget_rect,
                           size=update_colour_widget_rect)

        def update_colour_widget_colour(instance, value):
            output_colour.rgb = (red_slider.value,
                                 green_slider.value,
                                 blue_slider.value)
        red_slider.bind(value=update_colour_widget_colour)
        green_slider.bind(value=update_colour_widget_colour)
        blue_slider.bind(value=update_colour_widget_colour)

        root_widget.add_widget(drawing_widget)
        root_widget.add_widget(red_slider)
        root_widget.add_widget(green_slider)
        root_widget.add_widget(blue_slider)
        root_widget.add_widget(colour_row)

        colour_row.add_widget(colour_label)
        colour_row.add_widget(colour_widget)

        return root_widget

This is a lot of code to drop all at once, but read it carefully and you’ll see that it’s only the same concepts already introduced: we instantiate Widgets, add them to one another, and create bindings so that things automatically happen when Kivy properties are changed. In this case, we make use of the value Kivy property of the Slider widget, which gives its current value (changing automatically when the slider is moved).

Run the code and you should see something like the image below. You can update the colour in the bottom right by moving the sliders.

Sliders bound to a colour change

A problem now becoming obvious is that all this code is kind of verbose, and also it can be a little unclear what is happening - Widget instantiation is in a different place to where the Widgets are added to one another, which is different again to where their events are bound. You can mitigate this with a careful app structure and following whatever coding conventions you like, but some of it is unavoidable given how Python works.

It’s for this reason that Kivy comes with kv language, a simple but powerful declaration language specifically designed for creating Kivy widget trees. If learning a new language sounds worrying…don’t be concerned! Kv isn’t a general purpose language, it doesn’t have much special syntax and is targeted specifically at Kivy widgets. It also uses normal Python code wherever possible (we’ll see that soon).

All of the kv language stuff discussed below is documented on the Kivy website; I’ll cover the basics, but you can find more information there.

First, get rid of all the Python code from above, and replace the root widget return with the following:

class Interface(BoxLayout):
    pass

class DrawingApp(App):

    def build(self):
        root_widget = Interface()
        return root_widget

Kv language works by writing rules for Widget classes, which will be automatically applied every time you instantiate one. We can use kv for almost everything added to the app so far, but this time we’ll construct the gui step by step to see how each part is added with the new kv syntax. We’ll be writing a kv rule for the new Interface class.

To start using kv language, write the following code in a file named drawing.kv. This name comes from the name of the App class, minus the App at the end if present, and in lowercase (e.g. if you named your App MySuperKivyApp you’d need to name the file mysuperkivy.kv). This is only necessary if you want the file to be automatically loaded, you can also load files or string manually. Our first kv code is:

<Interface>:
    orientation: 'vertical'
    Label:
        text: 'label added with kv'
        font_size: 50

Run the code again, and you should see the a Label with the given text, as the kv file is automatically loaded and its <Interface> rule applied.

Label added with kv rule.

This demonstrates the core rules of kv syntax. A kv rule is created with the <WidgetName>: syntax. You can make a rule for any widget, including built in ones (Kivy internally has a large kv file), and if you make multiple rules for the same Widget then all of them are applied one by one.

Below the rule creation, we indent by 4 spaces and define values for Kivy properties of the widget, and add child widgets. Lines like orientation: 'vertical' set Kivy properties just like we did previously in the Python code. Note that everything to the right of the colon is normal Python code - that doesn’t matter here, but for instance we could equally well write orientation: ''.join(['v', 'e', 'r', 't', 'i', 'c', 'a', 'l']) and it would be exactly the same. You can set any Kivy property of a widget in this way, finding the available options in the documentation as previously discussed.

We can also add child widgets by writing the widget name with a colon, then indenting by a further 4 spaces, as is done here with the Label. After this you can keep going as deep as you like, setting properties or adding more child widgets.

We can use these pieces of syntax to construct the previous Python interface entirely in kv:

<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 50
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 50
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 50
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:

This hasn’t yet set up the event binding, but the full widget tree has been constructed entirely using the kv syntax described above. The immediate advantage of this is that kv language directly expresses the widget tree - there are no longer separate steps for instantiating Widgets, setting their properties and adding them to one another. Instead, you get to see everything at once.

This gui doesn’t yet have the behaviour of the Python one (i.e. having the sliders control output colour), but in the interest of keeping these tutorials relatively short, I’ll stop here for now. In the next tutorial will see how kv language also makes event binding very easy.

Next tutorial: More kv language

Full code

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from random import random

class DrawingWidget(Widget):
    def __init__(self):
        super(DrawingWidget, self).__init__()

        with self.canvas:
            Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size,
                                  pos=self.pos)
        self.bind(pos=self.update_rectangle,
                  size=self.update_rectangle)

    def update_rectangle(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size

    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        if not self.collide_point(*touch.pos):
            return

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return

        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]


class Interface(BoxLayout):
    pass

class DrawingApp(App):

    def build(self):
        root_widget = Interface()
        return root_widget

DrawingApp().run()

drawing.kv:

<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 50
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 50
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 50
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 50
        Label:
            text: 'output colour:'
        Widget:

Kivy tutorial 007: Introducing kv language

Central themes: kv language, building a gui, integration with Python

The goal of this tutorial will be to build up a simple gui around the DrawingWidget built in the last two tutorials. A nice simple goal would be to let the user select the colour of the lines. Kivy actually has a ColorPicker Widget for this purpose (see the documentation), but we’ll skip that for now in order to continue demonstrating Kivy widget construction.

Note

Since all Kivy widgets are built out of other Widgets and canvas instructions, you might like to think about how you’d build the ColorPicker from scratch.

Let’s start with the code from last time, minus the now-unnecessary red Rectangle:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from random import random

class DrawingWidget(Widget):
    def __init__(self):
        super(DrawingWidget, self).__init__()

        with self.canvas:
            Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size,
                                  pos=self.pos)
        self.bind(pos=self.update_rectangle,
                  size=self.update_rectangle)


    def update_rectangle(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size

    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]


class DrawingApp(App):

    def build(self):
        root_widget = DrawingWidget()
        return root_widget

DrawingApp().run()

I’ll demonstrate adding the new gui components in two ways; first in pure Python as has been demonstrated in previous tutorials, and second using kv language instead. So, here’s a Python implementation of the new features we want, beginning with importing the Widget classes we’ll need:

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider

Slider is a previously-unseen Widget displaying a draggable marker. We’ll be using a Slider for each primary colour (red, blue, green), and using this to set the Color when a Line is drawn.

We can now update the build method of DrawingApp, replacing the root widget and adding the new gui components:

class DrawingApp(App):

    def build(self):
        root_widget = BoxLayout(orientation='vertical')

        drawing_widget = DrawingWidget()

        red_slider = Slider(min=0, max=1, value=0.5,
                            size_hint_y=None, height=80)
        green_slider = Slider(min=0, max=1, value=0.5,
                            size_hint_y=None, height=80)
        blue_slider = Slider(min=0, max=1, value=0.5,
                            size_hint_y=None, height=80)

        colour_row = BoxLayout(orientation='horizontal',
                               size_hint_y=None, height=80)
        colour_label = Label(text='output colour:')
        colour_widget = Widget()

        # We draw a Rectangle on colour_widget exactly the same way as
        # with DrawingWidget, just without making a new class
        with colour_widget.canvas:
            output_colour = Color(red_slider.value,
                                  green_slider.value,
                                  blue_slider.value)
            output_rectangle = Rectangle()
        def update_colour_widget_rect(instance, value):
            output_rectangle.pos = colour_widget.pos
            output_rectangle.size = colour_widget.size
        colour_widget.bind(pos=update_colour_widget_rect,
                           size=update_colour_widget_rect)

        def update_colour_widget_colour(instance, value):
            output_colour.rgb = (red_slider.value,
                                 green_slider.value,
                                 blue_slider.value)
        red_slider.bind(value=update_colour_widget_colour)
        green_slider.bind(value=update_colour_widget_colour)
        blue_slider.bind(value=update_colour_widget_colour)

        root_widget.add_widget(drawing_widget)
        root_widget.add_widget(red_slider)
        root_widget.add_widget(green_slider)
        root_widget.add_widget(blue_slider)
        root_widget.add_widget(colour_row)

        colour_row.add_widget(colour_label)
        colour_row.add_widget(colour_widget)

        return root_widget

This is a lot of code to drop all at once, but read it carefully and you’ll see that it’s only the same concepts already introduced: we instantiate Widgets, add them to one another, and create bindings so that things automatically happen when Kivy properties are changed. In this case, we make use of the value Kivy property of the Slider widget, which gives its current value (changing automatically when the slider is moved).

Run the code and you should see something like the image below. You can update the colour in the bottom right by moving the sliders. Cool.

Sliders bound to a colour change

A problem now becoming obvious is that all this code is kind of verbose, and also it can be a little unclear what is happening - Widget instantiation is in a different place to where the Widgets are added to one another, which is different again to where their events are bound. You can mitigate this with a careful app structure and following whatever coding conventions you like, but some of it is unavoidable given how Python works.

It’s for this reason that Kivy comes with kv language, a simple but powerful language specifically designed for creating Kivy widget trees. If learning a new language sounds worrying…don’t be concerned! Kv doesn’t have much special syntax and is targeted specifically at Kivy widgets, and much of the code you write is actually normal Python (we’ll see that soon).

All of the kv language stuff discussed below is documented on the Kivy website; I’ll cover the basics, but you can find more information there.

First, get rid of all the Python code from above, and replace the root widget return with the following:

class Interface(BoxLayout):
    pass

class DrawingApp(App):

    def build(self):
        root_widget = Interface()
        return root_widget

kv language works by writing rules for Widget classes, which will be automatically applied every time you instantiate one. We can use kv for almost everything added to the app so far, but this time we’ll construct the gui step by step to see how each part is added with the new kv syntax. We’ll be writing a kv rule for the new Interface class.

To start using kv language, write the following code in a file named drawing.kv. This name comes from the name of the App class, minus the App at the end if present, and in lowercase (e.g. if you named your App MySuperKivyApp you’d need to name the file mysuperkivy.kv). This is only necessary if you want the file to be automatically loaded, you can also load files or string manually. Our first kv code is:

<Interface>:
    orientation: 'vertical'
    Label:
        text: 'label added with kv'
        font_size: 50

Run the code again, and you should see the a Label with the given text, as the kv file is automatically loaded and its <Interface> rule applied.

Label added with kv rule.

This demonstrates the core rules of kv syntax. A kv rule is created with the <WidgetName>: syntax. You can make a rule for any widget, including built in ones (Kivy internally has a large kv file), and if you make multiple rules for the same Widget then all of them are applied one by one.

Below the rule creation, we indent by 4 spaces and define values for Kivy properties of the widget, and add child widgets. Lines like orientation: 'vertical' set Kivy properties just like we did previously in the Python code. Note that everything to the right of the colon is normal Python code - that doesn’t matter here, but for instance we could equally well write orientation: ''.join(['v', 'e', 'r', 't', 'i', 'c', 'a', 'l']) and it would be exactly the same. You can set any Kivy property of a widget in this way, finding the available options in the documentation as previously discussed.

We can also add child widgets by writing the widget name with a colon, then indenting by a further 4 spaces, as is done here with the Label. After this you can keep going as deep as you like, setting properties or adding more child widgets.

We can use these pieces of syntax to construct the previous Python interface entirely in kv:

<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 80
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 80
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 80
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 80
        Label:
            text: 'output colour:'
        Widget:

This hasn’t yet set up the event binding, but the full widget tree has been constructed entirely using the kv syntax described above. The immediate advantage of this is that kv language directly expresses the widget tree - there are no longer separate steps for instantiating Widgets, setting their properties and adding them to one another. Instead, you get to see everything at once.

This gui doesn’t yet have the behaviour of the Python one (i.e. having the sliders control output colour), but in the interest of keeping these tutorials relatively short, I’ll stop here for now. In the next tutorial will see how kv language also makes event binding very easy.

Full code

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from random import random

class DrawingWidget(Widget):
    def __init__(self):
        super(DrawingWidget, self).__init__()

        with self.canvas:
            Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size,
                                  pos=self.pos)
        self.bind(pos=self.update_rectangle,
                  size=self.update_rectangle)

    def update_rectangle(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size

    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        if not self.collide_point(*touch.pos):
            return

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return

        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]


class Interface(BoxLayout):
    pass

class DrawingApp(App):

    def build(self):
        root_widget = Interface()
        return root_widget

DrawingApp().run()

drawing.kv:

<Interface>:
    orientation: 'vertical'
    DrawingWidget:
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 80
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 80
    Slider:
        min: 0
        max: 1
        value: 0.5
        size_hint_y: None
        height: 80
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 80
        Label:
            text: 'output colour:'
        Widget:

Kivy tutorial 006: Let’s draw something

This is number 6 in a series of introductory Kivy tutorials.

Central themes: Handling touch or mouse input, more canvas instructions

In this tutorial we’ll directly add touch handling to the basic code developed in tutorial 5, starting with the code from last time:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color

class DrawingWidget(Widget):
    def __init__(self):
        super(DrawingWidget, self).__init__()

        with self.canvas:
            Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size,
                                  pos=self.pos)
            Color(1, 0, 0, 1)  # note that we must reset the colour
            Rectangle(size=(300, 100),
                      pos=(300, 200))
        self.bind(pos=self.update_rectangle,
                  size=self.update_rectangle)

    def update_rectangle(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size


class DrawingApp(App):

    def build(self):
        root_widget = DrawingWidget()
        return root_widget

DrawingApp().run()

We’ve already seen some input interaction via the Button widget, where we could bind to its on_press event to have a function called whenever the Button was clicked. This is great for a Button, but is not a general way to handle interaction - it gives no indication of the position of the touch, or any other information like the button clicked on the mouse.

Kivy achieves general mouse/touch handling via the on_touch_down, on_touch_move and on_touch_up methods of all Widget classes. Let’s dive in with an example, modifying our DrawingWidget:

from random import random

class DrawingWidget(Widget):
    def __init__(self):
        super(DrawingWidget, self).__init__()

        with self.canvas:
            Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size,
                                  pos=self.pos)
            self.rect_colour = Color(1, 0, 0, 1)  # note that we must reset the colour
            Rectangle(size=(300, 100),
                      pos=(300, 200))
        self.bind(pos=self.update_rectangle,
                  size=self.update_rectangle)


    def update_rectangle(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size

    def on_touch_down(self, touch):
        self.rect_colour.rgb = (random(), random(), random())
        print('touch pos is {}'.format(touch.pos))

Note that the only changes are to set self.rect_colour, and to add the on_touch_down method. Run the code now, and whenever you click the screen you should see the colour of the rectangle change.

How does this work? The answer is that whenever a mouse click or touch is registered, the root widget’s on_touch_down method is called, with a touch object holding information about the touch: you can see this here, where we access the pos of this object to get the pixel coordinates of its position. Each widget passes this touch object to all its children. For this reason, it’s important to call super(...) if you want the touch to also be passed to the current Widget’s children, though as it happens that’s not actually important here.

Note that although these methods are called on_touch_..., and I’ve called the argument touch, they are used for both mouse and touch handling; these events are handled in exactly the same way, except that the touch object may contain different information such as the button clicked (in the case of the mouse). I’ll mostly refer to this input as ‘touch’, but this always includes mouse interaction too.

The other methods I mentioned, on_touch_move and on_touch_up, work the same way; they are called whenever that thing happens, though only when on_touch_down has already happened, you don’t get events when moving the mouse without having clicked. We can use this to achieve drawing.

First, change the kivy.graphics import to include Line:

from kivy.graphics import Rectangle, Color, Line

Then, add modify on_touch_down and on_touch_move to draw and update a Line each time:

def on_touch_down(self, touch):
    super(DrawingWidget, self).on_touch_down(touch)

    with self.canvas:
        Color(random(), random(), random())
        self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

def on_touch_move(self, touch):
    self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]

Run the code again, and try clicking and dragging…you should see a line! Each time you click and drag the line has a different colour, as we add a new random Color instruction before its instruction each time. We’re updating it by adding the x and y value of the touch position to the Line’s points, every time the touch is moved.

Lines drawn in example app

You can also note that we only use with self.canvas when the Line is instantiated - not when it is updated. This is because we only need to add the Line canvas instruction to the canvas once, after that the gui will automatically be updated whenever the Line changes, including if we modified e.g. its width. Try changing self.line.width in on_touch_move and see what happens.

Note: This way of storing the line (in self.line) isn’t very robust if there are multiple simultaneous interactions, e.g. in a multitouch display. This is easy to resolve by storing the reference somewhere more appropriate, e.g. we could set touch.ud['line'] to store a reference specific to the touch object (ud stands for “user dictionary”, see the documentation). You might like to try implementing this.

You could continue here by experimenting with other actions in response to touches, such as drawing different things (e.g. a Rectangle at the touch position rather than a Line) or doing more complex modifications to existing instructions.

With the basic drawing apparatus set up, the next tutorial will introduce the kv markup language, showing how a gui can easily be constructed without some of the Python boilerplate that comes from using a general purpose language for creating a gui.

Next tutorial: Introducing kv language

Full code

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from random import random

class DrawingWidget(Widget):
    def __init__(self):
        super(DrawingWidget, self).__init__()

        with self.canvas:
            Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size,
                                  pos=self.pos)
            self.rect_colour = Color(1, 0, 0, 1)  # note that we must reset the colour
            Rectangle(size=(300, 100),
                      pos=(300, 200))
        self.bind(pos=self.update_rectangle,
                  size=self.update_rectangle)


    def update_rectangle(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size

    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]


class DrawingApp(App):

    def build(self):
        root_widget = DrawingWidget()
        return root_widget

DrawingApp().run()

Kivy tutorial 006: Let’s draw something

Central themes: Handling touch or mouse input, more canvas instructions

In this tutorial we’ll directly add touch handling to the basic code developed in tutorial 5, starting with the code from last time:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color

class DrawingWidget(Widget):
    def __init__(self):
        super(DrawingWidget, self).__init__()

        with self.canvas:
            Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size,
                                  pos=self.pos)
            Color(1, 0, 0, 1)  # note that we must reset the colour
            Rectangle(size=(300, 100),
                      pos=(300, 200))
        self.bind(pos=self.update_rectangle,
                  size=self.update_rectangle)

    def update_rectangle(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size


class DrawingApp(App):

    def build(self):
        root_widget = DrawingWidget()
        return root_widget

DrawingApp().run()

We’ve already seen some input interaction via the Button widget, where we could bind to its on_press event to have a function called whenever the Button was clicked. This is great for a Button, but is not a general way to handle interaction - it gives no indication of the position of the touch, or any other information like the button clicked on the mouse.

Kivy achieves general mouse/touch handling via the on_touch_down, on_touch_move and on_touch_up methods of all Widget classes. Let’s dive in with an example, modifying our DrawingWidget:

from random import random

class DrawingWidget(Widget):
    def __init__(self):
        super(DrawingWidget, self).__init__()

        with self.canvas:
            Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size,
                                  pos=self.pos)
            self.rect_colour = Color(1, 0, 0, 1)  # note that we must reset the colour
            Rectangle(size=(300, 100),
                      pos=(300, 200))
        self.bind(pos=self.update_rectangle,
                  size=self.update_rectangle)


    def update_rectangle(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size

    def on_touch_down(self, touch):
        self.rect_colour.rgb = (random(), random(), random())
        print('touch pos is {}'.format(touch.pos))

Note that the only changes are to set self.rect_colour, and to add the on_touch_down method. Run the code now, and whenever you click the screen you should see the colour of the rectangle change.

How does this work? The answer is that whenever a mouse click or touch is registered, the root widget’s on_touch_down method is called, with a touch object holding information about the touch: you can see this here, where we access the pos of this object to get the pixel coordinates of its position. Each widget passes this touch object to all its children. For this reason, it’s important to call super(...) if you want the touch to also be passed to the current Widget’s children, though as it happens that’s not actually important here.

Note that although these methods are called on_touch_..., and I’ve called the argument touch, they relate to both mouse and touch handling; these events are handled in exactly the same way, except that the touch object may contain different information such as the button clicked (in the case of the mouse). I’ll switch to mostly referring to this input as ‘touch’, but this always includes mouse interaction too.

The other methods I mentioned, on_touch_move and on_touch_up, work the same way; they are called whenever that thing happens, though only when on_touch_down has already happened, you don’t get events when moving the mouse without having clicked. We can use this to achieve drawing.

First, change the kivy.graphics import to include Line:

from kivy.graphics import Rectangle, Color, Line

Then, add modify on_touch_down and on_touch_move to draw and update a Line each time:

def on_touch_down(self, touch):
    super(DrawingWidget, self).on_touch_down(touch)

    with self.canvas:
        Color(random(), random(), random())
        self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

def on_touch_move(self, touch):
    self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]

Run the code again, and try clicking and dragging…you should see a line! Each time you click and drag the line has a different colour, as we add a new random Color instruction before its instruction each time. We’re updating it by adding the x and y value of the touch position to the Line’s points, every time the touch is moved.

Lines drawn in example app

You can also note that we only use with self.canvas when the Line is instantiated - not when it is updated. This is because we only need to add the Line canvas instruction to the canvas once, after that the gui will automatically be updated whenever the Line changes, including if we modified e.g. its width. Try changing self.line.width in on_touch_move and see what happens.

Note

This way of storing the line (in self.line) isn’t very robust if there are multiple simultaneous interactions, e.g. in a multitouch display. This is easy to resolve by storing the reference more cleverly, including in the touch object itself, but I’ve just ignored the issue here.

You could continue here by experimenting with other actions in response to touches, such as drawing different things (e.g. a Rectangle at the touch position rather than a Line) or doing more complex modifications to existing instructions.

With the basic drawing apparatus set up, the next tutorial will introduce the kv markup language, showing how a gui can easily be constructed without some of the Python boilerplate that comes from using a general purpose language for creating a gui.

Full code

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line

from random import random

class DrawingWidget(Widget):
    def __init__(self):
        super(DrawingWidget, self).__init__()

        with self.canvas:
            Color(1, 1, 1, 1)
            self.rect = Rectangle(size=self.size,
                                  pos=self.pos)
            self.rect_colour = Color(1, 0, 0, 1)  # note that we must reset the colour
            Rectangle(size=(300, 100),
                      pos=(300, 200))
        self.bind(pos=self.update_rectangle,
                  size=self.update_rectangle)


    def update_rectangle(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size

    def on_touch_down(self, touch):
        super(DrawingWidget, self).on_touch_down(touch)

        with self.canvas:
            Color(random(), random(), random())
            self.line = Line(points=[touch.pos[0], touch.pos[1]], width=2)

    def on_touch_move(self, touch):
        self.line.points = self.line.points + [touch.pos[0], touch.pos[1]]


class DrawingApp(App):

    def build(self):
        root_widget = DrawingWidget()
        return root_widget

DrawingApp().run()