This document is a tutorial on how to use the Kefir utility to create PyGTK programs. It goes through the creation of a simple coding journal through an Object Oriented design using visual proxies. It uses Glade and Kefir to create the GUI and matching Python classes, and then explains how to use them to develop the actual program.
This document is covered by the FDL. Included code is covered by either the GPL or the LGPL where marked, and not legal for redistribution, copyrighted, and for education purposes when unmarked. For further questions about liscensing, please contact the author
In this tutorial, I am going to show you the basics of developing a Kefirized app using Kefir. I will assume that you know how to use Glade at a simple level, and you have read an article or two on GTK's object structure. Having written a GUI application will also help, so that you will have something to contrast the process with, however it should not be necessary. In this first installment, we will focus on hooking together the different pieces of the GUI together. Our goal will be a Code Journal, based on a similar program written by a colleague of mine.
If you would like a simple tutorial on using Glade, please take a look at the original tutorial written by Sandino Flores Moreno which can be found here. This tutorial explains how to use Glade to develop simple apps using tepache, the predecessor to kefir. The principles however remain the same, although we will expand further on them here. Reading it is highly reccomended
Writing PyGTK Applications in a visual way
The original source for the code journal can be found under the following files. The code includes a few bugs, so it should not be used yet for day to day usage. It is merely there so you can see how to write a program using a method other than Kefir.
Since our design will be a bit more object oriented than the original Code Journal, so a short introduction to our design philosophy. Our application is built up of a set of very intelligent Objects with a rather large vocabulary. Some of these objects are going to be composed of other intelligent Objects. Our goal is for the graphical part of the application to be a graphical representation of our Objects running inside our machine. However, we don't know exactly what these objects look like. In fact, we aren't allowed to know what they look like, except for what they tell us, or we would be breaking the Object Oriented model. Rather the objects themselves know what they look like, and we can ask them for their graphical representation.
I'm going to introduce a concept here. Those of you familiar with Java may have seen this explicitly spelled out, however python does the same thing, although in a different sort of way. When presented with an Object we don't recognize, our natural instinct is to convert it to something more familiar. For example, if we have an object modelling the flight of a paper airplane, and we want to print that to console, we call Python's built in function str(). Likewise in java, every method implements Object.toString(). In fact, all str() does is call obj.__repr__. In this way, it's the responsibility of the original coder to explain his new object in terms you can understand.
TODO: Do a seperate write up explaining this concept in much more detail
In our Kefirized system, we have two ways of representing Objects. The already mentioned 'to data type' relationship makes an independant copy of our original object. Although this is useful when printing to the console, or when we need an actual conversion, sometimes, we need to make changes via our new object. I'm going to refer to this relation ship as 'as data type'. The difference is, rather than having a single one time link to the original object, we will instead have a constantly always open link between the two objects. Rather than being two peer objects, however, it will instead be a single master object, and then proxies to the master object. In this tutorial, we will start off by designing our visual proxies first, and then expanding objects based on this.
One last thing before we get started. A short list of responsibilities masters have to proxies:
The catch here, I've said nothing about how this is done at all, because the implementation is left up to you. Hopefully this tutorial will explain how to do this effectively however.
The first thing to do is not actually to open up any programs at all, but rather, to make a list of the objects we need. Our program will let the user manage two types of data. The first is a simple "JournalEntry". Internally, it will have some sort of title, and a space for entering data in. The second will be a "TodoList". It will have a title as well, although it's main space will be taken up with a List object. They will both be refered to as the Generic Object "DataContent", TODO: Fill in whether we make an abstract base class or not.
Another object we're going to use is a "ProjectList". It will contain our list of projects we are journaling, and for each project a TodoList and a JournalEntry per day. So far it's been very simple. The catch is, it lets us search for entries by project, and by date. In fact, it's going to be capable of raising a single everytime we select something new, and give us a reference to some abstract DataContent. When searching by date alone, the resulting DataContent may be an aggregation of material from several projects. We may not have permission to edit it either, but the project list doesn't need to know that. This shall be developed further.
Finally, we want our application to be reusable. We aren't creating a CodeJournalApplication, but an Application that has a CodeJournal. We could also make an application later that has a CodeJournal as well as a Calendar. They might even be aware of each other, but that is not important to us. Once we've created them, and hooked them up, they will run independantly. Again, this will be further elaborated on
So to recap, here's a list of all the objects we will be creating
So enough thinking! Open up Glade, and setup five windows following these examples. The first component we need is a TodoListProxy. So make a window named exactly so. The name you pick here is what we're gonna see in python later. Then name it's immediate child 'head'. These are the only two names you have to follow in the example, the rest you can name anything that makes sense to you. All we need in this window is a Vbox, that has a Label, and a TreeView. Finally set this window as invisible.
Basically, all windows need to have a head object, and later i'll give you an example not to do it as well. You also want every window invisible, because you can always show them later. Our next window is a Journal Entry. Instead of using a TreeView, we want a TextView.
Next up is our project list box. Inside is a VBox. The top half has an HBox with the label and combo box. The bottom half is a TreeView.
we're more than halfway there. Now we need CodeJournal. This is where we do some of our magic. Glade has a widget called custom widget, with which you could use to include someting like MozEmbed and make a custom Mozilla webbrowser. In fact, it's been done in under 100 lines, so if you want to know more about that, read Writing PyGTK Applications in a visual way. In our case, we're gonne be making custom widgets, so there is no need to use someone else's just yet. In glade, we also have to specify the name of the callback we'll use, and this is important later. Best follow the examples below.
Almost Done! Finally, we need something to turn this into an application, and that's CodeJournalApp. We'll be using these proxies to compose this view, but we only need one. The only complicated part is the menubar, and we'll be covering that much later, so leave it as it's default. Just remember to leave this window visible.
Done! Now save your glade file as codejournalproxies.glade. (I hope you've been saving periodically too). The next task is to kefirize our glade file to python. To do this, open up a command prompt, and enter the following in the same folder you glade file is in.
k0905902@NTR3807 /d/eclipse/codejournal/src $ python.exe ../../kefir-unstable/src/kefir.py codejournalproxies.glade using new header using new app u'window CodeJournalProxy' using new class CodeJournalProxy u'window ProjectListProxy' using new class ProjectListProxy u'window JournalEntryProxy' using new class JournalEntryProxy u'window TodoProxy' using new class TodoProxy u'window CodeJournalAppProxy' using new class CodeJournalAppProxy using new run written file codejournalproxies.py
And now all we need to do is insert our callbacks. Open up codejournalproxies.py in your favorite variant of emacs, vi, or eclipse, and find the callbacks we entered in earlier, when me made up the custom widgets. These will be methods that are called when GTK tries to create our windows, and we need them to return a Widget. Your code should look like this.
This goes in CodeJournalAppProxy
#@-- widget CodeJournalAppProxy._render_code_journal_proxy { def _render_code_journal_proxy(self, str1, str2, int1, int2): self.code_journal_proxy = CodeJournalProxy() widget = self.code_journal_proxy.as_widget() #GTK doesn't know what a proxy is, but it does know what a widget is widget.show_all() return widget #@-- widget CodeJournalAppProxy._render_code_journal_proxy }
These go in CodeJournalProxy
#@-- widget CodeJournalProxy._render_project_list_proxy { def _render_project_list_proxy(self, str1, str2, int1, int2): self.project_list_proxy = ProjectListProxy() widget = self.project_list_proxy.as_widget() widget.show_all() return widget #@-- widget CodeJournalProxy._render_project_list_proxy } #@-- widget CodeJournalProxy._render_data_content_proxy { def _render_data_content_proxy(self, str1, str2, int1, int2): self.data_content_proxy = TodoProxy() widget = self.data_content_proxy.as_widget() widget.show_all() return widget #@-- widget CodeJournalProxy._render_data_content_proxy }
Finally, kefir isn't sure which is the main proxy, so it makes a bunch of them in main(). Fix this like so:
#@-- init main { def main(): #@-- init main } #@-- body main { code_journal_app_proxy = CodeJournalAppProxy() code_journal_app_proxy.run() #@-- body main }
You're probably wondering what all those comments are, and why are there so many of them. Kefir uses those comments to track the different parts of the python file. Everytime you rerun kefir, it dissects the file based on those tags and and saves the contents for later. Afterwards, the program might not be in the same order, but none of your custom code will be changed. This allows you to make changes to you glade file, without breaking your program, which we will do later. Bear in mind, these callbacks are far from final as well. We are just using them now as place holders, so you can see how this will all fit together later.
That concludes this first installement, in the next, we're gonna continue working on the GUI, and following that, we're gonna throw some real life objects in the background.