Archive for Python

Saving a file using NSSavePanel

Saving a file was something I’d been avoiding for a while, as I hadn’t been able to find the right control in Interface Builder to make it happen. This had led me to believe it was going to be difficult and was delayed for a long time.

Today, I bit the bullet and spent some time to figure this out. My suspicion was that there existed a control that would do the work for me, as file handling is quite consistent from a UI perspective in Mac OS X. Reading through Cocoa Programming didn’t provide any enlightenment, so I went back to the XCode documentation.

By this stage of my Cocoa learning, I really should have known better. Apple’s documentation has solved too many problems for me to be consulting it as a last resort. Unfortunately, I suspect it is a lesson I will have to keep learning.

Apple provides this information about the NSSavePanel class. Finding this class was my ‘aha!’ moment for the day. No wonder I couldn’t find it in Interface Builder, it isn’t an IB control in the normal sense.

In Python, the simple version looks something like:


from AppKit import *

def saveFile_(self):
    sp = NSSavePanel.savePanel()
    sp.setRequiredFileType_('ics')

    if sp.runModal() < = 0:
        return

    print 'Filename: ' + sp.filename()

This will throw a standard file save modal dialog box and ask for a filename to save. There are a bunch more options on the NSSavePanel class, which I may explore later. The interesting part will be making the save panel appear in a sheet.

Something to explore later.

Re-opening the main NSWindow

I was faced with what seemed a very simple problem, when I closed the window on my application, I didn’t have a way to open it again. For a document based application, it isn’t too much of an issue, the framework allows you to open or create a new document.

Mac OS X is a bit divided at the moment on this functionality. There are some single window apps that behave this way, and others that close themselves when you close the main window. System Preferences is an example of this.

For my application, I wanted it to have a single main window, and I wanted the user to be able to click on the dock icon and have it re-open if it wasn’t already there. Same as Mail, Address Book, iTunes and others.

The trick to making this happen is the applicationShouldHandleReopen:hasVisibleWindows: method of your application delegate. This method is called with the NSApplication and a flag to indicate whether or not any windows are visible for the application.


def applicationShouldHandleReopen_hasVisibleWindows_(self, theApp, hasVisibleWindows):
    if hasVisibleWindows:
        return True

    for win in theApp.windows():
        # Autosave Name set in .nib
        if win.frameAutosaveName() == 'mainWindow':
            win.makeKeyAndOrderFront_(NSNormalWindowLevel)
            return False

    return True

To be honest, my first version was a bit simpler again, the for loop looked remarkably like:


    win = theApp.windows()[0]

Too many years programming and I couldn’t leave the [0] alone. Given that there could be more than one window in my application, I needed a way to identify the window that I needed to open. Cocoa provides such a feature, the AutoSave name for a window. This property can be set in Interface Builder.

Simply give your main window a name, I chose ‘mainWindow’, and search for a window with that name.

Once you have a handle on the NSWindow, you need to call makeKeyAndOrderFront:. This method makes the window the Key window, which gives it keyboard focus and makes it visible. The method requires a window level and given that the main window isn’t doing anything strange, I used NSNormalWindowLevel.

The test for visible windows is a bit of a shortcut. If there is already a visible window, I don’t want any extra ones showing up, so I return True and let Cocoa deal with the Dock click (or Application open) as it normally would. If I did open a window, then I return False, as there is nothing further for Cocoa to do.

In the end, a simple solution to a simple problem.

Crossroads on Languages

Recently, I’ve been distracted by reading Paul Graham’s essays as a result of a Tim Bray posting.

My interests have been serious piqued as a result of one of his themes of using Lisp to write web applications. As a higher order language, the theory is that programmers will be more productive and that given the types of programmers who learn Lisp, you can attract better programmers.

It is a very interesting argument. Something that I’ve been giving lots of thought. My interests at the moment are in learning to write native Mac OS X applications. Python fell in my lap as an option mostly due to my background in Miranda and then the more popular (and powerful, useful, free) Haskell.

Part of my motivation behind learning Cocoa programming has been to write a native application that talks to a web based back end. For some tasks, I’m still not convinced that a web browser is the Right Thing, regardless of how much AJAX code you put in place. This is why the Cocoa apps I’ve been writing have typically avoided building their own data layer. One is built on regular expressions and the other on Address Book and iCal.

The bit I’ve been avoiding is writing the back end. Past experiences on this have been in Perl, which is useful, but I just don’t like. I’d rather not have to put $ signs everywhere.

Python seems to have the clean aspect and enough libraries for doing things so I’d been assuming that I’d use Python for the back end too. However, most of my thinking in Python is really derived from my Haskell days, so why not write the back end in Haskell?

I also may have been distracted somewhat by a few other developments such as Ruby on Rails and Django, as even with a thick client in Cocoa talking to a back end, in this day and age, an application really should have a web based version too.

Investigation has also gone into using XUL as it is both web based, cross platform and somewhat richer than AJAX.

All in all, it is a happy time to be in where there is such a wide range of equally valid choices. It now comes down to a question of tradeoffs.

iCal interactions

Apple was nice enough to provide a feature rich interface for the AddressBook, and so I started my investigations of iCal with high hopes. Alas, these were to be dashed, and in rather short order.

XCode documentation wasn’t enlightening, Apple’s web site was notably quiet … something was amiss.

This article by Rod Schmidt over at O’Reilly started to shine some light on matters. To quote some key pieces of the puzzle:

Apple doesn’t provide any APIs to read iCal’s data, but you can do it yourself.

And:

All the data for iCal is stored in .ics files in ~/Library/Calendars. There is one .ics file for each calendar you have created.

Ah ha! A starting point. Now to be honest I don’t have much in my iCal calendars, as I tend to use Exchange for calendaring, so I put a few things in and then went hunting. To my confusion, no such folder. It had to be somewhere.

According to a range of posts, Apple was definitely storing iCal information in a standard format, and with Spotlight now requiring everything to be files, it had to be somewhere.


% cd ~/Library
% find . -name '*.ics'
./Application Support/iCal/Sources/711885EF-B88B-44AE-B63E-A1409474922D.calendar/corestorage.ics
./Application Support/iCal/Sources/C28120BF-AE28-49AE-8869-E6616428A12A.calendar/corestorage.ics
./Caches/com.apple.iCal/inbox.calendar/corestorage.ics

This didn’t look as promising, but at least the .ics files have been located. Ignoring the cache directory, the other directory names didn’t look particularly nice, but included in each of them is a trusty Info.plist file that includes some useful properties.

  • Key - this appears to be the name of the directory and a unique key identifying the calendar.
  • Title - Only appears for normal calendars (see below).
  • Type - Two types that I’ve seen so far are com.apple.ical.sources.naivereadwrite (normal calendars) and com.apple.ical.sources.birthdays (birthday calendar).

At this point, I think it is safe to assume that the underlying structures changed somewhat in Tiger (or earlier) from the simplistic store mentioned in the O’Reilly article. Nothing too tricky though, so it should be possible to check for either type of directory store and locate the appropriate files.

If anyone has further information on iCal file storage, either in terms of links or corrections to anything I’ve mentioned, please post to the comments.

The next step would be to be able to manipulate these files. If I can avoid it, I’d rather not write a RFC2445 parser. Fortunately, several people have beaten me to it.

iCal.py provides a simple interface into the calendar file, with a simple class that wraps the file. The directory and file loading needs a bit of messing around with for Tiger.

I was looking for a bit more and found it in iCalendar by Max M. The library is LGPL’d which doesn’t cause me issues, but may have implications if you are wanting it for commercial software. Installation was typical Python style, pain free.

After installation and changing to one of the directories which contain my .ics files, the following allowed me to read the calendar (.ics) file:


>>> from icalendar import *
>>> cal = Calendar.from_string(open('corestorage.ics','rb').read())
>>> cal
VCALENDAR({'CALSCALE': 'GREGORIAN', 'VERSION': '2.0', 'PRODID': '-//Apple Computer\, Inc//iCal 2.0//EN'})
>>> for component in cal.walk():
...     component.name
...
'VCALENDAR'
'VEVENT'

The API provides a range of useful documentation and examples, with unit tests for all the features. These in themselves also provide useful code snippets for deriving usage. The API also supports creating and modifying .ics files.

My aim in all this is to be able to update the underlying data for iCal. Brief tests show that changing the .ics files while iCal is running doesn’t quite lead to user interface updates. On the bright side, the Index files seem to be re-generated on closing and opening iCal.

Further investigation of this should hopefully lead to some way of coaxing iCal into playing nicely. At the moment I suspect it may involve a bit of Applescript to drive the application into a refresh if required.

(Update: refer to comments for the insight I was missing. On Tiger, Sync Services does exactly what I’m wanting in terms of keeping a calendar in sync with my app. For earlier versions, I should be able to live with exporting a calendar file.)

PyObjC article available over at ADC

Apple has recently posted an introduction to PyObjC over at ADC. The article is courtesy of the Red Shed and even includes movies of the Interface Builder bits.

This is a sensational result for the PyObjC team for several reasons. First up, there are likely to be more developers writing PyObjC applications, which will result in an even larger community. Secondly, and perhaps more importantly, the article provides an official looking stamp for the community

My congratulations to all involved, both in terms of making PyObjC a reality and in terms of producing a Ruby on Rails style piece of marketing!