Creating an Application in Kivy: Part 4

You know, when I was doing my planning, I expected Part 3 of this tutorial to get us all the way to rendering a window containing the buddy list. So it’s somewhat amusing that this time, I don’t expect to be at that state at the end of this part. Instead, I want to focus on a few problems and annoyances with the existing code. Never fear, though, we’ll definitely be back to coding Kivy interfaces in Part 5!

In this part, we’ll focus on fixing a problem with the interface, handling errors on login, and taking care of that need to call “disconnect” on the xmpp object after displaying the buddy list. Be forewarned however! You may also encounter digressions on version control, debugging, and testing.

If you’ve gotten lost or want to jump in without doing the previous tutorials, you can start from the example repository as of the end of part 3:

git clone https://github.com/buchuki/orkiv.git
git checkout end_part_three

Table Of Contents

Here are links to other parts of this tutorial.

  1. Part 1: Introduction to Kivy
  2. Part 2: A basic KV Language interface
  3. Part 3: Handling Events
  4. Part 4: Code and Interface Improvements
  5. Part 5: Rendering a Buddy List
  6. Part 6: ListView Interaction
  7. Part 7: Receiving messages and interface fixes
  8. Part 8: Width based layout
  9. Part 9: Deploying your Kivy application

Handling tab and enter events on text input

When I was testing the interface in part 2, I found that I naturally wanted to tab from one textfield to another, a common paradigm for desktop interfaces. Most desktop oriented toolkits have built-in support for what they call “tab order”; the order in which focus switches from one field to another when the tab key is pressed. Kivy doesn’t supply any tools for this. I think this highlights both Kivy’s emphasis on keeping a simple API and the fact that a lot of it was designed to work with mobile devices, which typically interact by touch and have neither physical keyboards nor a tab key. However, that doesn’t mean we can’t tab between our fields, it just means we have to do a bit more legwork.

Legwork generally starts with a web search. Lately, I wonder if I shouldn’t exercise my brain to come up with a solution on my own before turning to a search engine. However, deadlines are always tight, and Stack Overflow so often has an answer. Now, the code provided there doesn’t work quite the way I want it to, but it does tell us how to move forward in solving this task. Let’s get our hands dirty. We can start by adding a new class to our __main__.py:

(commit)

from kivy.uix.textinput import TextInput  # At the top

class AccountDetailsTextInput(TextInput):
    next = ObjectProperty()

    def _keyboard_on_key_down(self, window, keycode, text, modifiers):
        if keycode[0] == 9:  # 9 is the keycode for 
            self.next.focus = True
        elif keycode[0] == 13:  # 13 is the keycode for 
            self.parent.parent.parent.login()  # this is not future friendly
        else:
            super(AccountDetailsTextInput, self)._keyboard_on_key_down(
                    window, keycode, text, modifiers)

Yikes! That’s a little bit more complex piece of code than we’ve seen so far. It’s a good thing it’s so short! Let’s go through each staement. We start by creating a new widget called AccountDetailsTextInput that inherits from TextInput. This means that it looks and behaves exactly like a TextInput object… because it is one! However, we modify it a bit. First, we give it a next ObjectProperty just like we did to register TextInputs as properties on the AccountDetailsForm class in Part 3. We’ll be assigning a value to this next property inside the KV Language file later.

Then we override the _keyboard_on_key_down method on TextInput. You’re probably wondering how I knew that was the method I needed to override. Admittedly, the Stack Overflow answer provided a good hint, but I also read through the Kivy source code to make sure I knew what I was overriding and how it worked. Reading through other people’s source code can be intimidating, but is is the absolute best way to understand how something works. It’s also extremely educational, especially if you come across constructs that you don’t understand and have to look them up.

If you’re not familiar with Object Oriented Programming, you might want an explanation of what “overriding” is. Basically, whenever this method is called, we want it to do something different from what would happen if the same method were called on the parent class. Specifically, we want to handle the and keypresses in a different way.

So that’s what the conditional inside the method does. It checks the keycode value to see if it is 9 or 13. I didn’t know that the keycode was a tuple, but the source code and stack overflow question both indicated that the first value stores the keycode. I do happen to know that 9 and 13 are the keycodes for tab and enter because I’ve written similar handlers before. Can you think of a way (other than the aforementioned search engine) to determine what the keycode is for a given keypress?

Instead of the conditional in the code above, you could simply do a print(keycode[0]) inside this method and hook up the appropriate KV language syntax as we’ll discuss below. When you run the program, you’d be able to see the output on the console when the tab or enter key was pressed. There’s the information you need. Run with it!

The line where we focus the next object property is pretty straightforward; we just set a boolean value on it and let Kivy take care of the details. However, the line for the enter button is a bit tricky. To be honest, I hate this line of code. It’s very fragile. It calls self.parent to get the parent of the text input, which is a GridLayout, then calls .parent on that which yelds a BoxLayout (we’re navigating up through the orkiv.kv hierarchy here), and calls parent one more time to get the AccountDetailsForm. Then we can finally call the login method on that, to do the same thing as if we had clicked the login button. Thus users can enter their details, tab to the next box, and login without lifting their hands from the keyboard!

The reason I hate this line of code is that future changes in orkiv.kv could easily break it. If we decide to insert another Layout into the mix, this line will need to have another .parent added. If we decide to remove the surrounding BoxLayout and put the GridLayout as a direct child of AnchorLayout, it will again break. Worse, if we made such a change and ran the program, we might see that the new layout looked exactly as we wanted it to, but totally forget that we needed to test the tab-order widget.

There are other ways to get access to the AccountDetailsForm and its login method, but I’m not too keen on them either. We could call Orkiv.get_running_app().root. Or we could add an ObjectProperty to the AccountDetailsTextInput and set that property to point at the magic root variable. This latter is probably the most extensible, but it means that the Kivy Language file needs to be a bit uglier. I chose to stick with the .parent notation, but to include a comment to remind me that if I look at this section of code in the future, it would be ideal if I had an insight for how to implement it more elegantly.

Finally, we have the weird else clause which covers any case where the user has presed a key other than tab or enter. In that event, we just want the parent TextInput class to do what it would normally do; display the character in the text field on the screen. So we use the super builtin and pass it first the class we want to get the parent of, and the instance of that class, namely self. This returns basically the same object as self but as if it was an instance of TextInput instead of AccountDetailsTextInput. Then when we call _keyboard_on_key_down on that class, we get the default behavior for the class.

The rather arcane syntax of super is much simpler in Python 3, where we could just call super._keyboard_on_key_down The two argument syntax is still supported, as it is necessary to support a feature called multiple inheritance. That’s something I recommend you don’t learn about too early, not because it is a particularly complicated construct, but because it is, in my opinion, much less useful than it appears and you are quite likely to misapply it!

Oy gevalt, but that was a lot of explanation for not very much code. Luckily, you can probably figure out the changes to the Kivy Language file for yourself:

(commit)

Label:
    text: "Server"
AccountDetailsTextInput:
    id: server_input
    next: username_input
Label:
    text: "Username"
AccountDetailsTextInput:
    id: username_input
    next: password_input
Label:
    text: "Password"
AccountDetailsTextInput:
    next: server_input
    password: True
    id: password_input

We did two things to each TextInput here. First, we changed it to be an instance of the new AccountDetailsTextInput and second, we added a next attribute to each of them. This next points at the id of whichever input box it makes sense to be the next in the tab order.

But what about invalid logins?

Have you tried intentionally or accidentally entering invalid account details into this form and submitting it? The application dies outright, which is neither optimal nor friendly. Users are not infallible. In fact, users can be incredibly dedicated to totally breaking your fancy interface! Let’s try to anticipate some of their mistakes and supply an error message that makes a little sense instead of just crashing.

Let me tell you up front, this turned out to be harder than I expected! I don’t want to do anything too complicated for the intended audience of this tutorial. I ended up poking around in the sleekxmpp source code quite a bit to come up with this solution. I don’t like the results, but I saved it for you on a separate git branch. Too many tutorials pretend that the solution they present to you is the obvious one; instead, I want you to see that programming is often all about exploration and backing up and trying again.

The problem with the code I linked is that it locks up the interface so it’s hard to interact with. Since nothing can happen until connection has occurred anyway, I think it will be better to pop up a window that tells the user to wait while we perform the connection.
Before I show you the code, I want to describe how I was testing it as I developing it. I performed several different tasks and changed the code until all of them were working. This was time consuming (and the reason this tutorial is a little late) and tricky. Each time I changed things, I had to go through all the tasks to make sure that fixing one problem hadn’t broken other things. Often it had, which meant I had something else to fix and test. However, at the end of this section, you’ll be able to perform all these tasks with expected results:

  • Log into my personal jabber server and see the buddy list
  • Log into a throw away google account and see the buddy list
  • Click the login button without entering any details and get an error message to try again
  • Enter credentials for a host that does not exist and get an error message to try again
  • Enter invalid credentials for the google account and get an error message to try again

Let’s start by building a Kivy Modal View widget. A ModalView essentially sits in front of the other widgets, by default taking up the whole window, though you can make it look like it’s floating on top of the interface by giving it a size. How did I know this was the widget I wanted? I didn’t. I had to search through the Kivy API library for what I wanted. I first stumbled across the Popup widget, but it has extra features that we don’t want. However, I saw that it inherits from ModalView and that’s exactly what we need.

So we’re going to create a ModalView object that notifies the user that a connection is being attempted. If the credentials are invalid, it will give them a short message and invite them to return to the login widget and try again. If they are valid, it will print the Buddy List on itself. Let’s start adding this new class to the __main__.py:

(commit)

from kivy.uix.modalview import ModalView  # At the top of the file
from kivy.uix.label import Label

class ConnectionModal(ModalView):
    def __init__(self, jabber_id, password):
        super(ConnectionModal, self).__init__(auto_dismiss=False,
            anchor_y="bottom")
        self.label = Label(text="Connecting to %s..." % jabber_id)
        self.add_widget(self.label)
        self.jabber_id = jabber_id
        self.password = password

This is similar to what we did for the AccountDetailsForm above. We create a subclass of ModalView and initialize it with a jabber_id and password. We initialize the superclass, passing in a couple of arguments to tweak the behavior. We disable auto_dismiss because we don’t want the popup to disappear until we tell it to. Otherwise, it would disappear if the user clicked on it anywhere, even though it’s still trying to connect. We also supply anchor_y because ModalView inherits from AnchorLayout. When we add a button later, we’ll want it to pop up in front of the Label at the bottom of the screen. Notice how we are constructing a user interface in Python code here and that it’s rather bulky compared to the KV Language way of constructing an interface. However, since there’s only one widget for now and a second one created dynamically, it doesn’t really make sense to style this class in the KV Language.

We then construct a Label and add it to the Layout using add_widget. We store the reference to label on self so that we can update the text later. We similarly store the jabber_id and password.

Obviously, we aren’t constructing this widget anywhere, so it’s not going to be displayed if we run the code. Let’s rewrite the login() method to pop up this window instead of trying to connect. Note that if you were developing on your own, you might want to keep a copy of the current login() code in a separate buffer because we’ll be needing to adapt it to live in this new class, soon. Or you can just check your git history to see how the login method used to look. git show HEAD^:orkiv/__main__.py will show what it looked like in the previous revision. HEAD^ says “previous revision” to git. Yes it’s arcane syntax. You can see dozens of other ways to specify which revision you want to show in the git rev-parse manual.

Anyway, let’s display that popup in the login method:

(commit)

def login(self):
    jabber_id = self.username_box.text + "@" + self.server_box.text
    modal = ConnectionModal(jabber_id, self.password_box.text)
    modal.open()

We kept the one line from the old login() method which constructs the jabber_id. But now instead of connecting, we construct a ConnectionModal widget and instruct it to open itself. If you run this, it will no longer try to connect to jabber at all. Even though the connection code still exists on the Orkiv, that method is not being called. Obviously, calling that is the next step, but lets make tweak that connect_to_jabber method first to make it inform us when it cannot connect.

I didn’t notice the need for this change until later on when I was doing my own testing, but it’ll be easier to explain if we do it now. It’s common to do this when crafting commits in git (less so in Mercurial, which is an endless source of frustration for me). Instead of committing each piece of poorly thought out code as I go, I figure out what needs to be done and then design the commits so that they tell a coherent story to anyone that reads the history. If you read through the Examples committed on the github repo you can see each scene in this story as it is played out. It is much easier for you to understand than if I had committed each of my mistaken attempts in whatever crazy order that they occurred. When crafting git history, remember that there will always be a reader of your code and try to make the most elegant story possible.

Specifically, replace the single self.xmpp.connect() line with the following four lines:

(commit)

from sleekxmpp.exceptions import XMPPError  # At the top of the file

self.xmpp.reconnect_max_attempts = 1
connected = self.xmpp.connect()
if not connected:
    raise XMPPError("unable to connect")

There are actually two fixes here. I had to scour the sleekxmpp documentation to find the reconnect_max_attempts property. Without it, the client keeps trying to connect over and over and never fails. However, if we tell it not to bother trying to reconnect at all, for some reason, it fails to connect to gmail. So we allow it to reconnect once to keep gmail happy, but not indefinitely to keep our users happy.

Second, the connect() function returns a boolean value indicating whether the connection was successful. If it’s not successful, we want to communicate to whatever method called connect_to_jabber that it was unsuccessful. So we raise an exception that, handily enough, sleekxmpp has defined in their code. When we raise an exception, execution immediately stops in this method. However, we can catch the exception in the calling code. Let’s add that calling method to the ConnectionModal class now:

(commit)

from sleekxmpp.jid import InvalidJID  # At the top of the file

def connect_to_jabber(self):
    app = Orkiv.get_running_app()
    try:
        app.connect_to_jabber(self.jabber_id, self.password)
        self.label.text = "\n".join(app.xmpp.client_roster.keys())
    except (XMPPError, InvalidJID):
        self.label.text = "Sorry, couldn't connect, check your credentials"
    finally:
        if hasattr(app, "xmpp") and app.xmpp:
            app.xmpp.disconnect()

Notice that this method is called connect_to_jabber just like the one on the Orkiv class, but this one is attached to ConnectionManager. There is no law against reusing names if it makes sense to do so, as long as they are in a different namespace (the Orkiv namespace is completely independent from the ConnectionModal namespace). In fact, if you recall the consistency rant from Part 3, you will know that it is often desirable to do so.

We have a connect_to_jabber method on the ConnectionModal class that calls a totally different connect_to_jabber method on the Orkiv class. The latter method might raise either an XMPPError or an InvalidJID error. The former is raised explicitly if connected is False; the latter is actually raised inside the sleekxmpp code. Either way, it gets propagated up to the the ConnectionModal‘s method, where we catch it using a try...except clause. We also add a finally clause that checks if an xmpp property has been added to the app and, if so, disconnects it to keep it from freezing up the app, as we discussed in Part 3.

If you would like more information on exceptions, see the Python tutorial. There's also good coverage of Exception handling in my book, Python 3 Object Oriented Programming.

It only takes one line of code to have this connect_to_jabber called as soon as the ConnectionModal is opened. ModalView has a open event, so we just have to assign it a callable (any function or method, such as connect_to_jabber is a callable) that is called whenever that event is invoked. So hook this up as the last line of ConnectionModal's __init__ method:

(commit)

self.on_open = self.connect_to_jabber

There's one more thing I want to do. When there is a failure, instead of just showing a message and forcing the user to close the app, it would be much better if they had a way to go back to the login form. Let's add a button that allows them to try again. Clicking this button will hide the modal window and the original login form that was behind it will be visible so the user can edit the details. This can be done by replacing the contents of the except clause with the following code:

(commit)

from kivy.uix.button import Button  # At top of file

self.label.text = "Sorry, couldn't connect, check your credentials"
button = Button(text="Try Again")
button.size_hint = (1.0, None)
button.height = "40dp"
button.bind(on_press=self.dismiss)
self.add_widget(button)

Here we're adding more user interface in Python code and once again we see that it's bulky compared to the KV Language. We're still rendering the label, but we're also adding a button to the bottom of the window. We're giving it a 40 display pixel height, remembering to set size_hint to None in the y dimension so the layout knows to check the height. We then bind the on_press event of the button to the dismiss method (a callable, remember?) on ModalView. When the button is clicked, the modal disappears. Now it's much easier to test each of the failure conditions I listed earlier, one after the other.

Technically, this interface still freezes up when we connect, but it's less invasive since we're rendering a label instead of broken text areas. The correct fix would probably be to connect in another thread and then schedule interface updates using the Kivy clock, but that's a bit advanced for this tutorial.

Exiting gracefully

The last problem I want to address today is the fact that we disconnect as soon as we render the Buddy List. That's not going to be very useful when we get around to actually sending and receiving messages. However, not disconnecting causes the program to hang when we close the window because the xmpp object is sitting there doing it's job: waiting for incoming jabber messages.

We need to disconnect that object when the window closes. Luckily, Kivy gives us an event on the App class, named stop, that we can hook into to do just that. But let's arrange our code nicely.

I spend a lot of time rewriting code that I wrote before. Why? To make it more maintainable. I need the code to be readable in the future when I've forgotten what it does. Python is designed to be a very readable language, but that doesn't mean that it's self organizing. It's one thing to write readable sentences in English, but quite another thing to have a coherent and cohesive paragraph.

Even though it's temporary code, the hasattr call in our exception handler (which checks if the xmpp property has been added to the object or not) is a code smell. It would be better if the xmpp property always exists on the object and to check whether or not it is a valid value. So let's set that property to None by adding an initializer method to the Orkiv class:

(commit)

def __init__(self):
    super(Orkiv, self).__init__()
    self.xmpp = None

Let's also add a disconnect_xmpp method to the Orkiv class that does the necessary cleanup for us. This method will be called both when the app stops and when the login method fails (but not on successful login). It's a bit more involved than the dirty call to xmpp.disconnect() that we were making in our temporary code, because we want to be sure to get it right:

(commit)

def disconnect_xmpp(self):
    if self.xmpp and self.xmpp.state.ensure("connected"):
        self.xmpp.abort()
    self.xmpp = None

The conditional checks too things. First, it checks that self.xmpp is an actual object, and not None. If this check is False, python won't even bother with the second check, which is to see if the ClientXMPP object is connected. I had to actually look through the sleekxmpp source code to figure out how to check if it's connected using that weird state.ensure syntax. If xmpp IS connected, then we call abort(), which calls the disconnect() method we were calling earlier as well as some other cleanup tasks. Finally, regardless of whether it was connected, we set the xmpp property to None which both frees the memory used by sleekxmpp for garbage collection and puts the app in its known initial state in case we want to try to connect again.

While we're add it, let's hook up the stop event handler on the Orkiv. All it needs to do, for now, is call the method we just added:

(commit)

def on_stop(self):
    self.disconnect_xmpp()

You might be wondering why we have two methods, instead of just putting the disconnect_xmpp method body inside of on_stop. There are a couple reasons. One is that we're going to be calling disconnect_xmpp from a the exception handler in the next example. Of course, we could just call on_stop instead, but that wouldn't be as readable; the name disconnect_xmpp very clearly describes what the method does. Second, there is every chance that we'll later have to add additional code to the stop event handler to clean up other things when the app closes. By that point, we'll likely have forgotten that on_stop is being called from other places, and we may not want the new cleanup code to be called during the connection/login phase. Instant bug! It is often extremely worthwhile to use longer code if it is more readable than a shorter alternative.

Let's do that last step now: call the disconnect_xmpp method when login fails. We can remove the finally clause, since we no longer want to disconnect if login is successful. And we can remove the hasattr call, since that check is now being done more elegantly inside the disconnect_xmpp method itself:

(commit)

except (XMPPError, InvalidJID):
    self.label.text = "Sorry, couldn't connect, check your credentials"
    button = Button(text="Try Again")
    button.size_hint = (1.0, None)
    button.height = "40dp"
    button.bind(on_press=self.dismiss)
    self.add_widget(button)
    app.disconnect_xmpp()

More on maintaining history

That's it for coding in this part of the tutorial. I'm a bit concerned that the changes introduced in this tutorial were hard to follow, since we were making sweeping changes all over the file, but I wanted to describe them one at a time. If you're having trouble following it, you might have better luck looking at the individual git commits. If you select a commit, github will highlight the lines that have been added and removed in each change, making it easier to follow. It also allows you to see the entire file (there's a "View File" button) if you're feeling lost. I tried very hard to craft the changes in each commit as well as the commit message so that the history is very easy to follow.

You should try to do this yourself when you are developing. It is unlikely your history will be quite as readable as this repository (my real life repos certainly aren't!), because it is generally considered unwise to edit your history after you have pushed it to a public repository that other people have seen (think of trying to edit a published book after somebody buys it.) However, before you have pushed your changes, it is perfectly ok to rewrite and reorganize your commits so that they make the maximum amount of sense.

Git provides several tools for such work. One of the easiest to understand is the git commit --amend command. This command is like a regular commit, except that instead of creating a new commit with whatever changes you have prepared with git add, it alters the last committed patch to include those changes. It even allows you to edit the commit message! This is very useful if you make a commit and then realize you forgot to change something in that commit. For example, it's a terrific way to exclude useless, cluttering commits with messages like "Conform to pep8", "I forgot to remove a debugging statement", "Update documentation", or the most heinous commit message of all: "Blah".

Another really handy tool that I used extensively while developing this particular tutorial is git add -p <filename>. Because I didn't actually know exactly how my attempts with the ConnectionModal code were going to work out, I wrote and tested everything before writing the narrative. But then I wanted to commit only specific parts of what I had written in each example, each commit containing a single, coherent collection of changes. git add -p is an extended version of git add (the p is short for --patch) that loops over the changes in the file and allows you to interactively select exactly which hunks you want to go into the next commit. There are quite a few options you can use during each step; press the h to get an overview. Don't forget to call git commit when you're done!

Monetary feedback

If you liked the tutorial and would like to see similar work published in the future, please support me. I hope to one day reduce the working hours at my day job to have more time to devote to open source development and technical writing. If you think this article was valuable enough that you would have paid for it, please consider supporting me in one or more of the following ways:

Finally, if you aren't in the mood or financial position to help fund this work, you can always do your part to share it on your favorite social platforms.

13 Comments

  1. [...] Creating an Application In Kivy: Part 2 Creating an Application in Kivy: Part 4 [...]

  2. On an iPhone, when you’re doing keyboard entry into a text field, there is a “Next” button to move to the next text field. That seems a bit like tab order. Does Kivy support that? (I haven’t ever done iPhone or Android app programming, so I’m not sure of the technical details.)

    • Dusty Phillips says:

      I can’t say for certain as I haven’t worked with iphone either. My guess is that iOS would generate a keycode of some sort when the next key is pressed and that Kivy would be able to respond to that in the same way I did in this tutorial. It’s even possible it generates the same keycode as a tab key. I doubt that Kivy has any built-in support for it, though.

  3. Owais Lone says:

    Screenshots of the apps in every part would be really helpful

    • Dusty Phillips says:

      I’ll try to go back and add some later. I’m having a bit of trouble meeting my self-imposed deadlines of “a week to ten days” per part, so I’m afraid those got cut. But I’ll get to it!

  4. Gian Mario Tagliaretti says:

    Hi Dusty,

    I guess you are probably using a development version af the sleekxmpp library and not the last 1.0 release, there is no jid module in the stable release (downloaded from their website)

    from sleekxmpp.jid import InvalidJID
    ImportError: No module named jid