kiVy : Kv language - chiun/python-syntax-pretty-docs GitHub Wiki

Kv language

Rule context

Root rule, root attribute of the App instance:

Widget:

A class rule, defines how any instance of that class will be graphically represented:

<MyWidget>:

Rules use indentation for delimitation, as python. 4 spaces

3 keywords specific to Kv language:

  • app always refers to the instance of your application.
  • root refers to the base widget/template in the current rule
  • self always refer to the current widget

🐱

Special syntaxes

There are two special syntaxes to define values for the whole Kv context:

To access python modules and classes from kv,

#:import name x.y.z
#:import isdir os.path.isdir
#:import np numpy

is equivalent to:

from x.y import z as name
from os.path import isdir
import numpy as np

in python.

To set a global value, #:set name value is equivalent to: name = value in python.

Instantiate children

To declare the widget has a child widget, instance of some class, just declare this child inside the rule:

MyRootWidget:
    BoxLayout:
        Button:
        Button:

The example above defines that our root widget, an instance of MyRootWidget, which has a child that is an instance of the BoxLayout. That BoxLayout further has two children, instances of the Button class.

A python equivalent of this code could be:

root = MyRootWidget()
box = BoxLayout()
box.add_widget(Button())
box.add_widget(Button())
root.add_widget(box)

In kv, you can set properties of the child widget directly in the rule:

GridLayout:
    cols: 3

The value is evaluated as a python expression, and all the properties used in the expression will be observed, that means that if you had something like this in python (this assume self is a widget with a data ListProperty):

grid = GridLayout(cols=len(self.data))
self.bind(data=grid.setter('cols'))

To have your display updated when your data change, you can now have just:

GridLayout:
    cols: len(root.data)

Event Bindings

Widget:
    on_size: my_callback()

You can pass the values dispatched by the signal using the args keyword:

TextInput:
    on_text: app.search(args[1])

Extend canvas

Kv lang can be used to define the canvas instructions of your widget like this:

MyWidget:
    canvas:
        Color:
            rgba: 1, .3, .8, .5
        Line:
            points: zip(self.data.x, self.data.y)

Referencing Widgets

In a widget tree there is often a need to access/reference other widgets. The Kv Language provides a way to do this using id’s. Think of them as class level variables that can only be used in the Kv language. Consider the following:

<MyFirstWidget>:
    Button:
        id: f_but
    TextInput:
        text: f_but.state

<MySecondWidget>:
    Button:
        id: s_but
    TextInput:
        text: s_but.state

An id is limited in scope to the rule it is declared in, so in the code above s_but can not be accessed outside the rule.

Warning When assigning a value to id, remember that the value isn’t a string. There are no quotes: good -> id: value, bad -> id: 'value' An id is a weakref to the widget and not the widget itself. As a consequence, storing the id is not sufficient to keep the widget from being garbage collected. To demonstrate:

<MyWidget>:
    label_widget: label_widget
    Button:
        text: 'Add Button'
        on_press: root.add_widget(label_widget)
    Button:
        text: 'Remove Button'
        on_press: root.remove_widget(label_widget)
    Label:
        id: label_widget
        text: 'widget'

Although a reference to label_widget is stored in MyWidget, it is not sufficient to keep the object alive once other references have been removed because it’s only a weakref. Therefore, after the remove button is clicked (which removes any direct reference to the widget) and the window is resized (which calls the garbage collector resulting in the deletion of label_widget), when the add button is clicked to add the widget back, a ReferenceError: weakly-referenced object no longer exists will be thrown.

To keep the widget alive, a direct reference to the label_widget widget must be kept. This is achieved using id.__self__ or label_widget.__self__ in this case. The correct way to do this would be:

<MyWidget>:
    label_widget: label_widget.__self__

Accessing Widgets defined inside Kv lang in your python code

Consider the code below in my.kv:

<MyFirstWidget>:
    # both these variables can be the same name and this doesn't lead to
    # an issue with uniqueness as the id is only accessible in kv.
    txt_inpt: txt_inpt
    Button:
        id: f_but
    TextInput:
        id: txt_inpt
        text: f_but.state
        on_text: root.check_status(f_but)

In myapp.py:

class MyFirstWidget(BoxLayout):

    txt_inpt = ObjectProperty(None)

    def check_status(self, btn):
        print('button state is: {state}'.format(state=btn.state))
        print('text input text is: {txt}'.format(txt=self.txt_inpt))

txt_inpt is defined as a ObjectProperty initialized to None inside the Class.

txt_inpt = ObjectProperty(None)

At this point self.txt_inpt is None. In Kv lang this property is updated to hold the instance of the TextInput referenced by the id txt_inpt.:

txt_inpt: txt_inpt

From this point onwards, self.txt_inpt holds a reference to the widget identified by the id txt_input and can be used anywhere in the class, as in the function check_status. In contrast to this method you could also just pass the id to the function that needs to use it, like in case of f_but in the code above.

There is a simpler way to access objects with id tags in Kv using the ids lookup object. You can do this as follows:

<Marvel>
  Label:
    id: loki
    text: 'loki: I AM YOUR GOD!'
  Button:
    id: hulk
    text: "press to smash loki"
    on_release: root.hulk_smash()

In your python code:

class Marvel(BoxLayout):

    def hulk_smash(self):
        self.ids.hulk.text = "hulk: puny god!"
        self.ids["loki"].text = "loki: >_<!!!"  # alternative syntax

When your kv file is parsed, kivy collects all the widgets tagged with id’s and places them in this self.ids dictionary type property. That means you can also iterate over these widgets and access them dictionary style:

for key, val in self.ids.items():
    print("key={0}, val={1}".format(key, val))

Note Although the self.ids method is very concise, it is generally regarded as ‘best practice’ to use the ObjectProperty. This creates a direct reference, provides faster access and is more explicit.

Dynamic Classes

<MyBigButt@Button>:
    text_size: self.size
    font_size: '25sp'
    markup: True

<MyWidget>:
    MyBigButt:
        text: "Hello world, watch this text wrap inside the button"
    MyBigButt:
        text: "Even absolute is relative to itself"
    MyBigButt:
        text: "repeating the same thing over and over in a comp = fail"
    MyBigButt:

This class, created just by the declaration of this rule, inherits from the Button class and allows us to change default values and create bindings for all its instances without adding any new code on the Python side.

⚠️ **GitHub.com Fallback** ⚠️