An actual history of Python on Android

This ZDNet article was published a few days ago about how "Python apps might soon be running on Android". It summarises some recent developments in Android support for CPython, but disappointingly it’s highly misleading about some key points. In particular the article states that "apps written in Python may one day run natively on iOS and Android devices", but in fact people have been doing this since at least 2011.

I thought I’d write some short details about the actual history of Python on Android from my own perspective. It’s something I’ve been involved in on some level for about 8 years, and for a significant part of that I’ve been a primary maintainer of python-for-android. This is a build tool for creating APKs from Python applications, originally created for Kivy but now more generic so that it can also support e.g. flask running on the device with a webview gui, or more recently Pygame via its upcoming SDL2 support.

Kivy app screenshots

Example Python apps for Android. Large version. From left to right: Flat Jewels, ColourBlind, Kognitivo, Lazy Baduk, Pyonic interpreter, Barly (original Python version).

Notes on the term "native"

A key problem point around discussing Python on Android in that the term "native" is overloaded to mean two different things.

The first refers to simply compiling CPython to run "natively" with the Android kernel and libraries, just as it does on desktop platforms, and this is what the ZDNet article refers to with e.g. the quote "Android devices are now fast enough, and the Android kernel permissive enough, to run CPython itself". This is not a recent development, it has been technically possible for many years, although the article is correct that it comes with disadvantages and not everything works the same as on desktop.

The second is using the "native" GUI toolkit for the platform. On Android that means using the same widget toolkit as normal Java-based development. This is the part that is difficult and not yet widely supported, see below for more discussion about the recent developments. I’m actually not sure if the ZDNet article intends to focus on this, but perhaps fails because the author doesn’t understand the distinction, certainly the article focuses on CPython technical details.

It’s absolutely true that having Python applications seamlessly using the native GUI toolkit is essential to making Python a serious alternative to Java for generic Android development. However, there are huge areas of Python development where this simply isn’t a big concern, e.g. everything from games (Pygame, Kivy, Renpy), to scientific visualisation (matplotlib, Vispy, other toolkits), to novel user interfaces (Jupyter notebooks or other apps that don’t care about the native GUI toolkit for whatever reason). All of these things are tremendously popular on the desktop without using the native GUI toolkit, because that detail is either irrelevant or specifically at odds with what they want to do.

History of Python for Android projects

The following projects are ones I remember as interesting and historically important in terms of creating Python applications on Android, in rough date order. This list undoubtedly isn’t complete and is strongly based on events as I remember them, I might have missed other important projects and I’m certainly missing details from before about 2012! I’ve also focused on application build tools, not the many individual patches and Python contributions that made them possible.

Pygame Subset for Android (PGS4A)

Pygame gained rudimentary Android support fairly early in Android’s history. I’m not sure exactly what happened when, but this old release announcement is from February 2011 and isn’t the first version.

PGS4A worked by compiling CPython for Android, with a modified version of SDL (the gui library that Pygame uses), and some Java code to get the app to start and display a surface that pygame could draw to. I believe there were some limitations to what you could do (there’s a reason it was a "subset" of Pygame), but I’m not sure what these were.

I remember people were actively using PGS4A around 2012 when I started looking at it myself, but its popularity slowly diminished over time, probably due to lack of developer support combined with its limitations.

Renpy

Ren’Py is a visual novel engine with great cross-platform support. Actually, I suspect it’s quietly one of the most successful Python game engine projects, e.g. you can find quite a few Renpy-made games available via Steam.

Renpy gained Android support a long time ago, maybe in 2011 or earlier. I believe that as with PGS4A, it worked by combining pygame with a modified version of SDL and some custom Java code to display an app surface that Renpy could draw to. I’m not sure if the Renpy build tools actually had history in common with PGS4A, but I don’t think they were the same project at any point I remember.

Renpy’s Android support hasn’t been static, it’s evolved since 2011. At some point around 2014-2015 it switched to use a fork of Pygame using SDL2, with corresponding updates to the Android build process. This makes sense because SDL2 itself supports Android properly, removing a huge maintenance burden. Note that this pygame_sdl2 project is not the same as the SDL2 support currently under development from the core Pygame team, I don’t think Renpy’s pygame_sdl2 is in wide use and I don’t know what its limitations are.

Kivy and python-for-android

Kivy is easily the most well known Android-supporting Python toolkit I’m aware of, and has been since around 2012 when I came across it myself after failing to get a Java Android tutorial working. Kivy is a graphical toolkit that was not specifically designed for mobile support, but instead focused on being generically cross-platform and supporting novel user interfaces. This turned out very timely, as these properties made it very well suited for Android and iOS support, although Kivy also supports desktop use. Kivy draws its GUI using OpenGL, which has the advantage of working essentially the same on all different platforms, but this can also be a disadvantage in that it means not using the native GUI toolkit as discussed above.

My understanding is that Kivy’s Android support was originally based on Renpy’s Android build tools, which through some amount of collaboration and changes from different places ended up being the genesis for Kivy’s python-for-android project, first commit November 2011. Renpy’s Android build project then shifted to use a fork of python-for-android at some point, after which the projects have been developed separately.

Python-for-android was originally quite Kivy-specific, but was totally rewritten in 2015 to be a more generic and modular build tool, coinciding with Kivy’s own transition to SDL2 as a default backend (in fact this is where I really got involved). Since then its breadth of support has increased dramatically.

Beeware

BeeWare is a collection of tools and libraries for building Python applications across different platforms, both desktop and mobile. These projects have a particular focus on manipulating the native graphical toolkits of a given platform, e.g. on Android they want to use the same "native" GUI widgets as a normal Java-built application. Its toga toolkit provides a platform-independent GUI abstraction for this, in combination with platform-specific toolkits for each individual target.

I believe the BeeWare developers (or at least the core developer Russell Keith-Magee) did some initial experimentation using CPython on Android somewhere around 2015. The idea there would be to create and manipulate the normal Java-native GUI widgets using Java Native Interface (JNI). This is actually possible, it’s something we also support in Kivy and is occasionally useful to e.g. display a webview. Unfortunately it has some key disadvantages including that Python is still quite slow to start, and in particular that Android used to enforce a fairly low limit on the number of JNI references that could be simultaneously maintained, which makes building a full GUI impractical. My understanding is that a combination of these factors made CPython use impractical for Beeware on Android.

BeeWare instead switched to creating VOC, a Python code to Java bytecode transpiler. This converts the Python input into genuine Java bytecode that can run as a normal app without the above limitations. I haven’t tried this for some time, but I understand it works fine. However, it seems the difficulty of supporting the full breadth of Python libraries has been a barrier (at least, that’s been my impression from watching discussions about it, I think it’s still under active development and working well).

Fortunately Android itself has improved, and in particular the limitation on JNI references is no longer present in recent versions. In 2019 BeeWare switched back to targeting CPython on Android, supported by a PSF grant. It is the outcome of this grant that led to the discussions and article I linked at the top. See the end of this post for a brief summary.

Chaquopy

Chaquopy provides build tools for both including Python code in Java applications, and building apps entirely in Python. I’m not sure about the technical details, but I’ve been consistently aware of it as an active project since about 2017, so it may be useful to anyone interested in this sort of thing. I guess there must be some overlap with what python-for-android does, but Chaquopy’s integration of Python and Java code seems to be more of a focus.

pyqtdeploy

The popular Qt graphical framework supports Android. Python bindings to this framework are quite popular, so it’s not a huge surprise that there’s some level of Python for Android support using Qt for the GUI. As far as I’m aware pyqtdeploy is the primary build tool for this, using the PyQt bindings, but I may not be up to date about it. I’ve never seen this to be very popular, but I don’t know if there’s a reason for this beyond its relative obscurity.

What are actually the recent developments in CPython on Android?

Various contributors have driven improvements in CPython’s Android support over the years. I would give more details but honestly I’ve never found the time to get deeply involved so the historical summary is limited by my own ignorance! This has brought CPython to the point of being fairly easy to compile for Android as of about version 3.6. For instance, python-for-android’s CPython build recipe applies no essential patches to the Python 3 source, just an appropriate set of build arguments.

The key recent development is the BeeWare project’s switch to CPython explained above. They’ve made a specific goal of understanding where CPython’s Android support can be improved, and getting involved to resolve these problems. This means attacking both individual technical issues (e.g. getting Python’s test suit passing correctly), and longer-term structural problems (e.g. the suggestion described in the ZDNet article to create a stripped-back Python kernel for mobile use).

These recent developments are great, and hopefully will lead to huge improvements in the ease of deploying Python applications for Android, especially addressing the missing functionality of using the native Java GUI toolkit. However, let’s not forget the history of CPython on Android, people have been creating applications for both business and pleasure for many years.

An actual history of Python on Android

This ZDNet article was published a few days ago about how “Python apps might soon be running on Android”. It summarises some recent developments in Android support for CPython, but disappointingly it’s highly misleading about some key points. In particular the article states that “apps written in Python may one day run natively on iOS and Android devices”, but in fact people have been doing this since at least 2011.

I thought I’d write some short details about the actual history of Python on Android from my own perspective. It’s something I’ve been involved in on some level for about 8 years, and for a significant part of that I’ve been a primary maintainer of python-for-android. This is a build tool for creating APKs from Python applications, originally created for Kivy but now more generic so that it can also support e.g. flask running on the device with a webview gui, or more recently Pygame via its upcoming SDL2 support.

Kivy app screenshots

Example Python apps for Android. Large version. From left to right: Flat Jewels, ColourBlind, Kognitivo, Lazy Baduk, Pyonic interpreter, Barly (original Python version).

Notes on the term “native”

A key problem point around discussing Python on Android in that the term “native” is overloaded to mean two different things.

The first refers to simply compiling CPython to run “natively” with the Android kernel and libraries, just as it does on desktop platforms, and this is what the ZDNet article refers to with e.g. the quote “Android devices are now fast enough, and the Android kernel permissive enough, to run CPython itself”. This is not a recent development, it has been technically possible for many years, although the article is correct that it comes with disadvantages and not everything works the same as on desktop.

The second is using the “native” GUI toolkit for the platform. On Android that means using the same widget toolkit as normal Java-based development. This is the part that is difficult and not yet widely supported, see below for more discussion about the recent developments. I’m actually not sure if the ZDNet article intends to focus on this, but perhaps fails because the author doesn’t understand the distinction, certainly the article focuses on CPython technical details.

It’s absolutely true that having Python applications seamlessly using the native GUI toolkit is essential to making Python a serious alternative to Java for generic Android development. However, there are huge areas of Python development where this simply isn’t a big concern, e.g. everything from games (Pygame, Kivy, Renpy), to scientific visualisation (matplotlib, Vispy, other toolkits), to novel user interfaces (Jupyter notebooks or other apps that don’t care about the native GUI toolkit for whatever reason). All of these things are tremendously popular on the desktop without using the native GUI toolkit, because that detail is either irrelevant or specifically at odds with what they want to do.

History of Python for Android projects

The following projects are ones I remember as interesting and historically important in terms of creating Python applications on Android, in rough date order. This list undoubtedly isn’t complete and is strongly based on events as I remember them, I might have missed other important projects and I’m certainly missing details from before about 2012! I’ve also focused on application build tools, not the many individual patches and Python contributions that made them possible.

Pygame Subset for Android (PGS4A)

Pygame gained rudimentary Android support fairly early in Android’s history. I’m not sure exactly what happened when, but this old release announcement is from February 2011 and isn’t the first version.

PGS4A worked by compiling CPython for Android, with a modified version of SDL (the gui library that Pygame uses), and some Java code to get the app to start and display a surface that pygame could draw to. I believe there were some limitations to what you could do (there’s a reason it was a “subset” of Pygame), but I’m not sure what these were.

I remember people were actively using PGS4A around 2012 when I started looking at it myself, but its popularity slowly diminished over time, probably due to lack of developer support combined with its limitations.

Renpy

Ren’Py is a visual novel engine with great cross-platform support. Actually, I suspect it’s quietly one of the most successful Python game engine projects, e.g. you can find quite a few Renpy-made games available via Steam.

Renpy gained Android support a long time ago, maybe in 2011 or earlier. I believe that as with PGS4A, it worked by combining pygame with a modified version of SDL and some custom Java code to display an app surface that Renpy could draw to. I’m not sure if the Renpy build tools actually had history in common with PGS4A, but I don’t think they were the same project at any point I remember.

Renpy’s Android support hasn’t been static, it’s evolved since 2011. At some point around 2014-2015 it switched to use a fork of Pygame using SDL2, with corresponding updates to the Android build process. This makes sense because SDL2 itself supports Android properly, removing a huge maintenance burden. Note that this pygame_sdl2 project is not the same as the SDL2 support currently under development from the core Pygame team, I don’t think Renpy’s pygame_sdl2 is in wide use and I don’t know what its limitations are.

Kivy and python-for-android

Kivy is easily the most well known Android-supporting Python toolkit I’m aware of, and has been since around 2012 when I came across it myself after failing to get a Java Android tutorial working. Kivy is a graphical toolkit that was not specifically designed for mobile support, but instead focused on being generically cross-platform and supporting novel user interfaces. This turned out very timely, as these properties made it very well suited for Android and iOS support, although Kivy also supports desktop use. Kivy draws its GUI using OpenGL, which has the advantage of working essentially the same on all different platforms, but this can also be a disadvantage in that it means not using the native GUI toolkit as discussed above.

My understanding is that Kivy’s Android support was originally based on Renpy’s Android build tools, which through some amount of collaboration and changes from different places ended up being the genesis for Kivy’s python-for-android project, first commit November 2011. Renpy’s Android build project then shifted to use a fork of python-for-android at some point, after which the projects have been developed separately.

Python-for-android was originally quite Kivy-specific, but was totally rewritten in 2015 to be a more generic and modular build tool, coinciding with Kivy’s own transition to SDL2 as a default backend (in fact this is where I really got involved). Since then its breadth of support has increased dramatically.

Beeware

BeeWare is a collection of tools and libraries for building Python applications across different platforms, both desktop and mobile. These projects have a particular focus on manipulating the native graphical toolkits of a given platform, e.g. on Android they want to use the same “native” GUI widgets as a normal Java-built application. Its toga toolkit provides a platform-independent GUI abstraction for this, in combination with platform-specific toolkits for each individual target.

I believe the BeeWare developers (or at least the core developer Russell Keith-Magee) did some initial experimentation using CPython on Android somewhere around 2015. The idea there would be to create and manipulate the normal Java-native GUI widgets using Java Native Interface (JNI). This is actually possible, it’s something we also support in Kivy and is occasionally useful to e.g. display a webview. Unfortunately it has some key disadvantages including that Python is still quite slow to start, and in particular that Android used to enforce a fairly low limit on the number of JNI references that could be simultaneously maintained, which makes building a full GUI impractical. My understanding is that a combination of these factors made CPython use impractical for Beeware on Android.

BeeWare instead switched to creating VOC, a Python code to Java bytecode transpiler. This converts the Python input into genuine Java bytecode that can run as a normal app without the above limitations. I haven’t tried this for some time, but I understand it works fine. However, it seems the difficulty of supporting the full breadth of Python libraries has been a barrier (at least, that’s been my impression from watching discussions about it, I think it’s still under active development and working well).

Fortunately Android itself has improved, and in particular the limitation on JNI references is no longer present in recent versions. In 2019 BeeWare switched back to targeting CPython on Android, supported by a PSF grant. It is the outcome of this grant that led to the discussions and article I linked at the top. See the end of this post for a brief summary.

Chaquopy

Chaquopy provides build tools for both including Python code in Java applications, and building apps entirely in Python. I’m not sure about the technical details, but I’ve been consistently aware of it as an active project since about 2017, so it may be useful to anyone interested in this sort of thing. I guess there must be some overlap with what python-for-android does, but Chaquopy’s integration of Python and Java code seems to be more of a focus.

pyqtdeploy

The popular Qt graphical framework supports Android. Python bindings to this framework are quite popular, so it’s not a huge surprise that there’s some level of Python for Android support using Qt for the GUI. As far as I’m aware pyqtdeploy is the primary build tool for this, using the PyQt bindings, but I may not be up to date about it. I’ve never seen this to be very popular, but I don’t know if there’s a reason for this beyond its relative obscurity.

What are actually the recent developments in CPython on Android?

Various contributors have driven improvements in CPython’s Android support over the years. I would give more details but honestly I’ve never found the time to get deeply involved so the historical summary is limited by my own ignorance! This has brought CPython to the point of being fairly easy to compile for Android as of about version 3.6. For instance, python-for-android’s CPython build recipe applies no essential patches to the Python 3 source, just an appropriate set of build arguments.

The key recent development is the BeeWare project’s switch to CPython explained above. They’ve made a specific goal of understanding where CPython’s Android support can be improved, and getting involved to resolve these problems. This means attacking both individual technical issues (e.g. getting Python’s test suit passing correctly), and longer-term structural problems (e.g. the suggestion described in the ZDNet article to create a stripped-back Python kernel for mobile use).

These recent developments are great, and hopefully will lead to huge improvements in the ease of deploying Python applications for Android, especially addressing the missing functionality of using the native Java GUI toolkit. However, let’s not forget the history of CPython on Android, people have been creating applications for both business and pleasure for many years.

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

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 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 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 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: