Archive for May, 2005

Part 3 - Using py2app to Apple-fy the app

py2app provides a few more things than just packaging up the code into a bundle. To make the app more of a first class citizen in the Apple world, I’m going to add nice things like an icon, an About box and a proper name.

Apple provides quite a nice way of specifying all of this information in a .plist file and py2app is quite happy to make use of it. As the example, I’m going to take the list of birthdays made in parts 1 and 2 and improve it without changing the code, well not too much.

First step is to let py2app know where to find our .plist file. This involves a change to setup.py by creating an options entry to give the name of the .plist file:


setup(
    data_files=['English.lproj'],
    app=['main.py'],
    options=dict(py2app=dict(plist='Info.plist'))
)

Everything else is accomplished by adding in entries to the Info.plist file. According to the Mac OS X ‘Runtime Configuration: Guidelines For Configuring Applications’ guide, there are a bunch of keys that need to be set as a minimum. I’m going to go through each key and what they actually do for your app.

To give my application a name, other than main.app, it is a matter of setting the CFBundleName property in the Info.plist file. There is a bit of boilerplate required which does nice XML things about specifying schemas and encodings. I’ve also added the package type in for now, with APPL representing an Application.


<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE plist PUBLIC '-//Apple Computer//DTD PLIST 1.0//EN'
'http://www.apple.com/DTDs/PropertyList-1.0.dtd'>
<plist version='1.0'>
<dict>
    <!-- Used by py2app to generate application bundle -->
    <key>CFBundleName</key>
    <string>Date List</string>

    <!-- Package type is an Application -->
    <key>CFBundlePackageType</key>
    <string>APPL</string>
</dict>
</plist>

At this point, building the application will result in the name of the application bundle being set correctly. Additionally, the application name will appear in the menu bar and in the About box. If you do a Get Info on the application bundle, it also shows up as an Application.

There are some other fields that Mac OS X will set for you in the About box if you fill in the appropriate properties. For example:


    <!-- Version number - appears in About box -->
    <key>CFBundleShortVersionString</key>
    <string>1.2</string>

    <!-- Build number - appears in About box -->
    <key>CFBundleVersion</key>
    <string>12</string>

    <!-- Copyright notice - apears in About box -->
    <key>NSHumanReadableCopyright</key>
    <string>© Geoff Wilson 2005</string>

The About box is looking pretty good at this point, but for one key thing. That default icon simply has to go. Fortunately, it is as simple as specifying the appropriate property, and putting the icon file in the English.lproj directory.


    <key>CFBundleIconFile</key>
    <string>DateList</string>

All well and good, but I had absolutely no idea how to create an icon for Mac OS X. I’d gone through enough pain trying to get the favicon working for this site. Mac OS X icons looked much tricker. Fortunately, Mac OS X comes with its own tool for building icons, called Icon Composer. It lives under /Developer/Applications/Utilities

Icon development is something that you should take some time to consider for a real application. There is a good article at the O’Reilly site, and also some information from Apple. The short version is to drag an image into Icon Composer and save the results.

The icon file will be saved with an .icns extension, but there doesn’t seem to be a need to worry about that in the property file. In the interests of avoiding graphic design for a minute, I used the banner image from this site. This has the downside of making a solid square, but it means I can avoid Photoshop for a bit longer.

After re-building the application (to copy the icon file to the bundle), my icon turns up in all sorts of useful places like the About box, the Dock and the Finder.

About box

There are a few more properties that are required to be set in the property file. The CFBundleIdentifier property is used to uniquely identify your application. The remaining properties, I’m not too sure as to where they are used, but they seem obvious enough to set values for. English is my development environment and the executable and bundle display name are set to the bundle name so that everything matches.


    <!-- Globally unique identifier -->
    <key>CFBundleIdentifier</key>
    <string>com.pseudofish.DateList</string>

    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>

    <key>CFBundleExecutable</key>
    <string>Date List</string>

    <key>CFBundleDisplayName</key>
    <string>Date List</string>

The complete Info.plist is included with the updated example. Now replete with a proper name, dock icon and an About box.

How to install Oracle on Mac OS X 10.4 (Tiger)

Install instructions are now available for getting Oracle to work on Mac OS X 10.4. I’m happily able to type sqlplus in Terminal and not have link errors. It is even nicer to have my database restored so that it has something to connect to!

The steps are as follows.

  • Type the command
  • 
     sudo gcc_select 3.3
    
  • install oracle with oracle installer
  • The success cases have been with an Entreprise installation. Your milage may vary with the other options, but they are typically a subset of Enterprise so shouldn’t pose a problem.

    At the end of the installation, the database creation don’t work (it’s normal). You could disable database creation in the install wizard if this bothers you.

  • Type the commands
  • 
     cd $ORACLE_HOME/lib
    mv libnnz10.dylib libnnz10.dylib.ori
    relink all
    mv libnnz10.dylib.ori libnnz10.dylib
    

Now you can run dbca and create a database

Note: you must ensure that your ORACLE_HOME environment variable is set before calling the relink all script.

Part 2 - Using Address Book and making an app

Having successfully created a simple Cocoa application using Python in Part 1, the next step is to integrate data from Apple’s Address Book (AB).

The PyObjC example of Address Book usage is a good place to start, and the Apple documentation includes any other information needed. I started with the example, played around a bit and then read the documentation mainly for the constants to get the fields I wanted.

The first step is to create a separate file to include the Address Book interaction. I put this into DateList.py. For a simple start, I’ve made the interaction a function that returns the data to the Delegate class. This is called dateFields(), and it creates a connection to the Address Book and loads the fields for it.


import AddressBook

def dateFields():
    book = AddressBook.ABAddressBook.sharedAddressBook()
    return bookFields(book)

bookFields generates the actual list using a list comprehension. validPerson filters the list to only include records which have the birthday field set.


def validPerson(person):
    return person.valueForProperty_(
        AddressBook.kABBirthdayProperty) != None

def bookFields(book):
    return [ personToFields(p) 
        for p in book.people() if validPerson(p) ]

I’m still in awe over how nicely the PyObjC does the translation behind the scenes. The native python list constructs loop effortlessly across the Objective C objects.

The next part in the story is to actually create the tuple with the person’s name and birthday. If you recall, this should look something like:


{'date': 1977-08-04 12:00:00 GMT, 'name': u'Geoff Wilson'}

I’ve hidden this away in the personToFields function, with two functions to get some details, as follows:


def personToFields(person):
    return {
        'date': getBirthday(person),
        'name': getPersonName(person)
    }

def getPersonName(person):
   return person.valueForProperty_(
        AddressBook.kABFirstNameProperty
        ) + ' ' 
        + person.valueForProperty_(
        AddressBook.kABLastNameProperty)

def getBirthday(person):
    return person.valueForProperty_(
        AddressBook.kABBirthdayProperty)

At this stage you should have enough code to be able to query your Address Book. Python allows you to add the equivalent of a main function to a source file. Similar to the Java usage, I’ve used this to write quick test code to my module. Better would be to write unit test cases, but this isn’t an example of TDD.

Add the following to your code:


if __name__ == '__main__':
    print dateFields()

And then run the result:


% python DateList.py
[{'date': 2004-04-04 12:00:00 Australia/Melbourne,
'name': u'Test User'}]
%

Note: if you don’t have any records in Address Book, you won’t see any results.

The change to integrate this into the user interface is to update the delegate to request the data from the dateFields() function. You also need to add the appropriate import call:


from PyObjCTools import NibClassBuilder
from DateList import dateFields

NibClassBuilder.extractClasses('MainMenu')

class DateListDelegate(NibClassBuilder.AutoBaseClass):
    def items(self):
        return dateFields()

The magical bit is that there is no need to re-compile and no need to change the user interface. You should now see something like:

Birthday list

The source code for the sample application from this tutorial is available here.

PyObjC released for Tiger (and other stuff)

PyObjC has been officially released for Tiger. This is a big upgrade, and includes wrappers for some of the new Tiger technologies, better py2app support and a heap of bug fixes.

So if you weren’t happy following the dev release using subversion, you now have no excuses. Oh yeah, and it will still work on 10.3, with some testing done for 10.2 (if you are still using 10.2 and have issues, please use the mailing list to let the developers know of any issues).

I also found a nice article if you want to start right from the beginning of almost no development knowledge at all for creating Python based Objective C applications. Assumes no knowledge of XCode or Interface Builder (IB) or python for that matter.

For something more interesting, xml.com have a very good article, Unicode Secrets by Uche Ogbuji, which explains how to use unicode properly in Python. Essential reading if you have even vague thoughts of ever using XML in an application.

Clay Shirky on Ontologies

Clay Shirky has a great article up about the history of classifications of things (ontologies).

With movements afoot on the great Semantic Web, which seem to be way too bogged down in theory without significant practical applications, he asks the question of “Where to now?”

His theory is that the concept of tagging things (much like a blog post or a del.icio.us link) will take us to the next level of knowledge integration. Search having been the light to come out of the categorisation of old.

As an aside, it is interesting how the categorisation of things follows the Long Tail. And it is the ability of tagging to support the long tail that will help it survive.

Mac ~= PC?

Over on Apple matters there is a nice long essay on how a mac and a pc are similar, but that a mac is still better.

I think that the reason as to why an Apple solution is better than a PC based one is in the little things that add to meaning. Kathy paints the picture over on Creating Passionate Users

Having spent months traveling with my Samsung laptop with XP pro and similar hardware spec to my PowerBook, I swear I am never jumping on a plane without my PowerBook again. It may involve having to take two computers.

Working for a Windows based software company, I spend most of my day using various flavours of Microsoft product, and for the 95% case, it is all there. And on features the Microsoft product will often beat the Apple product.

So what’s the difference?

When asked why user switching was done using over the top rotating cubes, Apple responded with (roughly) “Because we could”. In reality, by adding the extra touches, the extra niceties, they are adding meaning to their products. This is reflected in the loyalty of their users.

I write this to you, from the keyboard of my PowerBook :)

Part 1 - Creating a basic CocoaBindings app with PyObjC

Python provides a clean, easy to learn, easy to develop in programming language. Cocoa provides a rich, easy to program framework for building applications on Mac OS X. Given that development should be easy, add PyObjC into the mix and the richness of Cocoa is available naturally from within Python. Now to see how easy it is to write a very simple application.

My first steps with attempting to create a Cocoa application with Python involved working through the tutorial on the PyObjC site. This is a very good place to start if you have an understanding of Cocoa. Some understanding of Python will also be helpful.

Note: If you are running on Tiger, you’ll need the latest version.

The most important thing to learn is how to build one of the sample applications:


python setup.py py2app -A

This will create an application bundle to run in the dist directory, using symbolic links to the code. As your source code is linked rather than copied, any changes you make are reflected in the bundle so you don’t need to re-deploy. Just re-run the application and you will be using the new code.

It is worth spending some time playing with some of the samples. The PyObjC team have done a very good job of putting together a wide range of examples. See /Developer/Python/PyObjC/Examples on your system. Try changing the files around a bit, I find it a useful way to learn when I break things by trying to add new stuff.

The first step in making your own application is to create a build script. This contains the information that py2app will use to generate your Cocoa application. A basic version looks like:


from distutils.core import setup
import py2app

setup(
    app=['main.py'],
    data_files=['English.lproj']
)

The app field is the name of the python file that will be called first. In some of the examples this is called __main__.py and in others it is the name of the application. I don’t like underscores so much, so I’m going with main.py.

The data_files field contains a list of directories in which data files are stored. For the moment, the only data file that you need to consider is the NIB files. These specify the interface for your application and are created by Interface Builder. If you wanted to load images and various other data, your entry might look like:


data_files=['English.lproj', 'data', 'images']

The next step is to create the main file, in this case, main.py:


from PyObjCTools import AppHelper

import DateListDelegate

AppHelper.runEventLoop(argv=[])

The first line imports the AppHelper which is then called to run the main event loop for the application. This does a range of interesting Objective C / Cocoa interactions behind the scene, but thanks to PyObjC, we don’t need to worry about that.

The second line imports the python file that we will now create, DateListDelegate.py.


from PyObjCTools import NibClassBuilder

NibClassBuilder.extractClasses('MainMenu')

class DateListDelegate(NibClassBuilder.AutoBaseClass):
    def items(self):
        return []

There are two important things that happen in this script. Firstly, our user interface is loaded using the NibClassBuilder. Secondly, we create a class to use in our interface which is going to provide us with a list of data. For the moment, we simply return the empty list [].

I’m not going to explain in detail how to use Interface Builder. What I am going to do is to work through what is required to make our simple application fly.

From Interface Builder, create a new Cocoa Application and store it as MainMenu in a sub-folder called English.lproj. You may need to create this folder if you haven’t previously.

Next, add an NSTableView to your Window. This control is available in the Cocoa-Data palette. The palette(s) are the bunch of controls that should turn up in the top-right of your screen.

Cocoa Data palette

If you select Test Interface from the File menu in Interface Builder, you should see your window with a completely empty box. Not particularly compelling yet, but one step at a time.

You can now run your application from the command line. Save the Nib and build a linked version. It is easier to use linked for now as we will be changing the python files a bit and it saves re-deploying each time you run the application.


% python setup.py py2app -A
% open dist/main.app

We now need to let Interface Builder know how to talk to the Python class we created earlier in DateListDelegate.py. In the MainMenu window, jump to the Classes tab and scroll left lots until you see the NSObject class. With that selected, from the Classes menu chose Subclass NSObject, and name your new class: DateListDelegate.

It is important that this class name exactly matches the class name that you create in Python, as some Cocoa magic happens behind the scenes to glue this all together.

With your new class highlighted, choose the Instantiate DateListDelegate option from the Classes menu. You should now have a funky blue cube in the Instances tab of the MainMenu window.

We need to have our class managed for us, so we’ll connect it to the application. To do this hold down Control then click and drag from the File’s Owner instance to the DateListDelegate instance. You should see the Outlets tab of the Connections view of the Inspector. From the Inspector, select the Connect button. This will connect our delegate class with the delegate property of the main application.

In order to join together the control on the Window with our Delegate class, we need a Controller object. This is where CocoaBindings really help us out, as you just need to drag a NSArrayController from the Cocoa-Controllers palette to the Instances tab of the MainMenu window.

Building blocks

Now we get up to the fun part, gluing it all together!

Firstly, we need some test data to play with. In a future article, I’m going to look at populating this from an external source, but for now we are just looking at the user interface, so we can fake it.

Open up the DateListDelegate.py file and update the items method to look something like:


def items(self):
    return [
        {'date': 1980, 'name': 'John Doe'},
        {'date': 1970, 'name': 'Amanda Smith'},
        {'date': 1960, 'name': 'Bill Branson'}
    ]

The data representation is how the Key-Value pairs, used by CocoaBindings, are constructed in Python. This is something that will be covered in more detail when we look at generating the structure from real data. If you want more information, see the Key-Value Coding section of the Apple developer site, or look through the examples in PyObjC.

The keys in our example are date and name, so we’d better let our Controller know that these are the fields it is going to be looking after.

In the Attributes section of the Inspector (Tools->Show Inspector) with the NSArrayController selected, there is an Add button. Add two keys, and rename them to the names that we have assigned in the data we are providing, date and name.

Not much use, unless our controller knows how to access our data. This is achieved from the Bindings view of the NSArrayController in the Inspector.

Drop down the contentArray property, and select “File’s Owner (NSApplication)” as the Bind to: value.

Then enter the following into the Model Key Path:


delegate.items

You should now see the following in the Inspector:

Binding the delegate

If you try running the application now, you’ll notice we are not quite there yet. Our controller knows about the data, our table knows about the array that populates it, but the columns don’t know what to show.

Double click on the title area of the first column in the NSTableView that is on the Window. This selects the actual table column, and also allows you to enter in a heading. Call the first one Name.

Then in the Bindings view in the Inspector, drop down the value property and select name from the Model Key Path: combo box. You should notice that our key names are pre-populated in this combo box, thanks to the binding to the controller.

Repeat the process for the second column, binding that to the date key.

Our application can now be tested, simply by running it from the command line:


% open dist/main.app

Try clicking on a column title and your list should automatically sort, both by name and by date. All without writing any sorting code, or any event code for the click to select columns.

Completed application

To build a distributable version of the application, re-build without the symbolic link option:


% python setup.py py2app
% open dist/main.app

This will create an application bundle that can be deployed to a Mac OS X machine that doesn’t include PyObjC. This means that your user has no way of knowing that you’ve built a Cocoa app without writing a line of Objective C code.

Where to from here? It is worth looking through the examples included with PyObjC as they cover a wide variety of programming topics in Cocoa. In the next article, we’ll look at adding some actual features so it deserves to be called an application.

The source code for the sample application from this tutorial is available here.

Additional articles

Part 2 - Integrating Address Book

Part 3 - Making it look like an application

Bonus link

Birthday Notes - finished, open source app, based on these articles

Learning Python

Obviously attempting to write Cocoa applications in Python would require some knowledge of the language. Fortunately, there is some very good documentation including a very easy to read tutorial.

If you were looking for a way to progress further, you could consider some online challenges. And as with most games, there is an online spoiler, which will help if stuck on the first few, but you won’t learn as much.

There are also some good books available, but I haven’t read any so can’t recommend a good one.

Thanks go to Apple for including python on Mac OS X, which means you can start from the Terminal:


gmwils@mayumi % python
Python 2.3.5 (#1, Mar 20 2005, 20:38:20)
>>> 2**38
274877906944L

My own route to learning python was that I was sick of perl, didn’t want to use Visual Basic and needed to do some COM programming on Win32. Having previously spent way too long coding in Haskell, I’ve jumped into python with somewhat of a content sigh. I’m actually happy coding again :)

Now that I can use Python for Windows programming I’m going to try to dodge Objective C on my way to Cocoa programming.

HFS+ (Case-sensitive, Journaled)

After tracking down why a few apps broke on my new Tiger install, I was starting to ponder the wisdom of formating my main drive as case sensitive. Most things seem to work, but there are a fair few apps that are breaking.

It then occurred to me that as a developer writing software for Mac OS X I owed it to myself to be running on a case sensitive drive for testing purposes. The pain I’m going through with other applications I will never go through with my own apps.

For the few apps that are sufficiently broken to not be fixable by a bit of file re-naming, it is a simple matter of isolating them in their own case insensitive disk image.

Of course I should be able to have a separate partition that I can make case insensitive, but with only 40Gb available I tend to run out of space a lot and am not prepared to have to deal with multiple partitions too.

WSDL - where to now?

WSDL is useful in the enterprise space where toolsets do most of the heavy lifting, but it really is too much hard work to write WSDL from scratch. Tim Bray muses about how WSDL could be improved, likening it to SGML for complexity and asking what the XML simplification would be.

Update: Dion Hinchcliffe has provided a good summary of the various alternatives that are in the wings at the moment.

There is a lot happening in this space, even if it is getting confusing. I’ve just been to a seminar where “AJAX” was quoted as the new web service. Amusing given how it is just the result of gluing together existing technologies in an interesting way.