Archive for Cocoa Bindings

ADC Core Data Video Tutorial

See here for details on a new tutorial on Core Data.

In the vein of Ruby on Rails style video presentations, you can download the narrated video (38Mb) and learn very quickly how to put together a Core Data application.

Note: for the bandwidth challenged, it is broken down into smaller pieces on the ADC site.

As someone who is largely self taught on the Cocoa side of things, it was instructive to watch another developer at work with Apple’s tools. Aside from learning about Core Data, I learnt some productivity improvements for using Interface Builder and XCode.

If you are writing Cocoa apps, this is well worth a watch!

Birthday Notes

Birthday Notes is an application with two purposes. On one hand it provides an easy way to keep track of people’s birthdays on Mac OS X. The other role it plays is to learn Cocoa programming using Python, Cocoa and PyObjC.

Why bother with yet another birthday tracking tool? Quite simply, most were too difficult to get the information that I wanted. The main aim of Birthday Notes is to provide a list of upcoming birthdays with relevant information.

For example, I rarely think of people’s birthdays in terms of their birth date (eg 1977-08-04). My brain is wired so that I remember that my birthday is on the 4th of August and that I’m currently 28 and turning 29 on my next birthday. Rather than show the birth date, Birthday Notes shows the birthday and the person’s age they turn on that day.

Birthday Notes features integration to Apple’s existing applications for storing data. This means that iSync support comes for free. The current focus is on a maintenance and reporting tool, so you can edit a person’s birthday from within Birthday Notes, but not create or delete. For additional actions, simply double click on the person’s name - they will be revealed in AddressBook.

I like to know some trivia about people’s birthdays, so Birthday Notes includes a bit of useless astrological information.

Birthday Notes is available for download. Requires OS X 10.4 or greater.

Any thoughts, suggestions, bugs or feedback should be sent to gmwils AT pseudofish.com, or posted in comments on this post.

For those interested in the code, it is available under a GPL license. To compile, Python 2.4, a recent build of PyObjC, and icalendar are needed.

Finally, my thanks to those who have been helping with alpha testing. This release marks the start of a public beta.

Adding Filtering to list by subclassing an NSArrayController

In the previous series of articles, we’ve covered getting started, basic Address Book usage, and making an app bundle. This time, we’ll look at adding in some basic filtering to the list of dates.

To add filtering, we need to extend the NSArrayController class. The first step is to create a new python class, ArrayControllerWithSearch. This will extend the standard array controller with a search method.


from PyObjCTools import NibClassBuilder

class ArrayControllerWithSearch(NibClassBuilder.AutoBaseClass):
    searchString = None

    def search_(self, sender):
        self.searchString = sender.stringValue()

This doesn’t do anything particularly interesting, except to store the search string for the other methods to use. The AutoBaseClass means that the class we are going to derive from will be specified in Interface Builder and then allocated dynamically.

The next step is to over-ride the method that actually lists the objects in the array, arrangeObjects_. (recalling that the underscore is required for when Objective C wants a ‘:’).


def arrangeObjects_(self, objects):
    # retrieve a reference to the same method in the base class
    supermethod = super(ArrayControllerWithSearch, 
        self).arrangeObjects_

    if self.searchString is None or self.searchString == '':
        return supermethod(objects)

    return supermethod(list(filterObjects(objects, self.searchString)))

The supermethod variable is actually a function pointer to the arrangeObjects_ method on the super class. This gets used under either case, so it is easier to have the code to access this in one place.

filterObjects will be defined next. The search is going to be rather simplistic in that it will check if the search string is contained within the name of the person.


def filterObjects(objs, searchString):
    lowerSearch = searchString.lower()
    for obj in objs:
        if(lowerSearch in obj['name'].lower()):
            yield obj
            continue

Also, add an import statement for the class in the main application (main.py):


from PyObjCTools import AppHelper

import DateListDelegate
import ArrayControllerWithSearch

AppHelper.runEventLoop(argv=[])

In Interface Builder, subclass NSArrayController in the Classes tab, and call the sub class the same name as your custom controller (ArrayControllerWithSearch). In the Attributes section of the Inspector (with the Classes tab still selected), add a new Action, called search:

From the Instances tab, select the NSArrayController for the list of names. In the Inspector, select Custom Class, and then select the new controller class:

ArrayControllerWithSearch subclass

Add a search control to the form. This will call the search_ method added to the controller.

Search control

Now, Ctrl-drag from the search control to the array controller in the Instances tab. This creates a connection. Select the search: action and then press the Connect button.

Congratulations! You now have a searchable list of birthdays for all your friends and family, integrated into your Address Book.

The complete example is available here and includes a working application bundle in dist/.

Making a NSDocument application with PyObjC

To break things up a bit, I’ve been working on a different application, with the intent to return to the birthday example from Parts 1, 2 and 3 at a later date.

I’d been playing with a regular expression builder from the .NET world and decided that a similar tool for Mac OS X seemed like a good idea. To be useful, I wanted each regular expression that I was working on to be treated as a separate document, and to be able to store not only the regular expression, but also any test data I’d been using.

Future features will include some form of highlighting, replace expressions, more ways to run the expression against some data, the ability to generate code snippets (python, perl, etc) and some graphical way of building expressions.

From within XCode, create a new project and select PyObjC Document-based Application. Give the project a name (ie. RegExStudio), pick somewhere to store it and you should end up with the a working project that implements a very basic text editor. Build and run the application to try it out.

The project wizard creates a bunch of very useful things, however to make things easier, we are going to use Cocoa Bindings instead of the connection based version implemented by the wizard. The name of your project is used in things like the document’s type, so where I’m using RegExStudio, use whatever your application is called.

To start, open the RegExStudioDocument.py file and remove all of the class implementation, except for the windowNibName method. This defines which NIB file contains the interface for our document.

You should now have something like:


class RegExStudioDocument(NibClassBuilder.AutoBaseClass):

    def windowNibName(self):
        return u'RegExStudioDocument'

The aim is to start with three keys for the controller object to manage: regularExpression, testString and search. The first two will be standard strings, with the third a dynamically calculated value based on the first two. This will allow our result to update as the expression or the test string change.

Next up, set some default values. This isn’t completely required, but makes it easier to use. Once I figure out preferences, then these values will be configurable, but one step at a time.


    def init(self):
        self = super(RegExStudioDocument, self).init()
        self.regularExpression = 'w+'
        self.testString = 
            'The quick brown fox jumped over the lazy dog.'
        return self

This is all that is required to get the first two keys working, the third requires another two pieces of code. Firstly, we use python’s regular expression capabilities to do the search. It is also worth ensuring that an invalid regular expression results in a valid value, thus the try block:


    def search(self):
        try:
            result = re.search(self.regularExpression,
                 self.testString).group()
        except:
            return ''

        return result

Secondly, we need to let the object know that this is a calculated value, and that changes to either of the first two, will update search key. This is done by setting which keys trigger a change notification, with the following method:


RegExStudioDocument.
    setKeys_triggerChangeNotificationsForDependentKey_(
    [u'regularExpression', u'testString'],
    u'search'
)

Note that this is a method call on the document itself, not a definition of a member, so it should be at the same indent level as the class. Put it at the end of the file for now.

From within XCode, open the RegExStudioDocument.nib file. This will launch Interface Builder with the user interface for the document.

Resize the default NSTextView so that it takes up a third of the screen and give it a text label above of Regular Expression. Add two more NSTextView controls, with text labels of Input Text and Result Text.

Uncheck Multiple Fonts Allowed on all three NSTextView controls in the Inspector window. On the Result Text uncheck the Editable option, as attempting to write to the search key will cause errors.

To use Cocoa bindings, there needs to be a controller object of some sort. As we are dealing with a single document in each window, drag a NSObjectController object to the Instances pane. Ctrl-drag from the controller to the File's Owner object and connect to the content outlet. This sets the controller up to manage the RegExStudioDocument.

With the controller connected add the three keys, regularExpression, testString and search to the controller’s attributes.

Bind each of the controls to the appropriate key using the value property. Double click on the NSTextView controls to access the right binding set. A single click will only select the bounding object, which is a NSScrollView.

Finally, ensure that the Continuously Updates Values option is set for the first two controls. This option will mean that the regular expression is evaluated on every key press, giving you a live view of the result.

Set Continuously Updates Values

Now if you run the application, you can enter in a regular expression and have it applied to the input text!


% open build/RegExStudio.app

Before I go, two more pieces of code … add the following methods to the RegExStudioDocument class:


    def dataRepresentationOfType_(self, aType):
        # Create a dictionary to put the variable in
        dict = NSMutableDictionary.alloc().init()
        dict['regex'] = self.regularExpression
        dict['text'] = self.testString
        return NSKeyedArchiver.archivedDataWithRootObject_(dict)

    def loadDataRepresentation_ofType_(self, data, aType):
        # Extract to a NSMutableDictionary
        dict = NSKeyedUnarchiver.unarchiveObjectWithData_(data)
        self.regularExpression = dict['regex']
        self.testString = dict['text']
        return True

The above two methods over-ride existing NSDocument methods and will encode and decode the regular expression and test data to and from a NSKeyedArchiver. Behind the scenes, the wizard and the NSDocument infrastructure have implemented the Save and Open functionality.

Congratulations, you now have a NSDocument based application which can save files. The complete example is available here.

Aside: In the process of building RegExStudio, I discovered that a similar tool already existed and also used PyObjC! If you actually need a tool for regular expressions, RegExplor has a better feature set … for now.

Binding to a NSTextView

If you have done much Cocoa programming previously, you may find this amusingly obvious. However if you are learning from the start, this may trip you up. So I’ll share my lesson that I learnt today.

I’m working on a very simple single object binding exercise. I wanted to have properties of the object displayed in different NSTextView controls. However, when I clicked on one, the only binding options I had were hidden and tooltip.

Not particularly useful, so I swapped to NSTextField controls only to find that they didn’t support multiple lines. After spending some time RTFM‘ing, I found that there is a bit more to the NSTextView than was initially apparent to me. It actually has a whole wealth of things to bind to. The caveat is that in Interface Builder, you need to double click on the control in order to select the text area within.

The NSTextView reference also mentions that different bindings are available depending on what options you enable on the control. By selecting single font (which makes sense for my app), there appears a binding control for value which takes a simple string, just like NSTextField.

For future reference, if the class name in the heading of the Inspector window isn’t the one you thought it should be or the control’s class ends in View, then try double clicking the control. This works for columns in NSTableView, the text in a NSTextView and the columns in a NSOutlineView.