Blog

Awesome Kivy Revelations

We are using kivy for the GUI of the knit editor. It can be used to create Android, Windows, Linux, Mac and IOS apps. You can divide UI-design and code by using the kv language. In this blog post, I want to share some revelations I had when using kivy. This includes showing you how you can update translations automatically when the configuration changes and subtracting values. This awaits you:

Automatic Update of Translated Text in the Kivy UI

In projects like Django, there is the “_” function that allows translations of text. The implementation usually look calls a “gettext” function of some sort, which takes a string and returns its translation as a string. What we have in the kniteditor, is an observable translation, with the same interface:

The difference is that the observable translation can be used like the “_” function but has additional methods that allow the UI to register and be notified when the language changes. When the language is changed, the global “gettext” is replaced by the “gettext” in the new language and, inthe last line, the observers are notified about the change.

To see what this does, we can look at the whole implementation. I would like to give the whole picture, first, as it clarifies the context and discuss them below.

"""Observable Translations for kivy that change when the language changes.
The functionality of this module is highly inspired by
`kivy-gettext-example <https://github.com/tito/kivy-gettext-example>`.
"""
from kivy.lang import Observable
class ObservableTranslation(Observable):
"""This class allows kivy translations to be updated with the language."""
def __init__(self, translate):
"""Create a new translation object with a translation function.
:param translate: a callable that translates the text.
Even when the language is changed,
it returns the text for the current language.
"""
super().__init__()
self._translate = translate
self._observers = []
def __call__(self, text):
"""Call this object to translate text.
:param str text: the text to translate
:return: the text translated to the current language
"""
return self._translate(text)
def fbind(self, name, func, args, **kwargs):
"""Add an observer. This is used by kivy."""
self._observers.append((name, func, args, kwargs))
def funbind(self, name, func, args, **kwargs):
"""Remove an observer. This is used by kivy."""
key = (name, func, args, kwargs)
if key in self._observers:
self._observers.remove(key)
def language_changed(self):
"""Update all the kv rules attached to this text."""
for name, func, args, kwargs in self._observers:
func(args, None, None)
__all__ = ["ObservableTranslation"]

The constructor takes the “_” function. When the object is called like a function, the “__call__” method is invoked, translating like “_”. If we only have these two methods, the observable translation works just like the “_” function.

“fbind” and “funbind” are the methods that are called when a translation is used in the kv language. They add and remove the observes from the list of observes.

“language_changed” walks through the observers and tells everyone of then to update. With this, you already have a updated translations when “change_language_to” is called.

Here is how you create a button in the kv language that uses the translation function:

#:import _ kniteditor.localization._
Root:
Button:
text: _("Load")

Updating the Translations From the Configuration

Ultimately, you want the translations to be changed, when the built-in kivy configuration changed. In the video, seconds, you have seen what it can look like.

“build_config” is calls when the application starts to fill the empty configuration with useful values.

“build” is called when the application creates its window, at this point in time, the translation is updated.

“build_settings” is called when the F1 key is pressed or the Android settings menu is activated. “settings_sepcification” is a JSON string used by kivy to build the settings.

“on_config_change” is called, when the configuration is updated. Through e.g. the settings dialog.

With these components together, you can have a updated language every time the language is changed. The video shows an application which uses this code.

Bonus Material: Kivy’s Update is Mighty

In the kv language, variables are accessed via identifiers and not via global variables. As such, the kv language can register every time a Property (like “StringProperty” and “ObjectProperty”) is used. It an register when they change. Thus you can automatically update formulas whenever a value changes.

in this example you can see how a value is subtracted but still, an automatic update is possible: