Designing forms with Python and Kivy

ÔŽ┐form, python, kivy, kivyforms
form

There are many libraries to create graphical interfaces with Python. For example, I tried with wxPython, PyQt and Kivy. The latter is the one I liked the most for a number of reasons:

  • It's multiplatform and works in GNU/Linux, Windows, OS X, Android, iOS and Raspberry Pi. I've only tried the first two, but I've been able to verify that it's designed for use with keyboard and mouse or with touch screens.
  • It's not based on the platforms native widgets but has its own, so the applications look the same on all platforms.
  • It's fast; the graphics engine works on OpenGL ES 2 and many parts are written with Cython.
  • You can design the interfaces directly with Python or using Kv language.
  • It's a relatively new project, and well maintained.
  • Good documentation and many examples.

I'm working on an apps development framework, so I need developers to be able to design forms quickly, WYSIWYG way, and they can also include their own themes, colors, etc. Thanks to Kv language, this task is easier for me. It's something similar to QML in Qt, and allows to save compositions in text files that can be loaded later by the application, without having to modify any code.

For example, the Kivy Hello World would be:

    # main.py file
    from kivy.app import App

    class TestApp(App):
        pass

    TestApp().run()

    # test.kv file
    Button:
        text: 'Hello World'
  

To design the forms, I created a widget that provides a canvas, in which it's possible to add buttons, labels, text fields and things like that. The project is called KivyForms and is currently beta. You can also watch a video with an example of using KivyForms. The idea is that the widget has a powerful API to be integrated into any application that needs forms composition.

Look what the widget contains:

    class FormCanvas(BoxLayout):
        def __init__(self, *args, **kwargs):
            super(FormCanvas, self).__init__(*args, **kwargs)

            self._canvas = StackLayout(
                orientation='lr-tb',
                padding=[10, 10, 10, 10],
                spacing=[10, 10]
            )
            super(FormCanvas, self).add_widget(self._canvas)
  

FormCanvas is based on a Kivy BoxLayout, and has a StackLayout inside, where form widgets will be added. The StackLayout allows other widgets to stack without occupying the full size of the screen.

    def add_widget(self, widget):
        g = Grabbable(
            height=self.widgets_height,
            size_hint=self.widgets_size_hint
        )
        g.add_widget(widget)
        self._canvas.add_widget(g)
  

Each time we add a widget, a Grabbable item is instantiated for containing the widget we want to add. It's this Grabbable item that we really add to the canvas. This allows us to select the widget and move it around the form to design the composition.

There is a Destination item to indicate where the widget will be placed when the drag operation ends.

    class Destination(Label):
        def __init__(self, **kwargs):
            super(Destination, self).__init__(**kwargs)
            self.text = 'Widget goes here'
            self.color = [1, 1, 0, 1]
            with self.canvas.after:
                Color(1, 1, 0, 1)
                self.box = Line(dash_length=8, dash_offset=4)

        def update_box(self):
            self.box.points = [
                self.x, self.y,
                self.x, self.y + self.height,
                self.x + self.width, self.y + self.height,
                self.x + self.width, self.y,
                self.x, self.y
            ]
  

This item, which is a simple label with dotted border, is very useful for the developer to be able to place widgets in the right place.

Finally, it's possible to export the composition to Kv language. KivyForms is in charge of filtering all the Grabbable items and the configuration needed for the design of the form, so we can save the composition directly to a file, ready to be used in our programs.

KivyForms is distributed under MIT license. Although I prefer GPL licenses, in this case I have decided to use, for consistency, the same license as Kivy, so you are invited to collaborate on the project.