Archive for July 2013

Creating an Application in Kivy: Part 7

I don’t know about you, but I was pretty excited at the end of Part 6 when I was able to send a message to myself on another account. It’s taken several weeks to get here, but by the end of this part, we’ll have a [mostly|barely] working jabber client that can send and receive messages and isn’t too annoying to interact with.

If you haven’t been following along or want to start this example at the same point at which I’m starting, you can clone the github repository with my examples:

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

Remember to create and activate a virtualenv populated with the appropriate dependencies, as discussed in part 1.

Table Of Contents

If you’ve never thought about it before, let me tell you: maintaing a table of contents on WordPress is a royal pain in the ass.

  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

Receiving Messages

Receiving messages with SleekXMPP isn’t quite as simple as sending them, but it’s pretty damn simple! SleekXMPP has an event handler, not unlike the one Kivy provides, but don’t confuse them! SleekXMPP is running in a separate thread and doesn’t know or care that Kivy is running it’s own event loop. Further, they use different APIs, which basically means that they use different method names to accomplish similar things, like issuing an event or listening to receive one.

Thus, in order to not be confused with Kivy Events, we don’t call our handler on_message or something in that. Instead, we add a handle_xmpp_message method to the OrkivRoot class:

(commit)

def handle_xmpp_message(self, message):
    if message['type'] not in ['normal', 'chat']:
        return
    jabber_id = message['from'].bare

    if jabber_id not in self.chat_windows:
        self.chat_windows[jabber_id] = ChatWindow(jabber_id=jabber_id)
    self.chat_windows[jabber_id].chat_log_label.text += "(%s) %s: %s\n" % (
            datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
            jabber_id,
            message['body'])

When called by SleekXMPP, this method is passed a message object, which is some kind of stanza object. I didn’t bother to fully understand how this object is constructed; instead I stole some code from the SleekXMPP quickstart and then introspected it by printing stuff to the terminal when the handler is called. It can often be useful to print(dir(message)) to see what kind of attributes and methods an unknown object has. print(type(message)) can also be useful in that knowing the name of a class can help figure out where to look in the documentation or source code.

So after a wee bit of trial and error, I came up with the above method. It first checks the type, and gets the bare attribute off the jabber_id (the documentation helped here). Then, if the chat window does not exist, we create it, the exact same way we did when the user created the object. Even if the user is not currently looking at the chat, the message will show up in it. We then append the new message to the label using the same format we used when sending messages.

Now, hook up the event handler at the moment the xmpp object is constructed in the connect_to_jabber method of the Orkiv. It just takes one new line of code at the end of the method:

(commit)

    self.xmpp.add_event_handler('message', self.root.handle_xmpp_message)

We’ll make the label look a bit cleaner shortly, but first: test it! Start chatting with your jabber friends. Tell them how you’ve written your own jabber client using Python and Kivy. If they aren’t sufficiently impressed, find new friends!

Cleaning up duplicate code

There’s a couple pieces of code in our OrkivRoot and ChatWindow classes that look very similar to each other. First, the code for creating a new chat window only if one doesn’t exist is identical. And the code for formatting a new chat message and appending it to the chat log is also congruent.

It is a good idea to remove pieces of duplicate code as soon as you notice them. Duplicate code is a “code smell”; it indicates that your design is not good. From a practical point of view, it means maintenance can be difficult if you decide to change something (say the formatting of the date on a chat message) and forget to update multiple places at once. Of course right now you remember it’s in two places, but six months from now when these two classes have been refactored into separate files, you might not remember so easily.

There are a variety of patterns to remove duplicate code, and each situation can be different. The easiest thing to do is to refactor the things that stay the same into a function and pass the things that change in as arguments. Or you might be able to use object oriented programming and inheritance. Python’s decorator syntax or context managers can also be extremely useful for reducing duplicate code. The point is, as soon as you’ve hit the copy-paste key combination, you should start thinking about the most effective API that can be designed to remove redundancy.

In this case, we can just add a couple new methods, one to OrkivRoot and one to ChatWindow. Let’s start by refactoring the code that creates a chat window only if it doesn’t exist:

(commit)

    def get_chat_window(self, jabber_id):
        if jabber_id not in self.chat_windows:
            self.chat_windows[jabber_id] = ChatWindow(jabber_id=jabber_id)
        return self.chat_windows[jabber_id]

    def show_buddy_chat(self, jabber_id):
        self.remove_widget(self.buddy_list)
        self.add_widget(self.get_chat_window(jabber_id))

    def handle_xmpp_message(self, message):
        if message['type'] not in ['normal', 'chat']:
            return
        jabber_id = message['from'].bare

        chat_window = self.get_chat_window(jabber_id)
        chat_window.chat_log_label.text += "(%s) %s: %s\n" % (
                datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
                jabber_id,
                message['body'])

All we did was add a new method that gets the chat window from the dictionary, creating one if it doesn’t exist. Then we simplify the two methods that were previously performing this task to simply call that method. Those methods are now easier to follow. The basic principle is to abstract the complex code into it’s own function so that calling code is more readable.

Now let’s do the same thing for message formatting by adding a append_chat_message method to the ChatWindow class:

(commit)

    def send_message(self):
        app = Orkiv.get_running_app()
        app.xmpp.send_message(
            mto=self.jabber_id,
            mbody=self.send_chat_textinput.text)
        self.append_chat_message("Me:", self.send_chat_textinput.text)
        self.send_chat_textinput.text = ''

    def append_chat_message(self, sender, message):
        self.chat_log_label.text += "(%s) %s: %s\n" % (
                sender,
                datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
                message)

We also make a similar change to the code in OrkivRoot that handles incoming text events:

def handle_xmpp_message(self, message):
    if message['type'] not in ['normal', 'chat']:
        return
    jabber_id = message['from'].bare

    chat_window = self.get_chat_window(jabber_id)
    chat_window.append_chat_message(jabber_id, message['body'])

Interface Improvements

Let’s make some improvements to that chat window. Start by modifying the method we just added to include some color to help the reader identify who sent what. The Label class has a feature that allows you to include markup on the label. Unfortunately, it has it’s own microsyntax instead of using any standard markup languages. This is good because it allows the language to target the things that Kivy labels can do, but not so great in that you have yet another markup to learn. (My life would be much easier if I was not constantly switching between ReST, Markdown, Trac wiki syntax, Mediawiki syntax, and HTML! Except for HTML, I’ve never really been able to learn or distinguish them, so I have to look up the syntax every time I write it.)

The Kivy label syntax is reminiscent of bbcode popular on web forums, so if you’ve used that, you should be comfortable. I’m not going to go into detail of the syntax, since the documentation does a great job of that, but I will show you how to use colors and bold text to make our ChatWindow label much more eye-pleasing:

(commit)

    from kivy.utils import escape_markup  # At top of file

    def send_message(self):
        app = Orkiv.get_running_app()
        app.xmpp.send_message(
            mto=self.jabber_id,
            mbody=self.send_chat_textinput.text)
        self.append_chat_message("Me", self.send_chat_textinput.text, color="aaffbb")
        self.send_chat_textinput.text = ''

    def append_chat_message(self, sender, message, color):
        self.chat_log_label.text += "[b](%s) [color=%s]%s[/color][/b]: %s\n" % (
                datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                color,
                escape_markup(sender),
                escape_markup(message))

We added a color parameter to append_chat_message. It needs to be a hextet like HTML colors. Then we used the markup syntax to make the usernames colored and the dates and usernames bold. We also escape the message and sender strings so that users don’t get confused if they accidentally include Kivy formatting tags.

Remember to add the color parameter when calling this function from the handle_xmpp_message method on OrkivRoot:

    chat_window.append_chat_message(jabber_id, message['body'], color="aaaaff")

Finally, this won’t actually render as markup until we tell the label that we want markup rendered. This can be done easily by setting the markup property on the label in the KV Language file:

    Label:
        markup: True
        id: chat_log_label

Now if you run the program and chat with someone, you can easily distinguish who said what! However, if your chat gets too long, it gets truncated, both horizontally and vertically. Let’s make it scroll using a Kivy ScrollView. This can be done entirely in orkiv.kv, though it’s not trivial to get it right:

(commit)

    ScrollView:
        Label:
            size_hint_y: None
            text_size: (root.width, None)
            size: self.texture_size
            markup: True
            id: chat_log_label

In addition to wrapping the label in a scroll view, we also had to set a few sizing properties on the label. The size_hint_y is set to None or else the label would always be exactly the same size as the ScrollView. We want it to be bigger than the scroll view, or else there wouldn’t be anything to scroll!. In fact, we want it to be exactly the same size as the texture_size, which gets updated whenever lines get added to the Label. Finally, to make the lines wrap (because we don’t want to do ugly horizontal scrolling), we set the text_size to have the same width as the parent.

This can be a bit confusing, so let me explain, hopefully correctly (I’m a bit confused, myself, to be honest). First, we have a ScrollView which does not have an explicit size, so it’s size is set for us by the parent BoxLayout. The ScrollView contains a Label, which contains a Texture. The height of the label is set to be the height of the texture, which is calculated from the number of lines being displayed, and updated dynamically. Further, the width of the text rendered on the texture is constrained to be the width of the root widget, which forces the text to wrap. When the text wraps, the texture size gets bigger, and the label gets bigger because it’s bound to the texture size. and the size of the label can be as big as it wants since it’s scrollable.

These changes have the side effect of left-aligning chat, which I was expecting to have to do manually. Go Kivy! One more thing I want to do is scroll the window to the bottom if there’s any new text. This is easily done by adding a single line to the append_chat_message method:

(commit)

    def append_chat_message(self, sender, message, color):
        self.chat_log_label.text += "[b](%s) [color=%s]%s[/color][/b]: %s\n" % (
                datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                color,
                escape_markup(sender),
                escape_markup(message))
        self.chat_log_label.parent.scroll_y = 0.0

The scroll_y attribute on ScrollView is basically a percentage. it returns the current scroll position if you get it, and if you set it, it will scroll the window; 0 is the bottom, 1 is the top, and anything in between is… well, anything in between.

Now I’d like to have the form send a message on an enter keypress, not just when I click the button. This has been annoying me in testing this app and annoyances should be addressed. We can use the same technique we used in part 4. However, instead of hard-coding a call when the enter key is pressed, let’s create a new reusable widget that fires an event when enter is pressed. We’ve had quite a bit of experience binding functions to events, but we haven’t yet tried creating an event of our own.

We’ll call the new widget EnterTextInput. Binding and dispatching events is actually surprisingly simple, so our new class is pretty short:

(commit)

class EnterTextInput(TextInput):
    def __init__(self, **kwargs):
        self.register_event_type("on_enter_key")
        super(EnterTextInput, self).__init__(**kwargs)

    def _keyboard_on_key_down(self, window, keycode, text, modifiers):
        if keycode[0] == 13:  # 13 is the keycode for <enter>
            self.dispatch("on_enter_key")
        else:
            super(EnterTextInput, self)._keyboard_on_key_down(
                    window, keycode, text, modifiers)


    def on_enter_key(self):
        pass

We explicitly register the new event type with the EventDispatcher, which is a superclass of Widget (and therefore, a superclass of TextInput) and provides the methods associated with dispatching events. Then we use a super() call to do the parent initialization.

Next, we override _keyboard_on_key_down, just as we did in the AccountDetailsTextInput class. If the user pressed enter, we dispatch our custom event; otherwise we just let the superclass do it’s thing.

Now we have to hook up that event. I want to do that on the ChatWindow text entry, but first, let’s remove some more code duplication. You might not have noticed, but when I created the _keyboard_on_key_down method above, I copy-pasted some code from the AccountDetailsTextInput. It turns out we can make our AccountDetailsTextInput class extend the new code to reduce code duplication. That means if we make any improvements to the EnterTextInput class in the future, the subclass will get them for free. Here’s the new version of AccountDetailsTextInput:

(commit)

    class AccountDetailsTextInput(EnterTextInput):
        next = ObjectProperty()

        def _keyboard_on_key_down(self, window, keycode, text, modifiers):
            if keycode[0] == 9:  # 9 is the keycode for <tab>
                self.next.focus = True
            else:
                super(AccountDetailsTextInput, self)._keyboard_on_key_down(
                        window, keycode, text, modifiers)

        def on_enter_key(self):
            self.parent.parent.parent.login()  # this is not future friendly

Basically, we just removed the code for dealing with the enter key and add an event handler that does the same thing when the enter key is pressed. It’s a simple refactor, and arguably isn’t terribly more readable than the original version. However, it is more maintainable. For example, if in the future we discover that an enter key can generate different keycodes in different countries, we only have to fix it in one place.

Now let’s also hook up the new event on our ChatWindow in the KV language file. Change the TextInput to a EnterTextInput and hook up the new event:

(commit)

    EnterTextInput:
        id: send_chat_textinput
        on_enter_key: root.send_message()

That’s much better! However, there’s still a bit to be desired in terms of notifications. Let’s make Kivy play a sound whenever a message comes in! I grabbed a creative commons sound (attributed to User TwistedLemon) from Free sound and saved it as orkiv/sounds/in.wav. You can also steal a sound from your favourite IM program or record something yourself. Playing the sound is as easy as loading it in the OrkivRoot initializer:

(commit)

from kivy.core.audio import SoundLoader  # At top of file

    self.in_sound = SoundLoader.load("orkiv/sounds/in.wav")

And playing it in the handle_xmpp_message method:

    self.in_sound.play()

We’re running a bit short on space for this week, but there’s one more thing I’d like to do. Right now, when we receive a notification, it’s impossible to know who sent the message, unless we currently have that chat window open. Let’s make our BuddyList highlight those users who have new messages.

The plan I have for this is not the best plan, as it doesn’t clearly separate data from display. We’re adding a new piece of data for each jabber_id (whether or not it has new messages), but instead of creating a proper data model, I’m just going to store that data in a set. Remember, the args_converter is drawing data from the xmpp presence and other locations, and the attribute of whether something is selected is stored in the state of the BuddyListItem, so in some ways, the args_converter IS a data model.

It’s not elegant. However, making it elegant requires both that the ListView API be properly designed, and that we stop abusing it to take only the parts we want. So let’s go with simple for now!

Start by adding an attribute to the BuddyList.__init__ method. It will be a set object, and will contain only jabber_ids that have new messages:

(commit)

    def __init__(self):
        super(BuddyList, self).__init__()
        self.app = Orkiv.get_running_app()
        self.list_view.adapter.data = sorted(self.app.xmpp.client_roster.keys())
        self.new_messages = set()

We use a set instead of a list because sets are more efficient when we want to ask the question “is this object in the set”. We aren’t going to be doing that millions of times and the size of the set isn’t going to be tens of thousands of objects, so it’s not a really important distinction for this code. However, it’s good to get in the habit of using and knowing which data structures to use for which tasks. At some point in your programming career, you will want to study up on “Algorithms Analysis” and “Data Structures” to help you make these decisions. For now, just remember that any time you are keeping a collection of objects so you can repeatedly use the in keyword on it, use a set. Any time you need to know the order of objects, use a list.

Before we start adding messages to this set, let’s create a method on BuddyList that forces the listview to redraw itself. This method shouldn’t really be required if we were using the ListView API properly and it was working properly, but we aren’t and it’s not:

(commit)

    def force_list_view_update(self):
        self.list_view.adapter.update_for_new_data()
        self.list_view._trigger_reset_populate()

Now we need to add users to the set whenever we get a new message in handle_xmpp_message, telling the BuddyList to redraw itself as we do so:

(commit)

    if chat_window not in self.children:
        self.buddy_list.new_messages.add(jabber_id)
        self.buddy_list.force_list_view_update()

We only add the “new message” notification if the chat window is not the currently displayed one. If it is, then the new message shows up on the screen immediately, so it’s not unread.

Similarly, when the user selects a new buddy from the buddy list, we need to clear the new message status for that user:

    def show_buddy_chat(self, jabber_id):
        self.remove_widget(self.buddy_list)
        self.add_widget(self.get_chat_window(jabber_id))
        self.buddy_list.new_messages.discard(jabber_id)
        self.buddy_list.force_list_view_update()

The discard method, unlike remove does not raise an exception if the item is not in the set.

Of course, running this code won’t actually highlight anything. Knowing that the jabber_id has a new message doesn’t help us unless we do something with that knowledge. Let’s replace the background color if statement in BuddyList.roster_converter with the following:

(commit)

   if jabberid in self.new_messages:
       result['background_color'] = (0.6, 0.4, 0.6, 1)
   else:
       result['background_color'] = (0, 0, 0, 1)

   if index % 2:
       result['background_color'] = (x + .3 for x in result['background_color']) 

Basically, if it’s a new message, we give it a new color. But then, to maintain the alternating colors of list items, we add 0.3 to the colors in every other row. Overall, the interface is working pretty well.

Next time, we’ll see if we can get it to render the chat window beside the buddy list, if the window is wide enough! However, please note that it’s going to be a couple weeks before I can publish that article; I’m on vacation this weekend and the following weekend I’ll be at Pycon Canada (look me up if you’re there!).

Monetary feedback

While, in previous parts of this tutorial, I’ve been soliciting funds to support my continued involvement in writing and open source projects, this week, I’m going to ask you to fund Mathieu Virbel instead. Mathieu is the inventor and (utterly brilliant) lead developer of Kivy. The more time he can spend developing Kivy, the better it is for all of us (including me)!

Of course, you’re also welcome to tip me instead, or any of the other wonderful members of the Kivy team.

Or just promote Kivy and my books and articles on your favourite social media platforms. Spread the word, it’s always appreciated!

Creating an Application in Kivy: Part 6

Welcome back, or if you’ve just stumbled across this tutorial, welcome to the middle of something! You might want to jump back to the beginning if that’s the case. Don’t worry, we’ll wait for you to catch up.

We’re learning Kivy by developing a simple chat application together on the XMPP protocol. So far we’re able to display a Buddy List that doesn’t look too shabby, but we haven’t even tried chatting with anyone yet. Given the complexities of the sleekxmpp library, I’m only hoping that it won’t be too difficult!

If you want to start this part with a clean slate, in exactly the state that I’m starting it, you can clone my public repository:

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

Remember to create and activate a virtualenv populated with the appropriate dependencies, as discussed in part 1.

Table Of Contents

Here’s some lovely navigation for you:

  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

About The Current Kivy ListView API

I’m afraid this article’s going to be going up a little late because I’ve spent a great deal of time trying to understand the ListView API for interacting with the user. I’ve read through all the documentation, I’ve read through all the examples included with Kivy, and I’ve read through the source code for all the views, adapters, and widgets. I’ve come to the conclusion that, unlike the elegance in most of Kivy, the ListView API simply sucks. I think my favorite aspect of the documentation was the statement, “Warning: This code is still experimental, and its API is subject to change in a future version.”

I did discover that Kivy’s ListView API provides all the tools to do what we want, but it’s mixed in with a whole bunch of tools that don’t work at all, don’t work the way you would expect, or are far more complicated than necessary. Figuring out which of those tools to use and how to use them was a perfect chance to practice skills for dealing with frustration.

The good news is that when I complained about the ListView API on the #kivy IRC channel I was told that the developers are already actively working on improving it!

I have tried, in these tutorials, to explain not only how to do things, but also how to figure out how to do things. I’m not going to do that for this part for a few reasons. First, there is no clear explanation on the web for “how to effectively use Kivy’s ListView API”, so I’d like this tutorial to fill that niche. Second, with the API redesign in the works, I don’t want to spend a lot of time writing outdated content. Third, the approach I used to understand the API was basic brute force, and not educational in any way. Read all the documentation, read all the sources, and write tons of test code. By trial and error and a bit of good judgment, I think you’ll come to the same conclusions I did. Finally, I spent so much time doing that brute force work that I can’t easily summarize the steps I followed or all the paths I explored.

How to Ignore Most of The Bad Bits

I’m sure you’re aching to see some code, but let’s discuss exactly what we want the interface to look like, first. While our BuddyListItem widget contains several other widgets, we want it to highlight the entire widget with a different background color when it is touched. Essentially, even though multiple items are being displayed on the widget, we want the whole widget to behave like a button. Later, we’ll want to pop up a chat window when the selection event happens.

That “behave like a button” line is the key. The Button class inherits from label and adds some things for button state and touch events. Then Kivy extends that with a ListItemButton class that manages the state of multiple buttons in a ListView. As the Kivy examples show, using this class as the cls argument to a ListView works flawlessly. You touch a button, it gets highlighted. Unfortunately, adding widgets to a button doesn’t work so well because Button is not a layout. If only we could make our BuddyListItem a button AND a layout. Hmmm:

(commit)

from kivy.uix.listview import ListItemButton  # At top of file

class BuddyListItem(BoxLayout, ListItemButton):
    jabberid = StringProperty()
    full_name = StringProperty()
    status_message = StringProperty()
    online_status = StringProperty()
    background = ObjectProperty()

This is multiple inheritance; BuddyListItem is inheriting from both the BoxLayout and ListItemButton superclasses. I honestly did not expect this to work with widgets, since the inheritance graph is a weird diamond. However, the Kivy devs have been careful to support multiple inheritance (by always passing **kwargs to super calls). When I ran this code and saw selectable buttons in my ListView, I said to myself, “Holy shit! It works!” Then I giggled softly and promptly forgave the Kivy team for the confusing API faux pas.

Now we get to do one of my favorite activities: Deleting code. One of the nice things we inherit from the Button class is a background_color property. This means we can take the entire canvas.before section out of orkiv.kv:

(commit)

<BuddyListItem>:
    size_hint_y: None
    height: "75dp"

    BoxLayout:
        size_hint_x: 3
        orientation: 'vertical'
        Label:
            text: root.jabberid
            font_size: "25dp"
            size_hint_y: 0.7
        Label:
            text: root.status_message
            color: (0.5, 0.5, 0.5, 1.0)
            font_size: "15dp"
            size_hint_y: 0.3
    Label:
        text: root.full_name
    Image:
        source: "orkiv/icons/" + root.online_status + ".png"

Nothing new here at all, it’s just shorter! You can also remove the background property from __main__.py. Now, just change the arg_converter to set background_color instead of background and the button will look less buttony and more listitemy:

(commit)

if index % 2:
    result['background_color'] = (0, 0, 0, 1)
else:
    result['background_color'] = (0.05, 0.05, 0.07, 1)

Finally, I’m not too keen on the default red color for the selected item, so let’s change the selected_color (this is a property available on ListItemButton) in the Kivy file:

(commit)

<BuddyListItem>:
    size_hint_y: None
    height: "75dp"
    selected_color: (0.2, 0.1, 0.2, 1.0)

Run this code and you should be able to touch any list item and see it highlighted. Now that’s what I call an easy way to display selected items on a ListView! Don’t take these as gospel, since your needs may differ, but here’s some ideas that I came up with while puzzling through the documentation and code for this API:

  • Don’t use DictAdapter. As far as I can tell, you can more easily use ListAdapter on the keys and reference the underlying dictionary directly from args_coverter, rather than trying to maintain the sorted_keys parameter.
  • As I mentioned in Part 5 don’t use the template argument to ListAdapter, as that functionality has been deprecated.
  • Pretend that CompositeListItem does not exist, never did exist, and could not possibly exist.
  • ListItemLabel is not interactive and is for display only, not selection.
  • While the documentation suggests that you can create your own item class, if you need selection, use ListItemButton. You can either extend it and add widgets as we did here, or put individual buttons inside another view, depending on your requirements.
  • If you don't use ListItemButton and you need selection, you need to make sure your object provides a on_release event, since that is what ListView is hardcoded to trigger selection on.
  • propagate_selection_to_data and SelectableDataItem add a lot of complexity and are only worth it if you really know they are worth it. Don't "avoid them at all costs", but make sure you really need them before trying.

Designing a Chat window

I have an exercise for you that I don't think you'll have any trouble with if you've followed the previous parts of this series. Of course, I'll provide examples and explanations shortly if you get stuck.

Here's the exercise: Create an empty new ChatWindow class that extends BoxLayout. Style the class in the KV language so that it contains a vertical box layout holding a label identifying the user you are chatting with, a second label containing the text of the conversation, and a horizontal box layout containing a Button to return to the buddy list, a TextInput and a second Button to send chat text. Try to figure out which of these widgets need to have an id in the KV Language file hooked up to an ObjectProperty in the python file. Finally, ensure the widgets have appropriate size and size_hint properties to look reasonable.

Here's how my ChatWindow code turned out:

(commit)

class ChatWindow(BoxLayout):
    jabber_id = StringProperty()
    chat_log_label = ObjectProperty()
    send_chat_textinput = ObjectProperty()    

And here's my first attempt at styling. It's a compliment to Kivy's simplicity and elegance that when I actually hooked up the events to display this code, it looked exactly like I expected it to:

(commit)

    <ChatWindow>:
        orientation: "vertical"
        chat_log_label: chat_log_label
        send_chat_textinput: send_chat_textinput
        Label:
            text: "chatting with " + root.jabber_id
            size_hint_y: None
            height: "40dp"
        Label:
            id: chat_log_label
        BoxLayout:
            size_hint_y: None
            height: "50dp"
            Button:
                size_hint_x: None
                width: "70dp"
                text: "Buddies"
            TextInput:
                id: send_chat_textinput
            Button:
                size_hint_x: None
                width: "70dp"
                text: "Send"

Now we need an event to display a ChatWindow when a user makes a selection. Kivy's ListView API has the ability to propagate selection events to the adapter and even the model behind the adapter. However, it's rather involved and I suggest avoiding it unless you actually have a reason to store the advanced selection stuff. Unfortunately, this means you can't listen to the adapter's on_selection_change event.

However, for our use case, all we really want to do is show the chat window if the user touches an item in the list. Hence, we just need to listen for a touch event on the BuddyListItem (which extends Button and therefore has all of Button's events) and react to that.

The reaction will be to display a new ChatWindow object on the root. This means adding a new method to OrkivRoot:

(commit)

def show_buddy_chat(self, jabber_id):
    self.remove_widget(self.buddy_list)
    chat_window = ChatWindow(jabber_id=jabber_id)
    self.add_widget(chat_window)

When called, this code will simply clear the window and display a new, empty chat window in it's place. One thing I may not have mentioned before is how ChatWindow accepts jabber_id as a keyword argument, even though there is no __init__ method that sets the argument. This is because Kivy automatically maps keyword arguments to Property objects on the class or any parent classes, whether we defined those properties in our code or they were defined by the Kivy widget itself.

Now all we have to do is hook up the on_release event on the BuddyListItem in the KV Language file. Note that if you are doing more complicated selection, you may want to hook up an on_state handler instead. It will be called whenever the state property changes (for selection or deselection).

While we're at it, we can also add a similar event to render the buddy list again when the Buddies button is clicked inside the ChatWindow:

(commit)

<BuddyListItem>:
    size_hint_y: None
    height: "75dp"
    selected_color: (0.2, 0.1, 0.2, 1.0)
    on_release: app.root.show_buddy_chat(self.jabberid)

and

    Button:
        size_hint_x: None
        width: "70dp"
        text: "Buddies"
        on_release: app.root.show_buddy_list()

Now, if we run the app, we can switch between the buddy list and a chat view whenever we want. This will be useful for a phone-sized layout, and in a later tutorial we'll make it possible to switch to a side-by side view for tablets and desktops.

However, this isn't an optimal code design. First, it's wasteful of resources; each time, we go back to the Buddy List view, the list is constructed afresh, from scratch. Worse, new chat windows are also constructed each time we go back to a jabberid, meaning that any previous conversation would be lost. Let's fix both these things in the OrkivRoot class:

(commit)

class OrkivRoot(BoxLayout):
    def __init__(self):
        super(OrkivRoot, self).__init__()
        self.buddy_list = None
        self.chat_windows = {}

    def show_buddy_list(self):
        self.clear_widgets()
        if not self.buddy_list:
            self.buddy_list = BuddyList()
        for buddy_list_item in self.buddy_list.list_view.adapter.selection:
            buddy_list_item.deselect()
        self.add_widget(self.buddy_list)

    def show_buddy_chat(self, jabber_id):
        self.remove_widget(self.buddy_list)
        if jabber_id not in self.chat_windows:
            self.chat_windows[jabber_id] = ChatWindow(jabber_id=jabber_id)
        self.add_widget(self.chat_windows[jabber_id])

We added an initializer that sets a couple variables for us. In the show_buddy_list method, we now instantiate the BuddyList only if it has not been previously created. However, this means that selection is kept between calls, so we have to loop over the selected items and deselect them.

This might need some explaining. The ListView doesn't know anything about selection; that is kept track of in the ListAdapter. The selection property is a list containing all BuddyListItem objects that are currently selected. Because BuddyListItem is a ListItemButton, which provides the SelectableView, interface, it has a deselect method that we can use to disable selection of that item.

The chat_windows dictionary maps jabber_id keys to individual ChatWindow objects, any of which can be displayed when the associated user is touched in the buddy list.

Sending a Message

Ok, now that our chat text box is looking pretty sweet and we can switch between buddies at will, let's try actually sending a message! Add a new method, send_message to the ChatWindow widget in __main__.py:

(commit)

    import datetime  # At top of file

    def send_message(self):
        app = Orkiv.get_running_app()
        app.xmpp.send_message(
            mto=self.jabber_id,
            mbody=self.send_chat_textinput.text)
        self.chat_log_label.text += "(%s) Me: %s\n" % (
                datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
                self.send_chat_textinput.text)
        self.send_chat_textinput.text = ''

This is actually fairly simple code considering how exciting it is to send a message and have it show up on a different account when we run it. First we get the app and instruct the xmpp object to send a message. I got this code from the SleekXMPP documentation. Then, while that message is finding it's way to our other chat client, we update the chat_log_label text so that it contains a new line with the current date and the text we sent. Finally, we update the send_chat_textinput to be empty so we can send another value right away.

Now we just need to invoke this method whenever the send button is clicked. I'm sure you can figure out how to do that by yourself. It's just one line of code in the Kv Language file:

(commit)

    Button:
        size_hint_x: None
        width: "70dp"
        text: "Send"
        on_release: root.send_message()

Now for the fun part. If you have two different jabber accounts, connect your normal instant messenger to one of them, and then log into Orkiv with the other one. Send a message to the "normal" messenger and giggle manically when it arrives. This is why we program: the success is so tangible!

And I'll leave you with that success for today. In the next part, we'll hook up the message receiving side of things and make several interface improvements to make the app much more usable.

Monetary feedback

In previous parts of this tutorial, I've asked users to support me financially if they'd like to see more of it. I'm trying to gauge whether it is feasible for me to consider switching from full time work to being supported entirely by open source development, writing, and education in the (far) future.

Instead, today, I'd like to take some time to thank those of you who have answered my call. It's impossible to tell how many book sales come from readers if these essays, or how many Gittips were prompted by this work rather than my various other open source projects, but as both have increased in the weeks that I've been publishing this series, it's clear that some of you support my work. I would like to thank you from the bottom of my heart. I truly believe that an economy based on paying what the buyer, rather than the seller, thinks something is worth, will one day lead to a happier, more peaceful world.

I'd also like to thank all of you who have tweeted, commented, blogged, or otherwise spread the word about these articles. I think they are one of the best available resources for new Kivy developers outside of the excellent Kivy documentation itself. I am exhilarated to know that so many of you agree!

Creating an Application in Kivy: Part 5

In Part 4, we took a break from developing Kivy interfaces and focused on improving the interface we already had. In this part, we’ll get back to interface development by creating a widget to display the buddy list. Here’s the plan: we’ll start with a new widget to render the information we’re getting from the roster. Then we’ll figure out how to query sleekxmpp to get additional information about the user, such as their online status and status message.

If you haven’t been following along or want to start with a clean slate, you can clone the state of the example repository as of the end of part 4:

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

Remember to create and activate a virtualenv populated with the appropriate dependencies, as discussed in part 1.

Table Of Contents

If you’re just joining us, you might want to jump to earlier steps in 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

Rendering A Basic ListView

So far, after successful login, a list of usernames is printed on the ConnectionModal popup. That’s not quite what we want to do. Rather, we want to dismiss the popup and render the buddy list in the root window instead of the login form.

The first thing we’ll have to do here is wrap the login form in a new root widget. We should have done this from the start, but to be honest, I didn’t think of it. This widget will be a sort of manager for the AccountDetailsForm, BuddyList, and ChatWindow. If we only ever wanted to show one window at a time, we could use Kivy’s ScreenManager class to great effect. However, I hope to display widgets side by side if the window is wide enough, so let’s try coding it up manually.

First we can add an OrkivRoot (leaving it empty for now) class to __main__.py. Let’s add a BuddyList class while we’re at it, since we’ll need that shortly.

(commit)

from kivy.uix.boxlayout import BoxLayout  # at top of file

class BuddyList(BoxLayout):
    pass


class OrkivRoot(BoxLayout):
    pass

Next, we update the orkiv.kv to make OrkivRoot the new root object (replacing the current AccountDetailsForm: opening line) and make any instances of OrkivRoot contain an AccountDetailsForm:

(commit)

OrkivRoot:

<OrkivRoot>:
    AccountDetailsForm:

Now let’s add a show_buddy_list method to the OrkivRoot class. This method will simply clear the contents of the widget and construct a new BuddyList object for now:

(commit)

class OrkivRoot(BoxLayout):
    def show_buddy_list(self):
        self.clear_widgets()
        self.buddy_list = BuddyList()
        self.add_widget(self.buddy_list)

And finally, we can remove the temporary code that renders buddy list jabberids on the ConnectionModal label and replace it with a call to show_buddy_list:

(commit)

def connect_to_jabber(self):
    app = Orkiv.get_running_app()
    try:
        app.connect_to_jabber(self.jabber_id, self.password)
        app.root.show_buddy_list()
        self.dismiss()

Note that we also explicitly dismiss the ConnectionModal. The new BuddyList widget, which is currently blank, will be displayed as soon as we have a valid connection.

Let’s add some styling to that BuddyList class in our orkiv.kv file. We’ll add a ListView which provides a scrollable list of widgets. Of course, we’re not actually putting anything into the ListView, so if we run it now, it won’t show anything… but it’s there!

(commit)

<BuddyList>:
    list_view: list_view
    ListView:
        id: list_view

Notice that I gave the ListView an id property and connected it to a list_view property on the root widget. If you remember part 3 at all, you’re probably expecting to add an ObjectProperty to the BuddyList class next. You’re right!

(commit)

class BuddyList(BoxLayout):
    list_view = ObjectProperty()

    def __init__(self):
        super(BuddyList, self).__init__()
        self.app = Orkiv.get_running_app()
        self.list_view.adapter.data = sorted(self.app.xmpp.client_roster.keys())

Since you guessed that ObjectProperty was coming, I added an initializer as well. I have to keep you interested, after all! The initializer first sets up the superclass, then sets the list_view‘s data to the buddy list keys we’ve been using all along. There’s a couple complexities in this line of code, though. First note the call to sorted, which accepts a python list (in this case containing the ids in the buddy list) and returns a copy of the list in alphabetical order. Second, we are setting the list_view.adapter.data property. Underneath the hood, ListView has constructed a SimpleListAdapter for us. This object has a data property that contains a list of strings. As soon as that list is updated, the adapter and ListView work together to update the display.

The reason that the data is stored in an adapter is to separate the data representation from the controller that renders that data. Thus, different types of adapters can be used to represent different types of data. It’s possible to construct your own adapter as long as you implement the relevant methods, but this is typically unnecessary because the adapters that come with Kivy are quite versatile.

Try running this code. Upon successful login, the modal dialog disappears and the buddy list pops up in sorted order. Resize the window to be smaller than the list and you can even see that the ListView takes care of scrolling.

The SimpleListAdapter is not sufficient for our needs. We need to be able to display additional information such as the user’s status and availability, and we want to lay it out in a pretty way. We’ll also eventually want to allow selecting of a Buddy in the list so we can chat with them. SimpleListAdapter supports none of this. However, Kivy’s ListAdapter does. So let’s edit the orkiv.kv to use a ListAdapter for the BuddyList class:

(commit)

# at top of file:
#:import la kivy.adapters.listadapter  # at top of file
#:import lbl kivy.uix.label

<BuddyList>:
    list_view: list_view
    ListView:
        id: list_view
        adapter: la.ListAdapter(data=[], cls=lbl.Label)

First, we import the listadapter module using the name la. This has a similar effect to a from kivy.adapters import listadapter as la line in a standard Python file. This #:import syntax can be used to import any python module that you might need inside the kv language file. The syntax is always #:import <alias> <module_path>. However, you can only import modules, you can’t import specific classes inside modules. This is why I used a rather uninformative variable name (la). When we construct the ListAdapter at the end of the snippet, the shortened form happens to be more readable, since we are immediately following it by a class name.

We also import the label class as lbl. While it seems odd to have to import Label when we have seen Label used in other parts of the KVLanguage file, we have to remember that sometimes KV Language allows us to switch to normal Python. That’s what’s happening here; the code after adapter: is normal python code that doesn’t know about the magic stuff Kivy has imported. So we have to import it explicitly.

This is probably not the best way to do this; we’re probably going to have to replace this view with a custom ListView subclass later, but it suits our needs for the time being.

That code is simply constructing a new ListAdapter object. It assigns empty data (that will be replaced in BuddyList.__init__) and tells the ListAdapter that the cls to render the data should be a Label. So when we run this code, we see a scrollable list of Labelcode> objects each with their text attribute automatically set to one of the jabber ids we set up in the BuddyList initializer.

However, a Label still isn’t the right widget for rendering a buddy list item. We need to show more information in there, like whether they are available or away and what their status message is. A label won’t cover this, at least not cleanly, so let’s start writing a custom widget instead.

First, add a BuddyListItem class to the __main__.py:

(commit)

from kivy.properties import StringProperty  # At top of file

class BuddyListItem(BoxLayout):
    text = StringProperty()

By default, ListAdapter passes a text property into the widget that is used as its cls. So we hook up such a property which will be rendered by the label in the KV language file. This is a StringProperty, which behaves like any other property but is restricted to character content. Now we can use this class in the KV Language file:

(commit)

# at top of file:
#:import ok __main__

<BuddyListItem>:
    size_hint_y: None
    height: "100dp"
    Label:
        text: root.text

<BuddyList>:
    list_view: list_view
    ListView:
        id: list_view
        adapter: la.ListAdapter(data=[], cls=ok.BuddyListItem)

Don’t forget to make the adapter cls point at the new BuddyListItem class. You can also remove the Label import at the top.

We set a height property on the BuddyListItem mostly just to demonstrate that the custom class is being used when we run the app. Notice also how the label is referencing the root.text property we set up in the python file.

This property is being set, seemingly by magic, to the value from the data list we set on ListAdapter. In fact, it’s not magic; Kivy is just doing a lot of work on our behalf. For each string in that list, it’s constructing a new BuddyListItem object and adding it to that scrollable window. It then sets properties on that object using its default arg_converter. The arg_converter is responsible for taking an item from a data list and converting it to a dictionary of properties to be set on the cls object. The default is to simply take a string and set the text property to that string. Not so magic after all.

And now we’re going to make it less magic. Instead of a text property, let’s make a few properties that better reflect the kind of data we want to display:

(commit)

class BuddyListItem(BoxLayout):
    jabberid = StringProperty()
    full_name = StringProperty()
    status_message = StringProperty()
    online_status = StringProperty()

Don’t try running this without updating the KV file, since it’s trying to reference a text property that is no longer there:

(commit)

<BuddyListItem>:
    size_hint_y: None
    height: "40dp"
    Label:
        text: root.jabberid
    Label:
        text: root.full_name
    Label:
        text: root.status_message
    Label:
        text: root.online_status

Of course, running it in this state will bring us an empty window, since the ListAdapter is still using the default args_converter. Let’s add a new method to the BuddyList class. Make sure you’re sitting down for this one, it’s a bit complicated:

(commit)

def roster_converter(self, index, jabberid):
    result = {
        "jabberid": jabberid,
        "full_name": self.app.xmpp.client_roster[jabberid]['name']
    }

    presence = sorted(
        self.app.xmpp.client_roster.presence(jabberid).values(),
        key=lambda p: p.get("priority", 100), reverse=True)

    if presence:
        result['status_message'] = presence[0].get('status', '')
        show = presence[0].get('show')
        result['online_status'] = show if show else "available"
    else:
        result['status_message'] = ""
        result['online_status'] = "offline"

    return result

Deep breaths, we’ll explore this code one line at a time. First, the new method is named roster_converter and takes two arguments, the index of the item in the list (which we’ll ignore for now), and the jabberid coming in from the data list. These are the normal arguments for any ListAdapter args_converter in Kivy.

Next, we start constructing the result dictionary that we’ll be returning from this method. Its keys are the properties in the BuddyListItem class. It’s values, of course, will be displayed on the window.

Then we ask sleekxmpp to give us presence information about the user in question. This is not a trivial piece of code. I don’t want to spend a lot of time on it, since it involves ugly Jabber and sleekxmpp details, and our focus is on Kivy. In fact, you can skip the next paragraph if you’re not interested.

First, sleekxmpp’s client_roster.presence method returns a dictionary mapping connected resources to information about that resource. For simplicity, we’re ignoring resources here, but the basic idea is that you can be connected from two locations, say your cell phone and your laptop, and you’ll have two sets of presence information. Since we don’t care about resources, we ignore the keys in the dictionary and ask for just the values(). However, we still need to pick the “most important” resource as the one that we want to get presence information from. We do this by wrapping the list in the sorted function. Each resource has an integer priority, and the highest priority is the resource we want. So we tell the sorted function to sort by priority, passing it a key argument that gets the priority using a lambda function. The function defaults to 100 if the resource doesn’t specify a priority; thus we prefer an empty priority over any other value. The reversed keyword puts the highest priority at the front of the list.

Seriously, I hope you skipped that, because it’s a lot of information that is not too pertinent to this tutorial, even if the code it describes is. It took me a lot of trial and error to come up with this code, so just be glad you don’t have to!

So, assuming a list of resources is returned for the given jabber id, we pick the front one off the list, which is the one deemed to have highest priority. We then set the status_message directly from that item, defaulting to an empty string if they user hasn’t set one. The show key in this dict may return the empty string if the user is online (available) without an explicit status, so we check if the value is set and default to “available” if it is not.

However, there’s a chance that call returns a completely empty list which tells us what?: That the user is not online. So we have that else clause constructing a fake presence dict indicating that the user is offline.

Finally, things become simple again. We return the constructed dictionary that maps property names to values, and let Kivy populate the list for us. All we have to do for that to happen is tell Kivy to use our custom args_converter instead of the default one. Change the adapter property in the KV Language file to:

(commit)

adapter:
    la.ListAdapter(data=[], cls=ok.BuddyListItem,
    args_converter=root.roster_converter)

Notice that I split the command into two lines (indentation is important, if a bit bizarre.) in order to conform to pep 8‘s suggestion that line length always be less than 80 characters.

Now, finally, if you run this code and login, you should see all the information about your buddies pop up! However, it’s still a wee bit ugly. Let’s find some ways to spice it up.

One simple thing we can do is highlight different rows by giving each one a background color. We can start by adding a background attribute as an ObjectProperty to the BuddyListItem. Then we can set this value inside the roster_converter:

(commit)

if index % 2:
    result['background'] = (0, 0, 0, 1)
else:
    result['background'] = (0.05, 0.05, 0.07, 1)

Remember that index parameter that is passed into all args_converters and we were ignoring. Turns out it comes in handy. We can take the modulo (that’s the remainder after integer devision if you didn’t know) 2 of this value to determine if it’s an even or odd numbered row. If it’s odd (the remainder is not zero), then we set the background to black. Otherwise, we set it to almost black, a very deep navy. The background color is set as a tuple of 4 values, the RGBA value (Red, Green, Blue, Alpha). Each of those takes a float between 0 and 1 representing the percentage of each of those to add to the color.

Now, most Kivy widgets don’t have a background attribute, so we’re going to have to draw a full screen rectangle on the widget’s canvas. This is great news for you, because we get to cover the canvas or graphics instructions, something this tutorial has neglected so far. The canvas is a drawing surface that accepts arbitrary drawing commands. You can think of it as lower level than widget, although in reality, widgets are also drawn on the canvas, and every widget has a canvas. Here’s how we set up a rectangle on the BuddyListItem canvas:

(commit)

canvas.before:
    Color:
        rgba: self.background
    Rectangle:
        pos: self.pos
        size: self.size

I put this code above the labels in the BudyListItem. The canvas.before directive says “issue this group of commands before painting the rest of the widget”. We first issue a Color command, setting its rgba attribute to the 4-tuple we defined as the background. Then we issue a Rectangle instruction, using the currently set Color and setting the position and size to be dynamically bound to the size of the window. If you resize the window, the rectangle will be automatically redrawn at the correct size.

Another thing I want to do to improve the interface is render status messages underneath the username. They could go in a smaller font and maybe different color. This can be done entirely in KV Language:

(commit)

<BuddyListItem>:
    size_hint_y: None
    height: "75dp"
    canvas.before:
        Color:
            rgba: self.background
        Rectangle:
            pos: self.pos
            size: self.size
    
    BoxLayout:
        size_hint_x: 3
        orientation: 'vertical'
        Label:
            text: root.jabberid
            font_size: "25dp"
            size_hint_y: 0.7
        Label:
            text: root.status_message
            color: (0.5, 0.5, 0.5, 1.0)
            font_size: "15dp"
            size_hint_y: 0.3
    Label:
        text: root.full_name
    Label:
        text: root.online_status

I changed the height of the rows to be more friendly. The canvas I left alone, but I added a BoxLayout around two of the labels to stack one above the other in a vertical orientation. I made this BoxLayout 3 times as wide as the other boxes in the horizontal layout by giving it a size_hint_x. The two labels also get size hints, font size, and a color.

When we run this it looks… a little better. Actually, it still looks like ass, but this is because of my incompetence as a designer and has nothing to do with Kivy!

Now one more thing I’d like to do is render a status icon instead of the text “available”, “offline”, etc. Before we can do that, we need some icons. I wanted something in the public domain since licensing on this tutorial is a bit hazy. A web search found this library and I found some public domain icons named circle_<color> there. I grabbed four of them using wget:

(commit)

cd orkiv
mkdir icons
wget http://openiconlibrary.sourceforge.net/gallery2/open_icon_library-full/icons/png/48x48/others/circle_green.png -O icons/available.png
wget http://openiconlibrary.sourceforge.net/gallery2/open_icon_library-full/icons/png/48x48/others/circle_grey.png -O icons/offline.png
wget http://openiconlibrary.sourceforge.net/gallery2/open_icon_library-full/icons/png/48x48/others/circle_yellow.png -O icons/xa.png 
wget http://openiconlibrary.sourceforge.net/gallery2/open_icon_library-full/icons/png/48x48/others/circle_red.png -O icons/away.png

Once the files are saved, all we have to do to render them is replace the online_status label with an Image:

(commit)

    Image:
        source: "orkiv/icons/" + root.online_status + ".png"

And that’s it for part 5. I promised we would render a complete buddy list, and that’s what we’ve done! In part 6, we’ll make this list interactive. Until then, happy hacking!

Monetary feedback

If you are enjoying this tutorial and would like to see similar work published in the future, please support me. I plan to one day reduce the working hours at my day job to devote more time 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 share it on your favorite social platforms.

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.