Kivy - Language



This chapter explains the important features, syntax and the usage of Kivy's Design Language. The Kivy language (also know as "kv" Language) mainly describes the user interface of a Kivy app. It is similar to Qt's QML. The rule definitions in the "kv" language are similar to CSS rules.

Purpose

The layout and placement of widgets in a Kivy app are coded into the build() method of Kivy's App class. For applications with simpler user interface, this may work well. However, with the increasing complexity of the layout design, it quickly becomes too verbose and too difficult to maintain. Hence there is a need to separate the design part of the application from the processing logic. The "kv" language helps in achieving this separation of concerns.

With the "kv" language script, you can create your widget tree in a declarative way and bind the widget properties to callbacks in a much more natural manner. One of the drawbacks of programmatically designing the interface is that the appearance cannot be viewed until the application is executed. On the other hand, the kviewer utility provides an interactive view of the design and placement of widgets, without having to load the script in the application.

Loading the "kv" Script

Once the design of widgets is finalized, it is loaded in the App class. This can be done in two ways.

Using the Naming Convention

Save the "kv" script with a ".kv" extension and its name corresponding to the name of App class. If the App class name ends with app keyword, it must be omitted. For example, if the App class is named as DemoApp as below,

Class DemoApp(App):
   ...
   ...

Then the kv file should be named as demo.kv. Kivy automatically loads the design into the build() method of the App class. If the kv file defines a root Widget, it will be attached to the App's root attribute and used as the base of the application widget tree.

Using the Builder Class

If the name of the "kv" file doesn't follow the above convention, the script can be loaded into the app with load_file() method of Loader class, which is available in the "kivy.lang" module.

from kivy.app import App
from kivy.lang import Builder

Builder.load_file('mykv.kv')

MydemoApp(App):
   def build(self):
      pass
      
MydemoApp().run()

You may also embed the entire "kv" language script as a string into the Python code and load it in the App class with Builder.load_string() method.

from kivy.app import App
from kivy.lang import Builder

kv="""
#kv script goes here
"""

MydemoApp(App):
   def build(self):
      Builder.load_string(kv)
      
MydemoApp().run()

The "kv" language has the following important elements −

  • Rules − A rule in "kv" is similar to a CSS rule. It applies to specific widgets or classes inheriting the specified widget class. A "kv" rule can specify interactive behaviour or use them to add graphical representations of the widgets they apply to.

  • root Widget − You can use the language to create your entire user interface. A "kv" file must contain only one root widget at most.

  • Dynamic Classes − Dynamic classes let you create new widgets and rules on-the-fly, without any Python declaration.

A kv script describes the content of a Widget. You can have one root rule, and any number of class or template rules. The root rule is declared by declaring the class of your root widget, without any indentation and followed by the ":" symbol. It sets the root attribute of the App instance.

Widget:

To declare a class rule, put the name of a widget class between "< >" and followed by the ":" symbol. It defines the appearance and behavior of any instance of that class −

<MyWidget>:

Kv language rules use indentation for delimitation, same as a Python source code. The thumb rule to remember is that with the angular brackets it's a rule and without them, it's a root widget.

A schematic example

<MyClass>:
   prop1: value1
   prop2: value2
   
   canvas:
      CanvasInstruction1:
         canvasprop1: value1
      CanvasInstruction2:
         canvasprop2: value2
   AnotherClass:
      prop3: value1

There are three keywords specific to the "kv" language −

self − always refer to the current widget.

Button:
text: 'My state is %s' % self.state

root − refers to the base widget in the current rule and represents the root widget of the rule (the first instance of the rule) −

<MyWidget>:
   custom: 'Hello world'
   Button:
      text: root.custom

app − always refers to the instance of your application.

Label:
   text: app.name

The Kivy app window can contain only one widget as its root object. However, if you need to compose the app interface with more than one controls, you have to use the layout widget, and place multiple UX widgets in it and then put the layout as the root widget on the app window.

The following "kv" file script defines a grid layout which can then be used with the App class −

GridLayout:
   cols:1
   Label:
      text:'Hello'
      font_size:100
      color:(1,0,0,1)
   Button:
      text='Ok'
      font_size:100

In the above case, the Label widget and a Button are placed in a one-column GridLayout. However, the objects and their properties cannot be accessed from within the Python code. To be able to do so, define the id property in the "kv" script.

GridLayout:
   cols:1
   Label:
      id:l1
      text:'Hello'
      font_size:60
      color:(1,0,0,1)
   Button:
   id:b1
      text='Ok'
      font_size:60

When this layout is loaded into the build() method, the App class can access the widget properties through this id. IT has a ids property which is a dictionary with id:value key-value pairs.

print (self.ids.l1.text)

Let us start with the kv script as follows, consisting of a label and a button in grid layout.

GridLayout:
   cols:1
   Label:
      text:'Hello'
      font_size:60
      color:(1,0,0,1)
   TextInput:
      text:'Hello'
      font_size:60
   Button:
      text:'Ok'
         font_size:60

The Kivy application code loads the above "kvdemo.kv" script −

from kivy.app import App
from kivy.core.window import Window

Window.size = (720,400)
class KvDemoApp(App):
   def build(self):
      pass
      
KvDemoApp().run()

Let us add an event handler in the Kivy application. To access the widget properties, we define id properties to label, textinput and button. We define a class rule in the "kv" script and put MyWidget in angular bracket on top.

<MyWidget>:
   cols:1
   size:root.size
   Label:
      id:l1
      text:'Hello'
      font_size:60
      color:(1,0,0,1)
   TextInput:
      id:t1
      text:'Hello'
      font_size:60
   Button:
      id:b1
      text:'Ok'
      font_size:60
      on_press:root.callback(*args)

Note that the on_press property of button is bound to the callback() method that we will write in the root class, i.e., MyWidget class.

The MyWidget class inherits the GridLayout widget. Inside the class, a callback() method has been provided. It updates the label caption to the text entered in the text box when the button is pressed.

Add the following class in the "kvdemoapp.py" code −

from kivy.uix.gridlayout import GridLayout
class MyWidget(GridLayout):
def callback(self, *args):
print ("hello")
self.ids.l1.text=self.ids.t1.text

After running the program, type some text in the text box, and press the button. The text on the label will change accordingly.

Kivy Language

We can also provide the entire logic in the "kv" script only. We need no define the callback in Python class. Use the id property of label and textinput from within the "kv" code.

<MyWidget>:
   cols:1
   size:root.size
   Label:
      id:l1
      text:'Hello'
      font_size:60
      color:(1,0,0,1)
   TextInput:
      id:t1
      text:'Hello'
      font_size:60
   Button:
      id:b1
      text:'Ok'
      font_size:60
      on_press: l1.text=t1.text

Here, the on_press event sets the label (id is l1) text to the text in the textbox with id as "t1". On the Python code side, the MyWidget class will just have a pass statement.

Thus, we can see that the use of "kv" design language makes the app development very convenient as the programming part and design part are isolated.

Advertisements