Hy (lisp) and Kivy

I was recently reminded of the super cool Hy project. Hy is a lisp that compiles to python’s own abstract syntax tree, so it works perfectly with existing Python code (including with Cython etc.) but also exposes all the power of lisp.

For instance, here’s a simple Kivy application that simply displays a Label with the obligatory Hy pun, but written in Hy. I’ve included the normal Python code as comments so you can see exactly what the code is doing. If you’re new to Kivy and want to understand what the code actually does, check out my Kivy crash course:

(import [kivy.app [App]]
        [kivy.uix.label [Label]])
;; from kivy.app import App
;; from kivy.uix.label import Label


(defclass HyApp [App]
  [[build
    (fn [self]
      (apply Label [] {"text" "Hy world!"
                       "font_size" 100
                       "color" (, 0 1 0 1)}))]])

;; class HyApp(App):
;;     def build(self):
;;         return Label(text="Hy world!",
;;                      font_size=100,
;;                      color=(0, 1, 0, 1))

(.run (HyApp))

;; HyApp().run()

This works great, though only with python3 due to a small bug in Kivy - the kwargs of Label are eventually read in cython with a variable typed as str, which in python2 excludes the unicode Hy passes. Still, that’s not surprising even if it’s cool - part of the point of Hy is to interoperate perfectly with Python.

A tougher problem is how to use Kivy’s kv language with Hy. kv is a simple domain-specific language for declaring widget trees, making it easy to define event-driven interactions between the different properties of widgets. It’s really useful and we tend to recommend using it as much as possible, so it’d be great to have it work with Hy. I won’t explain the language here (you can see the Kivy doc or my own tutorials), but the key point is that much of it consists of interpreting normal python code, which I’d like to replace with Hy code.

It turns out making this work is actually really easy. Here’s the relevant part of lang.py in Kivy’s source, the file containing the code for the kv parser:

self.co_value = compile(value,
                        self.ctx.filename or '<string>',
                        mode)

value is the string of Python code whose output will set a property of a widget or be run when an event is registered. For instance, a line of kv code might be color: (1, 0, 0, some_function_of(self.alpha)), in which case value would be "(1, 0, 0, some_function_of(self.alpha))".

To make a line of Hy code work instead of Python, we can do an awful hack, replacing the above line with:

if value[-3:] == '#hy':
    from hy.importer import (ast_compile,
                             import_buffer_to_ast)
    from hy.compiler import hy_compile
    import ast
    ast_part = import_buffer_to_ast(value[:-3], '<stdin>')
    if mode == 'eval':
        ast_part = ast.Expression(ast_part.body[0].value)
    self.co_value = ast_compile(ast_part,
                                self.ctx.filename or '<string>',
                                mode)
else:
    self.co_value = compile(value,
                            self.ctx.filename or '<string>',
                            mode)

This new code checks if the line of Python ends with #hy, and if so runs the code through Hy’s own equivalent of compile (effectively parsing the Hy code to ast before doing the same thing as the normal Python code). I also have the extra muckiness of taking apart this ast if the compilation is in eval mode, because I couldn’t get Hy to return an ast.Expression in the first place. This is probably very easily and neatly fixed, but I’ve left it like this because a silly hack is good enough for a proof of concept. All credit for this part goes to the friendly Hy people on their irc channel, #hy on Freenode.

With this in place, we can write a new Python program, but this time use our Hy+kv language to define the widget tree. Here’s the new code on the Python (now Hy) side:

(import [kivy.app [App]]
        [kivy.lang [Builder]])

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

(setv root (Builder.load_file "hy.kv"))

;; root = Builder.load_file("hy.kv")

(defclass HyApp [App]
  [[build
    (fn [self]
      root)]])

;; class HyApp(App):
;;     def build(self):
;;         return root

(.run (HyApp))

;; HyApp().run()

This obviously depends on our new kv file, "hy.kv", whose contents are as below. Kivy users will notice this file would normally be loaded automatically because the app name starts with Hy, but something about Hy seems to have broken this so I manually loaded it with the Builder.

BoxLayout:
    orientation: "vertical"
    Label:
        id: label
        text: "What is your name?"
    TextInput:
        id: ti
        text: ""
    Button:
        text: (.format "Greet me as {}" ti.text) #hy
        on_press: (setv label.text (.format "Hy there {}" ti.text)) #hy

# as normal kv, except the final 2 rules would normally be:
# text: "Greet me as {}".format(ti.text)
# on_press: label.text = "Hy there {}".format(ti.text)

Running the code…it works perfectly! Here’s a picture after typing my name and clicking the button:

Image of Kivy program after running Hy code

For those not familiar with kv, one of its features is that it automatically detects property changes and updates dependent properties - in this case, the text of the button should change every time ti.text changes (i.e. every time a letter is typed in the TextInput). This works too with the new Hy interface, because the parser detects the dependency by searching the string for substrings like ti.text, and these have been unmodified by the move to Hy. Hy does support syntax that would break this relationship, but it’s quite convenient as it is.

So…there we go, Hy support in Kivy! The hack to make kv language work is pretty terrible, but it looks like a proper solution with this basis would work fine - we could subclass the kv parsing Builder to support a Hy loading option, removing the need for the #hy at the end of each Hy line.

Hy (lisp) and Kivy

I was recently reminded of the super cool Hy project. Hy is a lisp that compiles to python’s own abstract syntax tree, so it works perfectly with existing Python code (including with Cython etc.) but also exposes all the power of lisp.

For instance, here’s a simple Kivy application that simply displays a Label with the obligatory Hy pun, but written in Hy. I’ve included the normal Python code as comments so you can see exactly what the code is doing. If you’re new to Kivy and want to understand what the code actually does, check out my Kivy crash course:

(import [kivy.app [App]]
        [kivy.uix.label [Label]])
;; from kivy.app import App
;; from kivy.uix.label import Label


(defclass HyApp [App]
  [[build
    (fn [self]
      (apply Label [] {"text" "Hy world!"
                       "font_size" 100
                       "color" (, 0 1 0 1)}))]])

;; class HyApp(App):
;;     def build(self):
;;         return Label(text="Hy world!",
;;                      font_size=100,
;;                      color=(0, 1, 0, 1))

(.run (HyApp))

;; HyApp().run()

This works great, though only with python3 due to a small bug in Kivy - the kwargs of Label are eventually read in cython with a variable typed as str, which in python2 excludes the unicode Hy passes. Still, that’s not surprising even if it’s cool - part of the point of Hy is to interoperate perfectly with Python.

A tougher problem is how to use Kivy’s kv language with Hy. kv is a simple domain-specific language for declaring widget trees, making it easy to define event-driven interactions between the different properties of widgets. It’s really useful and we tend to recommend using it as much as possible, so it’d be great to have it work with Hy. I won’t explain the language here (you can see the Kivy doc or my own tutorials), but the key point is that much of it consists of interpreting normal python code, which I’d like to replace with Hy code.

It turns out making this work is actually really easy. Here’s the relevant part of lang.py in Kivy’s source, the file containing the code for the kv parser:

self.co_value = compile(value,
                        self.ctx.filename or '<string>',
                        mode)

value is the string of Python code whose output will set a property of a widget or be run when an event is registered. For instance, a line of kv code might be color: (1, 0, 0, some_function_of(self.alpha)), in which case value would be "(1, 0, 0, some_function_of(self.alpha))".

To make a line of Hy code work instead of Python, we can do an awful hack, replacing the above line with:

if value[-3:] == '#hy':
    from hy.importer import (ast_compile,
                             import_buffer_to_ast)
    from hy.compiler import hy_compile
    import ast
    ast_part = import_buffer_to_ast(value[:-3], '<stdin>')
    if mode == 'eval':
        ast_part = ast.Expression(ast_part.body[0].value)
    self.co_value = ast_compile(ast_part,
                                self.ctx.filename or '<string>',
                                mode)
else:
    self.co_value = compile(value,
                            self.ctx.filename or '<string>',
                            mode)

This new code checks if the line of Python ends with #hy, and if so runs the code through Hy’s own equivalent of compile (effectively parsing the Hy code to ast before doing the same thing as the normal Python code). I also have the extra muckiness of taking apart this ast if the compilation is in eval mode, because I couldn’t get Hy to return an ast.Expression in the first place. This is probably very easily and neatly fixed, but I’ve left it like this because a silly hack is good enough for a proof of concept. All credit for this part goes to the friendly Hy people on their irc channel, #hy on Freenode.

With this in place, we can write a new Python program, but this time use our Hy+kv language to define the widget tree. Here’s the new code on the Python (now Hy) side:

(import [kivy.app [App]]
        [kivy.lang [Builder]])

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

(setv root (Builder.load_file "hy.kv"))

;; root = Builder.load_file("hy.kv")

(defclass HyApp [App]
  [[build
    (fn [self]
      root)]])

;; class HyApp(App):
;;     def build(self):
;;         return root

(.run (HyApp))

;; HyApp().run()

This obviously depends on our new kv file, "hy.kv", whose contents are as below. Kivy users will notice this file would normally be loaded automatically because the app name starts with Hy, but something about Hy seems to have broken this so I manually loaded it with the Builder.

BoxLayout:
    orientation: "vertical"
    Label:
        id: label
        text: "What is your name?"
    TextInput:
        id: ti
        text: ""
    Button:
        text: (.format "Greet me as {}" ti.text) #hy
        on_press: (setv label.text (.format "Hy there {}" ti.text)) #hy

# as normal kv, except the final 2 rules would normally be:
# text: "Greet me as {}".format(ti.text)
# on_press: label.text = "Hy there {}".format(ti.text)

Running the code…it works perfectly! Here’s a picture after typing my name and clicking the button:

Image of Kivy program after running Hy code

For those not familiar with kv, one of its features is that it automatically detects property changes and updates dependent properties - in this case, the text of the button should change every time ti.text changes (i.e. every time a letter is typed in the TextInput). This works too with the new Hy interface, because the parser detects the dependency by searching the string for substrings like ti.text, and these have been unmodified by the move to Hy. Hy does support syntax that would break this relationship, but it’s quite convenient as it is.

So…there we go, Hy support in Kivy! The hack to make kv language work is pretty terrible, but it looks like a proper solution with this basis would work fine - we could subclass the kv parsing Builder to support a Hy loading option, removing the need for the #hy at the end of each Hy line.

Updating canvas instructions declared in Python

Continuing the theme of my last few posts, a common problem for new kivy users is creating canvas instructions that follow their parent widgets. For instance, here’s some code for a custom widget that tries to draw a red rectangle in its upper-right corner - this is fairly standard kivy code to draw directly on the widget canvas, and is documented here.

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color

class CornerRectangleWidget(Widget)
    def __init__(self, **kwargs):
        super(CornerRectangleWidget, self).__init__(**kwargs)

        with self.canvas:
            Color(1, 0, 0, 1)  # set the colour to red
            self.rect = Rectangle(pos=self.center,
                                  size=(self.width/2.,
                                        self.height/2.))

This looks like it will create a red rectangle, whose length is half the parent size in both directions, and whose position is the parent centre.

The surprise is that this is actually not what happens. Instead, the rectangle is always positioned at (50, 50) with size (50, 50), regardless of where the widget appears.

The reason for this is that these values really are based on the pos and size of the widget at the point where the canvas code was run; all widgets have a default position of (0, 0) and size of (100, 100), and this will not necessarily be updated (for instance by a parent layout class) until after their __init__ is run. However, the Rectangle properties receive only these initial values, and don’t know about the new position of the widget.

The solution is to simply hook into kivy’s event system to update the rectangle pos and size ourselves whenever the widget changes:

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color

class CornerRectangleWidget(Widget)
    def __init__(self, **kwargs):
        super(CornerRectangleWidget, self).__init__(**kwargs)

        with self.canvas:
            Color(1, 0, 0, 1)  # set the colour to red
            self.rect = Rectangle(pos=self.center,
                                  size=(self.width/2.,
                                        self.height/2.))

        self.bind(pos=self.update_rect,
                  size=self.update_rect)

    def update_rect(self, *args):
        self.rect.pos = self.pos
        self.rect.size = self.size

Now whenever the widget pos or size changes, our new method is called and the rectangle resized or repositioned as necessary. It will always track the widget’s upper right corner, so we get the visual effect we were originally looking for.

Of course, an even better solution (where possible) is to use kv language:

<CornerRectangleWidget@Widget>
    canvas:
        Color:
           rgba: 1, 0, 0, 1
        Rectangle:
           pos: self.center
           size: self.width / 2., self.height / 2.

This is shorter, simpler and clearer. We don’t need to manually set up the binding because kv automatically detects that we referred to properties of the parent widget and creates it automatically - something that isn’t really possible in python. This is one reason that we recommend using kv language wherever possible.

Updating canvas instructions declared in Python

Continuing the theme of my last few posts, a common problem for new kivy users is creating canvas instructions that follow their parent widgets. For instance, here’s some code for a custom widget that tries to draw a red rectangle in its upper-right corner - this is fairly standard kivy code to draw directly on the widget canvas, and is documented here.

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color

class CornerRectangleWidget(Widget)
    def __init__(self, **kwargs):
        super(CornerRectangleWidget, self).__init__(**kwargs)

        with self.canvas:
            Color(1, 0, 0, 1)  # set the colour to red
            self.rect = Rectangle(pos=self.center,
                                  size=(self.width/2.,
                                        self.height/2.))

This looks like it will create a red rectangle, whose length is half the parent size in both directions, and whose position is the parent centre.

The surprise is that this is actually not what happens. Instead, the rectangle is always positioned at (50, 50) with size (50, 50), regardless of where the widget appears.

The reason for this is that these values really are based on the pos and size of the widget at the point where the canvas code was run; all widgets have a default position of (0, 0) and size of (100, 100), and this will not necessarily be updated (for instance by a parent layout class) until after their __init__ is run. However, the Rectangle properties receive only these initial values, and don’t know about the new position of the widget.

The solution is to simply hook into kivy’s event system to update the rectangle pos and size ourselves whenever the widget changes:

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color

class CornerRectangleWidget(Widget)
    def __init__(self, **kwargs):
        super(CornerRectangleWidget, self).__init__(**kwargs)

        with self.canvas:
            Color(1, 0, 0, 1)  # set the colour to red
            self.rect = Rectangle(pos=self.center,
                                  size=(self.width/2.,
                                        self.height/2.))

        self.bind(pos=self.update_rect,
                  size=self.update_rect)

    def update_rect(self, *args):
        self.rect.pos = self.pos
        self.rect.size = self.size

Now whenever the widget pos or size changes, our new method is called and the rectangle resized or repositioned as necessary. It will always track the widget’s upper right corner, so we get the visual effect we were originally looking for.

Of course, an even better solution (where possible) is to use kv language:

<CornerRectangleWidget@Widget>
    canvas:
        Color:
           rgba: 1, 0, 0, 1
        Rectangle:
           pos: self.center
           size: self.width / 2., self.height / 2.

This is shorter, simpler and clearer. We don’t need to manually set up the binding because kv automatically detects that we referred to properties of the parent widget and creates it automatically - something that isn’t really possible in python. This is one reason that we recommend using kv language wherever possible.

Updating canvas instructions declared in Python

Continuing the theme of my last few posts, a common problem for new kivy users is creating canvas instructions that follow their parent widgets. For instance, here’s some code for a custom widget that tries to draw a red rectangle in its upper-right corner - this is fairly standard kivy code to draw directly on the widget canvas, and is documented here.

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color

class CornerRectangleWidget(Widget)
    def __init__(self, **kwargs):
        super(CornerRectangleWidget, self).__init__(**kwargs)

        with self.canvas:
            Color(1, 0, 0, 1)  # set the colour to red
            self.rect = Rectangle(pos=self.center,
                                  size=(self.width/2.,
                                        self.height/2.))

This looks like it will create a red rectangle, whose length is half the parent size in both directions, and whose position is the parent centre.

The surprise is that this is actually not what happens. Instead, the rectangle is always positioned at (50, 50) with size (50, 50), regardless of where the widget appears.

The reason for this is that these values really are based on the pos and size of the widget at the point where the canvas code was run; all widgets have a default position of (0, 0) and size of (100, 100), and this will not necessarily be updated (for instance by a parent layout class) until after their __init__ is run. However, the Rectangle properties receive only these initial values, and don’t know about the new position of the widget.

The solution is to simply hook into kivy’s event system to update the rectangle pos and size ourselves whenever the widget changes:

from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color

class CornerRectangleWidget(Widget)
    def __init__(self, **kwargs):
        super(CornerRectangleWidget, self).__init__(**kwargs)

        with self.canvas:
            Color(1, 0, 0, 1)  # set the colour to red
            self.rect = Rectangle(pos=self.center,
                                  size=(self.width/2.,
                                        self.height/2.))

        self.bind(pos=self.update_rect,
                  size=self.update_rect)

    def update_rect(self, *args):
        self.rect.pos = self.pos
        self.rect.size = self.size

Now whenever the widget pos or size changes, our new method is called and the rectangle resized or repositioned as necessary. It will always track the widget’s upper right corner, so we get the visual effect we were originally looking for.

Of course, an even better solution (where possible) is to use kv language:

<CornerRectangleWidget@Widget>
    canvas:
        Color:
           rgba: 1, 0, 0, 1
        Rectangle:
           pos: self.center
           size: self.width / 2., self.height / 2.

This is shorter, simpler and clearer. We don’t need to manually set up the binding because kv automatically detects that we referred to properties of the parent widget and creates it automatically - something that isn’t really possible in python. This is one reason that we recommend using kv language wherever possible.

Wrapping text in Kivy’s Label

Another Kivy question that I often see (particularly recently for some reason) is about using the Label widget - how to have text wrap automatically, or the opposite, how to have the label automatically grow to accommodate its text. I’ve covered this before in the 9th Kivy crash course video, but here’s a quick write up of the basics.

The first thing to realise is how the Label works by default, it takes the text and draws it to a texture - in practical terms that’s an image of the characters. Everything you might want to do with the Label revolves around what this texture is really doing. By default, it does not wrap the text (unless you put in linebreak characters manually), it just makes one long image on a single row. This image is is placed right in the middle of the label, centered in both directions, which is fine for short text snippets but will overhang the Label on both sides if the text is too long.

This also leads to some other annoying behaviour - as well as the text not wrapping, you might have observed that the halign and valign properties seem to do nothing by default. This is because they orient things not inside the widget, but inside the texture…which is the exact size it needs to contain the text so alignments change nothing.

To solve all these problems, you can manually set the size of the texture with text_size, a tuple of width and height, e.g.

Label:
    text_size: self.size

This reverses the default behaviour - instead of the texture growing to fit the text, the text will be wrapped to fit the texture! If there is space to spare, it is aligned within the texture according to the halign and valign properties.

The Label also has another useful property, the texture_size, which holds the actual size of the texture. You can use that do bind behaviour to the size of the text. For instance, a common requirement is to create a Label that grows as long as it needs to contain its text, but which wraps it to a certain width. We can combine both of the above ideas to accomplish this:

Label:
    size_hint_y: None
    text_size: self.width, None
    height: self.texture_size[1]

If you (for instance) place this label in a ScrollView, it will be Scrollable over exactly the right distance to fit in all the text.

Wrapping text in Kivy’s Label

Another Kivy question that I often see (particularly recently for some reason) is about using the Label widget - how to have text wrap automatically, or the opposite, how to have the label automatically grow to accommodate its text. I’ve covered this before in the 9th Kivy crash course video, but here’s a quick write up of the basics.

The first thing to realise is how the Label works by default, it takes the text and draws it to a texture - in practical terms that’s an image of the characters. Everything you might want to do with the Label revolves around what this texture is really doing. By default, it does not wrap the text (unless you put in linebreak characters manually), it just makes one long image on a single row. This image is is placed right in the middle of the label, centered in both directions, which is fine for short text snippets but will overhang the Label on both sides if the text is too long.

This also leads to some other annoying behaviour - as well as the text not wrapping, you might have observed that the halign and valign properties seem to do nothing by default. This is because they orient things not inside the widget, but inside the texture…which is the exact size it needs to contain the text so alignments change nothing.

To solve all these problems, you can manually set the size of the texture with text_size, a tuple of width and height, e.g.

Label:
    text_size: self.size

This reverses the default behaviour - instead of the texture growing to fit the text, the text will be wrapped to fit the texture! If there is space to spare, it is aligned within the texture according to the halign and valign properties.

The Label also has another useful property, the texture_size, which holds the actual size of the texture. You can use that do bind behaviour to the size of the text. For instance, a common requirement is to create a Label that grows as long as it needs to contain its text, but which wraps it to a certain width. We can combine both of the above ideas to accomplish this:

Label:
    size_hint_y: None
    text_size: self.width, None
    height: self.texture_size[1]

If you (for instance) place this label in a ScrollView, it will be Scrollable over exactly the right distance to fit in all the text.

Wrapping text in Kivy’s Label

Another Kivy question that I often see (particularly recently for some reason) is about using the Label widget - how to have text wrap automatically, or the opposite, how to have the label automatically grow to accommodate its text. I’ve covered this before in the 9th Kivy crash course video, but here’s a quick write up of the basics.

The first thing to realise is how the Label works by default, it takes the text and draws it to a texture - in practical terms that’s an image of the characters. Everything you might want to do with the Label revolves around what this texture is really doing. By default, it does not wrap the text (unless you put in linebreak characters manually), it just makes one long image on a single row. This image is is placed right in the middle of the label, centered in both directions, which is fine for short text snippets but will overhang the Label on both sides if the text is too long.

This also leads to some other annoying behaviour - as well as the text not wrapping, you might have observed that the halign and valign properties seem to do nothing by default. This is because they orient things not inside the widget, but inside the texture…which is the exact size it needs to contain the text so alignments change nothing.

To solve all these problems, you can manually set the size of the texture with text_size, a tuple of width and height, e.g.

Label:
    text_size: self.size

This reverses the default behaviour - instead of the texture growing to fit the text, the text will be wrapped to fit the texture! If there is space to spare, it is aligned within the texture according to the halign and valign properties.

The Label also has another useful property, the texture_size, which holds the actual size of the texture. You can use that do bind behaviour to the size of the text. For instance, a common requirement is to create a Label that grows as long as it needs to contain its text, but which wraps it to a certain width. We can combine both of the above ideas to accomplish this:

Label:
    size_hint_y: None
    text_size: self.width, None
    height: self.texture_size[1]

If you (for instance) place this label in a ScrollView, it will be Scrollable over exactly the right distance to fit in all the text.

Kivy Contest 2014

Just to announce here for anyone that hasn’t seen already…Kivy recently announced the Kivy org second programming contest! You can check out all the details at http://kivy.org/#contest!

To cover the key details here, entries are open now (you can sign up at the link above), and the contest starts in full on 15th April. There will be a broad theme, announced on that day, but many different kinds of app will be possible. You’ll have 4 weeks from the start date to complete your entry.

Entries will be judged on a range of criteria, so don’t be afraid to jump in!

Kivy Contest 2014

Just to announce here for anyone that hasn’t seen already…Kivy recently announced the Kivy org second programming contest! You can check out all the details at http://kivy.org/#contest!

To cover the key details here, entries are open now (you can sign up at the link above), and the contest starts in full on 15th April. There will be a broad theme, announced on that day, but many different kinds of app will be possible. You’ll have 4 weeks from the start date to complete your entry.

Entries will be judged on a range of criteria, so don’t be afraid to jump in!