This article continues a series on creating a Kivy application. At the end of Part 2, we had a basic form for entering Jabber login details. In this part, we’ll hook up the login button to actually connect to the jabber server and download a Buddy List.
Make sure you’ve completed the previous two parts, or if you want to start from a clean slate, you can clone the
end_part_two tag in my git repo for this project. Remember to do your own changes on a separate branch to avoid conflicts later.
git clone https://github.com/buchuki/orkiv.git # or 'git pull' if you've cloned before and want to update git checkout end_part_two git checkout -b working_changes source venv/bin/activate
Table Of Contents
Here are links to other parts of this tutorial.
- Part 1: Introduction to Kivy
- Part 2: A basic KV Language interface
- Part 3: Interacting with widgets and the user
- Part 4: Code and Interface Improvements
- Part 5: Rendering a Buddy List
- Part 6: ListView Interaction
- Part 7: Receiving messages and interface fixes
- Part 8: Width based layout
- Part 9: Deploying your Kivy application
Dictionary.com defines an event as “something that happens, esp. something important”. That’s a perfect description of events in Kivy. Kivy is firing events all the time, but we only have to pay attention to those that we consider important (in the upcoming example, clicking/touching the
Login button is important). Every graphical toolkit I have ever used has some concept of events. The difference between Kivy and those other toolkits is that in Kivy, event dispatch and handling is sane and uncomplicated.
If an event is something that happens, then an event handler is something that reacts when an event occurs. Our event handler will eventually log into the server and display the Buddy List, but lets start by printing a quote from a humorous scene in the film Rush Hour. We do this by adding a method to the
AccountDetailsForm class in
class AccountDetailsForm(AnchorLayout): def login(self): print("Click the goddamn button")
Remember in Part 1 I mentioned that object oriented programming allowed us to add functionality to existing classes? That’s what we are doing here. The
AnchorLayout class has no idea what it might mean to “login” to something, but it makes sense for
AccountDetailsForm to do so. So we add a method to the subclass named
login. A method is just a function that is aware of which object it is attached to (that’s why it has the parameter
self). At this point, we don’t care what object we are attached to, but it will become important soon.
So that method, which we named
login, is an event handler. Actually, technically, it’s just a method that happens to handle an event, and we will hook it up by calling that method from the true event handler. Let’s do that next. We just need to add one property to the login
Button in our
Button: size_hint_y: None height: "40dp" text: "Login" on_press: root.login()
Kivy is really really good about making programmers do the minimal amount of work required. If you add this one line of code and run it, you can click the
Login button repeatedly, and each time, the movie quote will show up on the terminal.
Button widget in Kivy has a
press event. The key to signifying an event handler is to add an
on_<eventname> property. Now this is where the KV Language gets a little creepy. The code that comes after the event handler property name is standard Python. In fact, you could just use
on_press: print("you clicked it") and it would work. Or if you wanted multiple lines of Python, you could put them on the next line, indented, as though the program code was a child widget.
Don’t ever do that. That’s my rule: never have two or more lines of python logic in a KV Language file. When you break this rule, make sure you know for certain that you have a good reason to do it and that you thought that reason all the way through. This will aide you in the future if you ever want to change the layout without trying to sift out the business logic that hasn’t changed. In my mind, the point of KV Language is to define layout and style. You should not mix logic (program code) with the styling. Instead, put the program code on the class in
__main__.py and use one line of logic to call that method.
There’s a small amount of magic going on here. The
root variable in a KV Language file always refers to the object that is at the left-most indent in our current block. In this case, it’s the
<AccountDetailsForm> class, which is the class containing the handy
login method. KV Language has two other “magic” variables that you may find useful:
app can be used anywhere in a KV Language file to refer to the object that inherits from
Application; so the
Orkiv object. Self refers to the current widget. If we called
self.x() in the
on_press handler, it would refer to the Login
Button object itself.
Interacting with widgets
Now, let’s figure out how to get some values out of the textboxes in the form. To this end, we’ll need to refer to objects in the KV Language file from inside the associated class in our
__main__.py. This requires a few sets of changes to both files. Let’s start with some minor additions to the elements of our form in
Label: text: "Server" TextInput: id: server_input Label: text: "Username" TextInput: id: username_input Label: text: "Password" TextInput: password: True id: password_input
We’ve simply added an identifier to each of our
TextInput boxes so that we can refer to them elsewhere in the code. Note that I gave them all consistent names ending in
_input. Consistency is very important in programming. Your memory is not infallible, and it would be silly to overtax it by trying to remember that one box is named
password and the other is named
UserNameTextInput. After years of programming, this statement seems blatantly obvious to me. However, I am frequently frustrated by non-developers who supply (for example) csv files that use spaces and underscores interchangeably or vary the capitalization of identifiers that I need to sanitize before using in program code. Do yourself and everyone else who works with your code a favor and strive for consistency. One really good way to do this in Python code is to strictly follow the Python Style Guidelines even if you disagree with some of the rules. I strongly recommend using a pep-8 highlighter in your editor (I personally use SublimeLinter) to aid you in this pursuit.
Ok, back to the code! The key takeaway is that by giving these widgets an
id, we can reference them in other parts of the KV Language file. Specifically, we can set them as the values of custom properties on the
AccountDetailsForm class rule like so:
<AccountDetailsForm>: anchor_y: "top" server_box: server_input username_box: username_input password_box: password_input BoxLayout:
We have defined three custom Kivy properties (named
password_box) on the
AccountDetailsForm class. Each of these properties is assigned a value of the
id of one of the text boxes.
It is common to name the properties the same as the ids on the boxes they point to (see my diatribe on consistency above!). However, I chose not to do that here for pedagogical reasons; it’s clear from this example that a property name is a completely different thing from an identifier.
This is no different from setting a property that is meaningful to the parent class such as
anchor_y.The difference is that the parent class (
AnchorLayout) doesn’t know about them. Neither does the child class, yet, but we’ll now fix that in
from kivy.properties import ObjectProperty # at top of file class AccountDetailsForm(AnchorLayout): server_box = ObjectProperty() username_box = ObjectProperty() password_box = ObjectProperty()
We added an import statement so we can access Kivy’s
ObjectProperty class. Then we specified three instances of this fancy property on the
AccountDetailsForm. These instances must have the same names as the properties defined in the KV Language file, since they are referring to the same thing. When the class is constructed, these three properties are defined as having values of
None. However, as part of the KV Language parsing process, Kivy applies the styling found in
orkiv.kv and overwrites those None values with the three values we specified in the previous example.
So we have widgets that are named by ids in our KV Language file that are being assigned to properties by the same KV Language file that are defined in the
__main__.py file. Got that? The upshot of this slightly convoluted pipeline is that we can now access those values inside the login method on our class:
def login(self): print(self.server_box.text) print(self.username_box.text) print(self.password_box.text)
If you run the code now, you should see the contents of the login form boxes displayed on the console when you click the Login button.
Choosing client libraries
Let’s take a break from programming and talk about something that has been overlooked in every programming textbook and tutorial I have ever read: How the hell do you figure out how to do what you need to do?
The short answer, as with every question asked in the last decade, is “Search Google!” (or rather, a search engine that doesn’t spy on you, such as Startpage.com or Duck Duck Go). That always brings out the more salient question, “what do I search for?”.
If you’ve recently started learning to program, you might be under the impression that programming is mostly about connecting loops, conditionals and data structures into some meaningful representation of an algorithm or design pattern. That’s certainly a major part of the job, but often, programming involves reusing existing code and connecting it. Software development is often more like building a lego set where you get to use a variety of prefabricated bricks that do almost exactly what you need, and then using contact cement to glue on some custom-built parts that don’t come with the lego set and can’t be found on store shelves.
Anyway, at this point we have a login form that we can enter details in and do something when the button is clicked. But we aren’t doing what we want to do, which is to display a buddy list for the given XMPP account. How can we do that?
Well, one option might be to read through the Jabber Protocol Specification and implement all the Jabber commands one by one. But that sounds like a lot of work. The absolutely most important attribute in a good programmer is laziness. This means spending hours to automate boring things so you never have to do them again. It also means spending extra time now to write your code in such a way that you or a coworker can reuse it in the future. Finally, it means taking advantage of other people’s work so you don’t have to redo it.
Most common tasks have been collected into what are called “libraries”. Kivy itself is a sort of library, a library for creating kick-ass graphical interfaces. Often when faced with a “how do I do this?” question, the next question should be “Are there any existing client libraries that I can use?” Imagine if you had to manually set the value of each pixel on the screen in order to render a button or textbox, and then process individual keypresses directly to find out what was in a text input box!
Before I started writing this tutorial, I wanted to make sure there was a client library I could use to connect to Jabber. I did a web search and found several options, best summarized by this question on Stack Overflow. Stack overflow is a great place to search for answers; if you can’t find what you are looking for, you can even ask the question yourself!
If a web search yields a nice collection of client libraries that might solve your problem, the next question is, “which one do I use?” The answer is to visit the home pages for each of the libraries and ask yourself questions like these:
- Is the license compatible with the license I want to use for my application?
- (If not open source) How much does it cost to license the library?
- (If open source) Does the source code for the library look well maintained and easy to read?
- Does the library appear to be actively developed and has there been a recent release?
- Is there an active user community talking about the library?
- Does the library appear to have useful (readable, up-to-date, complete) documentation?
- What avenues of support do I have if I have trouble?
- (If Python) Does it boast Python 3 support?
After some amount of research, I ended up choosing Sleek XMPP as it seems to have a modern, usable application programmer interface, reasonable source code and documentation, and a permissive license.
At this point, you’ll want to install SleekXMPP and it’s one dependency. Make sure the virtualenv is active and then issue the following command:
pip install sleekxmpp dnspython
Figuring out how to use a client library
The next step is making yourself familiar with the client library so you know what functions and methods to call to create the desired behavior. This depends entirely on the documentation for the library. I personally like to start coding as soon as possible and see what breaks. Then I refer to the documentation and figure out why it broke and evaluate how it needs to change. In the case of SleekXMPP I started exploring the documentation until I could answer the question, “What is the simplest possible command-line application I can use to retrieve a buddy list?”. This is it:
import sleekxmpp, sys xmpp = sleekxmpp.ClientXMPP(sys.argv, sys.argv) xmpp.connect() xmpp.process() xmpp.send_presence() xmpp.get_roster() print(xmpp.client_roster.keys()) xmpp.disconnect()
This script isn’t part of orkiv, it’s just some playing around I did before trying to integrate this collection of method calls into Orkiv. It took me about 20 minutes to come up with these few lines of code, mostly because the SleekXMPP documentation is a little rougher around the edges than I anticipated. I first implemented a couple of the tutorials in the Quickstart and got them working. However, the examples seem to use a verbose inheritance structure that seems quite unnecessary to me. So I tried to extract the guts into the procedural example you see above.
I do my testing inside a running Python shell — just type
python and you can start entering python commands right into the interpreter. I personally use IPython as much as possible, because its shell is so much nicer to use than the standard one. At first, my call to
get_roster() was returning an error; this was because I hadn’t noticed, in dissecting the inheritance structure, that I had to call
connect() first! However, after my trial and error phase were completed, I came up with the above code, which I saved as
sleekxmpp_buddylist.py and tested using
python orkiv/sleekxmpp_buddylist.py email@example.com password. I tested it with a throw-away Google account and my own personal jabber server. Works like a charm!
The script itself first imports a couple modules and creates a
ClientXMPP object, passing in the username (in the form “firstname.lastname@example.org”) and passwords from the command line. Then it calls several methods on this object to do some jabbery stuff. Then I output the value of the roster (“buddy list”), and disconnect.
Printing a buddy list on login button click
We could just copy the code above into the
login() method and modify it to use the jabber id and password we got from the form. However, that would be rather short-sighted. We know we’re going to need to keep this
ClientXMPP object around longer than a single button click, and indeed, longer than the lifetime of the login form. So instead, lets add a method to the
Orkiv application object itself:
from sleekxmpp import ClientXMPP # at the top of the file class Orkiv(App): def connect_to_jabber(self, jabber_id, password): self.xmpp = ClientXMPP(jabber_id, password) self.xmpp.connect() self.xmpp.process() self.xmpp.send_presence() self.xmpp.get_roster()
That’s a pretty simple method that creates an
xmpp attribute on the app class and runs the boilerplate code for connecting to jabber, as we discovered in the script above. Of course, this method won’t actually do anything if we don’t call it from somewhere. The obvious place to do that, of course is when we click the login button. The correct thing to do will be to connect to jabber and then render a buddy list in the window, but we’re running out of space, so let’s just print it to the console:
def login(self): jabber_id = self.username_box.text + "@" + self.server_box.text password = self.password_box.text app = Orkiv.get_running_app() app.connect_to_jabber(jabber_id, password) print(app.xmpp.client_roster.keys()) app.xmpp.disconnect()
Once again, the method is quite simple. We first grab the jabber id and password from the form box, being careful to concatenate the username and server to generate the jabber id. The next line, where we get the running app may require some explanation. We are calling a function on the
Orkiv class itself. Remember, a class describes an object, so we aren’t talking to an instance of that object here, just to the class. When a method is meant to be called on a class instead of an instance of that class, it is called a static method. In this case, the static method happens to return an instance of the class: the currently running app. Normally, there is only ever one running app in a Kivy program. In fact, you’d have to be both foolish and audacious to get more than one.
The other slightly tricky thing about this line is that we have not defined any
get_running_app method on the
Orkiv class. In fact, we only have one method,
connect_to_jabber. But remember that
Orkiv inherits functionality from the
App, and indeed,
App has a get_running_app static method. That’s what we’re calling here.
Once we have access to the active app object, it’s easy to print the roster and disconnect. Obviously, it wouldn’t make sense to disconnect immediately after login in a completed jabber client. However, if you don’t disconnect and test this, the program won’t stop running when you close the window; it leaves the xmpp object hooked up in the background trying to do the kind of stuff that jabber clients do. When I tried it, I had to force kill the process with
It is common, when coding, to do temporary things like this to test the program in its current state. We know we’ll remove that line in part 4, but… well, truth be told, part 4 hasn’t been written yet! In part 4, we’ll deal with some annoyances with this form, like the fact that we can’t tab between input fields, that we have no idea what happens if we provide incorrect login details, and that pesky problem of the jabber staying alive after we closed the window.
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 my 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:
- Purchase my books: Python 3 Object Oriented Programming and Hacking Happy
- Tip me on Gittip
- E-mail dusty at archlinux dot ca to let me know you would fund a book about Kivy via crowd funding
I’d particularly like to advertise Gittip, not for my own financial gain, but for everyone. I think a world where people are able to use gittip for their primary source of income is a world worth striving for. Even if you don’t choose to tip me, consider tipping someone, or if you are a producer of knowledge or art, market your own products and services through the generosity system.
Finally, if you aren’t in the mood or financial position to help fund this work, at least share it on your favorite social platforms!