A program is a process, not a thing. This also applies to life, the universe, and everything.

2004-11-24

Oblique Strategies

Today's Example is a simple, but full application. Our setup file is getting more complicated as we give the app a custom icon and a name which isn't taken from the main python file. We're finally using the menus for more than just default behaviors. We're loading in resources at runtime. We're adding a custom About box. And we're taking advantage of Python standard libraries from within a Cocoa program. One icon file + 224 lines of python, XML, and HTML.

Some years ago Brian Eno and Peter Schmidt created a deck of cards for brainstorming your way through artistic blocks. Each card had one idea, and they were called Oblique Strategies. The Whole Earth Review published a list of some of the strategies, the decks went through several editions, and there were quite a number of programs written to simulate drawing a card from the deck. The Oblique Strategies Web Site has more details. You can buy the deck from Brian Eno's site. And a group called curvedspace created a nice OS X version which you can download for free from their site.

The curvedspace app is so nice, in fact, that we're going to slavishly imitate it. There are only a couple of problems with it. First, you can't add your own quotes and sayings to the mix. Second, it's free, but not open source, so you can't patch it to allow you to add your own quotes. Tonight's example will build a version identical to the curvedspace tool, but which allows you to choose from among several quote files, not just the Oblique Strategies. A future exercise will be to allow the user to customize it with new quotes from within the running application.

Since there's quite a bit more code, and by reader request, this example is contained on a downloadable .dmg file. The file contains both the finished, runnable application, and all the source code. I'll just be describing highlights of what's different from earlier examples. You can download it all here.

What's new? First of all, the setup.py defines a plist resource inline to map some of the application features. This gives the application a name ("Oblique Strategies" rather than picking up "oblique" from the python file, and sets up some info for the About box. The setup call is a little more complex too, including English.lproj (which holds our icon) and Credits.html (which is the content of our About box), and passing in the plist we defined.

In the MainMenu.gsmarkup we have added a bunch of menu items to the File menu, to allow the user to pick a quotation file. What's interesting is that we've implemented 'About Oblique Strategies' and Edit->Copy, but those menu items didn't have to change.

In oblique.py, the main script, we implement a subclass of NSWindow called TexturedWindow. This is to work around a limitation of the current version of Renaissance, which doesn't support so-called Metal windows (because GNUstep doesn't have them). Nicola has fixed this in CVS, so it will be in the next release, but in the meantime it is a simple class (4 lines of code) and we use the instanceOf attribute of <window/> to call our subclass in the MainWindow.gsmarkup.

Our AppDelegate is similar to earlier examples, but has grown a couple of methods. The change() method is called by our one button to select another quotation at random from the selected file (or a random file if you like). The chooseFile_() method checks to see which menu item called it, and based on the menu item, selects the file for future calls to change(). There is one support function, getQuote(filename) which uses Python standard file manipulation and the random module to pick a quote (much less verbose than doing this from Objective-C).

All that's left are the quote files. These are simple text files with short quotations, one to a line. If a quote requires newlines, they can be embedded with '\n'. The included files have quotes from Martin Fowler's book "Refactoring," the book "The Pragmatic Programmer," the Magic 8-Ball, Jenny Holzer's Truisms, and more. Enjoy!

2004-11-11

Pre-built examples

I've been requested to make binary packages of the applications for people who haven't been following along at home. The first application I've made available is the GMarkup Browser, so you'll need some GMarkup files to browse with it, either from the example here, the Renaissance site (the source package has lots of examples in the Examples folder) or by writing your own (the whole point is that that it isn't hard to do). It's available from the Living Code project on SourceForge.

I'll make others available as I get the chance, now that I've figured out the SourceForge release system (sort of) and the steps for making a disk image for distribution. Soon I need to figure the Mac Package Manager and Installer (which py2app supports) so folks who want to install several apps don't end up with multiple copies of Renaissance too. Baby steps for now, there's a lot to learn.

Mac OS 10.3 only. Feedback appreciated. Coming soon: applications that I can post meaningful screenshots of.

2004-11-10

Packaging Frameworks

Packaging Renaissance applications (or any other frameworks) takes a bit more care than just wrapping your Python scripts. Today's exercise helps us get our apps out into the world.

Now that we can build cool OS X applications with Python and Renaissance, it would be cool if we could share them with others, wouldn't it. And that stumped me for a bit. We're using py2app to package our scripts as applications, and it knows how to include a framework, but it takes a bit more than that. Specifically, the wrapper which imports the framework into Python has to be a tiny bit smarter. Let's take a look at the wrapper I provided earlier in this series, to go in Python's site-packages/Renaissance/ folder.

Old __init__.py

import objc, AppKit, Foundation
objc.loadBundle('Renaissance', globals(), bundle_path='/Library/Frameworks/Renaissance.framework')
del objc, AppKit, Foundation


And the new, smarter version, which handles bundling.

__init__.py

import objc, AppKit, Foundation, os
if 'site-packages.zip' in __file__:
base_path = os.path.join(os.path.dirname(os.getcwd()), 'Frameworks')
else:
base_path = '/Library/Frameworks'
bundle_path = os.path.abspath(os.path.join(base_path, 'Renaissance.framework'))
objc.loadBundle('Renaissance', globals(), bundle_path=bundle_path)
del objc, AppKit, Foundation, os, base_path, bundle_path


That takes care of importing the Renaissance framework, now we just need to make sure it gets included in our application bundle. You can do this by passing it on the command line

python setup.py py2app --framework=Renaissance.framework

But I'd prefer to put it into the setup.py so we don't have to remember command-line flags. Py2app is new enough that the documentation is a little rough, but Bob Ippolito (author of py2app) is very responsive on the pythonmac mailing list and he gave the following advice.


If you use a python module that links to Renaissance, it will automatically get included. Otherwise, you have to specify it as a framework.

% python setup.py py2app -f Renaissance.framework

(you can specify a full path to the framework or the dylib inside the framework if you want)

or from setup.py it would look like:

setup(
app = [...],
options = dict(py2app=dict(
frameworks=['Renaissance.framework'],
)),
)

This command is your friend:
% python setup.py --help py2app

Every "long name" in the list corresponds to an option you can pass via the options dict. Hypens are converted to underscores. This same dance works for any distutils command, btw.


That's good to know about distutils, I've had trouble figuring out the mapping between command-line parameters and setup.py configuration. So here's the new version of setup.py for the browser app:

setup.py

'''
Smarter setup.py example, run with:
% python setup.py py2app
'''

from distutils.core import setup
import py2app
setup(
data_files = ['MainMenu.gsmarkup'],
app = ['browser.py',],
options=dict(py2app=dict(frameworks=['Renaissance.framework'],)),
)


There's still a lot more we can do with py2app, like adding custom icons, giving the application a better name, a version, the ability to advertise its ability to open certain types of files, etc. We're just getting to the good stuff.

Housekeeping

Various small improvements. Switched the template so code doesn't run off the edge so easily. Fixed whitespace, which I forgot to do after switching the template (thanks, Xavier, for pointing that out!). All the code for the renaissance examples is available via cvs from the SourceForge Living Code project, in the somewhat predictable cvs module, renaissance_examples. As some of the examples grow, I may only publish the highlights in the blog, and put the remainder in CVS. We'll se how it goes.

Coming attractions. I'm researching how to build the Renaissance projects so they can be distributed (I haven't forgotten you, Jorjun, I'm just still figuring it out myself). I can do it now (thanks, Bob!), but I want something more straightforward to build. Hopefully later tonight.

Now that we've got a brower for Renaissance files (see previous post), I wanted to create a markup file to show off most of the widgets and options, but realized there is no markup for tabbed views, so I'm going to try creating new tags from Python, and show how to do that. When I've got the tags which represent Cocoa widgets that do not yet have Renaissance representations working, then I'll put together the demo gsmarkup file.

Then back to the Small Nerd examples and a couple of other applications (ports of existing tools, nothing terribly original yet).

It's been nice to hear from people who are enjoying this series. If there are specific things you'd like to see, let me know, either in the comments, or at dethe(hat)livingcode.org

2004-11-08

GMarkup Browser

Today's exercise was to port the GMarkup Browser, an Objective-C program which comes with Renaissance, to Python. Using this allows you to see what various gmarkup files will look like when they are loaded, which is very handy as you're developing a program's UI, without having to build a program around them. The source code of Renaissance (available separately from the binary we installed a few entries ago) contains an Examples folder showing all of the tags in use, both as small toy examples and tests, and as full-fledged applications. It's well worth downloading this and poking around in it with the Browser.

There are three files today, the setup.py, a simple MainMenu.gsmarkup, and the browser.py application. Note that loading resources, especially menu resources, into a running program can cause bad side effects. So far I've been able to exit by ctrl-clicking the application icon and selecting quit, even when the menus disappeared, but use caution. Also, buttons wired up to the "terminate:" selector will exit your program if you click on them. With great power comes great responsibility, or something like that. Caveats aside, you can open multiple gsmarkup files at a time, just use the File->Open menu or Cmd-O.

MainMenu.gsmarkup

<?xml version="1.0"?>
<!DOCTYPE gsmarkup>
<gsmarkup>

<objects>
<menu type="main">
<menu title="GSMarkup Browser" type="apple">
<menuItem title="About GSMarkup Browser"
action="orderFrontStandardAboutPanel:"/>
<menuSeparator/>
<menu title="Services" type="services"/>
<menuSeparator/>
<menuItem title="Hide GSMarkup Browser" action="hide:" key="h"/>
<menuItem title="Hide Others" action="hideOtherApplications:"/>
<menuItem title="Show All" action="unhideAllApplications:"/>
<menuSeparator/>
<menuItem title="Quit GSMarkup Browser" action="terminate:" key="q"/>
</menu>
<menu title="File">
<menuItem title="Open" action="open:" key="o"/>
</menu>
<menu title="Window" type="windows">
<menuItem title="Minimize Window" action="performMiniaturize:" key="m"/>
<menuSeparator/>
<menuItem title="Bring All to Front" action="arrangeInFront:"/>
</menu>
</menu>
</objects>

</gsmarkup>


browser.py

'''
Port of GSMarkupBrowser from Objective-C to Python
'''

from Foundation import *
from AppKit import *
from Renaissance import *

class Owner(NSObject):

def takeValue_forKey_(self, obj, key):
#print 'Set value %s for key %s of NSOwner' % (obj, key)
pass

def bundleDidLoadGSMarkup_(self, notification):
if NSUserDefaults.standardUserDefaults().boolForKey_('DisplayAutoLayout'):
topLevelObjects = notification.userInfo().objectForKey_('NSTopLevelObjects')
for obj in topLevelObjects:
if obj.isKindOfClass_(NSWindow) or obj.isKindOfClass_(NSView):
obj.setDisplayAutoLayoutContainers_(True)

def applicationDidFinishLaunching_(self, notification):
self.open_(self)

def open_(self, notification):
filetypes = ['gsmarkup']
panel = NSOpenPanel.openPanel()
result = panel.runModalForDirectory_file_types_(None, None, filetypes)
if result == NSOKButton:
self.pathname = panel.filenames()[0]
#print 'Loading', self.pathname
didLoad = NSBundle.loadGSMarkupFile_externalNameTable_withZone_localizableStringsTable_inBundle_(self.pathname,
{'NSOwner': self}, None, None, None)
if didLoad:
print self.pathname, 'loaded!'
else:
#print 'Could not load', self.pathname
NSBeep()

def main():
defaults = NSUserDefaults.standardUserDefaults()
defaults.registerDefaults_({'DisplayAutoLayout': 'NO'})
app = NSApplication.sharedApplication()
owner = Owner.alloc().init()
app.setDelegate_(owner)
NSBundle.loadGSMarkupNamed_owner_('MainMenu', owner)
NSApp().run()

if __name__ == '__main__': main()


setup.py

'''
Minimal setup.py example, run with:
% python setup.py py2app
'''

from distutils.core import setup
import py2app
setup(
data_files = ['MainMenu.gsmarkup'],
app = ['browser.py'],
)

2004-11-07

Vancouver XML Users Group

I'll be presenting on Renaissance, OS X, and Python at the VanX meeting at 6:30 p.m. on Thursday, December 16th. By then I should have a lot more programs and utilities to show.

2004-11-05

Small Nerd Example Chapter 2

Aaron Hillegass' book, "Cocoa Programmig for Mac OS X" is a great way to learn Cocoa, Objective-C and Interface Builder, and I highly recommend you buy it to understand OS X programming (of course, you'll be buying the new, improved Second Edition, and I'm working off the first edition here, so things may not completely match up). In order to demonstrate how compact, yet readable, Renaissance + Python can be, I'm going to convert the example programs from this book. Aaron's company is called Big Nerd Ranch, and I was trying to find something to show which postings were examples from the book, so readers can follow along, but without implying in any way that Aaron has approved of these conversions in any way, so I'm going to call them the Small Nerd Examples (The small nerd being me).

Note that while you are developing, it is inconvenient to have to build the project every time you touch a file, so you can use python setup.py py2app --alias to build it once, then you can edit your files normally and the changes will be reflected when you run your application. Just remember to re-run this when you add a new file, and to re-run it without the alias flag when you're building any version to be distributed.

Like the previous example, Hello World, this one contains four files. The setup.py file will look radically similar to the Hello World version. The program itself is simple: A window with two buttons, one which seeds the random number generator with the current time, and another which generates and displays a random number between 1 and 100 in a text field.

MainMenu.gsmarkup

<?xml version="1.0"?>
<!DOCTYPE gsmarkup>
<gsmarkup>

<objects>

<menu type="main">
<menu title="ch02" type="apple">
<menuItem title="About ch02" action="orderFrontStandardAboutPanel:"/>
<menuSeparator/>
<menu title="Services" type="services"/>
<menuSeparator/>
<menuItem title="Hide ch02" action="hide:" key="h"/>
<menuItem title="Hide Others" action="hideOtherApplications:"/>
<menuItem title="Show All" action="unhideAllApplications:"/>
<menuSeparator/>
<menuItem title="Quit ch02" action="terminate:" key="q"/>
</menu>
<menu title="Window" type="windows">
<menuItem title="Minimize Window"
action="performMiniaturize:" key="m"/>
<menuSeparator/>
<menuItem title="Bring All to Front"
action="arrangeInFront:"/>
</menu>
</menu>
<menu title="Help" type="help">
<menuItem title="ch02 Help" keys="?"/>
</menu>
</objects>

</gsmarkup>


MainWindow.gsmarkup

<?xml version="1.0"?>
<!DOCTYPE gsmarkup>
<gsmarkup>

<objects>

<window delegate="#NSOwner" resizable="no">
<vbox>
<button target="#NSOwner" action="seed"
title="Seed random number generator with time"/>
<button target="#NSOwner" action="generate"
title="Generate random number"/>
<textField editable="no" id="text" align="center"/>
</vbox>
</window>

</objects>
<connectors>
<outlet source="#NSOwner" target="text" key="outputNum"/>
</connectors>

</gsmarkup>


ch02.py

'''
Hillegass Example, Ch. 02

'''
from Foundation import *
from AppKit import *
from Renaissance import *
import random

class AppDelegate(NSObject):
outputNum = None
def windowWillClose_(self, notification):
NSApp().terminate_(self)
def quit_(self, notification):
NSApp().terminate_(self)
def close_(self, notification):
NSApp().terminate_(self)
def applicationDidFinishLaunching_(self, notification):
NSBundle.loadGSMarkupNamed_owner_('MainWindow', self)
def seed(self):
random.seed()
def generate(self):
self.outputNum.setStringValue_(str(random.randint(1,100)))

def main():
app = NSApplication.sharedApplication()
delegate = AppDelegate.alloc().init()
app.setDelegate_(delegate)
NSBundle.loadGSMarkupNamed_owner_('MainMenu', delegate)
NSApp().run()

if __name__ == '__main__': main()


setup.py

'''
Run with:
% python setup.py py2app
or
% python setup.py py2app --alias # while developing
'''
from distutils.core import setup
import py2app

setup(
data_files = ['MainMenu.gsmarkup', 'MainWindow.gsmarkup'],
app = ['ch02.py'],
)


OK, this time out we've got about 52 lines of XML markup and 44 lines of Python. Of course, the program doesn't really do much more than Hello World, but we're getting somewhere. If anyone has questions about the code, or want more explanatory text around the Renaissance markup, please let me know in the comments or by email.

Renaissance Tags

Nicola Pera has done a great job building Renaissance, and has worked hard to document it well. The manual is clear and easy to follow, but is not complete yet. He invited me to help with the documentation, and I've found the source code easy enough to read that I might do that (Objective-C is actually quite Pythonic), but it's been years since I've hacked LaTeX and I'd rather spend my time posting examples of working code. So for now I'll just stick my notes here about the Renaissance elements which have not yet been documented in the manual.


<browser/>
titled="yes|no" (default: no)
allowsBranchSelection="yes|no" (default: yes)
allowsEmptySelection="yes|no" (default: no)
allowsMultipleSelection="yes|no" (default: no)
takesTitleFromPreviousColumn="yes|no" (default: yes)
separatesColumns="yes|no" (default: yes)
acceptsArrowKeys="yes|no" (default: yes)
hasHorizontalScrollbars="yes|no" (default: no)
doubleAction="[selector]"
minColumnWidth="[number]"
maxVisibleColumns="[number]"
matrixClass="[subclass of NSMatrix]"
cellClass="[subclass of NSCell]"

<colorwell/>
color="[Color]" (See section 2.3.3.6 of the manual for info on Color attributes)

<form/>
titleFont="[Font]" (See section 2.3.3.7 of the manual for info on Font attributes)
titleAlignment="left|right|center"

<:label/>
color="[Color]"
backgroundColor="[Color]"
font="[Font]"
align="left|right|center"

<matrix/> (contains matrixRow elements)
doubleAction="[Selector]"

<matrixRow/> (contains matrixCell elements)

<matrixCell/>
title="[String]"
action="[Selector]"
editable="yes|no"
selectable="yes|no"
tag="(???)" (I'm not sure how tags are used yet)

<outlineView/>
outlineColumn="[Number]"

<popupButton/> (contains popupButtonItems)
title="[String]"
pullsDown="yes|no"
autoenabledItems="yes|no" (default: yes)

<popupButtonItems/>
tag="(???)"
action="[Selector]"
key="(???)"
title="[String]"

<scrollView/>
hasHorizontalScroller="yes|no"
hasVerticalScroller="yes|no"
borderType="none|line|bezel|groove"

<secureTextField/>

<splitView/>
vertical="yes|no"

<tableColumn/>
identifier="(???)"
editable="yes|no"
title="[String]"
minWidth="[Number]"
maxWidth="[Number]"
width="[Number]"
resizable="yes|no"

<tableView/> (contains tableColumns)
dataSource="[Outlet]"
delegate="[Outlet]"
doubleAction="[Selector]"
allowsColumnReordering="yes|no"
allowsColumnResizing="yes|no"
allowsMultipleSelection="yes|no"
allowsEmptySelection="yes|no"
backgroundColor="[Color]"
drawsGrid="yes|no"
gridColor="[Color]"
autosaveName="[String]"

<textField/>
editable="yes|no"
selectable="yes|no"
align="left|right|center"

<textView/> (Note: Always put a textView inside a scrollView!)
editable="yes|no"
selectable="yes|no"
richText="yes|no"
usesFontPanel="yes|no"
allowsUndo="yes|no"
usesRuler="yes|no"
importGraphic="yes|no"


Well, that's what I have so far. I will try to keep this updated as I learn more, or simply point to the manual as it becomes more complete.

2004-11-03

Ouch

Just saw what the combination of my HTML, blogger's Atom feed, and Planet Python's aggregator did to my posts. I'll investigate it, but if anyone has bright ideas for a quick fix, please let me know at dethe.elza (at) livingcode.org.

Hello Renaissance

In my last post I promised a Hello World program for PyObjC + Renaissance. If you haven't got those installed, or aren't sure, please check out the prerequisites.

We'll be creating four files, each of which will be a template for upcoming examples. The menus will be defined in MainMenu.gsmarkup, the application window will be in MainWindow.gsmarkup, the application code will be in hello.py, and the py2app build script will be in setup.py. There is no reason that the menus and window have to be separated this way, but it will serve as an example for later, more complex applications, when you'll want to load in code from multiple

MainMenu.gsmarkup

<?xml version="1.0"?>
<!DOCTYPE gsmarkup>
<gsmarkup>

<objects>
<menu type="main">
<menu title="Hello World" type="apple">
<menuItem title="About Hello World"
action="orderFrontStandardAboutPanel:"/>
<menuSeparator/>
<menu title="Services" type="services"/>
<menuSeparator/>
<menuItem title="Hide Hello World" action="hide:" key="h"/>
<menuItem title="Hide Others" action="hideOtherApplications:"/>
<menuItem title="Show All" action="unhideAllApplications:"/>
<menuSeparator/>
<menuItem title="Quit Hello World" action="terminate:" key="q"/>
</menu>
<menu title="Edit">
<menuItem title="Cut" action="cut:" key="x"/>
<menuItem title="Copy" action="copy:" key="c"/>
<menuItem title="Paste" action="paste:" key="v"/>
<menuItem title="Delete" action="delete:"/>
<menuItem title="Select All" action="selectAll:" key="a"/>
</menu>
<menu title="Window" type="windows">
<menuItem title="Minimize Window" action="performMiniatureize:"
key="m"/>
<menuSeparator/>
<menuItem title="Bring All to Front" action="arrangeInFront:"/>
</menu>
</menu>
</objects>

</gsmarkup>


MainWindow.gsmarkup

<?xml version="1.0"?>
<!DOCTYPE gsmarkup>
<gsmarkup>

<objects>

<window title="Hello World" closable="NO" >
<vbox>
<label>Hello World!</label>
<button title="Click this button to quit" action="terminate:"/>
</vbox>
</window>

</objects>

</gsmarkup>


hello.py

from Foundation import *
from AppKit import *
from Renaissance import *

class MyApplicationDelegate(NSObject):
def cut_(self, sender):
pass
def applicationDidFinishLaunching_(self, notification):
NSBundle.loadGSMarkupNamed_owner_('MainWindow', self)

def main():
app = NSApplication.sharedApplication()
delegate = MyApplicationDelegate.alloc().init()
app.setDelegate_(delegate)
NSBundle.loadGSMarkupNamed_owner_('MainMenu', delegate)
NSApp().run()

if __name__ == '__main__': main()


setup.py

'''
Minimal setup.py example, run with:
% python setup.py py2app
'''

from distutils.core import setup
import py2app
setup(
data_files = ['MainMenu.gsmarkup', 'MainWindow.gsmarkup'],
app = ['hello.py'],
)


Commentary

OK, so 80 lines of code may seem excessive for a Hello World program. We could certainly do it in less, perhaps 20 total lines of Python and Renaissance. But what we have here is a complete working Cocoa application which behaves properly to standard keyboard shortcuts, supports services, etc. And that's not bad for 80 lines of code.

A New Renaissance

Yesterday I laid out the issues I have with using Interface Builder to create Cocoa applications (whether in Objective-C or Python), and my requirements for a replacement. To sum up, here are the requirements again



As I hinted at previously, I think I've found the tool I was looking forin GNUstep Renaissance, and as an added bonus, it can be used to create applications for Linux, unix, and Windows using the GNUstep framework. So although I'm interested mainly in building applications for OS X, there is still a chance for cross-platform compatibility.

So what does Renaissance look like? It's and XML format, similar to HTML and Mozilla XUL (but simpler than XUL). Today I will cover how to install Renaissance and set it up to use from PyObjC.

Prerequisites (this is my setup, others may work, but I haven't tested them).


  1. A Mac

  2. OS X (10.3)

  3. PyObjC (1.1), available from http://pyobjc.sourceforge.net/

  4. py2app (0.1.4), available from http://pythonmac.org/wiki/py2app (we'll use this to build our double-clickable applications)

  5. Renaissance framework (0.8), available from http://www.gnustep.it/Renaissance/Download.html (this is the secret sauce)



Once you have the prerequisites installed, you need to make Renaissance available from Python. In your site-packages directory (on my machine this is /System/Library/Frameworks/Python.framework/Versions/2.3/lib/python2.3/site-packages) add a Renaissance directory containing the following file:

__init__.py

import objc, AppKit, Foundation
objc.loadBundle('Renaissance', globals(),
bundle_path='/Library/Frameworks/Renaissance.framework')
del objc, AppKit, Foundation


Well, that was easy enough. Next up, a Hello World application.


2004-11-02

Losing my Nibs

I've been working with PyGame a lot and it's been hard slogging. PyGame is great for moving images around quickly, but I'm more interested in vector drawing, native UI widgets, and higher-level events than I can get from PyGame.

So lately I've been turning my attention more and more toward PyObjC. I'm writing games for my kids, who are playing them on OS X, and I'm much less willing to make sacrifices to get cross-platform behaviour. And when I only have a few hours a week to write code, I need to focus on high productivity tools. So I'm writing Cocoa code, using Python, and it's great. Bob Ippolito, Bill Bumgarner, Ronald Oussoren, Jack Jansen and the rest of the MacPython developers have produced an amazingly great tool. on top of Apple/Next's highly evolved Cocoa framework. The mapping is so good that standard Cocoa references work for me to bootstrap my way up the Cocoa learning curve, my favorite reference being AppKiDo, which gives a series of views on every class (being able to separate instance methods of a class from all the intance methods it inherits, for example). Great stuff.

My only problem with Cocoa development (besides all the things I still have to learn about it, I'm not yet a proficient Cocoa developer) is the reliance on Interface Builder and NIB files. I think Interface Builder is one of the best visual layout tools I've used, and it's great as far as it goes, but the problem is that you pretty much *have* to use it. I've tried to create applications which built their own interfaces from code, but I was unable to get the menus working because the Cocoa framework expects you to load the menus from a NIB at startup. I'm sure it can be done, but I couldn't figure out how, even with the help of the MacPython mailing list and wiki.

And NIB files, which is how Interface Builder stores the interface, are a problem for me too, because they are serialized objects, not descriptions, so they are binary goop which is inaccessible except to Interface Builder. I can't grep them or do a global search-and-replace if I change a method name. It's hard enough for me to find the right places in IB to hook into my application, but finding all the places later which I need to change when my code changes rapidly becomes nightmarish.

And all this leads to examples like Build a text editor in 15 minutes or An example using NSDocument which consist of a few lines of code (the power of Cocoa) accompanied by tons of screenshots of Interface Builder and paragraphs of instructions for wiring up your application. Even worse, as the second example demonstrates, since the author no longer hosts that article and Google doesn't cache the images, the screenshots no longer exist.

One more gripe. Because IB stores serialized objects, it doesn't pick up changes to your code for those object unless you specifically force it to. So what I want in a UI tool:

* Declarative
* Simple *and* powerful
* Able to replace NIB files and Interface Builder
* Text-based, not binary
* Agile for rapid development, adapts to rapidly changing code
* Able to specify simple apps completely, in code, without resorting to pictures or talking the user through mouse gestures

And the best part is that I think I've found it. More in my next post.

PyGame: http://pygame.org/

PyObjC: http://pyobjc.sourceforge.net/

MacPython Mailing List: http://mail.python.org/mailman/listinfo/pythonmac-sig

MacPython Wiki: http://www.pythonmac.org/wiki/FrontPage

AppKiDo: http://homepage.mac.com/aglee/downloads/

This page is powered by Blogger. Isn't yours?