Experiments with Python, PyObjC, and Cocoa

As a consultant, I am working on a piece of software for an engineering firm. The engineers would like an easy way to write plugins for the application. I thought to myself, “Myself, perhaps the plugins could be written using Python.” So, I downloaded the latest version of PyObjC and installed it. This posting is to share the three examples that I wrote in my exploration.

Disclaimer: I do not claim to be an expert on Python or PyObjC, but I thought the resulting code was interesting, so I thought I would share. I would be delighted to get suggestions on how to improve these examples.

A Python-Cocoa Application

The first challenge, then, was to write a Cocoa application using Python. I like Objective-C, why would I want to write an app in Python? If there were a cool Python library I wanted to use, I might use Python. So, I looked for such a library. In particular, I stumbled upon the iCalendar package which enables Python programs to read iCal files.

This actually related to something that I wanted to do. See, I’ve discovered the newest technology is 3” x 5” notecards. I carry a small stack in my pocket instead of anything electronic. The problem is that my wife and I keep (and share) our calendars in iCal. How can I get a whole bunch of events from iCal onto a 3” x 5” notecard? My first app Hipster Events does exactly that. Written entirely in Python, it uses the iCalendar package to parse all my iCal files and locate the events that are happening this week. They appear in a table view so that the user can decide which should be included.

You can create a binary so end users don’t have to install PyObjC or the iCalendar package to run HipsterEvents. How did I create this binary? I ran the following command:

python setup.py py2app

Truthfully, setup.py and py2app are quite a mystery to me, but they do seem to work.

Here is the Source. You will need PyObjC and the iCalendar packages mentioned above (follow the links to download). I’ve tried to comment this experiment thoroughly. It has a view class (for printing) and a controller class (for the user interface), both are written in Python. (I should mention that it does not currently deal with repeating events correctly. An exercise for the reader.)

An App That Can Be Scripted in Python

Now, let’s say that I have a regular Objective-C application, and I want to make it so that the user can extend it by writing Python scripts. So, I created a view to display graph objects (with nodes and edges). All these objects were written in Objective-C. Then, I added a text view, where the user could type in Python scripts that create graphs.

Here is the Source.. You will need to download and install PyObjC to build it.

Python Bundles

But, what my client really wants is to create plugins (AKA bundles) with nib files and python scripts that can be loaded into the application. So, I did that.

The stuff in the box for getting the parameters is a nib file in the plugin. There is also a python script in the bundle. The only tricky bits were letting the Python script know about the Objective-C classes. In particular, if the class is in the nib file, there is a handy method for pulling the information about the attributes of the class out of the nib file. If you want the NibClassBuilder to parse through Foo.nib to find the classes defined within, here is the line of python:

NibClassBuilder.extractClasses("Foo")

If there is an Objective-C class in the runtime, and you need a corresponding Python class, I did this:

Here is the Source. You will have to build both the bundle and the app:

python setup.py py2app
xcodebuild

Conclusion

I’ve worked with a couple of bridges to Objective-C. Before working at NeXT, I help create a bridge between Objective-C and Eiffel. When the Java bridge was created, I ported 20 example programs from Objective-C to Java. The Python bridge (PyObjC) is still a bit mysterious to me, but I can see that they have done a great job leveraging the dynamic nature of both languages to make it easy to work with. I am going to continue my experiments, and I’ll let you know how it works out.