Archive for June, 2005

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.

How to find Cocoa Developers?

MacSlash has opened up the forums for discussion on Finding Cocoa Developers. This topic should provide interesting reading for developers looking for where to go job hunting and also for employers looking for developers.

I can agree with the comment:

Cocoa has a TOUGH learning curve

Even replacing Objective-C with Python (thanks to PyObjC), Cocoa is still a large set of APIs and not something one can master overnight.

More Unicode for Python articles

O’Reilly sites have added two new articles on Unicode and Python.

The first one is on xml.com and is part 2 from the Unicode Secrets that was up last month.

The second article is on onlamp.com and is an extract from the Python Cookbook.

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.

Starting out with web services for Python

One of the things that I’ve been looking at is web services. So far, I’ve played around with .NET, Java (Axis) and PHP for writing and consuming them, however I wanted to be able to at least access them from Python.

A quick web search didn’t unearth a site which covered everything I was interested in, so a bit more digging was required. The following resources I’ve found useful so far:

  • SOAP Web Services from Dive into Python. Covers pretty much everything, including WSDL.
  • Python SOAP Libraries, Part 5 from IBM’s developerWorks. Good information on when things go bad. Also has some good references at the end.
  • Python Web Services. A container site that includes both the SOAPpy and the ZSI web services code bases for Python.
  • README for SOAPpy. Includes installation instructions and code examples

For now, I’m going to try out SOAPpy as it seems simpler. However, ZSI appears to have better support for complex types in WSDL. Complex types being the feature that tends to limit most WSDL tools.

As an example, here is a simple server:


import SOAPpy
import os

def systemInfo():
    return os.uname()

server = SOAPpy.SOAPServer(('localhost', 8080))
server.registerFunction(systemInfo)
server.serve_forever()

The client is as easy as:


>>> import SOAPpy
>>> proxy = SOAPpy.SOAPProxy('http://localhost:8080/')
>>> proxy.systemInfo()[0]
'Darwin'
>>>

os.uname() returns an array of data, which is encoded into XML by the server, and then decoded back into a Python array in the client. On the wire, the actual data looks like:


<SOAP-ENV:Body>
<systemInfoResponse SOAP-ENC:root="1">
<Result SOAP-ENC:arrayType="xsd:string[5]"
	xsi:type="SOAP-ENC:Array">
<item>Darwin</item>
<item>mayumi.local</item>
<item>8.1.0</item>
<item>Darwin Kernel Version 8.1.0:
Tue May 10 18:16:08 PDT 2005;
root:xnu-792.1.5.obj~4/RELEASE_PPC</item>
<item>Power Macintosh</item>
</Result>
</systemInfoResponse>
</SOAP-ENV:Body>

Dive into Python has further information on how to obtain debugging information, as well as an example of performing a Google search.

Incorrect directions in design

For the amusement of others I’m going to share with you what will hopefully provide an instructive story in my misadventures in software design. We are in the midst of a crusade at my day job for updating the installers for our products. Given that we make Windows software, installers are key, and a good one adds to the user experience.

After witnessing the beauty of how installers can magically be made as part of the build process, after building PyObjC, I set out on a mission. My goal: create a wonderful installer for my sample application.

(as an aside, if you are building Windows you need to check out WiX. Microsoft made this, use this and then open sourced this. Very cool.)

Many hours later, after reading all of the documentation for bdist_mpkg, reading through a whole bunch of source code, using grep over a whole bunch more and reading yet more stuff, I finally gave up. In caving, I packaged together the closest that I’d managed to get into an email.

Some one much wiser than me provided with very enlightening insight:

Don’t create installers for applications.

Simple really.

The blinding obviousness of this truth took some time to sink in. When on a mission, it takes a while for me to re-group. I consulted my memory. Yep. Every product on my mac that needed an installer I disliked. The applications I like, simply provided me with the application bundle and I could put it anywhere I wanted!

I was so convinced that I needed an installer, that I’d neglected to take a step back and look at my assumptions. On the bright side, I now know much more about the distribution of python modules than ever before.

Lesson for others, for Mac OS X, don’t create installers for applications, but if you need to install a python module, the hard work has been done for you

Mac OS X G5 performance compared to Linux on x86

There has been much coverage of the performance comparison posted by Johan De Gelas at Anandtech.

The aim of the article is supposedly to compare the G5 processor to a few x86 processors (Xeon & Opteron). However, the chosen operating systems seem a bit strange. On the x86 hardware, linux was chosen, but on powerpc hardware, Mac OS X was chosen.

Personally, if I was aiming to benchmark the chip, I would have used linux on powerpc to remove as many variables as possible from the comparison.

Given the amount of effort spent tuning apache and mysql for performance on linux, it doesn’t surprise me that it doesn’t perform so well on Mac OS X. I still remember how painful it was to get them to compile on the early betas, let alone run quickly.

The theories expressed as to why the performance is so bad on Mac OS X are quite off the mark.

Fortunate result of all of this is that Apple may spend some time investigating how the performance of apache and mysql could be improved. Although speculation has them heading off for x86 as the answer. Monday will be interesting.