Write your own Android application
Matt Holder investigates how to create cross-platform applications using a combination of Python and the Kivy framework.
Matt Holder investigates how to create your very own Android application using Python and the Kivy framework.
In this article, we are exploring Kivy, learning about its GUI language and how this links back to Python code and developing an application that can run on multiple platforms. Kivy is supported on Windows, Mac OS, Linux, Android and iOS.
By the end of this article, we will have developed an app that runs on desktop Linux as well as Android. We will also have introduced the necessary concepts to create a simple stopwatch application, rendered our simple user interface, written the logic to update the text and perform the counting function, as well as compiling an APK that can be installed on Android devices.
The complete code can be downloaded from
https://github.com/mattmole/LXF310-KivyStopwatch. So, without further ado, let’s begin.
The first thing to introduce is Kivy’s design language and how this links back to the Python code. As in other GUI frameworks, GUI elements can be defined within a supporting file, which means that the Python code itself can be less cluttered, due to the GUI elements being defined elsewhere. Kivy does seem to have more of a crossover with the code as variables from the Python code can be referenced within the KV design file. For example, you could reference a variable in the Python code, which is used to calculate the size of the window, based on the screen resolution and the platform that the code is running on. Conversely, it is also possible to link Python variables to items in the design file, so they can be updated accordingly.
Within Kivy, we can think of our GUI as a hierarchical structure. For example, a window with a single label can be thought of as the label being the child widget of the window. Expanding this a little, thinking of our stopwatch GUI, the window contains a vertical
BoxLayout, which is a child of the window, and the first child the BoxLayout has is a label. The second child of the BoxLayout is another BoxLayout, this time in horizontal mode, which has two children, which are both buttons.
The built-in behaviour of Kivy is that the window automatically adjusts to a change of window size. This is accomplished using size and position hints, which are relative measurements, based on sizes of other widgets on the GUI.
Kivy uses built-in layouts to allow any GUI to be created, and these are AnchorLayout, BoxLayout, FloatLayout, RelativeLayout, GridLayout, PageLayout, ScatterLayout and StackLayout. Some of these allow unconstrained positioning of widgets, while others require widgets to be relative to the position of others. You can learn more at https://kivy.org/doc/stable/ gettingstarted/layouts.html.
Design file
This code sample is used to define the GUI of the stopwatch and defines the location of the items on the screen. Here we are defining the window itself, a label to show the timing, and two buttons to reset, start and pause the timer. This file is called timer.kv and it links directly to the name of the application class in the code. The class is called TimerApp and the design file
App
#:kivy 1.0.9
BoxLayout: spacing: 50 padding: 50 size: root.width, root.height orientation: ‘vertical’
Label: id: timerLabel font_size: sp(0.25*self.width/pt(1)) size: self.texture_size
BoxLayout: spacing: 50 padding: 50 orientation: ‘horizontal’
Button: id: stopButton on_press: root.stopTimer() disabled: True size_hint_x: 0.5 size_hint_y: 0.5 pos_hint: {“center_x”: 0.5, “center_y”: 0.5} Image: source: ‘images/Stop.png’ y: self.parent.y x: self.parent.x size: self.parent.width, self.parent.height allow_stretch: True
Button: on_press: root.startTimer() size_hint_x: 0.5 size_hint_y: 0.5 pos_hint: {“center_x”: 0.5, “center_y”: 0.5} Image: source: ‘images/PlayPause.png’ y: self.parent.y x: self.parent.x size: self.parent.width, self.parent.height allow_stretch: True
On the first line of this code sample, we define that the program is designed for Kivy version 1.0.9. On the next line, we define the main window, which is called TimerWindow. This is important because it links directly with the name of one of the classes in the Python code. Similarly to Python, indentation is used to define when an object is the child of another object. For example, the BoxLayout is a child of the main window, and the label is a child of the BoxLayout.
On the next two lines, we define the variable names that will link the Python code to the GUI object – for simplicity, we have kept the variable and widget names the same. The next thing we need to do is to define a
BoxLayout, which has spacing and padding of 50 pixels and is the same size as the parent window (note the usage of root.width and root.height). When defining the BoxLayout, it can have an orientation of
Vertical, where widgets are rendered above and below each other, or Horizontal, where widgets are rendered alongside each other.
Once the BoxLayout has been defined, we now define which widgets we want to be shown. The first of these is the label, which is used to show the stopwatch as it counts time. The id of the label allows it to be referred to elsewhere and font_size defines the size of the text. By using this calculation, the font size changes based on the width of the window. The division by pt(1) was required to ensure the text was the correct size on Android. As the window size changes, so does the size of the text. Finally, when defining the label, the size entry sets the size including any padding as defined by the parent layout. Note how we are not defining any default text for the label in the design file.
The next widget to display in the BoxLayout is another BoxLayout, but this time with the type of Horizontal. The first BoxLayout displays the label on the first row and then the second row contains a second BoxLayout with two buttons, side by side. Both buttons are defined in the same way, so only the first is discussed here.
The stop button needs to be referred to from the main code, hence why it has an id value and is referenced near the top of the design file. Should text need to be displayed, the font_size argument can be
added, with a similar calculation to the one above. The on_press argument is now added, with the name of the Python function that is called when the button is pressed. This stop button effectively resets the counter, so can only be used once the timer has been started and then paused – this is why the button is initially disabled. The two size_hint arguments have been added next and these are used to define the size of the buttons, relative to their parent. This means that the buttons should occupy half of the space within their section of the BoxLayout. The pos_hint arguments are similar and refer to where the widget should be drawn. In our code sample, the buttons are in the middle of their section within the BoxLayout. Finally, we add the Image widget as a child of the button, which is used to draw the Stop or Play/Pause buttons within the buttons.
The size_hint and pos_hint arguments seem quite cryptic at first usage, but are incredibly important to allow the automatic scaling of the GUI to work as expected. Equivalent arguments exist if absolute referencing is preferred to relative.
The code
Before writing any code, we need to install some libraries so that we can create our application and get it to run. To install these requirements, open your terminal and enter the following:
$ python -m pip install --upgrade pip setuptools
virtualenv
$ mkdir folder_name
$ cd folder_name
$ python -m virtualenv .
$ source bin/activate
$ python -m pip install “kivy[base]”
$ pip install plyer
This code sample is split into discrete sections, covering things such as importing libraries and explaining each class. The code needs to be added to a file called main.py. from kivy.app import App from kivy.uix.widget import Widget from kivy.config import Config from kivy.clock import Clock from kivy.core.window import Window from datetime import timedelta from plyer import vibrator from the datetime library, along with the vibrator method from plyer – which allows access to the vibration functionality on our smart phones. class TimerWindow(Widget): def __init__(self): super().__init__()
self.zeroCountString = “0:00:00.000” self.timerLabel.text = self.zeroCountString
# Create a timer to update the stopwatch and display timer = Clock.schedule_interval(self. increaseTimer, 0.001) guiUpdateTimer = Clock.schedule_interval(self. updateWindow, 0.1)
self.startCount = False self.msCount = 0
def increaseTimer(self,dt): if self.startCount: self.msCount += dt*1000
def startTimer(self): try: vibrator.vibrate(0.1) # vibrate for 0.1 seconds except NotImplementedError: print(“Cannot call the vibrate function as it is not supported on this platform”) if self.startCount: self.startCount = False self.stopButton.disabled = False else: self.startCount = True self.stopButton.disabled = True
def stopTimer(self): try: vibrator.vibrate(0.1) # vibrate for 0.1 seconds except NotImplementedError: print(“Cannot call the vibrate function as it is not supported on this platform”) if not self.startCount: self.msCount = 0 self.timerLabel.text = self.zeroCountString
def updateWindow(self,dt): if self.startCount: