Kivy: Interactive Applications in Python

4.5 (2 reviews total)
By Roberto Ulloa
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. GUI Basics – Building an Interface

About this book

Mobiles and tablets have brought with them a dramatic change in the utility of applications. Compatibility has become essential, and this has increased the kind of interaction that users expect: gestures, multi-touches, animations, and magic pens. Kivy is an open source Python solution that covers these market needs with an easy-to-learn and rapid development approach. Kivy is growing fast and gaining attention as an alternative to the established developing platforms.

Kivy: Interactive Applications in Python quickly introduces you to the Kivy development methodology. You will learn some examples of how to use many of the Kivy components, as well as understand how to integrate and combine them into big projects. This book serves as a reference guide and is organized in such a way that once finished, you will have already completed your first project.

You will start by learning the Kivy Language for building User Interfaces (UI) and vector figures. We then proceed to the uses of Kivy events and properties to glue the UI with the application logic.

You then go on to build an entire User Interface (UI) starting from a hand-made sketch. Furthermore, you will go on to understand how to use the canvas and drawing instructions to create different types of geometrical figures. Finally, you will be introduced to a big set of interactive and smooth features: transformations (scale, rotate, and translate), gestures, animations, scheduling tasks, and multi-touch elements.

Kivy: Interactive Applications in Python expands your knowledge by introducing various components that improve the User Experience (UX). Towards the end of the book, you will be confident to utilize Kivy components and strategies to start any application or game you have in mind.

Publication date:
September 2013
Publisher
Packt
Pages
138
ISBN
9781783281596

 

Chapter 1. GUI Basics – Building an Interface

Kivy emerges as a successor of PyMT (a library for multitouch applications) with a simple but ambitious goal in mind — same code for every commonplace platform: Linux / Windows / Mac OS X / Macosx / Android / iOS (Mathieu Virbel, http://txzone.net/2011/01/kivy-next-pymt-on-android-step-1-done/). This support is being extended to Raspberry Pi thanks to a founding campaign started by Mathieu Virbel, the creator of Kivy. Kivy was introduced in the EuroPython 2011, as a Python framework designed for creating natural user interfaces.

So, let's start creating user interfaces using one of its fun and powerful components, the Kivy language (.kv). The Kivy language helps us to separate the logic from the presentation. This is a fundamental engineering concept that helps to keep an easy and intuitive code. Nonetheless, it is possible to build a Kivy application using pure Python and Kivy as a library. We will also learn those concepts in later chapters because they allow us to modify interfaces dynamically.

This chapter covers all the basics for building a graphical user interface (GUI) in Kivy. Afterwards, you will be able to build practically any GUI you have in your mind, and even make them responsive to the size of window! The following is a list of all the skills that you're about to learn:

  • Launching a Kivy application

  • The Kivy language

  • Creating and using widgets (GUI components)

  • Basic properties and variables of the widgets

  • Fixed, proportional, absolute, and relative coordinates

  • Organizing GUIs through layouts

  • Tips for achieving responsive GUIs

Apart from Python, this chapter requires some knowledge about Object-Oriented Programming (http://en.wikipedia.org/wiki/Object-oriented_programming) concepts. In particular, inheritance (http://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)) and the difference between instances (http://en.wikipedia.org/wiki/Instance_(computer_science)) and classes (http://en.wikipedia.org/wiki/Class_(computer_science)) will be assumed. Before starting, you will need to install Kivy (The instructions can be found in http://kivy.org/docs/installation/installation.html). The book examples were tested on Kivy 1.7.0 but a more recent version should work as well.

At the end of this chapter, we will be able to build a GUI starting from a pencil and paper sketch. We will introduce the main project of the book — the Comic Creator, and implement the main structure of the GUI.

 

Hello World!


Let's put our hands on our first code. The following is yet another Hello World program:

1. # File name: hello.py
2. import kivy
3. kivy.require('1.7.0')
4. 
5. from kivy.app import App
6. from kivy.uix.button import Label
7. 
8. class HelloApp(App):
9.   def build(self):
10.     return Label(text='Hello World!')
11. 
12. if __name__=="__main__":
13.   HelloApp().run()

This is merely a Python code. Launching a Kivy program is not different from launching any other Python application. In order to run the code, you just have to open a terminal (line of commands or console) and specify the command, python hello.py --size=150x100 (--size is a parameter to specify the screen size). In the preceding code, the lines 2 and 3 verify if you have the appropriate version of Kivy installed in your computer.

Note

If you try to launch your application with an older Kivy version (say 1.6.0), an exception is raised for the specified version. There is no exception raised if you have a more recent version. Of course, backwards compatibility is desired but not always possible, and so you might face problems if you use a newer version.

We omit this statement in most of the examples inside the book, but you will be able to find it again in the online codes, which you can download, and its use is strongly encouraged in real life projects. The program uses two classes from the Kivy library (lines 5 and 6): App and Label. The App class is the starting point of any Kivy application. The following screenshot shows the window containing a Label with the Hello World text:

Hello World Output

The way we use the App class is through inheritance. App becomes the base class of HelloApp (line 8), the subclass or child class. In practice, this means that the HelloApp class has all the properties and methods of App in addition to whatever we define in the body (lines 9 and 10) of the HelloApp class.

In this case, the HelloApp's body just modifies one of the existent App's methods, the build(self) method. This method returns the window content. In this case, a simple Label saying Hello World! (line 10). Finally, the line 13 creates an instance of HelloApp and runs it.

So, is Kivy just another library for Python? Well, yes. But as part of the library, Kivy offers its own language to separate the logic from the presentation. For example you could write the preceding Python code in two separate files. The first file would then include the Python lines as shown in the following code:

14. # File name: hello2.py
15. from kivy.app import App
16. from kivy.uix.button import Label
17. 
18. class Hello2App(App):
19.   def build(self):
20.     return Label()
21.
22. if __name__=="__main__":
23.   Hello2App().run()

The hello2.py code is very similar to hello.py. The difference is that the line 20 doesn't have the Hello World! message. Instead, the message has been moved to the text property in the second file (hello2.kv) which contains the Kivy language:

24. # File name: hello2.kv
25. #:kivy 1.7.0
26. <Label>:
27.   text: 'Hello World!'

How does Python or Kivy know that these files are related? This is quite important and tends to be confusing at the beginning. The key is in the name of the subclass of the App, that is, HelloApp.

Note

The initial part of the App's subclass name has to coincide with the name of the Kivy file. For example, if the definition of the class is class FooApp(App), then the name of the file must be foo.kv and it must be in the same directory of the main file (the one that executes the App's run() method).

Once that consideration is included, this example can be run in the same way we ran the hello.py. We just need to be sure that we are calling the new main file (hello2.py), python hello2.py -–size=150x100.

This is your first contact with the Kivy language, so let's go slowly. The #:Kivy 1.7.0 line of the hello2.kv code tells Python the minimal Kivy version that should be used. The line does the same that the lines 2 and 3 did in the hello.py code. The instructions that start with #: in the header of a Kivy language are called directives. We will also be omitting the version directive along the book, but remember to include it in your projects.

The <Label>: rule (line 26) indicates that we are going to modify the Label class by setting 'Hello World!' in the text property (line 27). This code generates the same output that was shown in the previous screenshot. There is nothing you can't do using pure Python and importing the necessary classes from the Kivy library as we did in the first example (hello.py). However, the separation of the logic from the presentation results in simpler and cleaner code. Therefore, this book explains all the presentation programming through the Kivy language, unless dynamic components are added.

You might be worrying that modifying the Label class affects all the instances we create from Label, because, they will all contain the same Hello World text. That is true. Therefore, in the following section, we are going to learn how to directly modify specific instances instead of classes.

 

Basic widgets – labels and buttons


In the previous section, we were already using the Label class, which is one of the widgets that Kivy provides. Widgets are the little interface blocks that we use to set up the GUI. Kivy has a complete set of widgets including buttons, labels, checkboxes, dropdowns, and so on. You can find them all in the Kivy API kivy.uix (http://kivy.org/docs/api-kivy.html) under the package kivy.uix.

It's a good practice to create your own Widget for your applications instead of using the Kivy classes directly as we did in hello2.kv (line 26). The following code shows how to do that through inheritance:

28. # File name: widgets.py
29. from kivy.app import App
30. from kivy.uix.widget import Widget
31. 
32. class MyWidget(Widget):
33.   pass
34. 
35. class WidgetsApp(App):
36.   def build(self):
37.     return MyWidget()
38. 
39. if __name__=="__main__":
40.   WidgetsApp().run()

In line 32 of the preceding code (widgets.py), we inherit from the base class Widget and create the subclass MyWidget, and, in line 37, we instantiated MyWidget instead of modifying the Kivy Label class directly as we did in hello2.py. The rest of the code is analogous to what we covered before. The following is the corresponding Kivy language code (widgets.kv):

41. # File name: widgets.kv
42. <MyWidget>:
43.  Button:
44.    text: 'Hello'
45.    pos: 0, 100
46.    size: 100, 50
47.    color: .8,.9,0,1
48.    font_size: 32
49.  Button:
50.    text: 'World!'
51.    pos: 100,0
52.    size: 100, 50
53.    color: .8,.9,0,1
54.    font_size: 32

Notice that now we are using buttons instead of labels. Most of the basic widgets in Kivy work in a very similar manner. In fact, Button is just a subclass of Label that includes more properties such as background color.

Compare the notation of line 26 (<Label>:) of hello2.kv with the line 43 (Button:) of the preceding code (widgets.kv). We used the class notation (<Class>:) for the Label (and for MyWidget) but another notation (Instance:) for Button. We just defined that MyWidget has two instances of Button (on line 43 and 49), and then we set the properties of those instances (the color is in RGBA format that stands for red, green, blue, and alpha/transparency).

The size and pos properties consist of fixed values, that is, the exact pixels on the window.

Note

Notice that the coordinate (0, 0) is at the bottom-left corner, that is, the Cartesian origin. Many other languages (including CSS) use the top-left corner as the (0, 0) coordinate, so be careful with this.

The following screenshot shows the output of the widgets.py and widgets.kv code files with some helpful annotations (on white color):

Creating our own widget

A couple of things could be improved in the previous code (widgets.kv). The first thing is that there are many repeated properties for the buttons such as pos, color and font_size. Instead of that, let's create our own Button as we did with MyWidget. The second is that the fixed position is quite annoying because the widgets don't adjust when the screen is resized because the position is fixed. Let's make the widgets more responsive:

55. # File name: widgets2.kv
56. <[email protected]>:
57.  color: .8,.9,0,1
58.  font_size: 32
59.  size: 100, 50
60.
61. <MyWidget>:
62.  MyButton:
63.    text: 'Hello'
64.    pos: root.x, root.top - self.height
65.  MyButton:
66.    text: 'World!'
67.    pos: root.right - self.width, root.y

In widgets2.kv we created (<[email protected]>:) and customized the MyButton class (as shown in lines 56 to 59) and instances (as shown in the lines 62 to 67).

Please note the difference between the way we defined MyWidget and MyButton. We need to specify @Class only if we didn't define the base class in the Python side as we did with MyWidget (line 32 of widgets.py). On the other hand, we had to define MyWidget in the Python side because we instantiated it directly (line 37 of widgets.py).

In this example, each Button's position is responsive in the sense that they will always be displayed in the corners of the screen, no matter what the window size is. In order to achieve that, we need to use the self and root variables. You might be familiar with the variable self. As you have probably guessed, it is just a reference to the Widget itself. For example, self.height (line 64) has a value of 50 because that is the height of that particular MyButton. The root variable is a reference to the Widget class at the top of the hierarchy. For example, the root.x (line 64) has a value of 0 because that is the position in the X-axis of the MyWidget instance created on in line 37 of widgets.py. Since, the MyWidget instance is the only one in the WidgetsApp, it uses all the space by default; therefore, the origin is (0, 0). The x, y, width, and height are also the widgets properties.

Still, fixed coordinates are an inefficient way of organizing widgets and elements in the window. Let's move on to something smarter: layouts.

 

Layouts


No doubt that fixed coordinates are the most flexible way of organizing elements in an n-dimensional space; however, it is very time-consuming. Instead, Kivy provides a good set of layouts instead, which facilitate the work of organizing widgets. A Layout is a Widget subclass that implements different strategies to organize embedded widgets. For example, one strategy could be organizing widgets in a grid (GridLayout).

Let's start with a simple FloatLayout example. It works very similar to the way we organize widgets directly inside another Widget, except that now we can use proportional coordinates (proportions of the total size of the window) rather than fixed coordinates (exact pixels). This means that we don't need the calculations we did in the previous section with self and root. The following is the Python code:

68. # File name: floatlayout.py
69. 
70. from kivy.app import App
71. from kivy.uix.floatlayout import FloatLayout
72. 
73. class FloatLayoutApp(App):
74.   def build(self):
75.     return FloatLayout()
76. 
77. if __name__=="__main__":
78.   FloatLayoutApp().run()

There is nothing new in the preceding code (floatlayout.py) except for the use of FloatLayout (on line 75). The interesting parts are in the Kivy language (floatlayout.kv):

79. # File name: floatlayout.py
80. <Button>:
81.   color: .8,.9,0,1
82.   font_size: 32
83.   size_hint: .4, .3
84. 
85. <FloatLayout>:
86.   Button:
87.     text: 'Hello'
88.     pos_hint: {'x': 0, 'top': 1}
89.   Button:
90.     text: 'World!'
91.     pos_hint: {'right': 1, 'y': 0}

In the floatlayout.kv code file, we use two new properties, size_hint and pos_hint, which work with the proportional coordinates with values ranging from 0 to 1; (0, 0) is the bottom-left corner and (1, 1) the top-right corner. For example, the size_hint on line 83 sets the width to 40 percent of the current window width and the height to 30 percent of the current window height. Something similar happens to the pos_hint but the notation is different: a Python dictionary where the keys (for example, 'x' or 'top') indicate which part of the widget is referenced. For instance, 'x' is the left border. Notice that we use the top key instead of y in line 88 and right instead of x in line 91. The top and right properties respectively reference the top and right edges of the Button, so it makes the positioning simpler. That doesn't mean we could have used x and y for both the axes. For example, something like pos_hint: {'x': .85, 'y': 0} on line 91. The right and top keys avoids some calculations and makes the code clearer. The next screenshot illustrates the output of the previous code with the available keys for the pos_hint dictionary:

Using FloatLayout

The available pos_hint keys ( x, center_x, right, y , center_y, and top) are useful for aligning to edges or centering. For example, pos_hint: {'center_x':.5, 'center_y':.5} would align a Widget in the middle, no matter what the size of the window is.

Could have we used top and right with the fixed positioning of widgets2.kv (in line 64 and 67)? Yes, we could; but notice that pos doesn't accept Python dictionaries ({'x':0,'y':0}) that just lists of values (0,0). Therefore, instead of using the pos property, we have to use the x, center_x, right, y, center_y, and top properties directly. For example, instead of pos: root.x, root.top - self.height, we would have used the following code:

  x: 0
  top: root.height

Notice that these properties always specify fixed values (pixels) and not proportional ones.

Note

If we want to use proportional coordinates, we have to be inside a Layout (or an App), and use the pos_hint property.

If we are using a Layout instance, can we force the use of fixed values? Yes, but there can be conflicts if we are not careful with the properties we use. If we use any Layout, then pos_hint and size_hint will have the priority. If we want to use fixed positioning properties (pos, x, center_x, right, y, center_y, and top), we have to ensure that we are not using the pos_hint property. Secondly, if we want to use the size, height, or width properties, we need to give a None value to the size_hint axis we want to use with the absolute values. For example, size_hint: (None, .10) allows using the height property, but it keeps the width as 10 percent of the windows size. The following table summarizes everything we learned about the positioning and sizing properties. The first and second columns indicate the name of the property and its respective value. The third and fourth columns indicate if it is available for layouts and for widgets:

Property

Value

For layouts

For widgets

size_hint

A pair [w, h] where, w and h express a proportion (from 0 to 1 or None)

Yes

No

size_hint_x size_hint_y

A proportion from 0 to 1 or None indicating width (size_hint_x) or height (size_hint_y)

Yes

No

pos_hint

A dictionary with one x-axis key (x, center_x, or right) and one y-axis key (y, center_y, or top). The values are proportions from 0 to 1

Yes

No

size

A pair [w, h] where, w and h indicate fixed width and height in pixels

Yes, but set size_hint: (None, None)

Yes

width

A value indicating a fixed number of pixels

Yes, but set size_hint_x: None

Yes

height

A value indicating a fixed number of pixels

Yes, but set size_hint_y: None

Yes

pos

A pair [x, y] indicating a fixed coordinate (x, y) in pixels

Yes, but don't use pos_hint

Yes

x, right, or center_x

They have fixed number of pixels

Yes, but don't use x, right, or center_x in pos_hint

Yes

y, top, or center_y

The have fixed number of pixels

Yes, but don't use y, top, or center_y in pos_hint

Yes

We have to be careful because some of the properties behave different according to the layout we are using. Kivy currently has seven different layouts; six of them are briefly described in the following table. The left column shows the name of the Kivy Layout class and the right column describes briefly how they work:

Layout

Details

FloatLayout

This layout organizes the widgets with proportional coordinates with the size_hint and pos_hint properties. The values are numbers between 0 and 1 indicating a proportion to the window size.

RelativeLayout

This layout operates in the same way as FloatLayout does, but the positioning properties (pos, x, center_x, right, y, center_y, and top) are relative to the Layout size and not the window size.

GridLayout

This layout organizes widgets in a grid. You have to specify at least one of the two properties: cols (for columns) or rows (for rows).

BoxLayout

This layout organizes widgets in one row or one column depending whether the value of property orientation is horizontal or vertical.

StackLayout

This layout is similar to BoxLayout but it goes to the next row or column when it runs out of space. In this layout, there is more flexibility to set the orientation. For example, 'rl-bt' organizes the widgets in right-to-left and bottom-to-top order. Any combination of lr (left to right), rl (right to left), tb (top to bottom), and bt (bottom to top) is allowed.

AnchorLayout

This layout organizes the widgets to a border or to the center. The anchor_x property indicates the x position (left, center or right), whereas anchor_y indicates the y position (top, center or bottom)

The seventh layout, which is the ScatterLayout, works similar to RelativeLayout but it allows multitouch gesturing for rotating, scaling, and translating. It is slightly different in its implementation so we will review it later. The Kivy API (http://kivy.org/docs/api-kivy.html) offers a detailed explanation and good examples on each of them. The behavioral difference of the properties depending on the Layout is sometimes unexpected but the following are some of the hints that will help you in the GUI building process:

  • The size_hint, size_hint_x, and size_hint_y properties work on all the layouts but the behavior might be different. For example, GridLayout will try to take an average of the x hints and y hints on the same row or column respectively.

  • It is advisable to use values from 0 to 1 with the size_hint, size_hint_x, and size_hint_y properties. However, you can also use values bigger than 1. Depending on the Layout, Kivy makes the Button bigger than the container or tries to recalculate a proportion based on the sum of the hints on the same axis.

  • The pos_hint property works only in FloatLayout, RelativeLayout, and BoxLayout. In BoxLayout, only the x keys (x, center_x, and right) work in the vertical orientation and vice versa. An analogous rule applies for the fixed positioning properties (pos, x, center_x, right, y, center_y, and top).

  • The size_hint , size_hint_x, and size_hint_y properties can always be set as None in favor of size, width, and height.

There are more properties and particularities of each Layout, but with the ones we have covered, you will be able to build almost any GUI you desire. In general, the recommendation is to use the layout as it is. Don't try to force the layout through an odd configuration of properties. Instead, it is better to use more layouts and embed them to reach our design goals. In the next section, we will teach you how to embed layouts, and we will offer a more comprehensive example of them.

 

Embedding layouts


The layouts studied in the previous section are subclasses of Widget. We have already been embedding widgets inside widgets since the beginning of this chapter and, of course, it won't matter if the widgets we are embedding are layouts as well. The following Python code is the base of a comprehensive example about embedding Layouts:

92. # File name: layouts.py
93. from kivy.app import App
94. from kivy.uix.gridlayout import GridLayout
95. 
96. class MyGridLayout(GridLayout):
97.   pass
98. 
99. class LayoutsApp(App):
100.   def build(self):
101.     return MyGridLayout()
102. 
103. if __name__=="__main__":
104.   LayoutsApp().run()

There's nothing new in the preceding code, we just implemented the MyGridLayout class. The final result is shown first in the following screenshot with a few indications in different colors:

In the preceding screenshot all the types of Kivy layouts are embedded into a GridLayout of 2 rows by 3 columns. This is a big example, so we are going to study the corresponding Kivy language code (layouts.kv) in five fragments. Don't be overwhelmed by the amount of code, it is very straightforward. The following code is the fragment 1:

105. # File name: layouts.kv (Fragment 1)
106. <MyGridLayout>:
107.   rows: 2
108.   FloatLayout:
109.     Button:
110.       text: 'F1'
111.       size_hint: .3, .3
112.       pos: 0, 0
113.   RelativeLayout:
114.     Button:
115.       text: 'R1'
116.       size_hint: .3, .3
117.       pos: 0, 0

In the preceding code, MyGridLayout is first defined by the number of rows (line 107). Then we add the first two layouts, FloatLayout and RelativeLayout with one Button each. Both the buttons (F1 and R1) have defined the property pos: 0,0 (lines 112 and 117) but you can notice in the preceding screenshot (Embedding Layouts) that the Button F1 (line 110) is in the bottom-left corner of the whole window, whereas the Button R1 (line 115) is in the bottom-left corner of the RelativeLayout. The reason for this is that the coordinates in FloatLayout are not relative to the position of the layout. It is important to mention that this wouldn't have happened if we have used pos_hint, which always use the relative coordinates.

In the fragment 2, one GridLayout is added to MyGridLayout as shown in the following code:

118. # File name: layouts.kv (Fragment 2)
119.   GridLayout:
120.     cols: 2
121.     spacing: 10
122.     Button:
123.       text: 'G1'
124.       size_hint_x: None
125.       width: 50
126.     Button:
127.       text: 'G2'
128.     Button:
129.       text: 'G3'
130.       size_hint_x: None
131.       width: 50

In the preceding code, we define two columns (line 120) and a spacing of 10 (line 121) which separates the internal widgets by 10 pixels from each other. Also notice that in the previous screenshot (Embedding Layouts), the first column is thinner than the second column. We achieved this by setting the size_hint_x property to None and width to 50 of the buttons G1 (line 122) and G3 (line 128).

In the fragment 3, an AnchorLayout is added as shown in the following code:

132. # File name: layouts.kv (Fragment 3)
133.   AnchorLayout:
134.     anchor_x: 'right'
135.     anchor_y: 'top'
136.     Button:
137.       text: 'A1'
138.       size_hint: [.5, .5]
139.     Button:
140.       text: 'A2'
141.       size_hint: [.2, .2] 

In the preceding code, we have specified anchor_x to right and anchor_y to top (line 134 and 135). You can notice in the previous screenshot (Embedding Layouts) how both the buttons (lines 136 and 139) of AnchorLayout have been placed in the top-right corner. This layout is very useful to embed other layouts inside it, for example top menu bars or side bars.

In the fragment 4, a BoxLayout is added as shown in the following code:

143. # File name: layouts.kv (Fragment 4)
144.    BoxLayout:
145.        orientation: 'horizontal'
146.        Button:
147.            text: 'B1'
148.        Button:
149.            text: 'B2'
150.            size_hint: [2, .3]
151.            pos_hint: {'y': .4}
152.        Button:
153.            text: 'B3' 

The preceding code illustrates the use of BoxLayout in its horizontal orientation. Also, in lines 150 and 151 we use size_hint and pos_hint to move the button B2 further up.

Finally, the fragment 5 adds a StackLayout as shown in the following code:

154. # File name: layouts.kv (Fragment 5)
155.  StackLayout:
156.    orientation: 'rl-tb'
157.    padding: 10
158.    Button:
159.      text: 'S1'
160.      size_hint: [.6, .2]
161.    Button:
162.      text: 'S2'
163.       size_hint: [.4, .4]
164.    Button:
165.      text: 'S3'
166.      size_hint: [.3, .2]
167.    Button:
168.      text: 'S4'
169.      size_hint: [.4, .3] 

Here we have added four buttons of different sizes. To understand the rules applied to organize the widgets in the rl-tb (right-to-left and top-to-bottom) orientation (line 156) please refer back to the previous screenshot (Embedding Layouts). Also, you can notice that the padding property (line 157) adds 10 pixels of space between the widgets and the border of the StackLayout.

 

Our Project – comic creator


You now have all the necessary concepts to create any interface you want. This section describes the project that we will complete along this section itself and the following three chapters. The basic idea of the project is a comic creator, a simple application to draw a stickman. The following sketch is a wireframe of the GUI we have in mind.

Comic creator sketch

We can distinguish several separate areas in the preceding sketch. Firstly, we need a drawing space (top-right) for our comics. We also need a tool box (top-left) with some drawing tools to draw our figures, and also some general options (second from bottom to top) such as clearing the screen, removing the last element, grouping elements, changing colors, and using the gestures mode. Finally, it will be useful to have a status bar (center-bottom) to provide some information to the user such as quantity of figures or the last action that has been performed. According to what we have learned along this chapter, there are multiple solutions to organize this screen but we will use the following:

  • We'll use AnchorLayout for the toolbox area in the top-left corner

  • We'll use Gridlayout of two columns for the drawing tools

  • We'll use AnchorLayout for the drawing space in the top-right corner

  • We'll use RelativeLayout to have a relative space to draw in

  • We'll use AnchorLayout for the general options and status bar area at the bottom

  • We'll use BoxLayout with vertical orientation to organize the general options on top of the status bar. We'll use also BoxLayout with horizontal orientation for the buttons of the general options, and again for the labels of the status bar.

We'll follow this structure by creating different files for each area: comiccreator.py, comiccreator.kv, toolbox.kv, generaltools.kv, drawingspace.kv, and statusbar.kv. Let's start with comiccreator.py as shown in the following code:

170. # File name: comiccreator.py
171. from kivy.app import App
172. from kivy.lang import Builder
173. from kivy.uix.anchorlayout import AnchorLayout
174. 
175. Builder.load_file('toolbox.kv')
176. Builder.load_file('drawingspace.kv')
177. Builder.load_file('generaloptions.kv')
178. Builder.load_file('statusbar.kv')
179. 
180. class ComicCreator(AnchorLayout):
181.   pass
182. 
183. class ComicCreatorApp(App):
184.   def build(self):
185.     return ComicCreator()
186. 
187. if __name__=="__main__":
188.   ComicCreatorApp().run()

We are explicitly loading some of the files with the Builder.load_file instruction (from line 175 to 178). There is no need to load the comiccreator.kv because it gets automatically loaded by the ComicCreatorApp name.

Note

The Builder class is in charge of loading and parsing all the Kivy language. The load_file method allows us to specify a file containing Kivy language rules that we want to include as part of the project.

For the ComicCreator we choose AnchorLayout. It is not the only option, but it illustrates more clearly within the code that the next level is composed of the AnchorLayout instances. You might wonder whether it might be possible to use a simple Widget. That would have been clear enough but unfortunately it is not possible because Widget doesn't honor the size_hint and pos_hint properties, which are necessary in the AnchorLayout internals.

The following is the code of the comiccreator.kv:

189. # File name: comiccreator.kv
190. <ComicCreator>:
191.   AnchorLayout:
192.     anchor_x: 'left'
193.     anchor_y: 'top'
194.     ToolBox:
195.       id: _tool_box
196.       size_hint: None,None
197.       width: 100
198.   AnchorLayout:
199.     anchor_x: 'right'
200.     anchor_y: 'top'
201.     DrawingSpace:
202.       size_hint: None,None
203.       width: root.width - _tool_box.width
204.       height: root.height - _general_options.height - _status_bar.height
205.   AnchorLayout:
206.     anchor_x: 'center'
207.     anchor_y: 'bottom'
208.     BoxLayout:
209.       orientation: 'vertical'
210.       GeneralOptions:
211.         id: _general_options
212.         size_hint: 1,None
213.         height: 48
214.       StatusBar:
215.         id: _status_bar
216.          size_hint: 1,None
217.          height: 24

The preceding code follows the proposed structure for the comic creator. There are basically three AnchorLayout instances in the first level (lines 191, 198, and 205) and a BoxLayout that organizes the general options and the status bar (line 208).

The lines 197, 213 and 217 set the width of the ToolBox to 100 pixels, the height of the GeneralOptions to 48 pixels, and the height of the StatusBar to 24 pixels respectively. This brings up an interesting problem. We would like that the DrawingSpace uses all the remaining width and height of the screen (no matter what the windows size is). In other words, we want the drawing space as big as possible without covering the other areas (tool box, general options and status bar). In order to solve this, we introduced the use of id (in lines 195, 211, and 215) that allows us to refer to other components inside the Kivy language. On lines 203 and 204 we subtract the tool_box width to the root width (line 203) and the general_options and status_bar height to the root height (line 204). Accessing these attributes is only possible through the id's we created, which can be used as variables inside the Kivy language.

Let's continue with the toolbox.kv as shown in the following code:

218. # File name: toolbox.kv
219. <[email protected]>:
220.   size_hint: None,None
221.   size: 48,48
222.   group: 'tool'
223. 
224. <[email protected]>:
225.   cols: 2
226.   padding: 2
227.   ToolButton:
228.     text: 'O'
229.     ToolButton:
230.     text: '/'
231.   ToolButton:
232.     text: '?'

We created a ToolButton class that defines the size of the drawing tools and also introduces a new Kivy Widget: ToggleButton. The difference from the normal Button is that it stays clicked until we click on it again. The following is an example of the toolbox with a ToolButton activated:

Toolbox area with an active ToggleButton

Moreover, it can be associated to other ToggleButton instances, so just one of them is clicked at a time. We can achieve this by assigning the same group property (line 222) to the ToggleButton instances we want to react together. In this case, we want all the instances of ToolButton instances to be part of the same group, so we set the group in the ToggleButton class definition (line 222).

On line 224, we implemented the ToolBox as a subclass of GridLayout and we added some character placeholders ('O', '/', and '?') to the ToolButton. These placeholders will be substituted for something appropriate representations in the following chapters.

The following is the code of generaloptions.kv:

233. # File name: generaloptions.kv
234. <[email protected]>:
235.   orientation: 'horizontal'
236.   padding: 2
237.   Button:
238.     text: 'Clear'
239.   Button:
240.     text: 'Remove'
241.   ToggleButton:
242.     text: 'Group'
243.   Button:
244.     text: 'Color'
245.   ToggleButton:
246.     text: 'Gestures'

In this case, when we used the ToggleButton instances (in lines 241 and 245), we didn't associate them to any group. Here, they are independent from each other and will just keep a mode or state. The preceding code only defines the GeneralOptions class, but there is no functionality associated yet. The following is the resulting screenshot of this area:

General Options area

The statusbar.kv file is very similar in the way it uses the BoxLayout:

247. # File name: statusbar.kv
248. <[email protected]>:
249.   orientation: 'horizontal'
250.   Label:
251.     text: 'Total Figures: ?'
252.   Label:
253.     text: "Kivy started"

The difference is that it organizes labels and not buttons. The output is as shown in the following screenshot:

Status Bar area

The following is the code of drawingspace.kv:

254. # File name: drawingspace.kv
255. <[email protected]>:
256.   Label:
257.     markup: True
258.     text: '[size=32px][color=#3e6643]The[/color] [sub]Comic[/sub] [i][b]Creator[/b][/i][/size]'

Apart from defining that DrawingSpace is a subclass of RelativeLayout, we introduce the Kivy markup, a nice feature for styling the text of the Label class. It works similar to the XML based languages. For example, in HTML (and XML based language) <b>I am bold</b> will specify bold text. First, you have to activate it (line 257) and then you just embed the text you want to style between [tag] and [/tag] (line 258). You can find the whole tag list and description in the Kivy API, in the documentation for Label at http://kivy.org/docs/api-kivy.uix.label.html.

In the previous example, size and color are self-explanatory; sub refers to the sub-indexed text; b refers to bold and i refers to italics. The following is the screenshot that shows the final GUI of our comic creator:

Final GUI of the Comic Creator

Along the following chapters we are going to add the respective functionality to this interface that, for now, consists of placeholders. However, it is exciting that with just a few lines of code, we implement a GUI that is ready to go for the rest of the book Comic Creator project. We will be working on its logic from now on.

 

Summary


This chapter covered all the basic, and some not so basic, concepts of Kivy. You learned how to configure classes, instances, and templates. The following is a list of Kivy elements we used in this chapter:

  • We learned basic widgets such as Widget, Button, ToggleButton, and Label

  • Layouts such as FloatLayout, RelativeLayout, BoxLayout, GridLayout, StackLayout, and AnchorLayout

  • A number of properties such as pos, x, y, center_x, center_y, top, right, size, height, width, pos_hint, size_hint, group, spacing, padding, color, text, font_size, cols, rows, orientation, anchor_x, and anchor_y

  • We also learned variables such as self, root, and id and markup tags such as size, color, b, i, and sub

  • We used the Builder to load (the load_file method) extra Kivy language files.

There are much more elements that we can use inside the Kivy language. After this chapter we understand the general ideas and we should be able to use most of the elements available for the GUI design. There is, however, a very important and particular element that we haven't studied yet: the canvas, which mainly allow us to draw vector shapes (such as circles and lines for our project) in the screen. This will be the major topic of the next chapter.

About the Author

  • Roberto Ulloa

    Roberto Ulloa has a diverse academic record in multiple disciplines within the field of computer science. Currently, he is working with artificial societies as part of his PhD thesis at the University of Western Ontario. He obtained an MSc degree from the University of Costa Rica and taught programming and computer networking there. He has earned a living as a web developer, working with Python/Django and PHP/Wordpress. He collaborates with various researchers while also working on his own projects, including his blog (http://robertour.com). He constantly worries that the Internet has already become aware of itself and that we are not able to communicate with it because of the improbability of it being able to speak any of the 6,000-plus odd human languages that exist on the planet.

    Browse publications by this author

Latest Reviews

(2 reviews total)
Leider für ältere Kivy Version, dennoch sehr brauchbar
The layout explanations are some of the best I have seen. Working through the Comic Creator application was very instructive. The canvas section was good too. I also "stole" the color picker for myself.. ;-)
Book Title
Access this book, plus 7,500 other titles for FREE
Access now