kiVy : Kv language - chiun/python-syntax-pretty-docs GitHub Wiki
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
🐱
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.
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)
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])
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)
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__
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.
<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.